Skip to content
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

feat(angular): add the extract-i18n executor #21802

Merged
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
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -6706,6 +6706,14 @@
"isExternal": false,
"disableCollapsible": false
},
{
"id": "extract-i18n",
"path": "/nx-api/angular/executors/extract-i18n",
"name": "extract-i18n",
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "webpack-browser",
"path": "/nx-api/angular/executors/webpack-browser",
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/manifests/nx-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@
"path": "/nx-api/angular/executors/application",
"type": "executor"
},
"/nx-api/angular/executors/extract-i18n": {
"description": "Extracts i18n messages from source code.",
"file": "generated/packages/angular/executors/extract-i18n.json",
"hidden": false,
"name": "extract-i18n",
"originalFilePath": "/packages/angular/src/executors/extract-i18n/schema.json",
"path": "/nx-api/angular/executors/extract-i18n",
"type": "executor"
},
"/nx-api/angular/executors/webpack-browser": {
"description": "The `webpack-browser` executor is very similar to the standard `browser` builder provided by the Angular Devkit. It allows you to build your Angular application to a build artifact that can be hosted online. There are some key differences: \n- Supports Custom Webpack Configurations \n- Supports Incremental Building",
"file": "generated/packages/angular/executors/webpack-browser.json",
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/packages-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@
"path": "angular/executors/application",
"type": "executor"
},
{
"description": "Extracts i18n messages from source code.",
"file": "generated/packages/angular/executors/extract-i18n.json",
"hidden": false,
"name": "extract-i18n",
"originalFilePath": "/packages/angular/src/executors/extract-i18n/schema.json",
"path": "angular/executors/extract-i18n",
"type": "executor"
},
{
"description": "The `webpack-browser` executor is very similar to the standard `browser` builder provided by the Angular Devkit. It allows you to build your Angular application to a build artifact that can be hosted online. There are some key differences: \n- Supports Custom Webpack Configurations \n- Supports Incremental Building",
"file": "generated/packages/angular/executors/webpack-browser.json",
Expand Down
55 changes: 55 additions & 0 deletions docs/generated/packages/angular/executors/extract-i18n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "extract-i18n",
"implementation": "/packages/angular/src/executors/extract-i18n/extract-i18n.impl.ts",
"schema": {
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Schema for Nx extract-i18n Executor",
"description": "Extracts i18n messages from source code.",
"outputCapture": "direct-nodejs",
"type": "object",
"properties": {
"buildTarget": {
"type": "string",
"description": "A builder target to extract i18n messages in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$"
},
"format": {
"type": "string",
"description": "Output format for the generated file.",
"default": "xlf",
"enum": [
"xmb",
"xlf",
"xlif",
"xliff",
"xlf2",
"xliff2",
"json",
"arb",
"legacy-migrate"
]
},
"progress": {
"type": "boolean",
"description": "Log progress to the console.",
"default": true
},
"outputPath": {
"type": "string",
"description": "Path where output will be placed."
},
"outFile": {
"type": "string",
"description": "Name of the file to output."
}
},
"additionalProperties": false,
"required": ["buildTarget"],
"presets": []
},
"description": "Extracts i18n messages from source code.",
"aliases": [],
"hidden": false,
"path": "/packages/angular/src/executors/extract-i18n/schema.json",
"type": "executor"
}
1 change: 1 addition & 0 deletions docs/shared/reference/sitemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@
- [browser-esbuild](/nx-api/angular/executors/browser-esbuild)
- [module-federation-dev-server](/nx-api/angular/executors/module-federation-dev-server)
- [application](/nx-api/angular/executors/application)
- [extract-i18n](/nx-api/angular/executors/extract-i18n)
- [webpack-browser](/nx-api/angular/executors/webpack-browser)
- [dev-server](/nx-api/angular/executors/dev-server)
- [webpack-server](/nx-api/angular/executors/webpack-server)
Expand Down
5 changes: 5 additions & 0 deletions packages/angular/executors.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"implementation": "./src/executors/application/application.impl",
"schema": "./src/executors/application/schema.json",
"description": "Builds an application with esbuild with support for incremental builds. _Note: this is only supported in Angular versions >= 17.0.0_."
},
"extract-i18n": {
"implementation": "./src/executors/extract-i18n/extract-i18n.impl",
"schema": "./src/executors/extract-i18n/schema.json",
"description": "Extracts i18n messages from source code."
}
},
"builders": {
Expand Down
1 change: 1 addition & 0 deletions packages/angular/executors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './src/executors/ng-packagr-lite/ng-packagr-lite.impl';
export * from './src/executors/package/package.impl';
export * from './src/executors/browser-esbuild/browser-esbuild.impl';
export * from './src/executors/application/application.impl';
export * from './src/executors/extract-i18n/extract-i18n.impl';

import { executeDevServerBuilder } from './src/builders/dev-server/dev-server.impl';

Expand Down
66 changes: 2 additions & 64 deletions packages/angular/src/builders/dev-server/dev-server.impl.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import type { BuilderContext } from '@angular-devkit/architect';
import type {
ApplicationBuilderOptions,
BrowserBuilderOptions,
DevServerBuilderOptions,
} from '@angular-devkit/build-angular';
import type { Schema as BrowserEsbuildBuilderOptions } from '@angular-devkit/build-angular/src/builders/browser-esbuild/schema';
import type { DevServerBuilderOptions } from '@angular-devkit/build-angular';
import {
joinPathFragments,
normalizePath,
parseTargetString,
readCachedProjectGraph,
type Target,
} from '@nx/devkit';
import { getRootTsConfigPath } from '@nx/js';
import type { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils';
Expand All @@ -28,6 +22,7 @@ import {
loadPlugins,
type PluginSpec,
} from '../../executors/utilities/esbuild-extensions';
import { patchBuilderContext } from '../../executors/utilities/patch-builder-context';
import { createTmpTsConfigForBuildableLibs } from '../utilities/buildable-libs';
import {
mergeCustomWebpackConfig,
Expand Down Expand Up @@ -288,60 +283,3 @@ async function loadIndexHtmlFileTransformer(
)
: await loadIndexHtmlTransformer(pathToIndexFileTransformer, tsConfig);
}

const executorToBuilderMap = new Map<string, string>([
[
'@nx/angular:browser-esbuild',
'@angular-devkit/build-angular:browser-esbuild',
],
['@nx/angular:application', '@angular-devkit/build-angular:application'],
]);

function patchBuilderContext(
context: BuilderContext,
isUsingEsbuildBuilder: boolean,
buildTarget: Target
): void {
const originalGetBuilderNameForTarget = context.getBuilderNameForTarget;
context.getBuilderNameForTarget = async (target) => {
const builderName = await originalGetBuilderNameForTarget(target);

if (executorToBuilderMap.has(builderName)) {
return executorToBuilderMap.get(builderName)!;
}

return builderName;
};

if (isUsingEsbuildBuilder) {
const originalGetTargetOptions = context.getTargetOptions;
context.getTargetOptions = async (target) => {
const options = await originalGetTargetOptions(target);

if (
target.project === buildTarget.project &&
target.target === buildTarget.target &&
target.configuration === buildTarget.configuration
) {
cleanBuildTargetOptions(options);
}

return options;
};
}
}

function cleanBuildTargetOptions(
options: any
):
| ApplicationBuilderOptions
| BrowserBuilderOptions
| BrowserEsbuildBuilderOptions {
delete options.buildLibsFromSource;
delete options.customWebpackConfig;
delete options.indexHtmlTransformer;
delete options.indexFileTransformer;
delete options.plugins;

return options;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { ExtractI18nBuilderOptions } from '@angular-devkit/build-angular';
import { parseTargetString, type ExecutorContext } from '@nx/devkit';
import { createBuilderContext } from 'nx/src/adapter/ngcli-adapter';
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
import { getInstalledAngularVersionInfo } from '../utilities/angular-version-utils';
import { patchBuilderContext } from '../utilities/patch-builder-context';
import type { ExtractI18nExecutorOptions } from './schema';

export default async function* extractI18nExecutor(
options: ExtractI18nExecutorOptions,
context: ExecutorContext
) {
const parsedBuildTarget = parseTargetString(options.buildTarget, context);
const browserTargetProjectConfiguration = readCachedProjectConfiguration(
parsedBuildTarget.project
);

const buildTarget =
browserTargetProjectConfiguration.targets[parsedBuildTarget.target];

const isUsingEsbuildBuilder = [
'@angular-devkit/build-angular:application',
'@angular-devkit/build-angular:browser-esbuild',
'@nx/angular:application',
'@nx/angular:browser-esbuild',
].includes(buildTarget.executor);

const builderContext = await createBuilderContext(
{
builderName: 'extrct-i18n',
description: 'Extracts i18n messages from source code.',
optionSchema: await import('./schema.json'),
},
context
);

/**
* The Angular CLI extract-i18n builder make some decisions based on the build
* target builder but it only considers `@angular-devkit/build-angular:*`
* builders. Since we are using a custom builder, we patch the context to
* handle `@nx/angular:*` executors.
*/
patchBuilderContext(builderContext, isUsingEsbuildBuilder, parsedBuildTarget);

const { executeExtractI18nBuilder } = await import(
'@angular-devkit/build-angular'
);
const delegateBuilderOptions = getDelegateBuilderOptions(options);

return await executeExtractI18nBuilder(
delegateBuilderOptions,
builderContext
);
}

function getDelegateBuilderOptions(
options: ExtractI18nExecutorOptions
): ExtractI18nBuilderOptions {
const delegateBuilderOptions: ExtractI18nBuilderOptions = { ...options };

const { major: angularMajorVersion } = getInstalledAngularVersionInfo();
if (angularMajorVersion <= 17) {
delegateBuilderOptions.browserTarget = delegateBuilderOptions.buildTarget;
delete delegateBuilderOptions.buildTarget;
}

return delegateBuilderOptions;
}
8 changes: 8 additions & 0 deletions packages/angular/src/executors/extract-i18n/schema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { ExtractI18nBuilderOptions } from '@angular-devkit/build-angular';

export type ExtractI18nExecutorOptions = Omit<
ExtractI18nBuilderOptions,
'browserTarget'
> & {
buildTarget: string;
};
45 changes: 45 additions & 0 deletions packages/angular/src/executors/extract-i18n/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Schema for Nx extract-i18n Executor",
"description": "Extracts i18n messages from source code.",
"outputCapture": "direct-nodejs",
"type": "object",
"properties": {
"buildTarget": {
"type": "string",
"description": "A builder target to extract i18n messages in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$"
},
"format": {
"type": "string",
"description": "Output format for the generated file.",
"default": "xlf",
"enum": [
"xmb",
"xlf",
"xlif",
"xliff",
"xlf2",
"xliff2",
"json",
"arb",
"legacy-migrate"
]
},
"progress": {
"type": "boolean",
"description": "Log progress to the console.",
"default": true
},
"outputPath": {
"type": "string",
"description": "Path where output will be placed."
},
"outFile": {
"type": "string",
"description": "Name of the file to output."
}
},
"additionalProperties": false,
"required": ["buildTarget"]
}
Loading