diff --git a/packages/angular/cli/src/commands/generate/cli.ts b/packages/angular/cli/src/commands/generate/cli.ts index 43e66ae18ffe..eaa0dad05429 100644 --- a/packages/angular/cli/src/commands/generate/cli.ts +++ b/packages/angular/cli/src/commands/generate/cli.ts @@ -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}` }), @@ -120,6 +122,41 @@ export class GenerateCommandModule return collectionName ? [collectionName] : [...(await this.getSchematicCollections())]; } + private async shouldAddCollectionNameAsPartOfCommand(): Promise { + 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 { + // 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. * @@ -130,23 +167,14 @@ export class GenerateCommandModule schematicName: string, options: Option[], ): Promise { - 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) @@ -165,6 +193,7 @@ export class GenerateCommandModule */ private async *getSchematics(): AsyncGenerator<{ schematicName: string; + schematicAliases?: Set; collectionName: string; }> { const seenNames = new Set(); @@ -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 }; } } } @@ -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]]; } diff --git a/tests/legacy-cli/e2e/tests/generate/schematic-aliases.ts b/tests/legacy-cli/e2e/tests/generate/schematic-aliases.ts new file mode 100644 index 000000000000..926c96d7aacc --- /dev/null +++ b/tests/legacy-cli/e2e/tests/generate/schematic-aliases.ts @@ -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'); + } +}