Skip to content

Commit

Permalink
Add, validate and test CLI options
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed Nov 27, 2022
1 parent 8eca755 commit 0290189
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 14 deletions.
21 changes: 14 additions & 7 deletions cli/help.md
Expand Up @@ -19,11 +19,11 @@ Basic options:
-p, --plugin <plugin> Use the plugin specified (may be repeated)
-v, --version Show version number
-w, --watch Watch files in bundle and rebuild on changes
--amd.id <id> ID for AMD module (default is anonymous)
--amd.autoId Generate the AMD ID based off the chunk name
--amd.basePath <prefix> Path to prepend to auto generated AMD ID
--amd.define <name> Function to use in place of `define`
--amd.forceJsExtensionForImports Use `.js` extension in AMD imports
--amd.id <id> ID for AMD module (default is anonymous)
--assetFileNames <pattern> Name pattern for emitted assets
--banner <text> Code to insert at top of bundle (outside wrapper)
--chunkFileNames <pattern> Name pattern for emitted secondary chunks
Expand All @@ -35,16 +35,21 @@ Basic options:
--no-esModule Do not add __esModule property
--exports <mode> Specify export mode (auto, default, named, none)
--extend Extend global variable defined by --name
--no-externalLiveBindings Do not generate code to support live bindings
--no-externalImportAssertions Omit import assertions in "es" output
--no-externalLiveBindings Do not generate code to support live bindings
--failAfterWarnings Exit with an error if the build produced warnings
--footer <text> Code to insert at end of bundle (outside wrapper)
--no-freeze Do not freeze namespace objects
--generatedCode <preset> Which code features to use (es5/es2015)
--generatedCode.arrowFunctions Use arrow functions in generated code
--generatedCode.constBindings Use "const" in generated code
--generatedCode.objectShorthand Use shorthand properties in generated code
--no-generatedCode.reservedNamesAsProps Always quote reserved names as props
--generatedCode.symbols Use symbols in generated code
--no-hoistTransitiveImports Do not hoist transitive imports into entry chunks
--no-indent Don't indent result
--no-interop Do not include interop block
--inlineDynamicImports Create single bundle when using dynamic imports
--no-interop Do not include interop block
--intro <text> Code to insert at top of bundle (inside wrapper)
--no-makeAbsoluteExternalsRelative Prevent normalization of external imports
--maxParallelFileOps <value> How many files to read in parallel
Expand All @@ -69,22 +74,24 @@ Basic options:
--no-systemNullSetters Do not replace empty SystemJS setters with `null`
--no-treeshake Disable tree-shaking optimisations
--no-treeshake.annotations Ignore pure call annotations
--treeshake.correctVarValueBeforeDeclaration Deoptimize variables until declared
--treeshake.manualPureFunctions <names> Manually declare functions as pure
--no-treeshake.moduleSideEffects Assume modules have no side effects
--no-treeshake.propertyReadSideEffects Ignore property access side effects
--no-treeshake.tryCatchDeoptimization Do not turn off try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects Assume unknown globals do not throw
--validate Validate output
--waitForBundleInput Wait for bundle input files
--watch.buildDelay <number> Throttle watch rebuilds
--no-watch.clearScreen Do not clear the screen when rebuilding
--watch.skipWrite Do not write files to disk when watching
--watch.exclude <files> Exclude files from being watched
--watch.include <files> Limit watching to specified files
--watch.onStart <cmd> Shell command to run on `"START"` event
--watch.onBundleStart <cmd> Shell command to run on `"BUNDLE_START"` event
--watch.onBundleEnd <cmd> Shell command to run on `"BUNDLE_END"` event
--watch.onBundleStart <cmd> Shell command to run on `"BUNDLE_START"` event
--watch.onEnd <cmd> Shell command to run on `"END"` event
--watch.onError <cmd> Shell command to run on `"ERROR"` event
--validate Validate output
--watch.onStart <cmd> Shell command to run on `"START"` event
--watch.skipWrite Do not write files to disk when watching

Examples:

Expand Down
19 changes: 13 additions & 6 deletions docs/01-command-line-reference.md
Expand Up @@ -350,11 +350,11 @@ Many options have command line equivalents. In those cases, any arguments passed
-p, --plugin <plugin> Use the plugin specified (may be repeated)
-v, --version Show version number
-w, --watch Watch files in bundle and rebuild on changes
--amd.id <id> ID for AMD module (default is anonymous)
--amd.autoId Generate the AMD ID based off the chunk name
--amd.basePath <prefix> Path to prepend to auto generated AMD ID
--amd.define <name> Function to use in place of `define`
--amd.forceJsExtensionForImports Use `.js` extension in AMD imports
--amd.id <id> ID for AMD module (default is anonymous)
--assetFileNames <pattern> Name pattern for emitted assets
--banner <text> Code to insert at top of bundle (outside wrapper)
--chunkFileNames <pattern> Name pattern for emitted secondary chunks
Expand All @@ -372,10 +372,15 @@ Many options have command line equivalents. In those cases, any arguments passed
--footer <text> Code to insert at end of bundle (outside wrapper)
--no-freeze Do not freeze namespace objects
--generatedCode <preset> Which code features to use (es5/es2015)
--generatedCode.arrowFunctions Use arrow functions in generated code
--generatedCode.constBindings Use "const" in generated code
--generatedCode.objectShorthand Use shorthand properties in generated code
--no-generatedCode.reservedNamesAsProps Always quote reserved names as props
--generatedCode.symbols Use symbols in generated code
--no-hoistTransitiveImports Do not hoist transitive imports into entry chunks
--no-indent Don't indent result
--interop <type> Handle default/namespace imports from AMD/CommonJS
--inlineDynamicImports Create single bundle when using dynamic imports
--no-interop Do not include interop block
--intro <text> Code to insert at top of bundle (inside wrapper)
--no-makeAbsoluteExternalsRelative Prevent normalization of external imports
--maxParallelFileOps <value> How many files to read in parallel
Expand All @@ -400,22 +405,24 @@ Many options have command line equivalents. In those cases, any arguments passed
--no-systemNullSetters Do not replace empty SystemJS setters with `null`
--no-treeshake Disable tree-shaking optimisations
--no-treeshake.annotations Ignore pure call annotations
--treeshake.correctVarValueBeforeDeclaration Deoptimize variables until declared
--treeshake.manualPureFunctions <names> Manually declare functions as pure
--no-treeshake.moduleSideEffects Assume modules have no side effects
--no-treeshake.propertyReadSideEffects Ignore property access side effects
--no-treeshake.tryCatchDeoptimization Do not turn off try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects Assume unknown globals do not throw
--validate Validate output
--waitForBundleInput Wait for bundle input files
--watch.buildDelay <number> Throttle watch rebuilds
--no-watch.clearScreen Do not clear the screen when rebuilding
--watch.skipWrite Do not write files to disk when watching
--watch.exclude <files> Exclude files from being watched
--watch.include <files> Limit watching to specified files
--watch.onStart <cmd> Shell command to run on `"START"` event
--watch.onBundleStart <cmd> Shell command to run on `"BUNDLE_START"` event
--watch.onBundleEnd <cmd> Shell command to run on `"BUNDLE_END"` event
--watch.onBundleStart <cmd> Shell command to run on `"BUNDLE_START"` event
--watch.onEnd <cmd> Shell command to run on `"END"` event
--watch.onError <cmd> Shell command to run on `"ERROR"` event
--validate Validate output
--watch.onStart <cmd> Shell command to run on `"START"` event
--watch.skipWrite Do not write files to disk when watching
```
The flags listed below are only available via the command line interface. All other flags correspond to and override their config file equivalents, see the [big list of options](guide/en/#big-list-of-options) for details.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -29,11 +29,12 @@
"test": "npm run build && npm run test:all",
"test:cjs": "npm run build:cjs && npm run test:only",
"test:quick": "mocha -b test/test.js",
"test:all": "concurrently --kill-others-on-fail -c green,blue,magenta,cyan,red 'npm:test:only' 'npm:test:browser' 'npm:test:typescript' 'npm:test:leak' 'npm:test:package'",
"test:all": "concurrently --kill-others-on-fail -c green,blue,magenta,cyan,red 'npm:test:only' 'npm:test:browser' 'npm:test:typescript' 'npm:test:leak' 'npm:test:package' 'npm:test:options'",
"test:coverage": "npm run build:cjs && shx rm -rf coverage/* && nyc --reporter html mocha test/test.js",
"test:coverage:browser": "npm run build && shx rm -rf coverage/* && nyc mocha test/browser/index.js",
"test:leak": "node --expose-gc test/leak/index.js",
"test:package": "node scripts/test-package.js",
"test:options": "node scripts/test-options.js",
"test:only": "mocha test/test.js",
"test:typescript": "shx rm -rf test/typescript/dist && shx cp -r dist test/typescript/ && tsc --noEmit -p test/typescript && tsc --noEmit",
"test:browser": "mocha test/browser/index.js",
Expand Down
93 changes: 93 additions & 0 deletions scripts/test-options.js
@@ -0,0 +1,93 @@
import { readFile } from 'node:fs/promises';

const [optionsText, helpText, commandReferenceText] = await Promise.all([
readFile(new URL('../docs/999-big-list-of-options.md', import.meta.url), 'utf8'),
readFile(new URL('../cli/help.md', import.meta.url), 'utf8'),
readFile(new URL('../docs/01-command-line-reference.md', import.meta.url), 'utf8')
]);

const optionSections = optionsText.split('\n### ');
let searchedOptions = '';
for (const section of optionSections.slice(1)) {
if (!(section.startsWith('Experimental') || section.startsWith('Deprecated'))) {
searchedOptions += section;
}
}

const findCliOptionRegExp = /CLI: (`(-(\w)[^`]*)`\/)?`(--([\w.]+)[^`]*)`/g;

const allCliOptions = [];
let match;

while ((match = findCliOptionRegExp.exec(searchedOptions)) !== null) {
allCliOptions.push({ long: match[5], short: match[3] });
}

const findOptionInHelpRegExp = /^(-(\w), )?--(no-)?([\w.]+)/gm;

const cliOptionsInHelp = new Map();

while ((match = findOptionInHelpRegExp.exec(helpText)) !== null) {
cliOptionsInHelp.set(match[4], match[2]);
}

let failed = false;

for (const { long, short } of allCliOptions) {
if (!cliOptionsInHelp.has(long)) {
failed = true;
console.error(`Could find neither --${long} nor --no-${long} in help.md.`);
}
const optionInHelp = cliOptionsInHelp.get(long);
if (short !== optionInHelp) {
failed = true;
console.error(
`Inconsistent option with shortcut. Expected -${short}/--${long}, got -${optionInHelp.short}/--${optionInHelp.long}.`
);
}
}

if (failed) {
process.exit(1);
}

let current = null;
for (const [long, short] of cliOptionsInHelp) {
if (!short) {
if (current && long < current) {
console.error(
`Options in help.md are not sorted properly. "${long}" should occur before "${current}".`
);
process.exit(1);
}
current = long;
}
}

const splitHelpText = helpText.split('\n');
for (const line of splitHelpText) {
if (line.length > 80) {
console.error(`The following line in help.md exceeds the limit of 80 characters:\n${line}`);
process.exit(1);
}
}

const helpOptionLines = splitHelpText.filter(line => line.startsWith('-'));

const cliFlagsText = commandReferenceText
.split('\n### ')
.find(text => text.startsWith('Command line flags'));
const optionListLines = cliFlagsText
.match(/```text\n([\S\s]*?)\n```/)[1]
.split('\n')
.filter(line => line.startsWith('-'));

for (const [index, line] of helpOptionLines.entries()) {
const optionListLine = optionListLines[index];
if (line !== optionListLine) {
console.error(
`The command lines in 01-command-line-reference.md does not match help.md. Expected line:\n${line}\n\nReceived line:\n${optionListLine}`
);
process.exit(1);
}
}

0 comments on commit 0290189

Please sign in to comment.