Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): properly serialize override with object arg for command line #12499

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 2 additions & 38 deletions packages/nx/src/tasks-runner/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { workspaceRoot } from '../utils/workspace-root';
import { NxJsonConfiguration } from '../config/nx-json';
import { joinPathFragments } from '../utils/path';
import { isRelativePath } from 'nx/src/utils/fileutils';
import { serializeOverridesIntoCommandLine } from 'nx/src/utils/serialize-overrides-into-command-line';

export function getCommandAsString(execCommand: string, task: Task) {
const args = getPrintableCommandArgsForTask(task);
Expand Down Expand Up @@ -362,42 +363,5 @@ function longRunningTask(task: Task) {

// TODO: vsavkin remove when nx-cloud doesn't depend on it
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<any, any>(value, { safe: true });
for (const flattenedKey in flattened) {
unparseOption(
`${key}.${flattenedKey}`,
flattened[flattenedKey],
unparsed
);
}
} else if (
typeof value === 'string' &&
stringShouldBeWrappedIntoQuotes(value)
) {
const sanitized = value.replace(/"/g, String.raw`\"`);
unparsed.push(`--${key}="${sanitized}"`);
} else if (value != null) {
unparsed.push(`--${key}=${value}`);
}
}

function stringShouldBeWrappedIntoQuotes(str: string) {
return str.includes(' ') || str.includes('{') || str.includes('"');
return serializeOverridesIntoCommandLine(options);
}
4 changes: 2 additions & 2 deletions packages/nx/src/utils/command-line-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ describe('splitArgs', () => {
{} as any
).overrides
).toEqual({
__overrides_unparsed__: ['--notNxArg=true'],
__overrides_unparsed__: ['--notNxArg'],
notNxArg: true,
});
});
Expand All @@ -127,7 +127,7 @@ describe('splitArgs', () => {
).overrides
).toEqual({
_: ['positional'],
__overrides_unparsed__: ['positional', '--notNxArg=true'],
__overrides_unparsed__: ['positional', '--notNxArg'],
notNxArg: true,
});
});
Expand Down
51 changes: 41 additions & 10 deletions packages/nx/src/utils/serialize-overrides-into-command-line.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,46 @@
export function serializeOverridesIntoCommandLine(args: {
import { flatten } from 'flat';

export function serializeOverridesIntoCommandLine(options: {
[k: string]: any;
}): string[] {
const r = args['_'] ? [...args['_']] : [];
Object.keys(args).forEach((a) => {
if (a !== '_') {
r.push(
typeof args[a] === 'string' && args[a].includes(' ')
? `--${a}="${args[a].replace(/"/g, '"')}"`
: `--${a}=${args[a]}`
const unparsed = options._ ? [...options._] : [];
for (const key of Object.keys(options)) {
const value = options[key];
if (key !== '_') {
serializeOption(key, value, unparsed);
}
}

return unparsed;
}

function serializeOption(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) => serializeOption(key, item, unparsed));
} else if (Object.prototype.toString.call(value) === '[object Object]') {
const flattened = flatten<any, any>(value, { safe: true });
for (const flattenedKey in flattened) {
serializeOption(
`${key}.${flattenedKey}`,
flattened[flattenedKey],
unparsed
);
}
});
return r;
} else if (
typeof value === 'string' &&
stringShouldBeWrappedIntoQuotes(value)
) {
const sanitized = value.replace(/"/g, String.raw`\"`);
unparsed.push(`--${key}="${sanitized}"`);
} else if (value != null) {
unparsed.push(`--${key}=${value}`);
}
}

function stringShouldBeWrappedIntoQuotes(str: string) {
return str.includes(' ') || str.includes('{') || str.includes('"');
}