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
chore(ts): move and tsify most of root yargs.js to lib/yargs #1670
Conversation
2346985
to
3169f48
Compare
To keep consistent with other parsing hint functions.
A single argsert signature for multi-signature functions is not enough to cover all cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a wonderful start to me. The behavior of the generics is complex enough that it will be hard for me to fully understand some of the decisions you've made until we add a suite of tests for the expected behavior. I'm fine with this suite of tests being added when we finish the conversion.
Perhaps we can add a test file along the lines of **test/typescript-interface.ts**
that just exercises a wide variety of expectations one might have, using the typed version of yargs?
@QmarkC could I bother you for review on this one? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't able to do a deep dive into this right now but I left a few comments from an initial review.
lib/apply-extends.ts
Outdated
@@ -32,7 +32,7 @@ function mergeDeep (config1: Dictionary, config2: Dictionary) { | |||
return target | |||
} | |||
|
|||
export function applyExtends (config: Dictionary, cwd: string, mergeExtends: boolean) { | |||
export function applyExtends (config: Dictionary, cwd: string, mergeExtends?: boolean) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer using the Configuration
interface over the more generic Dictionary
type.
I think mergeExtends would be cleaner by setting a default value of false
rather than marking it as an optional parameter.
A return type would be nice too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
applyExtends
does not only handle Configuration
objects (yargs-parser and yargs config), but also more generic app config. So using Configuration
would be too restrictive here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think mergeExtends would be cleaner by setting a default value of false rather than marking it as an optional parameter.
A return type would be nice too.
Committed
@@ -1,4 +1,4 @@ | |||
import { Dictionary, assertNotUndefined, assertNotTrue } from './common-types' | |||
import { Dictionary, assertNotStrictEqual } from './common-types' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use import type
? That's a TypeScript 3.8+ feature. The current semver used for typescript is ^3.7.0
, which resolves to 3.9.3
. So it might be a good idea to bump that for clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understood it, the main idea behind import type
is to be able to import type only without importing real js stuff, nor executing any module initialization code (as the import disappears from the resulting js code).
As long as we do not have such an issue, would this really be a clarification?
Also note that:
- regular imports only importing interfaces (as are most of our type-only imports, until we move to classes) are already removed from the resulting js code
- the specific
./common-types
import you point at cannot be a type import, as it also imports a helper function
I think this may however make sense in a future work when we will work on replacing self
stuff by real classes (to import only class types, and not the classes themselves).
lib/typings/yargs-parser.d.ts
Outdated
interface Options { | ||
/** An object representing the set of aliases for a key: `{ alias: { foo: ['f']} }`. */ | ||
alias: { [key: string]: string | string[] }; | ||
/** | ||
* Indicate that keys should be parsed as an array: `{ array: ['foo', 'bar'] }`. | ||
* Indicate that keys should be parsed as an array and coerced to booleans / numbers: | ||
* { array: [ { key: 'foo', boolean: true }, {key: 'bar', number: true} ] }`. | ||
*/ | ||
array: string[] | Array<{ key: string; boolean?: boolean, number?: boolean }>; | ||
/** Arguments should be parsed as booleans: `{ boolean: ['x', 'y'] }`. */ | ||
boolean: string[]; | ||
/** | ||
* Provide a custom synchronous function that returns a coerced value from the argument provided (or throws an error), e.g. | ||
* `{ coerce: { foo: function (arg) { return modifiedArg } } }`. | ||
*/ | ||
coerce: { [key: string]: CoerceCallback }; | ||
/** Indicate a key that represents a path to a configuration file (this file will be loaded and parsed). */ | ||
config: string | string[] | { [key: string]: ConfigCallback | boolean }; | ||
/** Provide configuration options to the yargs-parser. */ | ||
configuration: Partial<Configuration>; | ||
/** Indicate a key that should be used as a counter, e.g., `-vvv = {v: 3}`. */ | ||
count: string[]; | ||
/** Provide default values for keys: `{ default: { x: 33, y: 'hello world!' } }`. */ | ||
default: { [key: string]: any }; | ||
/** Environment variables (`process.env`) with the prefix provided should be parsed. */ | ||
envPrefix?: string; | ||
/** Specify that a key requires n arguments: `{ narg: {x: 2} }`. */ | ||
narg: { [key: string]: number }; | ||
/** `path.normalize()` will be applied to values set to this key. */ | ||
normalize: string[]; | ||
/** Keys should be treated as numbers. */ | ||
number: string[]; | ||
/** Keys should be treated as strings (even if they resemble a number `-x 33`). */ | ||
string: string[]; | ||
} | ||
|
||
interface CoerceCallback { | ||
(arg: any): any | ||
} | ||
|
||
interface ConfigCallback { | ||
(configPath: string): { [key: string]: any } | ||
} | ||
|
||
interface Parser { | ||
(argv: string | string[], opts?: Partial<Options>): Arguments; | ||
detailed(argv: string | string[], opts?: Partial<Options>): DetailedArguments; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- All of the options should be optional.
- Options is missing
configObjects
,__
, andkey
properties. - Missing types on a few of the other options.
- ConfigCallback can also return an Error
- Update Parser interface to remove the Partial wrapper around Options.
- Parser args can at least be
string | (string | number)[]
but we landed on usingstring | any[]
in theyargs-parser
PR to ensure there would not be a breaking change.
type ArrayOption = string | { key: string; string?: boolean, boolean?: boolean, number?: boolean, integer?: boolean };
type CoerceCallback = (arg: any) => any;
type ConfigCallback = (configPath: string) => { [key: string]: any } | Error;
interface Options {
/** An object representing the set of aliases for a key: `{ alias: { foo: ['f']} }`. */
alias?: { [key: string]: string | string[] };
/**
* Indicate that keys should be parsed as an array: `{ array: ['foo', 'bar'] }`.
* Indicate that keys should be parsed as an array and coerced to booleans / numbers:
* { array: [ { key: 'foo', boolean: true }, {key: 'bar', number: true} ] }`.
*/
array?: ArrayOption | ArrayOption[];
/** Arguments should be parsed as booleans: `{ boolean: ['x', 'y'] }`. */
boolean?: string | string[];
/** Indicate a key that represents a path to a configuration file (this file will be loaded and parsed). */
config?: string | string[] | { [key: string]: boolean | ConfigCallback };
/** configuration objects to parse, their properties will be set as arguments */
configObjects?: Array<{ [key: string]: any }>;
/** Provide configuration options to the yargs-parser. */
configuration?: Partial<Configuration>;
/**
* Provide a custom synchronous function that returns a coerced value from the argument provided (or throws an error), e.g.
* `{ coerce: { foo: function (arg) { return modifiedArg } } }`.
*/
coerce?: { [key: string]: CoerceCallback };
/** Indicate a key that should be used as a counter, e.g., `-vvv = {v: 3}`. */
count?: string | string[];
/** Provide default values for keys: `{ default: { x: 33, y: 'hello world!' } }`. */
default?: { [key: string]: any };
/** Environment variables (`process.env`) with the prefix provided should be parsed. */
envPrefix?: string;
/** Specify that a key requires n arguments: `{ narg: {x: 2} }`. */
narg?: { [key: string]: number };
/** `path.normalize()` will be applied to values set to this key. */
normalize?: string | string[];
/** Keys should be treated as strings (even if they resemble a number `-x 33`). */
string?: string | string[];
/** Keys should be treated as numbers. */
number?: string | string[];
/** i18n handler, defaults to util.format */
__?: (format: any, ...param: any[]) => string;
/** alias lookup table defaults */
key?: { [key: string]: any };
}
interface Parser {
(args: string | any[], opts?: Options): Arguments;
detailed(args: string | any[], opts?: Options): DetailedArguments;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the options should be optional
You should state is by using opts?: Partial<Options>
, not by making Options
properties optional, as it makes it hard for me to reuse your types this way (I always have to get rid of undefined).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other fixes integrated.
Hey @QmarkC 👋 added you as a collaborator on yargs, to make flagging you for reviews like this easier. Could I bother you to send an email to bencoe [at] google.com and I can onboard you to the project? |
@QmarkC Thanks for such a detailed review, and more than happy to welcome you onboard! Would you mind checking the still open discussions, to see if you agree with my replies? |
3rd and 4th steps of #1586: this PR moves and tsifies most of the root
yargs.js
tolib/yargs
, and fixes some bugs discovered on the way (see separate bugfix commits).