Skip to content

Commit

Permalink
fix(@angular/cli): handle duplicate arguments
Browse files Browse the repository at this point in the history
With this change we add a Yargs middleware that normalizes non Array options when the argument has been provided multiple times.

By default, when an option is non array and it is provided multiple times in the command line, yargs
will not override it's value but instead it will be changed to an array unless `duplicate-arguments-array` is disabled.
But this option also have an effect on real array options which isn't desired.

See: yargs/yargs-parser#163 (comment)

Closes #22956
  • Loading branch information
alan-agius4 authored and dgp1130 committed Apr 11, 2022
1 parent d87b858 commit ff4eba3
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/angular/cli/src/command-builder/command-runner.ts
Expand Up @@ -33,6 +33,7 @@ import { PackageManagerUtils } from '../utilities/package-manager';
import { CommandContext, CommandModuleError, CommandScope } from './command-module';
import { addCommandModuleToYargs, demandCommandFailureMessage } from './utilities/command';
import { jsonHelpUsage } from './utilities/json-help';
import { normalizeOptionsMiddleware } from './utilities/normalize-options-middleware';

const COMMANDS = [
VersionCommandModule,
Expand Down Expand Up @@ -145,6 +146,7 @@ export async function runCommand(args: string[], logger: logging.Logger): Promis
})
.demandCommand(1, demandCommandFailureMessage)
.recommendCommands()
.middleware(normalizeOptionsMiddleware)
.version(false)
.showHelpOnFail(false)
.strict()
Expand Down
@@ -0,0 +1,37 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import * as yargs from 'yargs';

/**
* A Yargs middleware that normalizes non Array options when the argument has been provided multiple times.
*
* By default, when an option is non array and it is provided multiple times in the command line, yargs
* will not override it's value but instead it will be changed to an array unless `duplicate-arguments-array` is disabled.
* But this option also have an effect on real array options which isn't desired.
*
* See: https://github.com/yargs/yargs-parser/pull/163#issuecomment-516566614
*/
export function normalizeOptionsMiddleware(args: yargs.Arguments): void {
// `getOptions` is not included in the types even though it's public API.
// https://github.com/yargs/yargs/issues/2098
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { array } = (yargs as any).getOptions();
const arrayOptions = new Set(array);

for (const [key, value] of Object.entries(args)) {
if (key !== '_' && Array.isArray(value) && !arrayOptions.has(key)) {
const newValue = value.pop();
// eslint-disable-next-line no-console
console.warn(
`Option '${key}' has been specified multiple times. The value '${newValue}' will be used.`,
);
args[key] = newValue;
}
}
}
19 changes: 19 additions & 0 deletions tests/legacy-cli/e2e/tests/misc/duplicate-command-line-option.ts
@@ -0,0 +1,19 @@
import { ng } from '../../utils/process';
import { expectFileToExist } from '../../utils/fs';

export default async function () {
const { stderr } = await ng(
'generate',
'component',
'test-component',
'--style=scss',
'--style=sass',
);

const warningMatch = `Option 'style' has been specified multiple times. The value 'sass' will be used`;
if (!stderr.includes(warningMatch)) {
throw new Error(`Expected stderr to contain: "${warningMatch}".`);
}

await expectFileToExist('src/app/test-component/test-component.component.sass');
}

0 comments on commit ff4eba3

Please sign in to comment.