Skip to content

Commit 84bb536

Browse files
author
Angular Builds
committed
dcee94b refactor(@angular-devkit/build-angular): split Webpack-specific functionality from i18n option creation
1 parent 99fa2c8 commit 84bb536

13 files changed

+145
-121
lines changed

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"name": "@angular-devkit/build-angular",
3-
"version": "18.0.0-next.2+sha-03d0fca",
3+
"version": "18.0.0-next.2+sha-dcee94b",
44
"description": "Angular Webpack Build Facade",
55
"main": "src/index.js",
66
"typings": "src/index.d.ts",
77
"builders": "builders.json",
88
"dependencies": {
99
"@ampproject/remapping": "2.3.0",
10-
"@angular-devkit/architect": "github:angular/angular-devkit-architect-builds#03d0fca",
11-
"@angular-devkit/build-webpack": "github:angular/angular-devkit-build-webpack-builds#03d0fca",
12-
"@angular-devkit/core": "github:angular/angular-devkit-core-builds#03d0fca",
10+
"@angular-devkit/architect": "github:angular/angular-devkit-architect-builds#dcee94b",
11+
"@angular-devkit/build-webpack": "github:angular/angular-devkit-build-webpack-builds#dcee94b",
12+
"@angular-devkit/core": "github:angular/angular-devkit-core-builds#dcee94b",
1313
"@babel/core": "7.24.4",
1414
"@babel/generator": "7.24.4",
1515
"@babel/helper-annotate-as-pure": "7.22.5",
@@ -20,7 +20,7 @@
2020
"@babel/preset-env": "7.24.4",
2121
"@babel/runtime": "7.24.4",
2222
"@discoveryjs/json-ext": "0.5.7",
23-
"@ngtools/webpack": "github:angular/ngtools-webpack-builds#03d0fca",
23+
"@ngtools/webpack": "github:angular/ngtools-webpack-builds#dcee94b",
2424
"@vitejs/plugin-basic-ssl": "1.1.0",
2525
"ansi-colors": "4.1.3",
2626
"autoprefixer": "10.4.19",

src/builders/dev-server/webpack-server.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const service_worker_plugin_1 = require("../../tools/webpack/plugins/service-wor
4242
const stats_1 = require("../../tools/webpack/utils/stats");
4343
const utils_1 = require("../../utils");
4444
const color_1 = require("../../utils/color");
45-
const i18n_options_1 = require("../../utils/i18n-options");
45+
const i18n_webpack_1 = require("../../utils/i18n-webpack");
4646
const load_translations_1 = require("../../utils/load-translations");
4747
const package_chunk_sort_1 = require("../../utils/package-chunk-sort");
4848
const version_1 = require("../../utils/version");
@@ -266,7 +266,7 @@ async function setupLocalize(locale, i18n, browserOptions, webpackConfig, cacheO
266266
compiler.hooks.thisCompilation.tap('build-angular', (compilation) => {
267267
if (i18n.shouldInline && i18nLoaderOptions.translation === undefined) {
268268
// Reload translations
269-
(0, i18n_options_1.loadTranslations)(locale, localeDescription, context.workspaceRoot, loader, {
269+
(0, i18n_webpack_1.loadTranslations)(locale, localeDescription, context.workspaceRoot, loader, {
270270
warn(message) {
271271
(0, webpack_diagnostics_1.addWarning)(compilation, message);
272272
},

src/utils/action-executor.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import { InlineOptions } from './bundle-inline-options';
9-
import { I18nOptions } from './i18n-options';
9+
import { I18nOptions } from './i18n-webpack';
1010
export declare class BundleActionExecutor {
1111
private workerOptions;
1212
private workerPool?;

src/utils/i18n-inlining.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
*/
88
import { BuilderContext } from '@angular-devkit/architect';
99
import { EmittedFiles } from '@angular-devkit/build-webpack';
10-
import { I18nOptions } from './i18n-options';
10+
import { I18nOptions } from './i18n-webpack';
1111
export declare function i18nInlineEmittedFiles(context: BuilderContext, emittedFiles: EmittedFiles[], i18n: I18nOptions, baseOutputPath: string, outputPaths: string[], scriptsEntryPointName: string[], emittedPath: string, missingTranslation: 'error' | 'warning' | 'ignore' | undefined): Promise<boolean>;

src/utils/i18n-options.d.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import { BuilderContext } from '@angular-devkit/architect';
9-
import { Schema as BrowserBuilderSchema, I18NTranslation } from '../builders/browser/schema';
10-
import { Schema as ServerBuilderSchema } from '../builders/server/schema';
11-
import { TranslationLoader } from './load-translations';
8+
import type { TranslationLoader } from './load-translations';
129
export interface LocaleDescription {
1310
files: {
1411
path: string;
@@ -30,11 +27,7 @@ export interface I18nOptions {
3027
export declare function createI18nOptions(projectMetadata: {
3128
i18n?: unknown;
3229
}, inline?: boolean | string[]): I18nOptions;
33-
export declare function configureI18nBuild<T extends BrowserBuilderSchema | ServerBuilderSchema>(context: BuilderContext, options: T): Promise<{
34-
buildOptions: T;
35-
i18n: I18nOptions;
36-
}>;
3730
export declare function loadTranslations(locale: string, desc: LocaleDescription, workspaceRoot: string, loader: TranslationLoader, logger: {
3831
warn: (message: string) => void;
3932
error: (message: string) => void;
40-
}, usedFormats?: Set<string>, duplicateTranslation?: I18NTranslation): void;
33+
}, usedFormats?: Set<string>, duplicateTranslation?: 'ignore' | 'error' | 'warning'): void;

src/utils/i18n-options.js

Lines changed: 4 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
1010
return (mod && mod.__esModule) ? mod : { "default": mod };
1111
};
1212
Object.defineProperty(exports, "__esModule", { value: true });
13-
exports.loadTranslations = exports.configureI18nBuild = exports.createI18nOptions = void 0;
14-
const node_fs_1 = __importDefault(require("node:fs"));
15-
const node_module_1 = require("node:module");
16-
const node_os_1 = __importDefault(require("node:os"));
13+
exports.loadTranslations = exports.createI18nOptions = void 0;
1714
const node_path_1 = __importDefault(require("node:path"));
18-
const schema_1 = require("../builders/browser/schema");
19-
const read_tsconfig_1 = require("../utils/read-tsconfig");
20-
const load_translations_1 = require("./load-translations");
21-
/**
22-
* The base module location used to search for locale specific data.
23-
*/
24-
const LOCALE_DATA_BASE_MODULE = '@angular/common/locales/global';
2515
function normalizeTranslationFileOption(option, locale, expectObjectInError) {
2616
if (typeof option === 'string') {
2717
return [option];
@@ -123,89 +113,6 @@ function createI18nOptions(projectMetadata, inline) {
123113
return i18n;
124114
}
125115
exports.createI18nOptions = createI18nOptions;
126-
async function configureI18nBuild(context, options) {
127-
if (!context.target) {
128-
throw new Error('The builder requires a target.');
129-
}
130-
const buildOptions = { ...options };
131-
const tsConfig = await (0, read_tsconfig_1.readTsconfig)(buildOptions.tsConfig, context.workspaceRoot);
132-
const metadata = await context.getProjectMetadata(context.target);
133-
const i18n = createI18nOptions(metadata, buildOptions.localize);
134-
// No additional processing needed if no inlining requested and no source locale defined.
135-
if (!i18n.shouldInline && !i18n.hasDefinedSourceLocale) {
136-
return { buildOptions, i18n };
137-
}
138-
const projectRoot = node_path_1.default.join(context.workspaceRoot, metadata.root || '');
139-
// The trailing slash is required to signal that the path is a directory and not a file.
140-
const projectRequire = (0, node_module_1.createRequire)(projectRoot + '/');
141-
const localeResolver = (locale) => projectRequire.resolve(node_path_1.default.join(LOCALE_DATA_BASE_MODULE, locale));
142-
// Load locale data and translations (if present)
143-
let loader;
144-
const usedFormats = new Set();
145-
for (const [locale, desc] of Object.entries(i18n.locales)) {
146-
if (!i18n.inlineLocales.has(locale) && locale !== i18n.sourceLocale) {
147-
continue;
148-
}
149-
let localeDataPath = findLocaleDataPath(locale, localeResolver);
150-
if (!localeDataPath) {
151-
const [first] = locale.split('-');
152-
if (first) {
153-
localeDataPath = findLocaleDataPath(first.toLowerCase(), localeResolver);
154-
if (localeDataPath) {
155-
context.logger.warn(`Locale data for '${locale}' cannot be found. Using locale data for '${first}'.`);
156-
}
157-
}
158-
}
159-
if (!localeDataPath) {
160-
context.logger.warn(`Locale data for '${locale}' cannot be found. No locale data will be included for this locale.`);
161-
}
162-
else {
163-
desc.dataPath = localeDataPath;
164-
}
165-
if (!desc.files.length) {
166-
continue;
167-
}
168-
loader ??= await (0, load_translations_1.createTranslationLoader)();
169-
loadTranslations(locale, desc, context.workspaceRoot, loader, {
170-
warn(message) {
171-
context.logger.warn(message);
172-
},
173-
error(message) {
174-
throw new Error(message);
175-
},
176-
}, usedFormats, buildOptions.i18nDuplicateTranslation);
177-
if (usedFormats.size > 1 && tsConfig.options.enableI18nLegacyMessageIdFormat !== false) {
178-
// This limitation is only for legacy message id support (defaults to true as of 9.0)
179-
throw new Error('Localization currently only supports using one type of translation file format for the entire application.');
180-
}
181-
}
182-
// If inlining store the output in a temporary location to facilitate post-processing
183-
if (i18n.shouldInline) {
184-
// TODO: we should likely save these in the .angular directory in the next major version.
185-
// We'd need to do a migration to add the temp directory to gitignore.
186-
const tempPath = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_fs_1.default.realpathSync(node_os_1.default.tmpdir()), 'angular-cli-i18n-'));
187-
buildOptions.outputPath = tempPath;
188-
process.on('exit', () => {
189-
try {
190-
node_fs_1.default.rmSync(tempPath, { force: true, recursive: true, maxRetries: 3 });
191-
}
192-
catch { }
193-
});
194-
}
195-
return { buildOptions, i18n };
196-
}
197-
exports.configureI18nBuild = configureI18nBuild;
198-
function findLocaleDataPath(locale, resolver) {
199-
// Remove private use subtags
200-
const scrubbedLocale = locale.replace(/-x(-[a-zA-Z0-9]{1,8})+$/, '');
201-
try {
202-
return resolver(scrubbedLocale);
203-
}
204-
catch {
205-
// fallback to known existing en-US locale data as of 14.0
206-
return scrubbedLocale === 'en-US' ? findLocaleDataPath('en', resolver) : null;
207-
}
208-
}
209116
function loadTranslations(locale, desc, workspaceRoot, loader, logger, usedFormats, duplicateTranslation) {
210117
let translations = undefined;
211118
for (const file of desc.files) {
@@ -230,12 +137,12 @@ function loadTranslations(locale, desc, workspaceRoot, loader, logger, usedForma
230137
if (translations[id] !== undefined) {
231138
const duplicateTranslationMessage = `[${file.path}]: Duplicate translations for message '${id}' when merging.`;
232139
switch (duplicateTranslation) {
233-
case schema_1.I18NTranslation.Ignore:
140+
case 'ignore':
234141
break;
235-
case schema_1.I18NTranslation.Error:
142+
case 'error':
236143
logger.error(`ERROR ${duplicateTranslationMessage}`);
237144
break;
238-
case schema_1.I18NTranslation.Warning:
145+
case 'warning':
239146
default:
240147
logger.warn(`WARNING ${duplicateTranslationMessage}`);
241148
break;

src/utils/i18n-webpack.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { BuilderContext } from '@angular-devkit/architect';
9+
import { Schema as BrowserBuilderSchema } from '../builders/browser/schema';
10+
import { Schema as ServerBuilderSchema } from '../builders/server/schema';
11+
import { I18nOptions, loadTranslations } from './i18n-options';
12+
export { I18nOptions, loadTranslations };
13+
export declare function configureI18nBuild<T extends BrowserBuilderSchema | ServerBuilderSchema>(context: BuilderContext, options: T): Promise<{
14+
buildOptions: T;
15+
i18n: I18nOptions;
16+
}>;

src/utils/i18n-webpack.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"use strict";
2+
/**
3+
* @license
4+
* Copyright Google LLC All Rights Reserved.
5+
*
6+
* Use of this source code is governed by an MIT-style license that can be
7+
* found in the LICENSE file at https://angular.io/license
8+
*/
9+
var __importDefault = (this && this.__importDefault) || function (mod) {
10+
return (mod && mod.__esModule) ? mod : { "default": mod };
11+
};
12+
Object.defineProperty(exports, "__esModule", { value: true });
13+
exports.configureI18nBuild = exports.loadTranslations = void 0;
14+
const node_fs_1 = __importDefault(require("node:fs"));
15+
const node_module_1 = require("node:module");
16+
const node_os_1 = __importDefault(require("node:os"));
17+
const node_path_1 = __importDefault(require("node:path"));
18+
const read_tsconfig_1 = require("../utils/read-tsconfig");
19+
const i18n_options_1 = require("./i18n-options");
20+
Object.defineProperty(exports, "loadTranslations", { enumerable: true, get: function () { return i18n_options_1.loadTranslations; } });
21+
const load_translations_1 = require("./load-translations");
22+
/**
23+
* The base module location used to search for locale specific data.
24+
*/
25+
const LOCALE_DATA_BASE_MODULE = '@angular/common/locales/global';
26+
async function configureI18nBuild(context, options) {
27+
if (!context.target) {
28+
throw new Error('The builder requires a target.');
29+
}
30+
const buildOptions = { ...options };
31+
const tsConfig = await (0, read_tsconfig_1.readTsconfig)(buildOptions.tsConfig, context.workspaceRoot);
32+
const metadata = await context.getProjectMetadata(context.target);
33+
const i18n = (0, i18n_options_1.createI18nOptions)(metadata, buildOptions.localize);
34+
// No additional processing needed if no inlining requested and no source locale defined.
35+
if (!i18n.shouldInline && !i18n.hasDefinedSourceLocale) {
36+
return { buildOptions, i18n };
37+
}
38+
const projectRoot = node_path_1.default.join(context.workspaceRoot, metadata.root || '');
39+
// The trailing slash is required to signal that the path is a directory and not a file.
40+
const projectRequire = (0, node_module_1.createRequire)(projectRoot + '/');
41+
const localeResolver = (locale) => projectRequire.resolve(node_path_1.default.join(LOCALE_DATA_BASE_MODULE, locale));
42+
// Load locale data and translations (if present)
43+
let loader;
44+
const usedFormats = new Set();
45+
for (const [locale, desc] of Object.entries(i18n.locales)) {
46+
if (!i18n.inlineLocales.has(locale) && locale !== i18n.sourceLocale) {
47+
continue;
48+
}
49+
let localeDataPath = findLocaleDataPath(locale, localeResolver);
50+
if (!localeDataPath) {
51+
const [first] = locale.split('-');
52+
if (first) {
53+
localeDataPath = findLocaleDataPath(first.toLowerCase(), localeResolver);
54+
if (localeDataPath) {
55+
context.logger.warn(`Locale data for '${locale}' cannot be found. Using locale data for '${first}'.`);
56+
}
57+
}
58+
}
59+
if (!localeDataPath) {
60+
context.logger.warn(`Locale data for '${locale}' cannot be found. No locale data will be included for this locale.`);
61+
}
62+
else {
63+
desc.dataPath = localeDataPath;
64+
}
65+
if (!desc.files.length) {
66+
continue;
67+
}
68+
loader ??= await (0, load_translations_1.createTranslationLoader)();
69+
(0, i18n_options_1.loadTranslations)(locale, desc, context.workspaceRoot, loader, {
70+
warn(message) {
71+
context.logger.warn(message);
72+
},
73+
error(message) {
74+
throw new Error(message);
75+
},
76+
}, usedFormats, buildOptions.i18nDuplicateTranslation);
77+
if (usedFormats.size > 1 && tsConfig.options.enableI18nLegacyMessageIdFormat !== false) {
78+
// This limitation is only for legacy message id support (defaults to true as of 9.0)
79+
throw new Error('Localization currently only supports using one type of translation file format for the entire application.');
80+
}
81+
}
82+
// If inlining store the output in a temporary location to facilitate post-processing
83+
if (i18n.shouldInline) {
84+
// TODO: we should likely save these in the .angular directory in the next major version.
85+
// We'd need to do a migration to add the temp directory to gitignore.
86+
const tempPath = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_fs_1.default.realpathSync(node_os_1.default.tmpdir()), 'angular-cli-i18n-'));
87+
buildOptions.outputPath = tempPath;
88+
process.on('exit', () => {
89+
try {
90+
node_fs_1.default.rmSync(tempPath, { force: true, recursive: true, maxRetries: 3 });
91+
}
92+
catch { }
93+
});
94+
}
95+
return { buildOptions, i18n };
96+
}
97+
exports.configureI18nBuild = configureI18nBuild;
98+
function findLocaleDataPath(locale, resolver) {
99+
// Remove private use subtags
100+
const scrubbedLocale = locale.replace(/-x(-[a-zA-Z0-9]{1,8})+$/, '');
101+
try {
102+
return resolver(scrubbedLocale);
103+
}
104+
catch {
105+
// fallback to known existing en-US locale data as of 14.0
106+
return scrubbedLocale === 'en-US' ? findLocaleDataPath('en', resolver) : null;
107+
}
108+
}

src/utils/normalize-cache.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
1010
exports.normalizeCacheOptions = void 0;
1111
const node_path_1 = require("node:path");
1212
/** Version placeholder is replaced during the build process with actual package version */
13-
const VERSION = '18.0.0-next.2+sha-03d0fca';
13+
const VERSION = '18.0.0-next.2+sha-dcee94b';
1414
function hasCacheMetadata(value) {
1515
return (!!value &&
1616
typeof value === 'object' &&

src/utils/output-paths.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import { I18nOptions } from './i18n-options';
8+
import { I18nOptions } from './i18n-webpack';
99
export declare function ensureOutputPaths(baseOutputPath: string, i18n: I18nOptions): Map<string, string>;

0 commit comments

Comments
 (0)