Skip to content

fix(@angular/cli): register schematic aliases when providing collection name in ng generate #24542

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

Merged
merged 1 commit into from
Jan 13, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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');
}
}