From fba08a505a9ff099dc96bb735ceb9effb291e2c1 Mon Sep 17 00:00:00 2001 From: Victor Savkin Date: Tue, 7 May 2019 10:51:59 -0400 Subject: [PATCH] feat(nx): support passing args to run-commands builder --- docs/api-builders/run-commands.md | 6 +++ .../run-commands/run-commands.builder.spec.ts | 48 +++++++++++++++++++ .../src/run-commands/run-commands.builder.ts | 38 ++++++++++++++- .../builders/src/run-commands/schema.json | 4 ++ 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/docs/api-builders/run-commands.md b/docs/api-builders/run-commands.md index b0307a393e7ee..763e415bd8148 100644 --- a/docs/api-builders/run-commands.md +++ b/docs/api-builders/run-commands.md @@ -4,6 +4,12 @@ Run commands ## Properties +### args + +Type: `string` + +Extra arguments. You can pass them as follows: ng run project:target --args='--wait=100'. You can them use {args.wait} syntax to interpolate them in angular.json + ### parallel Default: `true` diff --git a/packages/builders/src/run-commands/run-commands.builder.spec.ts b/packages/builders/src/run-commands/run-commands.builder.spec.ts index d9333d16fcbee..169724a05c391 100644 --- a/packages/builders/src/run-commands/run-commands.builder.spec.ts +++ b/packages/builders/src/run-commands/run-commands.builder.spec.ts @@ -194,4 +194,52 @@ describe('Command Runner Builder', () => { expect(result).toEqual({ success: false }); expect(readFile(f)).toEqual('1'); }); + + it('should throw when invalid args', async () => { + const root = normalize('/root'); + const f = fileSync().name; + + try { + await builder + .run({ + root, + builder: '@nrwl/run-commands', + projectType: 'application', + options: { + commands: [ + { + command: `echo {args.key} >> ${f}` + } + ], + args: 'key=value' + } + }) + .toPromise(); + } catch (e) { + expect(e.message).toEqual('Invalid args: key=value'); + } + }); + + it('should enable parameter substitution', async () => { + const root = normalize('/root'); + const f = fileSync().name; + const result = await builder + .run({ + root, + builder: '@nrwl/run-commands', + projectType: 'application', + options: { + commands: [ + { + command: `echo {args.key} >> ${f}` + } + ], + args: '--key=value' + } + }) + .toPromise(); + + expect(result).toEqual({ success: true }); + expect(readFile(f)).toEqual('value'); + }); }); diff --git a/packages/builders/src/run-commands/run-commands.builder.ts b/packages/builders/src/run-commands/run-commands.builder.ts index af203fe72385e..77efae5e9e980 100644 --- a/packages/builders/src/run-commands/run-commands.builder.ts +++ b/packages/builders/src/run-commands/run-commands.builder.ts @@ -15,6 +15,8 @@ export interface RunCommandsBuilderOptions { commands: { command: string }[]; parallel?: boolean; readyWhen?: string; + args?: string; + parsedArgs?: { [k: string]: string }; } export default class RunCommandsBuilder @@ -22,6 +24,10 @@ export default class RunCommandsBuilder run( config: BuilderConfiguration ): Observable { + config.options.parsedArgs = { + ...(config.options as any), + ...this.parseArgs(config.options.args) + }; return Observable.create(async observer => { if (!config || !config.options || !config.options.commands) { observer.error( @@ -62,7 +68,10 @@ export default class RunCommandsBuilder config: BuilderConfiguration ) { const procs = config.options.commands.map(c => - this.createProcess(c.command, config.options.readyWhen).then(result => ({ + this.createProcess( + this.transformCommand(c.command, config.options.parsedArgs), + config.options.readyWhen + ).then(result => ({ result, command: c.command })) @@ -106,7 +115,7 @@ export default class RunCommandsBuilder >(async (m, c) => { if ((await m) === null) { const success = await this.createProcess( - c.command, + this.transformCommand(c.command, config.options.parsedArgs), config.options.readyWhen ); return !success ? c.command : null; @@ -151,4 +160,29 @@ export default class RunCommandsBuilder }); }); } + + private transformCommand(command: string, args: any) { + const regex = /{args\.([^}]+)}/g; + return command.replace(regex, (_, group: string) => args[group]); + } + + private parseArgs(args: string) { + if (!args) { + return {}; + } + return args + .split(' ') + .map(t => t.trim()) + .reduce((m, c) => { + if (!c.startsWith('--')) { + throw new Error(`Invalid args: ${args}`); + } + const [key, value] = c.substring(2).split('='); + if (!key || !value) { + throw new Error(`Invalid args: ${args}`); + } + m[key] = value; + return m; + }, {}); + } } diff --git a/packages/builders/src/run-commands/schema.json b/packages/builders/src/run-commands/schema.json index cc216e3ebd2a6..db7759bb34f7d 100644 --- a/packages/builders/src/run-commands/schema.json +++ b/packages/builders/src/run-commands/schema.json @@ -25,6 +25,10 @@ "readyWhen": { "type": "string", "description": "String to appear in stdout or stderr that indicates that the task is done. This option can only be used when parallel is set to true. If not specified, the task is done when all the child processes complete." + }, + "args": { + "type": "string", + "description": "Extra arguments. You can pass them as follows: ng run project:target --args='--wait=100'. You can them use {args.wait} syntax to interpolate them in angular.json" } }, "required": ["commands"]