Skip to content

Commit

Permalink
fix(@angular/cli): register schematic aliases when providing collecti…
Browse files Browse the repository at this point in the history
…on name in `ng generate`

Previously, schematic aliases were not registered when a collection name was provided to `ng generate`.  Example: `ng generate c` where `c` is an alias for `component` would work, but `ng generate @schematics/angular:c` would fail. This commits fixes the schematic registration to handle the latter case.

Closes #24518
  • Loading branch information
alan-agius4 authored and angular-robot[bot] committed Jan 13, 2023
1 parent 89b9d94 commit 3ebb195
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 16 deletions.
67 changes: 51 additions & 16 deletions packages/angular/cli/src/commands/generate/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ export class GenerateCommandModule
// When 'describe' is set to false, it results in a hidden command.
describe: hidden === true ? false : typeof description === 'string' ? description : '',
deprecated: xDeprecated === true || typeof xDeprecated === 'string' ? xDeprecated : false,
aliases: Array.isArray(aliases) ? (aliases as string[]) : undefined,
aliases: Array.isArray(aliases)
? await this.generateCommandAliasesStrings(collectionName, aliases as string[])
: undefined,
builder: (localYargs) => this.addSchemaOptionsToCommand(localYargs, options).strict(),
handler: (options) =>
this.handler({ ...options, schematic: `${collectionName}:${schematicName}` }),
Expand Down Expand Up @@ -120,6 +122,41 @@ export class GenerateCommandModule
return collectionName ? [collectionName] : [...(await this.getSchematicCollections())];
}

private async shouldAddCollectionNameAsPartOfCommand(): Promise<boolean> {
const [collectionNameFromArgs] = this.parseSchematicInfo(
// positional = [generate, component] or [generate]
this.context.args.positional[1],
);

const schematicCollectionsFromConfig = await this.getSchematicCollections();
const collectionNames = await this.getCollectionNames();

// Only add the collection name as part of the command when it's not a known
// schematics collection or when it has been provided via the CLI.
// Ex:`ng generate @schematics/angular:c`
return (
!!collectionNameFromArgs ||
!collectionNames.some((c) => schematicCollectionsFromConfig.has(c))
);
}

/**
* Generate an aliases string array to be passed to the command builder.
*
* @example `[component]` or `[@schematics/angular:component]`.
*/
private async generateCommandAliasesStrings(
collectionName: string,
schematicAliases: string[],
): Promise<string[]> {
// Only add the collection name as part of the command when it's not a known
// schematics collection or when it has been provided via the CLI.
// Ex:`ng generate @schematics/angular:c`
return (await this.shouldAddCollectionNameAsPartOfCommand())
? schematicAliases.map((alias) => `${collectionName}:${alias}`)
: schematicAliases;
}

/**
* Generate a command string to be passed to the command builder.
*
Expand All @@ -130,23 +167,14 @@ export class GenerateCommandModule
schematicName: string,
options: Option[],
): Promise<string> {
const [collectionNameFromArgs] = this.parseSchematicInfo(
// positional = [generate, component] or [generate]
this.context.args.positional[1],
);

const dasherizedSchematicName = strings.dasherize(schematicName);
const schematicCollectionsFromConfig = await this.getSchematicCollections();
const collectionNames = await this.getCollectionNames();

// Only add the collection name as part of the command when it's not a known
// schematics collection or when it has been provided via the CLI.
// Ex:`ng generate @schematics/angular:component`
const commandName =
!!collectionNameFromArgs ||
!collectionNames.some((c) => schematicCollectionsFromConfig.has(c))
? collectionName + ':' + dasherizedSchematicName
: dasherizedSchematicName;
const commandName = (await this.shouldAddCollectionNameAsPartOfCommand())
? collectionName + ':' + dasherizedSchematicName
: dasherizedSchematicName;

const positionalArgs = options
.filter((o) => o.positional !== undefined)
Expand All @@ -165,6 +193,7 @@ export class GenerateCommandModule
*/
private async *getSchematics(): AsyncGenerator<{
schematicName: string;
schematicAliases?: Set<string>;
collectionName: string;
}> {
const seenNames = new Set<string>();
Expand All @@ -176,7 +205,10 @@ export class GenerateCommandModule
// If a schematic with this same name is already registered skip.
if (!seenNames.has(schematicName)) {
seenNames.add(schematicName);
yield { schematicName, collectionName };
const { aliases } = collection.description.schematics[schematicName];
const schematicAliases = aliases && new Set(aliases);

yield { schematicName, schematicAliases, collectionName };
}
}
}
Expand All @@ -196,8 +228,11 @@ export class GenerateCommandModule
this.context.args.positional[1],
);

for await (const { schematicName, collectionName } of this.getSchematics()) {
if (schematicName === schematicNameFromArgs) {
for await (const { schematicName, collectionName, schematicAliases } of this.getSchematics()) {
if (
schematicNameFromArgs &&
(schematicName === schematicNameFromArgs || schematicAliases?.has(schematicNameFromArgs))
) {
return [[schematicName, collectionName]];
}

Expand Down
14 changes: 14 additions & 0 deletions tests/legacy-cli/e2e/tests/generate/schematic-aliases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ng } from '../../utils/process';

export default async function () {
const schematicNameVariation = [
'component',
'c',
'@schematics/angular:component',
'@schematics/angular:c',
];

for (const schematic of schematicNameVariation) {
await ng('generate', schematic, 'comp-name', '--display-block', '--dry-run');
}
}

0 comments on commit 3ebb195

Please sign in to comment.