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

feat!: adds support for ESM and Deno #1708

Merged
merged 24 commits into from
Aug 22, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f49a5eb
feat!: adds support for ESM and Deno
bcoe Aug 6, 2020
38d2c60
chore: put more work into migration to ESM
bcoe Aug 8, 2020
497bb5d
chore: fix up test command
bcoe Aug 8, 2020
5d2dccb
chore: retire require-main-filename
bcoe Aug 9, 2020
9a77160
chore: minify output code
bcoe Aug 9, 2020
d5eab8a
chore: use the latest build of yargs-parser
bcoe Aug 9, 2020
58aac01
chore: walk back decision to minify; pull in cliui
bcoe Aug 9, 2020
9449229
chore: finish isolating APIs that don't work in Deno or browser
bcoe Aug 10, 2020
45c7fe1
chore: continue to abstract Node specific logic
bcoe Aug 10, 2020
6fd8c01
chore: standardize platform shim logic
bcoe Aug 11, 2020
17f4474
chore: reduce package size
bcoe Aug 16, 2020
013b7fc
chore: starting to add some tests for other platforms
bcoe Aug 16, 2020
73a3d72
chore: don't try to compile deno files
bcoe Aug 16, 2020
59e0bd8
chore: pulled in ESMified cliui
bcoe Aug 16, 2020
baf5263
test: continuing to build out tests for ESM
bcoe Aug 16, 2020
49a9e14
chore: test whether browser works
bcoe Aug 17, 2020
c2fda8a
chore: include browser shim
bcoe Aug 17, 2020
2b00525
chore: include new entrypoints in package
bcoe Aug 17, 2020
58846b2
chore: fix import
bcoe Aug 17, 2020
b4e8d2b
chore: slight tweak to example
bcoe Aug 17, 2020
845c68a
chore: implement minimal sprintf for browser
bcoe Aug 19, 2020
803a7d8
feat: expose helper for process args
bcoe Aug 19, 2020
7d80f03
docs: document new functionality
bcoe Aug 22, 2020
be3f192
docs: copy edits
bcoe Aug 22, 2020
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: 0 additions & 14 deletions .editorconfig

This file was deleted.

15 changes: 14 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
with:
node-version: ${{ matrix.node }}
- run: node --version
- run: npm install
- run: npm install --engine-strict
- run: npm test
windows:
runs-on: windows-latest
Expand All @@ -27,6 +27,19 @@ jobs:
node-version: 12
- run: npm install
- run: npm test
esm:
runs-on: ubuntu-latest
strategy:
matrix:
node: [14]
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- run: node --version
- run: npm install --engine-strict
- run: npm run test:esm
coverage:
runs-on: ubuntu-latest
steps:
Expand Down
6 changes: 0 additions & 6 deletions .mocharc.json

This file was deleted.

9 changes: 0 additions & 9 deletions .versionrc

This file was deleted.

7 changes: 3 additions & 4 deletions index.js → index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
// classic singleton yargs API, to use yargs
// without running as a singleton do:
// require('yargs/yargs')(process.argv.slice(2))
const yargs = require('./yargs')
const processArgv = require('./build/lib/process-argv')
const Yargs = require('./build/index.cjs')

Argv(processArgv.getProcessArgvWithoutBin())
Argv(Yargs.processArgv.getProcessArgvWithoutBin())

module.exports = Argv

function Argv (processArgs, cwd) {
const argv = yargs(processArgs, cwd, require)
const argv = Yargs(processArgs, cwd, require)
singletonify(argv)
return argv
}
Expand Down
28 changes: 28 additions & 0 deletions index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict'

// Bootstraps yargs in ESM mode:
import escalade from 'escalade/sync'
import { fileURLToPath } from 'url';
import Parser from 'yargs-parser'
import { dirname, resolve } from 'path'
import { YargsFactory } from './build/lib/yargs-factory.js'

// TODO: stop using createRequire as soon as we've ported: cliui, y18n, etc.
import { createRequire } from 'module';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@QmarkC if once we port y18n and cliui we'll be getting darn close to an ESM version of yargs.

Note that createRequire is Node.js specific, so won't port well to other runtimes.

const require = createRequire(import.meta.url);

const __dirname = dirname(fileURLToPath(import.meta.url));

const Yargs = YargsFactory({
findUp: escalade,
Parser,
require,
requireDirectory: require('require-directory'),
stringWidth: require('string-width'),
y18n: require('y18n')({
directory: resolve(__dirname, '../locales'),
updateFiles: false
})
})

export default Yargs
4 changes: 2 additions & 2 deletions lib/argsert.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { YError } from './yerror'
import { parseCommand, ParsedCommand } from './parse-command'
import { YError } from './yerror.js'
import { parseCommand, ParsedCommand } from './parse-command.js'

const positionName = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']
export function argsert (callerArguments: any[], length?: number): void
Expand Down
50 changes: 50 additions & 0 deletions lib/cjs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict'
// This file is used to bootstrap yargs for its legacy CommonJS interface:

import { argsert } from './argsert.js'
import { isPromise } from './utils/is-promise.js'
import { objFilter } from './utils/obj-filter.js'
import { globalMiddlewareFactory } from './middleware.js'
import { parseCommand } from './parse-command.js'
import * as processArgv from './utils/process-argv.js'
import { YargsFactory, rebase } from './yargs-factory.js'
import { YError } from './yerror.js'

// See https://github.com/yargs/yargs#supported-nodejs-versions for our
// version support policy. The YARGS_MIN_NODE_VERSION is used for testing only.
const minNodeVersion = (process && process.env && process.env.YARGS_MIN_NODE_VERSION)
? Number(process.env.YARGS_MIN_NODE_VERSION) : 10
if (process && process.version) {
const major = Number(process.version.match(/v([^.]+)/)![1])
if (major < minNodeVersion) {
throw Error(`yargs supports a minimum Node.js version of ${minNodeVersion}. Read our version support policy: https://github.com/yargs/yargs#supported-nodejs-versions`)
}
}

// Inject CommonJS dependencies:
const Parser = require('yargs-parser')
const { resolve } = require('path')
const y18n = require('y18n')
const Yargs = YargsFactory({
findUp: require('escalade/sync'),
Parser,
require: (require as any),
requireDirectory: require('require-directory'),
stringWidth: require('string-width'),
y18n: y18n({
directory: resolve(__dirname, '../locales'),
updateFiles: false
})
})

export default Object.assign(Yargs, {
argsert,
globalMiddlewareFactory,
isPromise,
objFilter,
parseCommand,
Parser,
processArgv,
rebase,
YError
})
29 changes: 14 additions & 15 deletions lib/command.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Dictionary, assertNotStrictEqual } from './common-types'
import { isPromise } from './is-promise'
import { applyMiddleware, commandMiddlewareFactory, Middleware } from './middleware'
import { parseCommand, Positional } from './parse-command'
import * as path from 'path'
import { RequireDirectoryOptions } from 'require-directory'
import { UsageInstance } from './usage'
import { inspect } from 'util'
import { ValidationInstance } from './validation'
import { YargsInstance, isYargsInstance, Options, OptionDefinition, Context, Configuration, Arguments, DetailedArguments } from './yargs'
import requireDirectory = require('require-directory')
import whichModule = require('which-module')
import Parser = require('yargs-parser')

import { Dictionary, assertNotStrictEqual, RequireDirectoryOptions, YargsMixin } from './common-types.js'
import { isPromise } from './utils/is-promise.js'
import { applyMiddleware, commandMiddlewareFactory, Middleware } from './middleware.js'
import { parseCommand, Positional } from './parse-command.js'
import { UsageInstance } from './usage.js'
import { ValidationInstance } from './validation.js'
import { YargsInstance, isYargsInstance, Options, OptionDefinition, Context, Configuration, Arguments, DetailedArguments } from './yargs-factory.js'
import whichModule from './utils/which-module.js'

const DEFAULT_MARKER = /(^\*)|(^\$0)/

Expand All @@ -21,7 +19,8 @@ export function command (
yargs: YargsInstance,
usage: UsageInstance,
validation: ValidationInstance,
globalMiddleware: Middleware[] = []
globalMiddleware: Middleware[] = [],
mixin: YargsMixin
) {
const self: CommandInstance = {} as CommandInstance
let handlers: Dictionary<CommandHandler> = {}
Expand Down Expand Up @@ -127,7 +126,7 @@ export function command (
}
return visited
}
requireDirectory({ require: req, filename: callerFile } as NodeModule, dir, opts)
mixin.requireDirectory({ require: req, filename: callerFile }, dir, opts)
}

// lookup module object from require()d command and derive name
Expand Down Expand Up @@ -368,7 +367,7 @@ export function command (
const config: Configuration = Object.assign({}, options.configuration, {
'populate--': true
})
const parsed = Parser.detailed(unparsed, Object.assign({}, options, {
const parsed = mixin.Parser.detailed(unparsed, Object.assign({}, options, {
configuration: config
}))

Expand Down Expand Up @@ -463,7 +462,7 @@ export interface CommandInstance {
context: Context,
req: NodeRequireFunction,
callerFile: string,
opts?: RequireDirectoryOptions<any>
opts?: RequireDirectoryOptions
): void
addHandler (
cmd: string | string[] | CommandHandlerDefinition,
Expand Down
38 changes: 38 additions & 0 deletions lib/common-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,41 @@ export function assertSingleKey (actual: string | string[] | Dictionary): assert
export function objectKeys<T> (object: T) {
return Object.keys(object) as (keyof T)[]
}

export interface RequireDirectoryOptions {
extensions?: ReadonlyArray<string>;
visit?: (commandObject: any, pathToFile: string, filename?: string) => any;
recurse?: boolean;
}

// Dependencies that might vary between CJS, ESM, and Deno are isolated:
export interface YargsMixin {
Parser: ({
camelcase: Function;
detailed: Function;
decamelize: Function;
}),
y18n: Y18N,
findUp: Function,
requireDirectory: Function
stringWidth: Function;
require: RequireType;
}

export interface RequireType {
(path: string): Function;
main: MainType;
}

export interface MainType {
filename: string;
children: MainType[];
}

export interface Y18N {
__(str: string, ...args: string[]): string;
__n(str: string, ...args: (string|number)[]): string;
getLocale(): string;
setLocale(locale: string): void;
updateLocale(obj: {[key: string]: string}): void;
}
17 changes: 9 additions & 8 deletions lib/completion.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { CommandInstance, isCommandBuilderCallback } from './command'
import * as templates from './completion-templates'
import { isPromise } from './is-promise'
import { parseCommand } from './parse-command'
import * as path from 'path'
import { UsageInstance } from './usage'
import { YargsInstance } from './yargs'
import { Arguments, DetailedArguments } from 'yargs-parser/build/lib/yargs-parser-types'
import { assertNotStrictEqual } from './common-types'

import { CommandInstance, isCommandBuilderCallback } from './command.js'
import * as templates from './completion-templates.js'
import { isPromise } from './utils/is-promise.js'
import { parseCommand } from './parse-command.js'
import { UsageInstance } from './usage.js'
import { YargsInstance } from './yargs-factory.js'
import { Arguments, DetailedArguments } from 'yargs-parser/build/lib/yargs-parser-types.js'
import { assertNotStrictEqual } from './common-types.js'

// add bash completions to your
// yargs-powered applications.
Expand Down
6 changes: 3 additions & 3 deletions lib/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { argsert } from './argsert'
import { isPromise } from './is-promise'
import { YargsInstance, Arguments } from './yargs'
import { argsert } from './argsert.js'
import { isPromise } from './utils/is-promise.js'
import { YargsInstance, Arguments } from './yargs-factory.js'

export function globalMiddlewareFactory<T> (globalMiddleware: Middleware[], context: T) {
return function (callback: MiddlewareCallback | MiddlewareCallback[], applyBeforeValidation = false) {
Expand Down
2 changes: 1 addition & 1 deletion lib/parse-command.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NotEmptyArray } from './common-types'
import { NotEmptyArray } from './common-types.js'

export function parseCommand (cmd: string) {
const extraSpacesStrippedCommand = cmd.replace(/\s{2,}/g, ' ')
Expand Down
84 changes: 0 additions & 84 deletions lib/typings/require-directory.d.ts

This file was deleted.