feat(core): properly passthrough flags unparsed to executors (#12890)

This commit is contained in:
Jason Jean 2022-11-01 15:05:30 -04:00 committed by GitHub
parent 6baf78cce8
commit 145dda709b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 146 additions and 235 deletions

View File

@ -129,6 +129,12 @@ Default: `false`
Rerun the tasks even when the results are available in the cache Rerun the tasks even when the results are available in the cache
### target
Type: `string`
Task to run for affected projects
### type ### type
Type: `string` Type: `string`

View File

@ -81,7 +81,7 @@
"name": "print-affected", "name": "print-affected",
"id": "print-affected", "id": "print-affected",
"file": "generated/cli/print-affected", "file": "generated/cli/print-affected",
"content": "---\ntitle: 'print-affected - CLI command'\ndescription: 'Prints information about the projects and targets affected by changes'\n---\n\n# print-affected\n\nPrints information about the projects and targets affected by changes\n\n## Usage\n\n```terminal\nnx print-affected\n```\n\nInstall `nx` globally to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpm nx`.\n\n### Examples\n\nPrint information about affected projects and the project graph:\n\n```terminal\n nx print-affected\n```\n\nPrint information about the projects affected by the changes between main and HEAD (e.g,. PR):\n\n```terminal\n nx print-affected --base=main --head=HEAD\n```\n\nPrints information about the affected projects and a list of tasks to test them:\n\n```terminal\n nx print-affected --target=test\n```\n\nPrints the projects property from the print-affected output:\n\n```terminal\n nx print-affected --target=build --select=projects\n```\n\nPrints the tasks.target.project property from the print-affected output:\n\n```terminal\n nx print-affected --target=build --select=tasks.target.project\n```\n\n## Options\n\n### all\n\nType: `boolean`\n\nAll projects\n\n### base\n\nType: `string`\n\nBase of the current branch (usually main)\n\n### configuration\n\nType: `string`\n\nThis is the configuration to use when performing tasks on projects\n\n### exclude\n\nType: `array`\n\nDefault: `[]`\n\nExclude certain projects from being processed\n\n### files\n\nType: `array`\n\nChange the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas\n\n### head\n\nType: `string`\n\nLatest commit of the current branch (usually HEAD)\n\n### help\n\nType: `boolean`\n\nShow help\n\n### nx-bail\n\nType: `boolean`\n\nDefault: `false`\n\nStop command execution after the first failed task\n\n### nx-ignore-cycles\n\nType: `boolean`\n\nDefault: `false`\n\nIgnore cycles in the task graph\n\n### runner\n\nType: `string`\n\nThis is the name of the tasks runner configured in nx.json\n\n### select\n\nType: `string`\n\nSelect the subset of the returned json document (e.g., --select=projects)\n\n### skip-nx-cache\n\nType: `boolean`\n\nDefault: `false`\n\nRerun the tasks even when the results are available in the cache\n\n### type\n\nType: `string`\n\nChoices: [app, lib]\n\nSelect the type of projects to be returned (e.g., --type=app)\n\n### uncommitted\n\nType: `boolean`\n\nUncommitted changes\n\n### untracked\n\nType: `boolean`\n\nUntracked changes\n\n### verbose\n\nType: `boolean`\n\nDefault: `false`\n\nPrints additional information about the commands (e.g., stack traces)\n\n### version\n\nType: `boolean`\n\nShow version number\n" "content": "---\ntitle: 'print-affected - CLI command'\ndescription: 'Prints information about the projects and targets affected by changes'\n---\n\n# print-affected\n\nPrints information about the projects and targets affected by changes\n\n## Usage\n\n```terminal\nnx print-affected\n```\n\nInstall `nx` globally to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpm nx`.\n\n### Examples\n\nPrint information about affected projects and the project graph:\n\n```terminal\n nx print-affected\n```\n\nPrint information about the projects affected by the changes between main and HEAD (e.g,. PR):\n\n```terminal\n nx print-affected --base=main --head=HEAD\n```\n\nPrints information about the affected projects and a list of tasks to test them:\n\n```terminal\n nx print-affected --target=test\n```\n\nPrints the projects property from the print-affected output:\n\n```terminal\n nx print-affected --target=build --select=projects\n```\n\nPrints the tasks.target.project property from the print-affected output:\n\n```terminal\n nx print-affected --target=build --select=tasks.target.project\n```\n\n## Options\n\n### all\n\nType: `boolean`\n\nAll projects\n\n### base\n\nType: `string`\n\nBase of the current branch (usually main)\n\n### configuration\n\nType: `string`\n\nThis is the configuration to use when performing tasks on projects\n\n### exclude\n\nType: `array`\n\nDefault: `[]`\n\nExclude certain projects from being processed\n\n### files\n\nType: `array`\n\nChange the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas\n\n### head\n\nType: `string`\n\nLatest commit of the current branch (usually HEAD)\n\n### help\n\nType: `boolean`\n\nShow help\n\n### nx-bail\n\nType: `boolean`\n\nDefault: `false`\n\nStop command execution after the first failed task\n\n### nx-ignore-cycles\n\nType: `boolean`\n\nDefault: `false`\n\nIgnore cycles in the task graph\n\n### runner\n\nType: `string`\n\nThis is the name of the tasks runner configured in nx.json\n\n### select\n\nType: `string`\n\nSelect the subset of the returned json document (e.g., --select=projects)\n\n### skip-nx-cache\n\nType: `boolean`\n\nDefault: `false`\n\nRerun the tasks even when the results are available in the cache\n\n### target\n\nType: `string`\n\nTask to run for affected projects\n\n### type\n\nType: `string`\n\nChoices: [app, lib]\n\nSelect the type of projects to be returned (e.g., --type=app)\n\n### uncommitted\n\nType: `boolean`\n\nUncommitted changes\n\n### untracked\n\nType: `boolean`\n\nUntracked changes\n\n### verbose\n\nType: `boolean`\n\nDefault: `false`\n\nPrints additional information about the commands (e.g., stack traces)\n\n### version\n\nType: `boolean`\n\nShow version number\n"
}, },
{ {
"name": "format:check", "name": "format:check",

View File

@ -581,7 +581,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
// check building project // check building project
let output = runCLI(`build ${project} --outputHashing none`); let output = runCLI(`build ${project} --outputHashing none`);
expect(output).toContain( expect(output).toContain(
`> nx run ${project}:build:production --outputHashing=none` `> nx run ${project}:build:production --outputHashing none`
); );
expect(output).toContain( expect(output).toContain(
`Successfully ran target build for project ${project}` `Successfully ran target build for project ${project}`
@ -590,7 +590,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
output = runCLI(`build ${project} --outputHashing none`); output = runCLI(`build ${project} --outputHashing none`);
expect(output).toContain( expect(output).toContain(
`> nx run ${project}:build:production --outputHashing=none [local cache]` `> nx run ${project}:build:production --outputHashing none [local cache]`
); );
expect(output).toContain( expect(output).toContain(
`Successfully ran target build for project ${project}` `Successfully ran target build for project ${project}`
@ -599,7 +599,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
// check building app1 // check building app1
output = runCLI(`build ${app1} --outputHashing none`); output = runCLI(`build ${app1} --outputHashing none`);
expect(output).toContain( expect(output).toContain(
`> nx run ${app1}:build:production --outputHashing=none` `> nx run ${app1}:build:production --outputHashing none`
); );
expect(output).toContain( expect(output).toContain(
`Successfully ran target build for project ${app1}` `Successfully ran target build for project ${app1}`
@ -608,7 +608,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
output = runCLI(`build ${app1} --outputHashing none`); output = runCLI(`build ${app1} --outputHashing none`);
expect(output).toContain( expect(output).toContain(
`> nx run ${app1}:build:production --outputHashing=none [local cache]` `> nx run ${app1}:build:production --outputHashing none [local cache]`
); );
expect(output).toContain( expect(output).toContain(
`Successfully ran target build for project ${app1}` `Successfully ran target build for project ${app1}`
@ -638,7 +638,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
// check building an app // check building an app
let output = runCLI(`build ${project} --outputHashing none`); let output = runCLI(`build ${project} --outputHashing none`);
expect(output).toContain( expect(output).toContain(
`> nx run ${project}:build:production --outputHashing=none` `> nx run ${project}:build:production --outputHashing none`
); );
expect(output).toContain( expect(output).toContain(
`Successfully ran target build for project ${project}` `Successfully ran target build for project ${project}`
@ -647,7 +647,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
output = runCLI(`build ${project} --outputHashing none`); output = runCLI(`build ${project} --outputHashing none`);
expect(output).toContain( expect(output).toContain(
`> nx run ${project}:build:production --outputHashing=none [local cache]` `> nx run ${project}:build:production --outputHashing none [local cache]`
); );
expect(output).toContain( expect(output).toContain(
`Successfully ran target build for project ${project}` `Successfully ran target build for project ${project}`

View File

@ -21,6 +21,37 @@ describe('Nx Running Tests', () => {
afterAll(() => cleanupProject()); afterAll(() => cleanupProject());
describe('running targets', () => { describe('running targets', () => {
describe('(forwarding params)', () => {
let proj = uniq('proj');
beforeAll(() => {
runCLI(`generate @nrwl/workspace:lib ${proj}`);
updateProjectConfig(proj, (c) => {
c.targets['echo'] = {
executor: 'nx:run-commands',
options: {
command: 'echo ECHO:',
},
};
return c;
});
});
it.each([
'--watch false',
'--watch=false',
'--arr=a,b,c',
'--arr=a --arr=b --arr=c',
'a',
'--a.b=1',
'--a.b 1',
'-- a b c --a --a.b=1',
'--ignored -- a b c --a --a.b=1',
])('should forward %s properly', (args) => {
const output = runCLI(`echo ${proj} ${args}`);
expect(output).toContain(`ECHO: ${args.replace(/^.*-- /, '')}`);
});
});
it('should execute long running tasks', async () => { it('should execute long running tasks', async () => {
const myapp = uniq('myapp'); const myapp = uniq('myapp');
runCLI(`generate @nrwl/web:app ${myapp}`); runCLI(`generate @nrwl/web:app ${myapp}`);

View File

@ -191,7 +191,9 @@ export const commandsObject = yargs
'Prints information about the projects and targets affected by changes', 'Prints information about the projects and targets affected by changes',
builder: (yargs) => builder: (yargs) =>
linkToNxDevAndExamples( linkToNxDevAndExamples(
withAffectedOptions(withPrintAffectedOptions(yargs)), withAffectedOptions(
withTargetOption(withPrintAffectedOptions(yargs), false)
),
'print-affected' 'print-affected'
), ),
handler: async (args) => { handler: async (args) => {
@ -406,9 +408,9 @@ function withPlainOption(yargs: yargs.Argv): yargs.Argv {
function withAffectedOptions(yargs: yargs.Argv): yargs.Argv { function withAffectedOptions(yargs: yargs.Argv): yargs.Argv {
return yargs return yargs
.parserConfiguration({ .parserConfiguration({
'camel-case-expansion': false, 'strip-dashed': true,
// allow parsing --env.SOME_ARG for cypress cli env args 'unknown-options-as-args': true,
'dot-notation': true, 'populate--': true,
}) })
.option('files', { .option('files', {
describe: describe:
@ -473,6 +475,12 @@ function withAffectedOptions(yargs: yargs.Argv): yargs.Argv {
'This is the configuration to use when performing tasks on projects', 'This is the configuration to use when performing tasks on projects',
type: 'string', type: 'string',
}) })
.option('prod', {
describe: 'Use the production configuration',
type: 'boolean',
default: false,
hidden: true,
})
.option('verbose', { .option('verbose', {
type: 'boolean', type: 'boolean',
describe: describe:
@ -494,15 +502,32 @@ function withAffectedOptions(yargs: yargs.Argv): yargs.Argv {
untracked: ['uncommitted', 'files', 'base', 'head', 'all'], untracked: ['uncommitted', 'files', 'base', 'head', 'all'],
uncommitted: ['files', 'untracked', 'base', 'head', 'all'], uncommitted: ['files', 'untracked', 'base', 'head', 'all'],
all: ['files', 'untracked', 'uncommitted', 'base', 'head'], all: ['files', 'untracked', 'uncommitted', 'base', 'head'],
})
.check((nxArgs) => {
if (
!nxArgs.files &&
!nxArgs.uncommitted &&
!nxArgs.untracked &&
!nxArgs.base &&
!nxArgs.head &&
!nxArgs.all &&
nxArgs._ &&
nxArgs._.length >= 3
) {
throw new Error(
`Nx no longer supports using positional arguments for base and head. Please use --base and --head instead.`
);
}
return true;
}); });
} }
function withRunManyOptions(yargs: yargs.Argv): yargs.Argv { function withRunManyOptions(yargs: yargs.Argv): yargs.Argv {
return yargs return yargs
.parserConfiguration({ .parserConfiguration({
'camel-case-expansion': false, 'strip-dashed': true,
// allow parsing --env.SOME_ARG for cypress cli env args 'unknown-options-as-args': true,
'dot-notation': true, 'populate--': true,
}) })
.option('projects', { .option('projects', {
describe: 'Projects to run (comma delimited)', describe: 'Projects to run (comma delimited)',
@ -513,6 +538,12 @@ function withRunManyOptions(yargs: yargs.Argv): yargs.Argv {
type: 'boolean', type: 'boolean',
default: true, default: true,
}) })
.option('prod', {
describe: 'Use the production configuration',
type: 'boolean',
default: false,
hidden: true,
})
.options('runner', { .options('runner', {
describe: 'Override the tasks runner in `nx.json`', describe: 'Override the tasks runner in `nx.json`',
type: 'string', type: 'string',
@ -596,16 +627,12 @@ function withDepGraphOptions(yargs: yargs.Argv): yargs.Argv {
} }
function withOverrides(args: any): any { function withOverrides(args: any): any {
const split = process.argv.indexOf('--'); args.__overrides_unparsed__ = (args['--'] ?? args._.slice(1)).map((v) =>
if (split > -1) { v.toString()
const overrides = process.argv.slice(split + 1); );
delete args._; delete args['--'];
return { ...args, __overrides__: overrides };
} else {
args['__positional_overrides__'] = args._.slice(1);
delete args._; delete args._;
return args; return args;
}
} }
function withParallelOption(yargs: yargs.Argv): yargs.Argv { function withParallelOption(yargs: yargs.Argv): yargs.Argv {
@ -623,12 +650,12 @@ function withOutputStyleOption(yargs: yargs.Argv): yargs.Argv {
}); });
} }
function withTargetOption(yargs: yargs.Argv): yargs.Argv { function withTargetOption(yargs: yargs.Argv, demandOption = true): yargs.Argv {
return yargs.option('target', { return yargs.option('target', {
describe: 'Task to run for affected projects', describe: 'Task to run for affected projects',
type: 'string', type: 'string',
requiresArg: true, requiresArg: true,
demandOption: true, demandOption,
global: false, global: false,
}); });
} }
@ -687,11 +714,11 @@ function withRunOneOptions(yargs: yargs.Argv) {
const executorShouldShowHelp = !( const executorShouldShowHelp = !(
process.argv[2] === 'run' && process.argv[3] === '--help' process.argv[2] === 'run' && process.argv[3] === '--help'
); );
const res = yargs const res = withOutputStyleOption(yargs)
.parserConfiguration({ .parserConfiguration({
'camel-case-expansion': false, 'strip-dashed': true,
// allow parsing --env.SOME_ARG for cypress cli env args 'unknown-options-as-args': true,
'dot-notation': true, 'populate--': true,
}) })
.option('prod', { .option('prod', {
describe: 'Use the production configuration', describe: 'Use the production configuration',

View File

@ -9,8 +9,7 @@ describe('splitArgs', () => {
{ {
base: 'sha1', base: 'sha1',
head: 'sha2', head: 'sha2',
notNxArg: true, __overrides_unparsed__: ['--notNxArg', '--override'],
override: true,
$0: '', $0: '',
}, },
'affected', 'affected',
@ -27,24 +26,22 @@ describe('splitArgs', () => {
it('should put every command start with nx to nxArgs', () => { it('should put every command start with nx to nxArgs', () => {
const nxArgs = splitArgsIntoNxArgsAndOverrides( const nxArgs = splitArgsIntoNxArgsAndOverrides(
{ {
'nx-key': 'some-value', nxBail: 'some-value',
nxKey: 'some-value', __overrides_unparsed__: ['--override'],
_: ['--override'],
$0: '', $0: '',
}, },
'affected', 'affected',
{} as any, {} as any,
{} as any {} as any
).nxArgs; ).nxArgs;
expect(nxArgs['nxKey']).toEqual('some-value'); expect(nxArgs['nxBail']).toEqual('some-value');
}); });
it('should default to having a base of main', () => { it('should default to having a base of main', () => {
expect( expect(
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
notNxArg: true, __overrides_unparsed__: ['--notNxArg', '--override'],
_: ['--override'],
$0: '', $0: '',
}, },
'affected', 'affected',
@ -61,8 +58,7 @@ describe('splitArgs', () => {
expect( expect(
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
notNxArg: true, __overrides_unparsed__: ['--notNxArg', '--override'],
_: ['--override'],
$0: '', $0: '',
}, },
'affected', 'affected',
@ -79,8 +75,7 @@ describe('splitArgs', () => {
expect( expect(
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
notNxArg: true, __overrides_unparsed__: ['--notNxArg', 'affecteda', '--override'],
_: ['affecteda', '--override'],
$0: '', $0: '',
}, },
'affected', 'affected',
@ -98,8 +93,7 @@ describe('splitArgs', () => {
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
files: [''], files: [''],
notNxArg: true, __overrides_unparsed__: ['--notNxArg'],
__positional_overrides__: [],
$0: '', $0: '',
}, },
'affected', 'affected',
@ -117,8 +111,7 @@ describe('splitArgs', () => {
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
files: [''], files: [''],
notNxArg: true, __overrides_unparsed__: ['positional', '--notNxArg'],
__positional_overrides__: ['positional'],
$0: '', $0: '',
}, },
'affected', 'affected',
@ -137,8 +130,7 @@ describe('splitArgs', () => {
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
files: [''], files: [''],
notNxArg: true, __overrides_unparsed__: ['explicit'],
_: ['explicit'],
$0: '', $0: '',
}, },
'affected', 'affected',
@ -151,19 +143,22 @@ describe('splitArgs', () => {
}); });
}); });
it('should throw when base and head are set as positional args', () => { it('should be able to parse arguments in __overrides__', () => {
expect(() => expect(
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
notNxArg: true, files: [''],
__positional_overrides__: ['sha1', 'sha2'], __overrides__: ['explicit'],
$0: '', $0: '',
}, },
'affected', 'affected',
{} as any, {} as any,
{} as any {} as any
) ).overrides
).toThrow(); ).toEqual({
__overrides_unparsed__: ['explicit'],
_: ['explicit'],
});
}); });
it('should set base and head based on environment variables in affected mode, if they are not provided directly on the command', () => { it('should set base and head based on environment variables in affected mode, if they are not provided directly on the command', () => {
@ -175,8 +170,7 @@ describe('splitArgs', () => {
expect( expect(
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
notNxArg: true, __overrides_unparsed__: ['--notNxArg', 'true', '--override'],
_: ['--override'],
$0: '', $0: '',
}, },
'affected', 'affected',
@ -192,8 +186,7 @@ describe('splitArgs', () => {
expect( expect(
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
notNxArg: true, __overrides_unparsed__: ['--notNxArg', 'true', '--override'],
_: ['--override'],
$0: '', $0: '',
head: 'directlyOnCommandSha1', // higher priority than $NX_HEAD head: 'directlyOnCommandSha1', // higher priority than $NX_HEAD
}, },
@ -210,8 +203,7 @@ describe('splitArgs', () => {
expect( expect(
splitArgsIntoNxArgsAndOverrides( splitArgsIntoNxArgsAndOverrides(
{ {
notNxArg: true, __overrides_unparsed__: ['--notNxArg', 'true', '--override'],
_: ['--override'],
$0: '', $0: '',
base: 'directlyOnCommandSha2', // higher priority than $NX_BASE base: 'directlyOnCommandSha2', // higher priority than $NX_BASE
}, },
@ -234,8 +226,8 @@ describe('splitArgs', () => {
it('should be a number', () => { it('should be a number', () => {
const parallel = splitArgsIntoNxArgsAndOverrides( const parallel = splitArgsIntoNxArgsAndOverrides(
{ {
_: [],
$0: '', $0: '',
__overrides_unparsed__: [],
parallel: '5', parallel: '5',
}, },
'affected', 'affected',
@ -249,8 +241,8 @@ describe('splitArgs', () => {
it('should default to 3', () => { it('should default to 3', () => {
const parallel = splitArgsIntoNxArgsAndOverrides( const parallel = splitArgsIntoNxArgsAndOverrides(
{ {
_: [],
$0: '', $0: '',
__overrides_unparsed__: [],
parallel: '', parallel: '',
}, },
'affected', 'affected',
@ -264,8 +256,8 @@ describe('splitArgs', () => {
it('should be 3 when set to true', () => { it('should be 3 when set to true', () => {
const parallel = splitArgsIntoNxArgsAndOverrides( const parallel = splitArgsIntoNxArgsAndOverrides(
{ {
_: [],
$0: '', $0: '',
__overrides_unparsed__: [],
parallel: 'true', parallel: 'true',
}, },
'affected', 'affected',
@ -279,8 +271,8 @@ describe('splitArgs', () => {
it('should be 1 when set to false', () => { it('should be 1 when set to false', () => {
const parallel = splitArgsIntoNxArgsAndOverrides( const parallel = splitArgsIntoNxArgsAndOverrides(
{ {
_: [],
$0: '', $0: '',
__overrides_unparsed__: [],
parallel: 'false', parallel: 'false',
}, },
'affected', 'affected',
@ -294,8 +286,8 @@ describe('splitArgs', () => {
it('should use the maxParallel option when given', () => { it('should use the maxParallel option when given', () => {
const parallel = splitArgsIntoNxArgsAndOverrides( const parallel = splitArgsIntoNxArgsAndOverrides(
{ {
_: [],
$0: '', $0: '',
__overrides_unparsed__: [],
parallel: '', parallel: '',
maxParallel: 5, maxParallel: 5,
}, },
@ -310,8 +302,8 @@ describe('splitArgs', () => {
it('should use the maxParallel option when given', () => { it('should use the maxParallel option when given', () => {
const parallel = splitArgsIntoNxArgsAndOverrides( const parallel = splitArgsIntoNxArgsAndOverrides(
{ {
_: [],
$0: '', $0: '',
__overrides_unparsed__: [],
parallel: '', parallel: '',
maxParallel: 5, maxParallel: 5,
}, },

View File

@ -1,104 +1,11 @@
import * as yargsParser from 'yargs-parser'; import * as yargsParser from 'yargs-parser';
import * as yargs from 'yargs'; import type { Arguments } from 'yargs';
import { TEN_MEGABYTES } from '../project-graph/file-utils'; import { TEN_MEGABYTES } from '../project-graph/file-utils';
import { output } from './output'; import { output } from './output';
import { NxJsonConfiguration } from '../config/nx-json'; import { NxJsonConfiguration } from '../config/nx-json';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import { serializeOverridesIntoCommandLine } from './serialize-overrides-into-command-line';
import { ProjectGraph } from '../config/project-graph'; import { ProjectGraph } from '../config/project-graph';
export function names(name: string): {
name: string;
className: string;
propertyName: string;
constantName: string;
fileName: string;
} {
return {
name,
className: toClassName(name),
propertyName: toPropertyName(name),
constantName: toConstantName(name),
fileName: toFileName(name),
};
}
/**
* Hyphenated to UpperCamelCase
*/
function toClassName(str: string): string {
return toCapitalCase(toPropertyName(str));
}
/**
* Hyphenated to lowerCamelCase
*/
function toPropertyName(s: string): string {
return s
.replace(/([^a-zA-Z0-9])+(.)?/g, (_, __, chr) =>
chr ? chr.toUpperCase() : ''
)
.replace(/[^a-zA-Z\d]/g, '')
.replace(/^([A-Z])/, (m) => m.toLowerCase());
}
/**
* Hyphenated to CONSTANT_CASE
*/
function toConstantName(s: string): string {
return s.replace(/([^a-zA-Z0-9])/g, '_').toUpperCase();
}
/**
* Upper camelCase to lowercase, hyphenated
*/
function toFileName(s: string): string {
return s
.replace(/([a-z\d])([A-Z])/g, '$1_$2')
.toLowerCase()
.replace(/[ _]/g, '-');
}
/**
* Capitalizes the first letter of a string
*/
function toCapitalCase(s: string): string {
return s.charAt(0).toUpperCase() + s.slice(1);
}
const runOne: string[] = [
'target',
'configuration',
'prod',
'runner',
'parallel',
'maxParallel',
'exclude',
'help',
'skipNxCache',
'outputStyle',
'nxBail',
'nxIgnoreCycles',
'verbose',
'cloud',
'dte',
];
const runMany: string[] = [...runOne, 'projects', 'all'];
const runAffected: string[] = [
...runOne,
'untracked',
'uncommitted',
'all',
'base',
'head',
'files',
'plain',
'select',
'type',
];
export interface RawNxArgs extends NxArgs { export interface RawNxArgs extends NxArgs {
prod?: boolean; prod?: boolean;
} }
@ -128,79 +35,40 @@ export interface NxArgs {
type?: string; type?: string;
} }
const ignoreArgs = ['$0', '_'];
export function splitArgsIntoNxArgsAndOverrides( export function splitArgsIntoNxArgsAndOverrides(
args: { [k: string]: any }, args: { [k: string]: any },
mode: 'run-one' | 'run-many' | 'affected' | 'print-affected', mode: 'run-one' | 'run-many' | 'affected' | 'print-affected',
options = { printWarnings: true }, options = { printWarnings: true },
nxJson: NxJsonConfiguration nxJson: NxJsonConfiguration
): { nxArgs: NxArgs; overrides: yargs.Arguments } { ): { nxArgs: NxArgs; overrides: Arguments } {
if (!args.__overrides__ && args._) { if (!args.__overrides_unparsed__ && args._) {
// required for backwards compatibility // required for backwards compatibility
args.__overrides__ = args._; args.__overrides_unparsed__ = args._;
delete args._;
}
// This handles the way Lerna passes in overrides
if (!args.__overrides_unparsed__ && args.__overrides__) {
// required for backwards compatibility
args.__overrides_unparsed__ = args.__overrides__;
delete args._; delete args._;
} }
const nxSpecific = const nxArgs: RawNxArgs = args;
mode === 'run-one' ? runOne : mode === 'run-many' ? runMany : runAffected; let overrides = yargsParser(args.__overrides_unparsed__ as string[], {
let explicitOverrides;
if (args.__overrides__) {
explicitOverrides = yargsParser(args.__overrides__ as string[], {
configuration: { configuration: {
'camel-case-expansion': false, 'camel-case-expansion': false,
'dot-notation': false, 'dot-notation': true,
}, },
}); });
if (!explicitOverrides._ || explicitOverrides._.length === 0) {
delete explicitOverrides._;
}
}
const overridesFromMainArgs = {} as any;
if (
args['__positional_overrides__'] &&
args['__positional_overrides__'].length > 0
) {
overridesFromMainArgs['_'] = args['__positional_overrides__'];
}
const nxArgs: RawNxArgs = {};
Object.entries(args).forEach(([key, value]) => {
const camelCased = names(key).propertyName;
if (nxSpecific.includes(camelCased) || camelCased.startsWith('nx')) {
if (value !== undefined) nxArgs[camelCased] = value;
} else if (
!ignoreArgs.includes(key) &&
key !== '__positional_overrides__' &&
key !== '__overrides__'
) {
overridesFromMainArgs[key] = value;
}
});
let overrides; if (!overrides._ || overrides._.length === 0) {
if (explicitOverrides) { delete overrides._;
overrides = explicitOverrides;
overrides['__overrides_unparsed__'] = args.__overrides__;
if (
Object.keys(overridesFromMainArgs).length > 0 &&
options.printWarnings
) {
const s = Object.keys(overridesFromMainArgs).join(', ');
output.warn({
title: `Nx didn't recognize the following args: ${s}`,
bodyLines: [
"When using '--' all executor args have to be defined after '--'.",
],
});
}
} else {
overrides = overridesFromMainArgs;
overrides['__overrides_unparsed__'] = serializeOverridesIntoCommandLine(
overridesFromMainArgs
);
} }
overrides.__overrides_unparsed__ = args.__overrides_unparsed__;
delete (nxArgs as any).$0;
delete (nxArgs as any).__overrides_unparsed__;
if (mode === 'run-many') { if (mode === 'run-many') {
if (!nxArgs.projects) { if (!nxArgs.projects) {
nxArgs.projects = []; nxArgs.projects = [];
@ -232,21 +100,6 @@ export function splitArgsIntoNxArgsAndOverrides(
}); });
} }
if (
!nxArgs.files &&
!nxArgs.uncommitted &&
!nxArgs.untracked &&
!nxArgs.base &&
!nxArgs.head &&
!nxArgs.all &&
overridesFromMainArgs._ &&
overridesFromMainArgs._.length >= 2
) {
throw new Error(
`Nx no longer supports using positional arguments for base and head. Please use --base and --head instead.`
);
}
// Allow setting base and head via environment variables (lower priority then direct command arguments) // Allow setting base and head via environment variables (lower priority then direct command arguments)
if (!nxArgs.base && process.env.NX_BASE) { if (!nxArgs.base && process.env.NX_BASE) {
nxArgs.base = process.env.NX_BASE; nxArgs.base = process.env.NX_BASE;

View File

@ -183,6 +183,7 @@ export async function parseCommand(
type: builderOptionTypes[key], type: builderOptionTypes[key],
choices: builderOptionsChoices[key], choices: builderOptionsChoices[key],
deprecated: builderDeprecatedOptions[key], deprecated: builderDeprecatedOptions[key],
hidden: builderOptions.hiddenOptions.includes(key),
})) || null, })) || null,
}; };
} }
@ -194,6 +195,7 @@ export function generateOptionsMarkdown(command): string {
command.options command.options
.sort((a, b) => sortAlphabeticallyFunction(a.name, b.name)) .sort((a, b) => sortAlphabeticallyFunction(a.name, b.name))
.filter(({ hidden }) => !hidden)
.forEach((option) => { .forEach((option) => {
response += `\n### ${ response += `\n### ${
option.deprecated ? `~~${option.name}~~` : option.name option.deprecated ? `~~${option.name}~~` : option.name