diff --git a/e2e/nx/src/run.test.ts b/e2e/nx/src/run.test.ts index dd8ca216eb55f..0518850a1b2be 100644 --- a/e2e/nx/src/run.test.ts +++ b/e2e/nx/src/run.test.ts @@ -60,6 +60,32 @@ describe('Nx Running Tests', () => { const output = runCLI(`echo ${proj} ${args}`); expect(output).toContain(`ECHO: ${args.replace(/^.*-- /, '')}`); }); + + it.each([ + { + args: '--test="hello world" "abc def"', + result: '--test="hello world" "abc def"', + }, + { + args: `--test="hello world" 'abc def'`, + result: '--test="hello world" "abc def"', + }, + { + args: `--test="hello world" 'abcdef'`, + result: '--test="hello world" abcdef', + }, + { + args: `--test='hello world' 'abcdef'`, + result: '--test="hello world" abcdef', + }, + { + args: `"--test='hello world' 'abcdef'"`, + result: `--test='hello world' 'abcdef'`, + }, + ])('should forward %args properly with quotes', ({ args, result }) => { + const output = runCLI(`echo ${proj} ${args}`); + expect(output).toContain(`ECHO: ${result}`); + }); }); it('should execute long running tasks', () => { diff --git a/packages/nx/bin/init-local.ts b/packages/nx/bin/init-local.ts index 41a8f02b59fd5..9ec83bca08f1f 100644 --- a/packages/nx/bin/init-local.ts +++ b/packages/nx/bin/init-local.ts @@ -81,10 +81,6 @@ export function rewriteTargetsAndProjects(args: string[]) { return newArgs; } -function wrapIntoQuotesIfNeeded(arg: string) { - return arg.indexOf(':') > -1 ? `"${arg}"` : arg; -} - function isKnownCommand(command: string) { const commands = [ ...Object.keys( diff --git a/packages/nx/src/executors/run-commands/run-commands.impl.spec.ts b/packages/nx/src/executors/run-commands/run-commands.impl.spec.ts index 65b79e9f1078c..aa2192f087380 100644 --- a/packages/nx/src/executors/run-commands/run-commands.impl.spec.ts +++ b/packages/nx/src/executors/run-commands/run-commands.impl.spec.ts @@ -323,6 +323,56 @@ describe('Run Commands', () => { ) ).toEqual('echo "hello world"'); }); + + it('should interpolate provided values with spaces', () => { + expect( + interpolateArgsIntoCommand( + 'echo', + { + unknownOptions: { hello: 'test 123' }, + parsedArgs: { hello: 'test 123' }, + } as any, + true + ) + ).toEqual('echo --hello="test 123"'); // should wrap in quotes + + expect( + interpolateArgsIntoCommand( + 'echo', + { + unknownOptions: { hello: '"test 123"' }, + parsedArgs: { hello: '"test 123"' }, + } as any, + true + ) + ).toEqual('echo --hello="test 123"'); // should leave double quotes + + expect( + interpolateArgsIntoCommand( + 'echo', + { + unknownOptions: { hello: "'test 123'" }, + parsedArgs: { hello: "'test 123'" }, + } as any, + true + ) + ).toEqual("echo --hello='test 123'"); // should leave single quote + + expect( + interpolateArgsIntoCommand( + 'echo', + { + __unparsed__: [ + '--hello=test 123', + 'hello world', + '"random config"', + '456', + ], + } as any, + true + ) + ).toEqual(`echo --hello="test 123" "hello world" "random config" 456`); // should wrap aroound __unparsed__ args with key value + }); }); describe('--color', () => { diff --git a/packages/nx/src/executors/run-commands/run-commands.impl.ts b/packages/nx/src/executors/run-commands/run-commands.impl.ts index 4675de6944f17..af8c4c8abc609 100644 --- a/packages/nx/src/executors/run-commands/run-commands.impl.ts +++ b/packages/nx/src/executors/run-commands/run-commands.impl.ts @@ -494,6 +494,7 @@ export function interpolateArgsIntoCommand( opts.parsedArgs[k] === opts.unknownOptions[k] ) .map((k) => `--${k}=${opts.unknownOptions[k]}`) + .map(wrapArgIntoQuotesIfNeeded) .join(' '); } if (opts.args) { @@ -505,7 +506,9 @@ export function interpolateArgsIntoCommand( opts.unparsedCommandArgs ); if (filterdParsedOptions.length > 0) { - args += ` ${filterdParsedOptions.join(' ')}`; + args += ` ${filterdParsedOptions + .map(wrapArgIntoQuotesIfNeeded) + .join(' ')}`; } } return `${command}${args}`; @@ -627,3 +630,21 @@ function registerProcessListener() { // will store results to the cache and will terminate this process }); } + +function wrapArgIntoQuotesIfNeeded(arg: string): string { + if (arg.includes('=')) { + const [key, value] = arg.split('='); + if ( + key.startsWith('--') && + value.includes(' ') && + !(value[0] === "'" || value[0] === '"') + ) { + return `${key}="${value}"`; + } + return arg; + } else if (arg.includes(' ') && !(arg[0] === "'" || arg[0] === '"')) { + return `"${arg}"`; + } else { + return arg; + } +}