Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use esm build in when using import #392

Merged
merged 13 commits into from May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/workflows/ci.yaml
Expand Up @@ -2,19 +2,25 @@ name: ci

on:
push:
branches:
- main
pull_request:

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [12, 14, 16]
experimental: [false]
include:
- node: 14
lts: true
- node: 16
lts: true
- node: 17
experimental: true
continue-on-error: ${{ matrix.experimental }}
lts: false
- node: 18
lts: false
continue-on-error: ${{ ! matrix.lts }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -9,3 +9,4 @@
node_modules/
lib/
coverage/
tests/smoke/*/yarn.lock
8 changes: 5 additions & 3 deletions examples/calculator.ts
@@ -1,11 +1,13 @@
import { program, command, argument } from '../src'
import { argument, command, program } from '../src/index.js'

type Args = { number: number[] }

const number = argument('number', { type: 'number', variadic: true })

const makeOutput = (op: string, initial = (args: Args) => 0) => (args: Args) =>
args.number.reduce((sum, arg) => eval(`${sum} ${op} ${arg}`), initial(args))
const makeOutput =
(op: string, initial = (args: Args) => 0) =>
(args: Args) =>
args.number.reduce((sum, arg) => eval(`${sum} ${op} ${arg}`), initial(args))

program()
.description('calculator')
Expand Down
2 changes: 1 addition & 1 deletion examples/cat.ts
@@ -1,5 +1,5 @@
import { readFileSync } from 'fs'
import { command, program } from '../src'
import { command, program } from '../src/index.js'

const cat = command('cat')
.description('Concatenate files')
Expand Down
4 changes: 2 additions & 2 deletions examples/dice.ts
@@ -1,4 +1,4 @@
import { program, command } from '../src'
import { command, program } from '../src/index.js'

async function rng(bounds: [number, number]) {
const [min, max] = bounds
Expand All @@ -14,4 +14,4 @@ const dice = program().add(
})
)

dice.repl()
dice.runOrRepl()
26 changes: 9 additions & 17 deletions examples/errors.ts
@@ -1,55 +1,47 @@
import { command, program } from '../src'
import { command, program } from '../src/index.js'

const app = program()

const syncOk = command('sync')
.description('Print sync message')
.action(function () {
console.log('ok/sync')
})
.action(() => 'ok/sync')

const asyncOk = command('async')
.description('Print async message')
.action(async function () {
console.log('ok/async')
})
.action(async () => 'ok/async')

const syncNok = command('sync')
.description('Throw sync error')
.action(function () {
.action(() => {
throw new Error('nok/sync')
})

const asyncNok = command('async')
.description('Throw async error')
.action(async function () {
.action(async () => {
throw new Error('nok/async')
})

const syncValidation = command('sync')
.description('Test validation error with sync handler')
.argument('required')
.action(function () {
console.log('call without arguments')
})
.action(() => 'call without arguments')

const asyncValidation = command('async')
.description('Test validation error with async handler')
.argument('required')
.action(async function () {
console.log('call without arguments')
})
.action(async () => 'call without arguments')

const noHandler = command('no_handler').description(
'Test missing command handler'
)

// Say bye
const success = (resolved: unknown) => console.log('[success]', resolved)
const success = (resolved: unknown) => console.log('resolved:', resolved)

// Print error message only (omit stack trace) and exit with a meaningful status
const fail = (error: any) => {
console.error('[failed]', String(error))
console.error('rejected:', String(error))

if (!app.isRepl()) {
process.exit(42)
Expand Down
2 changes: 1 addition & 1 deletion examples/foo.ts
@@ -1,4 +1,4 @@
import { program, command } from '../src'
import { command, program } from '../src/index.js'

const foo = command('foo')
.description('Outputs "bar".')
Expand Down
2 changes: 1 addition & 1 deletion examples/pizza.ts
@@ -1,4 +1,4 @@
import { program, command } from '../src'
import { command, program } from '../src/index.js'

const cmd = command()
.argument('address', {
Expand Down
2 changes: 1 addition & 1 deletion examples/pretty.ts
@@ -1,4 +1,4 @@
import { command, program } from '../src'
import { command, program } from '../src/index.js'

const app = program()
.description('JSON pretty printer')
Expand Down
2 changes: 1 addition & 1 deletion examples/repl.ts
@@ -1,4 +1,4 @@
import { command, program } from '../src'
import { command, program } from '../src/index.js'

let url: string | null = null

Expand Down
2 changes: 1 addition & 1 deletion examples/simple.ts
@@ -1,4 +1,4 @@
import { program, command } from '../src'
import { command, program } from '../src/index.js'

const echo = command('concat')
.description('Concatenate input')
Expand Down
2 changes: 1 addition & 1 deletion examples/types.ts
@@ -1,4 +1,4 @@
import { command, program } from '../src'
import { command, program } from '../src/index.js'

/**
* Keep in mind that argument/option types are not validated at runtime.
Expand Down
22 changes: 11 additions & 11 deletions package.json
Expand Up @@ -30,7 +30,7 @@
"main": "./lib/cjs/index.cjs",
"exports": {
"types": "./lib/types/index.d.ts",
"import": "./lib/cjs/index.cjs",
"import": "./lib/esm/index.js",
"require": "./lib/cjs/index.cjs"
},
"type": "module",
Expand All @@ -50,33 +50,33 @@
"test:unit": "jest",
"test:types": "tsd",
"test:smoke": "./tests/smoke/run.sh",
"start": "ts-node",
"start": "node --loader ts-node/esm",
"release": "semantic-release"
},
"dependencies": {
"@types/yargs": "17.0.2",
"@types/yargs": "17.0.10",
"enquirer": "^2.3.6",
"string-argv": "^0.3.1",
"typed-emitter": "^2.1.0",
"yargs": "^17.4.0"
"yargs": "^17.5.1"
},
"devDependencies": {
"@semantic-release/changelog": "6.0.1",
"@semantic-release/git": "10.0.1",
"@types/jest": "27.4.1",
"@types/node": "17.0.23",
"@types/jest": "27.5.1",
"@types/node": "17.0.33",
"convert-extension": "0.3.0",
"doctoc": "2.1.0",
"husky": "7.0.4",
"jest": "27.5.1",
"doctoc": "2.2.0",
"husky": "8.0.1",
"jest": "28.1.0",
"leasot": "13.1.0",
"mock-argv": "2.0.8",
"prettier": "2.6.2",
"semantic-release": "19.0.2",
"ts-jest": "27.1.4",
"ts-jest": "28.0.2",
"ts-node": "10.7.0",
"tsd": "0.20.0",
"typescript": "4.6.3"
"typescript": "4.6.4"
},
"tsd": {
"directory": "tests/types"
Expand Down
22 changes: 9 additions & 13 deletions src/command.ts
@@ -1,13 +1,10 @@
import { Arguments as BaseArguments, Argv, CommandModule } from 'yargs'
import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs'
import { Argument, ArgumentOptions } from './argument.js'
import { InferArgType } from './baseArg.js'
import { Option, OptionOptions } from './option.js'
import { prompter } from './prompter.js'

export type Arguments<T = {}> = T &
BaseArguments<T> & {
__promise?: Promise<any>
}
export type YargsArguments<T = {}> = ArgumentsCamelCase<T>

type CommandOptions = {
/**
Expand All @@ -30,7 +27,7 @@ type CommandOptions = {
type CommandRunner = (command: string) => Promise<unknown>

export interface HandlerFn<T> {
(args: Omit<T, '_' | '$0'>, commandRunner: CommandRunner): Promise<any> | any
(args: T, commandRunner: CommandRunner): Promise<any> | any
}

function isArgument(obj: Argument | Option | Command): obj is Argument {
Expand Down Expand Up @@ -213,6 +210,7 @@ export class Command<T = {}> {
aliases: [],
describe: this.options.hidden ? false : this.options.description || '',
builder: this.getBuilder(commandRunner),
// @ts-ignore Our handler returns a different type than void
handler: this.getHandler(commandRunner),
}
return module
Expand Down Expand Up @@ -261,23 +259,21 @@ export class Command<T = {}> {
}

/**
* Wraps the actual command handler to insert prompt and async handler logic.
* Takes command runner.
* Wraps the actual command handler to insert prompt and handler logic.
*/
private getHandler(commandRunner: CommandRunner) {
return async (argv: Arguments<T>) => {
const { _, $0, ...rest } = argv
return async (argv: YargsArguments<T> & { __promise?: Promise<any> }) => {
const prompterInstance = prompter(
[...this.getArguments(), ...this.getOptions()],
rest
argv
)

let promise = prompterInstance.prompt()

promise = promise.then((args) => {
promise = promise.then(({ _, $0, __promise, ...args }) => {
// @todo coerce all types and remove coerce option from baseArg
if (this.handler) {
return this.handler(args, commandRunner)
return this.handler(args as unknown as T, commandRunner)
}

// Display help if this command contains sub-commands
Expand Down
4 changes: 2 additions & 2 deletions src/program.ts
Expand Up @@ -4,7 +4,7 @@ import path from 'path'
import TypedEventEmitter from 'typed-emitter'
import { Argv, ParserConfigurationOptions } from 'yargs'
import createYargs from 'yargs/yargs'
import { Arguments, Command, command } from './command.js'
import { command, Command } from './command.js'
import { history, History } from './history.js'
import { Repl, repl } from './repl.js'
import { isPromise } from './utils.js'
Expand Down Expand Up @@ -215,7 +215,7 @@ export class Program extends (EventEmitter as new () => TypedEventEmitter<Events
return new Promise((resolve, reject) => {
// @ts-ignore Not sure why this is needed?
this.createYargsInstance()
.parse(cmd, {}, (err, argv: Arguments | Promise<Arguments>, output) => {
.parse(cmd, {}, (err, argv, output) => {
// We don't use yargs 17 promise style argv
if (isPromise(argv)) {
throw new Error('argv is of unexpected type')
Expand Down
8 changes: 7 additions & 1 deletion src/prompter.ts
@@ -1,7 +1,13 @@
import { prompt } from 'enquirer'
import Enquirer from 'enquirer'
import { Argument } from './argument.js'
import { Option } from './option.js'

/**
* Workaround for "The requested module 'enquirer' is a CommonJS module, which
* may not support all module.exports as named exports."
*/
const prompt = Enquirer.prompt

type PromptType =
| 'input'
| 'number'
Expand Down
7 changes: 3 additions & 4 deletions src/repl.ts
@@ -1,4 +1,3 @@
import { Prompt } from 'enquirer'
import { CompleterResult } from 'readline'
import nodeRepl, { REPLServer } from 'repl'
import { parseArgsStringToArgv } from 'string-argv'
Expand Down Expand Up @@ -53,9 +52,9 @@ export class Repl {
// Setup history
this.history?.hydrateReplServer(this.server)

// Fixes bug with hidden cursor after enquirer prompt
// @ts-ignore
new Prompt().cursorShow()
// Fixes bug with hidden cursor after enquirer prompt, this is identical to
// the enquirer method Prompt.cursorShow()
process.stdout.write(`\u001b[?25h`)
}

public stop() {
Expand Down