Skip to content

Commit

Permalink
feat(angular): add the extract-i18n executor (#21802)
Browse files Browse the repository at this point in the history
  • Loading branch information
leosvelperez authored Feb 14, 2024
1 parent b625a79 commit 343c0f6
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 64 deletions.
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;
}
68 changes: 68 additions & 0 deletions packages/angular/src/executors/extract-i18n/extract-i18n.impl.ts
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

0 comments on commit 343c0f6

Please sign in to comment.