From 1772c871ba97fd3d23960cd0305a944358f487dd Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:13:23 -0400 Subject: [PATCH] refactor(@angular/cli): provide a default extract-i18n target for applications The `extract-i18n` command will now use a default project target and builder name if the target entry is not explicitly defined. This allows the removal of additional configuration from an `angular.json` file. If the target is already present than it will take priority over any default builder behavior. Infrastructure is also now present to allow other architect commands to have similar functionality in the future. (cherry picked from commit afbe3119bfe6074d83fe9ac6a5db2aa08d108f34) --- .../architect-command-module.ts | 29 ++++++++++++++- .../cli/src/commands/extract-i18n/cli.ts | 37 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/packages/angular/cli/src/command-builder/architect-command-module.ts b/packages/angular/cli/src/command-builder/architect-command-module.ts index 9b6ff3877bd9..4855b629b360 100644 --- a/packages/angular/cli/src/command-builder/architect-command-module.ts +++ b/packages/angular/cli/src/command-builder/architect-command-module.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.dev/license */ +import { Target } from '@angular-devkit/architect'; +import { workspaces } from '@angular-devkit/core'; import { Argv } from 'yargs'; import { getProjectByCwd } from '../utilities/config'; import { memoize } from '../utilities/memoize'; @@ -28,7 +30,33 @@ export abstract class ArchitectCommandModule { abstract readonly multiTarget: boolean; + findDefaultBuilderName?( + project: workspaces.ProjectDefinition, + target: Target, + ): Promise; + async builder(argv: Argv): Promise> { + const target = this.getArchitectTarget(); + + // Add default builder if target is not in project and a command default is provided + if (this.findDefaultBuilderName && this.context.workspace) { + for (const [project, projectDefinition] of this.context.workspace.projects) { + if (projectDefinition.targets.has(target)) { + continue; + } + + const defaultBuilder = await this.findDefaultBuilderName(projectDefinition, { + project, + target, + }); + if (defaultBuilder) { + projectDefinition.targets.set(target, { + builder: defaultBuilder, + }); + } + } + } + const project = this.getArchitectProject(); const { jsonHelp, getYargsCompletions, help } = this.context.args.options; @@ -60,7 +88,6 @@ export abstract class ArchitectCommandModule return localYargs; } - const target = this.getArchitectTarget(); const schemaOptions = await this.getArchitectTargetOptions({ project, target, diff --git a/packages/angular/cli/src/commands/extract-i18n/cli.ts b/packages/angular/cli/src/commands/extract-i18n/cli.ts index bbe8bc07a634..4f3dea2d8e7e 100644 --- a/packages/angular/cli/src/commands/extract-i18n/cli.ts +++ b/packages/angular/cli/src/commands/extract-i18n/cli.ts @@ -6,6 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ +import { workspaces } from '@angular-devkit/core'; +import { createRequire } from 'node:module'; +import { join } from 'node:path'; import { ArchitectCommandModule } from '../../command-builder/architect-command-module'; import { CommandModuleImplementation } from '../../command-builder/command-module'; @@ -17,4 +20,38 @@ export default class ExtractI18nCommandModule command = 'extract-i18n [project]'; describe = 'Extracts i18n messages from source code.'; longDescriptionPath?: string | undefined; + + override async findDefaultBuilderName( + project: workspaces.ProjectDefinition, + ): Promise { + // Only application type projects have a default i18n extraction target + if (project.extensions['projectType'] !== 'application') { + return; + } + + const buildTarget = project.targets.get('build'); + if (!buildTarget) { + // No default if there is no build target + return; + } + + // Provide a default based on the defined builder for the 'build' target + switch (buildTarget.builder) { + case '@angular-devkit/build-angular:application': + case '@angular-devkit/build-angular:browser-esbuild': + case '@angular-devkit/build-angular:browser': + return '@angular-devkit/build-angular:extract-i18n'; + case '@angular/build:application': + return '@angular/build:extract-i18n'; + } + + // For other builders, check for `@angular-devkit/build-angular` and use if found. + // This package is safer to use since it supports both application builder types. + try { + const projectRequire = createRequire(join(this.context.root, project.root) + '/'); + projectRequire.resolve('@angular-devkit/build-angular'); + + return '@angular-devkit/build-angular:extract-i18n'; + } catch {} + } }