/
githubmirror
/
angular-cli
Обзор
Документация
Войти
/
githubmirror
/
angular-cli
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/angular/cli/src/commands/generate/cli.ts
283 строки
10 KB
Ash Ramirez
refactor(@angular/cli): update aio links -> adev links
06 июн 2024, 12:12
06 июн 2024, 12:12
434a374
Код
Авторство
О чём код?
/** * @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.dev/license */ import { strings } from '@angular-devkit/core'; import { Collection } from '@angular-devkit/schematics'; import { FileSystemCollectionDescription, FileSystemSchematicDescription, } from '@angular-devkit/schematics/tools'; import { ArgumentsCamelCase, Argv } from 'yargs'; import { CommandModuleError, CommandModuleImplementation, Options, OtherOptions, } from '../../command-builder/command-module'; import { SchematicsCommandArgs, SchematicsCommandModule, } from '../../command-builder/schematics-command-module'; import { demandCommandFailureMessage } from '../../command-builder/utilities/command'; import { Option } from '../../command-builder/utilities/json-schema'; import { RootCommands } from '../command-config'; interface GenerateCommandArgs extends SchematicsCommandArgs { schematic?: string; } export default class GenerateCommandModule extends SchematicsCommandModule implements CommandModuleImplementation<GenerateCommandArgs> { command = 'generate'; aliases = RootCommands['generate'].aliases; describe = 'Generates and/or modifies files based on a schematic.'; longDescriptionPath?: string | undefined; override async builder(argv: Argv): Promise<Argv<GenerateCommandArgs>> { let localYargs = (await super.builder(argv)).command({ command: '$0 <schematic>', describe: 'Run the provided schematic.', builder: (localYargs) => localYargs .positional('schematic', { describe: 'The [collection:schematic] to run.', type: 'string', demandOption: true, }) .strict(), handler: (options) => this.handler(options as ArgumentsCamelCase<GenerateCommandArgs>), }); for (const [schematicName, collectionName] of await this.getSchematicsToRegister()) { const workflow = this.getOrCreateWorkflowForBuilder(collectionName); const collection = workflow.engine.createCollection(collectionName); const { description: { schemaJson, aliases: schematicAliases, hidden: schematicHidden, description: schematicDescription, }, } = collection.createSchematic(schematicName, true); if (!schemaJson) { continue; } const { 'x-deprecated': xDeprecated, description = schematicDescription, hidden = schematicHidden, } = schemaJson; const options = await this.getSchematicOptions(collection, schematicName, workflow); localYargs = localYargs.command({ command: await this.generateCommandString(collectionName, schematicName, options), // 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(schematicAliases) ? await this.generateCommandAliasesStrings(collectionName, schematicAliases) : undefined, builder: (localYargs) => this.addSchemaOptionsToCommand(localYargs, options).strict(), handler: (options) => this.handler({ ...options, schematic: `${collectionName}:${schematicName}`, } as ArgumentsCamelCase< SchematicsCommandArgs & { schematic: string; } >), }); } return localYargs.demandCommand(1, demandCommandFailureMessage); } async run(options: Options<GenerateCommandArgs> & OtherOptions): Promise<number | void> { const { dryRun, schematic, defaults, force, interactive, ...schematicOptions } = options; const [collectionName, schematicName] = this.parseSchematicInfo(schematic); if (!collectionName || !schematicName) { throw new CommandModuleError('A collection and schematic is required during execution.'); } return this.runSchematic({ collectionName, schematicName, schematicOptions, executionOptions: { dryRun, defaults, force, interactive, }, }); } private async getCollectionNames(): Promise<string[]> { const [collectionName] = this.parseSchematicInfo( // positional = [generate, component] or [generate] this.context.args.positional[1], ); 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. * * @example `component [name]` or `@schematics/angular:component [name]`. */ private async generateCommandString( collectionName: string, schematicName: string, options: Option[], ): Promise<string> { const dasherizedSchematicName = strings.dasherize(schematicName); // 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 = (await this.shouldAddCollectionNameAsPartOfCommand()) ? collectionName + ':' + dasherizedSchematicName : dasherizedSchematicName; const positionalArgs = options .filter((o) => o.positional !== undefined) .map((o) => { const label = `${strings.dasherize(o.name)}${o.type === 'array' ? ' ..' : ''}`; return o.required ? `<${label}>` : `[${label}]`; }) .join(' '); return `${commandName}${positionalArgs ? ' ' + positionalArgs : ''}`; } /** * Get schematics that can to be registered as subcommands. */ private async *getSchematics(): AsyncGenerator<{ schematicName: string; schematicAliases?: Set<string>; collectionName: string; }> { const seenNames = new Set<string>(); for (const collectionName of await this.getCollectionNames()) { const workflow = this.getOrCreateWorkflowForBuilder(collectionName); const collection = workflow.engine.createCollection(collectionName); for (const schematicName of collection.listSchematicNames(true /** includeHidden */)) { // If a schematic with this same name is already registered skip. if (!seenNames.has(schematicName)) { seenNames.add(schematicName); yield { schematicName, collectionName, schematicAliases: this.listSchematicAliases(collection, schematicName), }; } } } } private listSchematicAliases( collection: Collection<FileSystemCollectionDescription, FileSystemSchematicDescription>, schematicName: string, ): Set<string> | undefined { const description = collection.description.schematics[schematicName]; if (description) { return description.aliases && new Set(description.aliases); } // Extended collections if (collection.baseDescriptions) { for (const base of collection.baseDescriptions) { const description = base.schematics[schematicName]; if (description) { return description.aliases && new Set(description.aliases); } } } return undefined; } /** * Get schematics that should to be registered as subcommands. * * @returns a sorted list of schematic that needs to be registered as subcommands. */ private async getSchematicsToRegister(): Promise< [schematicName: string, collectionName: string][] > { const schematicsToRegister: [schematicName: string, collectionName: string][] = []; const [, schematicNameFromArgs] = this.parseSchematicInfo( // positional = [generate, component] or [generate] this.context.args.positional[1], ); for await (const { schematicName, collectionName, schematicAliases } of this.getSchematics()) { if ( schematicNameFromArgs && (schematicName === schematicNameFromArgs || schematicAliases?.has(schematicNameFromArgs)) ) { return [[schematicName, collectionName]]; } schematicsToRegister.push([schematicName, collectionName]); } // Didn't find the schematic or no schematic name was provided Ex: `ng generate --help`. return schematicsToRegister.sort(([nameA], [nameB]) => nameA.localeCompare(nameB, undefined, { sensitivity: 'accent' }), ); } }