diff --git a/package.json b/package.json index 5a80898a5a..e0bfb04936 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,7 @@ "express": "4.17.1", "file-loader": "4.2.0", "find-cache-dir": "3.0.0", + "flat": "^5.0.2", "fork-ts-checker-webpack-plugin": "^3.1.1", "fs-extra": "7.0.1", "glob": "7.1.4", diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 0437f349a2..f18f6ef29a 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -68,6 +68,7 @@ "yargs": "^11.0.0", "chalk": "2.4.2", "@nrwl/cli": "*", - "axios": "0.19.2" + "axios": "0.19.2", + "flat": "^5.0.2" } } diff --git a/packages/workspace/src/tasks-runner/task-orchestrator.ts b/packages/workspace/src/tasks-runner/task-orchestrator.ts index 3fe0a5112f..2abcccdd2f 100644 --- a/packages/workspace/src/tasks-runner/task-orchestrator.ts +++ b/packages/workspace/src/tasks-runner/task-orchestrator.ts @@ -2,7 +2,7 @@ import { Cache, TaskWithCachedResult } from './cache'; import { cliCommand } from '../core/file-utils'; import { ProjectGraph } from '../core/project-graph'; import { AffectedEventType, Task } from './tasks-runner'; -import { getOutputs } from './utils'; +import { getOutputs, unparse } from './utils'; import { fork } from 'child_process'; import { DefaultTasksRunnerOptions } from './default-tasks-runner'; import { output } from '../utils/output'; @@ -318,13 +318,7 @@ export class TaskOrchestrator { } private getCommandArgs(task: Task) { - const args = Object.entries(task.overrides || {}).map(([prop, value]) => - typeof value === 'boolean' - ? value - ? `--${prop}` - : `--no-${prop}` - : `--${prop}=${value}` - ); + const args: string[] = unparse(task.overrides || {}); const config = task.target.configuration ? `:${task.target.configuration}` diff --git a/packages/workspace/src/tasks-runner/utils.spec.ts b/packages/workspace/src/tasks-runner/utils.spec.ts index c71f557929..df04766ec9 100644 --- a/packages/workspace/src/tasks-runner/utils.spec.ts +++ b/packages/workspace/src/tasks-runner/utils.spec.ts @@ -1,4 +1,7 @@ -import { getOutputsForTargetAndConfiguration } from '@nrwl/workspace/src/tasks-runner/utils'; +import { + getOutputsForTargetAndConfiguration, + unparse, +} from '@nrwl/workspace/src/tasks-runner/utils'; describe('utils', () => { describe('getOutputsForTargetAndConfiguration', () => { @@ -181,4 +184,59 @@ describe('utils', () => { ).toEqual(['dist/root-myapp']); }); }); + + describe('unparse', () => { + it('should unparse options whose values are primitives', () => { + const options = { + boolean1: false, + boolean2: true, + number: 4, + string: 'foo', + 'empty-string': '', + ignore: null, + }; + + expect(unparse(options)).toEqual([ + '--no-boolean1', + '--boolean2', + '--number=4', + '--string=foo', + '--empty-string=', + ]); + }); + + it('should unparse options whose values are arrays', () => { + const options = { + array1: [1, 2], + array2: [3, 4], + }; + + expect(unparse(options)).toEqual([ + '--array1=1', + '--array1=2', + '--array2=3', + '--array2=4', + ]); + }); + + it('should unparse options whose values are objects', () => { + const options = { + foo: { + x: 'x', + y: 'y', + w: [1, 2], + z: [3, 4], + }, + }; + + expect(unparse(options)).toEqual([ + '--foo.x=x', + '--foo.y=y', + '--foo.w=1', + '--foo.w=2', + '--foo.z=3', + '--foo.z=4', + ]); + }); + }); }); diff --git a/packages/workspace/src/tasks-runner/utils.ts b/packages/workspace/src/tasks-runner/utils.ts index 78c8bcd6c5..773d17384c 100644 --- a/packages/workspace/src/tasks-runner/utils.ts +++ b/packages/workspace/src/tasks-runner/utils.ts @@ -1,5 +1,6 @@ import { Task } from './tasks-runner'; import { ProjectGraphNode } from '../core/project-graph'; +import * as flatten from 'flat'; const commonCommands = ['build', 'test', 'lint', 'e2e', 'deploy']; @@ -75,3 +76,34 @@ export function getOutputsForTargetAndConfiguration( return []; } } + +export function unparse(options: Object): string[] { + const unparsed = []; + for (const key of Object.keys(options)) { + const value = options[key]; + unparseOption(key, value, unparsed); + } + + return unparsed; +} + +function unparseOption(key: string, value: any, unparsed: string[]) { + if (value === true) { + unparsed.push(`--${key}`); + } else if (value === false) { + unparsed.push(`--no-${key}`); + } else if (Array.isArray(value)) { + value.forEach((item) => unparseOption(key, item, unparsed)); + } else if (Object.prototype.toString.call(value) === '[object Object]') { + const flattened = flatten(value, { safe: true }); + for (const flattenedKey in flattened) { + unparseOption( + `${key}.${flattenedKey}`, + flattened[flattenedKey], + unparsed + ); + } + } else if (typeof value === 'string' || value != null) { + unparsed.push(`--${key}=${value}`); + } +} diff --git a/yarn.lock b/yarn.lock index 03727d4ed3..390a6ecc5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10094,6 +10094,11 @@ flat-cache@^2.0.1: rimraf "2.6.3" write "1.0.3" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flatted@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"