Skip to content

Commit 700b3a9

Browse files
fix(cli): add explicit support for Jest CLI arguments (#3444)
This adds explicit support to our CLI argument parsing code for Jest's CLI arguments by adding entries to the arrays we use to parse 'known' arguments. We declare all the Jest CLI arguments (as pulled from [the file in which they're implemented](https://github.com/facebook/jest/blob/main/packages/jest-cli/src/cli/args.ts)) in `src/cli/config-flags.ts`, so all of these flags are now fully-supported 'first class' (if you will) CLI flags.
1 parent 4e86d69 commit 700b3a9

File tree

4 files changed

+255
-21
lines changed

4 files changed

+255
-21
lines changed

src/cli/config-flags.ts

+134-3
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,65 @@ export const BOOLEAN_CLI_ARGS = [
3535
'verbose',
3636
'version',
3737
'watch',
38+
39+
// JEST CLI OPTIONS
40+
'all',
41+
'automock',
42+
'bail',
43+
// 'cache', Stencil already supports this argument
44+
'changedFilesWithAncestor',
45+
// 'ci', Stencil already supports this argument
46+
'clearCache',
47+
'clearMocks',
48+
'collectCoverage',
49+
'color',
50+
'colors',
51+
'coverage',
52+
// 'debug', Stencil already supports this argument
53+
'detectLeaks',
54+
'detectOpenHandles',
55+
'errorOnDeprecated',
56+
'expand',
57+
'findRelatedTests',
58+
'forceExit',
59+
'init',
60+
'injectGlobals',
61+
'json',
62+
'lastCommit',
63+
'listTests',
64+
'logHeapUsage',
65+
'noStackTrace',
66+
'notify',
67+
'onlyChanged',
68+
'onlyFailures',
69+
'passWithNoTests',
70+
'resetMocks',
71+
'resetModules',
72+
'restoreMocks',
73+
'runInBand',
74+
'runTestsByPath',
75+
'showConfig',
76+
'silent',
77+
'skipFilter',
78+
'testLocationInResults',
79+
'updateSnapshot',
80+
'useStderr',
81+
// 'verbose', Stencil already supports this argument
82+
// 'version', Stencil already supports this argument
83+
// 'watch', Stencil already supports this argument
84+
'watchAll',
85+
'watchman',
3886
] as const;
3987

4088
/**
4189
* All the Number options supported by the Stencil CLI
4290
*/
43-
export const NUMBER_CLI_ARGS = ['maxWorkers', 'port'] as const;
91+
export const NUMBER_CLI_ARGS = [
92+
'port',
93+
// JEST CLI ARGS
94+
'maxConcurrency',
95+
'testTimeout',
96+
] as const;
4497

4598
/**
4699
* All the String options supported by the Stencil CLI
@@ -53,8 +106,71 @@ export const STRING_CLI_ARGS = [
53106
'emulate',
54107
'root',
55108
'screenshotConnector',
109+
110+
// JEST CLI ARGS
111+
'cacheDirectory',
112+
'changedSince',
113+
'collectCoverageFrom',
114+
// 'config', Stencil already supports this argument
115+
'coverageDirectory',
116+
'coverageThreshold',
117+
'env',
118+
'filter',
119+
'globalSetup',
120+
'globalTeardown',
121+
'globals',
122+
'haste',
123+
'moduleNameMapper',
124+
'notifyMode',
125+
'outputFile',
126+
'preset',
127+
'prettierPath',
128+
'resolver',
129+
'rootDir',
130+
'runner',
131+
'testEnvironment',
132+
'testEnvironmentOptions',
133+
'testFailureExitCode',
134+
'testNamePattern',
135+
'testResultsProcessor',
136+
'testRunner',
137+
'testSequencer',
138+
'testURL',
139+
'timers',
140+
'transform',
141+
142+
// ARRAY ARGS
143+
'collectCoverageOnlyFrom',
144+
'coveragePathIgnorePatterns',
145+
'coverageReporters',
146+
'moduleDirectories',
147+
'moduleFileExtensions',
148+
'modulePathIgnorePatterns',
149+
'modulePaths',
150+
'projects',
151+
'reporters',
152+
'roots',
153+
'selectProjects',
154+
'setupFiles',
155+
'setupFilesAfterEnv',
156+
'snapshotSerializers',
157+
'testMatch',
158+
'testPathIgnorePatterns',
159+
'testPathPattern',
160+
'testRegex',
161+
'transformIgnorePatterns',
162+
'unmockedModulePathPatterns',
163+
'watchPathIgnorePatterns',
56164
] as const;
57165

166+
/**
167+
* All the CLI arguments which may have string or number values
168+
*
169+
* `maxWorkers` is an argument which is used both by Stencil _and_ by Jest,
170+
* which means that we need to support parsing both string and number values.
171+
*/
172+
export const STRING_NUMBER_CLI_ARGS = ['maxWorkers'] as const;
173+
58174
/**
59175
* All the LogLevel-type options supported by the Stencil CLI
60176
*
@@ -74,9 +190,10 @@ type ArrayValuesAsUnion<T extends ReadonlyArray<string>> = T[number];
74190
export type BooleanCLIArg = ArrayValuesAsUnion<typeof BOOLEAN_CLI_ARGS>;
75191
export type StringCLIArg = ArrayValuesAsUnion<typeof STRING_CLI_ARGS>;
76192
export type NumberCLIArg = ArrayValuesAsUnion<typeof NUMBER_CLI_ARGS>;
193+
export type StringNumberCLIArg = ArrayValuesAsUnion<typeof STRING_NUMBER_CLI_ARGS>;
77194
export type LogCLIArg = ArrayValuesAsUnion<typeof LOG_LEVEL_CLI_ARGS>;
78195

79-
type KnownCLIArg = BooleanCLIArg | StringCLIArg | NumberCLIArg | LogCLIArg;
196+
type KnownCLIArg = BooleanCLIArg | StringCLIArg | NumberCLIArg | StringNumberCLIArg | LogCLIArg;
80197

81198
type AliasMap = Partial<Record<KnownCLIArg, string>>;
82199

@@ -107,16 +224,25 @@ type ObjectFromKeys<K extends ReadonlyArray<string>, T> = {
107224
* in ConfigFlags, below
108225
*/
109226
type BooleanConfigFlags = ObjectFromKeys<typeof BOOLEAN_CLI_ARGS, boolean>;
227+
110228
/**
111229
* Type containing the possible String configuration flags, to be included
112230
* in ConfigFlags, below
113231
*/
114232
type StringConfigFlags = ObjectFromKeys<typeof STRING_CLI_ARGS, string>;
233+
115234
/**
116235
* Type containing the possible numeric configuration flags, to be included
117236
* in ConfigFlags, below
118237
*/
119238
type NumberConfigFlags = ObjectFromKeys<typeof NUMBER_CLI_ARGS, number>;
239+
240+
/**
241+
* Type containing the configuration flags which may be set to either string
242+
* or number values.
243+
*/
244+
type StringNumberConfigFlags = ObjectFromKeys<typeof STRING_NUMBER_CLI_ARGS, string | number>;
245+
120246
/**
121247
* Type containing the possible LogLevel configuration flags, to be included
122248
* in ConfigFlags, below
@@ -137,7 +263,12 @@ type LogLevelFlags = ObjectFromKeys<typeof LOG_LEVEL_CLI_ARGS, LogLevel>;
137263
* options we support and a runtime list of strings which can be used to match
138264
* on actual flags passed by the user.
139265
*/
140-
export interface ConfigFlags extends BooleanConfigFlags, StringConfigFlags, NumberConfigFlags, LogLevelFlags {
266+
export interface ConfigFlags
267+
extends BooleanConfigFlags,
268+
StringConfigFlags,
269+
NumberConfigFlags,
270+
StringNumberConfigFlags,
271+
LogLevelFlags {
141272
task?: TaskCommand | null;
142273
args?: string[];
143274
knownArgs?: string[];

src/cli/parse-flags.ts

+58-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import { CompilerSystem, LogLevel, LOG_LEVELS, TaskCommand } from '../declarations';
22
import { dashToPascalCase, toDashCase } from '@utils';
33
import {
4-
ConfigFlags,
4+
BOOLEAN_CLI_ARGS,
55
BooleanCLIArg,
6-
LogCLIArg,
7-
NumberCLIArg,
8-
StringCLIArg,
96
CLI_ARG_ALIASES,
10-
BOOLEAN_CLI_ARGS,
7+
ConfigFlags,
118
LOG_LEVEL_CLI_ARGS,
9+
LogCLIArg,
1210
NUMBER_CLI_ARGS,
11+
NumberCLIArg,
1312
STRING_CLI_ARGS,
13+
STRING_NUMBER_CLI_ARGS,
14+
StringCLIArg,
15+
StringNumberCLIArg,
1416
} from './config-flags';
1517

1618
/**
@@ -73,6 +75,7 @@ const parseArgs = (flags: ConfigFlags, args: string[]) => {
7375
BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName));
7476
STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName));
7577
NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName));
78+
STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName));
7679
LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName));
7780
};
7881

@@ -161,6 +164,52 @@ const parseNumberArg = (flags: ConfigFlags, args: string[], configCaseName: Numb
161164
}
162165
};
163166

167+
/**
168+
* Parse a CLI argument which may be either a string or a number
169+
*
170+
* @param flags the config flags object, while we'll modify
171+
* @param args our CLI arguments
172+
* @param configCaseName the argument we want to look at right now
173+
*/
174+
const parseStringNumberArg = (flags: ConfigFlags, args: string[], configCaseName: StringNumberCLIArg) => {
175+
if (!['number', 'string'].includes(typeof flags[configCaseName])) {
176+
flags[configCaseName] = null;
177+
}
178+
179+
const { value, matchingArg } = getValue(args, configCaseName);
180+
181+
if (value !== undefined && matchingArg !== undefined) {
182+
if (CLI_ARG_STRING_REGEX.test(value)) {
183+
// if it matches the regex we treat it like a string
184+
flags[configCaseName] = value;
185+
} else {
186+
// it was a number, great!
187+
flags[configCaseName] = Number(value);
188+
}
189+
flags.knownArgs!.push(matchingArg);
190+
flags.knownArgs!.push(value);
191+
}
192+
};
193+
194+
/**
195+
* We use this regular expression to detect CLI parameters which
196+
* should be parsed as string values (as opposed to numbers) for
197+
* the argument types for which we support both a string and a
198+
* number value.
199+
*
200+
* The regex tests for the presence of at least one character which is
201+
* _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
202+
* `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
203+
* support the admittedly unlikely use of scientific notation, like `"4e+0"`
204+
* for `4`).
205+
*
206+
* Thus we'll match a string like `"50%"`, but not a string like `"50"` or
207+
* `"5.0"`. If it matches a given string we conclude that the string should
208+
* be parsed as a string literal, rather than using `Number` to convert it
209+
* to a number.
210+
*/
211+
const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
212+
164213
/**
165214
* Parse a LogLevel CLI argument. These can be only a specific
166215
* set of strings, so this function takes care of validating that
@@ -202,7 +251,10 @@ const parseLogLevelArg = (flags: ConfigFlags, args: string[], configCaseName: Lo
202251
* @returns the value for the flag as well as the exact string which it matched from
203252
* the user input.
204253
*/
205-
const getValue = (args: string[], configCaseName: StringCLIArg | NumberCLIArg | LogCLIArg): CLIArgValue => {
254+
const getValue = (
255+
args: string[],
256+
configCaseName: StringCLIArg | NumberCLIArg | StringNumberCLIArg | LogCLIArg
257+
): CLIArgValue => {
206258
// for some CLI args we have a short alias, like 'c' for 'config'
207259
const alias = CLI_ARG_ALIASES[configCaseName];
208260
// we support supplying arguments in both dash-case and configCase

0 commit comments

Comments
 (0)