diff --git a/cli/help.md b/cli/help.md index 8d1c75e35b3..f47842fb87b 100644 --- a/cli/help.md +++ b/cli/help.md @@ -19,11 +19,11 @@ Basic options: -p, --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 for AMD module (default is anonymous) --amd.autoId Generate the AMD ID based off the chunk name --amd.basePath Path to prepend to auto generated AMD ID --amd.define Function to use in place of `define` --amd.forceJsExtensionForImports Use `.js` extension in AMD imports +--amd.id ID for AMD module (default is anonymous) --assetFileNames Name pattern for emitted assets --banner Code to insert at top of bundle (outside wrapper) --chunkFileNames Name pattern for emitted secondary chunks @@ -35,16 +35,21 @@ Basic options: --no-esModule Do not add __esModule property --exports 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 Code to insert at end of bundle (outside wrapper) --no-freeze Do not freeze namespace objects --generatedCode 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 Code to insert at top of bundle (inside wrapper) --no-makeAbsoluteExternalsRelative Prevent normalization of external imports --maxParallelFileOps How many files to read in parallel @@ -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 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 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 Exclude files from being watched --watch.include Limit watching to specified files ---watch.onStart Shell command to run on `"START"` event ---watch.onBundleStart Shell command to run on `"BUNDLE_START"` event --watch.onBundleEnd Shell command to run on `"BUNDLE_END"` event +--watch.onBundleStart Shell command to run on `"BUNDLE_START"` event --watch.onEnd Shell command to run on `"END"` event --watch.onError Shell command to run on `"ERROR"` event ---validate Validate output +--watch.onStart Shell command to run on `"START"` event +--watch.skipWrite Do not write files to disk when watching Examples: diff --git a/docs/01-command-line-reference.md b/docs/01-command-line-reference.md index 4af0954a175..966e651372f 100755 --- a/docs/01-command-line-reference.md +++ b/docs/01-command-line-reference.md @@ -350,11 +350,11 @@ Many options have command line equivalents. In those cases, any arguments passed -p, --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 for AMD module (default is anonymous) --amd.autoId Generate the AMD ID based off the chunk name --amd.basePath Path to prepend to auto generated AMD ID --amd.define Function to use in place of `define` --amd.forceJsExtensionForImports Use `.js` extension in AMD imports +--amd.id ID for AMD module (default is anonymous) --assetFileNames Name pattern for emitted assets --banner Code to insert at top of bundle (outside wrapper) --chunkFileNames Name pattern for emitted secondary chunks @@ -372,10 +372,15 @@ Many options have command line equivalents. In those cases, any arguments passed --footer Code to insert at end of bundle (outside wrapper) --no-freeze Do not freeze namespace objects --generatedCode 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 Handle default/namespace imports from AMD/CommonJS --inlineDynamicImports Create single bundle when using dynamic imports +--no-interop Do not include interop block --intro Code to insert at top of bundle (inside wrapper) --no-makeAbsoluteExternalsRelative Prevent normalization of external imports --maxParallelFileOps How many files to read in parallel @@ -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 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 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 Exclude files from being watched --watch.include Limit watching to specified files ---watch.onStart Shell command to run on `"START"` event ---watch.onBundleStart Shell command to run on `"BUNDLE_START"` event --watch.onBundleEnd Shell command to run on `"BUNDLE_END"` event +--watch.onBundleStart Shell command to run on `"BUNDLE_START"` event --watch.onEnd Shell command to run on `"END"` event --watch.onError Shell command to run on `"ERROR"` event ---validate Validate output +--watch.onStart 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. diff --git a/package.json b/package.json index 2d86a8c03ec..374245e0771 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/test-options.js b/scripts/test-options.js new file mode 100644 index 00000000000..4ac2e9ed4a3 --- /dev/null +++ b/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); + } +}