Skip to content

Commit e112095

Browse files
alan-agius4josephperrott
authored andcommitted
refactor(@angular-devkit/build-angular): use @angular/localize/tools entry-point
1 parent 4517ba0 commit e112095

File tree

6 files changed

+73
-220
lines changed

6 files changed

+73
-220
lines changed

packages/angular_devkit/build_angular/src/babel/presets/application.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import type {
10+
DiagnosticHandlingStrategy,
11+
Diagnostics,
12+
makeEs2015TranslatePlugin,
13+
makeEs5TranslatePlugin,
14+
makeLocalePlugin,
15+
} from '@angular/localize/tools';
916
import { strict as assert } from 'assert';
1017
import * as fs from 'fs';
1118
import * as path from 'path';
@@ -17,14 +24,11 @@ export type DiagnosticReporter = (type: 'error' | 'warning' | 'info', message: s
1724
* This must be provided for the ESM imports since dynamic imports are required to be asynchronous and
1825
* Babel presets currently can only be synchronous.
1926
*
20-
* TODO_ESM: Remove all deep imports once `@angular/localize` is published with the `tools` entry point
2127
*/
2228
export interface I18nPluginCreators {
23-
/* eslint-disable max-len */
24-
makeEs2015TranslatePlugin: typeof import('@angular/localize/src/tools/src/translate/source_files/es2015_translate_plugin').makeEs2015TranslatePlugin;
25-
makeEs5TranslatePlugin: typeof import('@angular/localize/src/tools/src/translate/source_files/es5_translate_plugin').makeEs5TranslatePlugin;
26-
makeLocalePlugin: typeof import('@angular/localize/src/tools/src/translate/source_files/locale_plugin').makeLocalePlugin;
27-
/* eslint-enable max-len */
29+
makeEs2015TranslatePlugin: typeof makeEs2015TranslatePlugin;
30+
makeEs5TranslatePlugin: typeof makeEs5TranslatePlugin;
31+
makeLocalePlugin: typeof makeLocalePlugin;
2832
}
2933

3034
export interface ApplicationPresetOptions {
@@ -52,15 +56,12 @@ type NgtscLogger = Parameters<
5256
typeof import('@angular/compiler-cli/linker/babel').createEs2015LinkerPlugin
5357
>[0]['logger'];
5458

55-
type I18nDiagnostics = import('@angular/localize/src/tools/src/diagnostics').Diagnostics;
56-
type I18nDiagnosticsHandlingStrategy =
57-
import('@angular/localize/src/tools/src/diagnostics').DiagnosticHandlingStrategy;
58-
function createI18nDiagnostics(reporter: DiagnosticReporter | undefined): I18nDiagnostics {
59-
const diagnostics: I18nDiagnostics = new (class {
60-
readonly messages: I18nDiagnostics['messages'] = [];
59+
function createI18nDiagnostics(reporter: DiagnosticReporter | undefined): Diagnostics {
60+
const diagnostics: Diagnostics = new (class {
61+
readonly messages: Diagnostics['messages'] = [];
6162
hasErrors = false;
6263

63-
add(type: I18nDiagnosticsHandlingStrategy, message: string): void {
64+
add(type: DiagnosticHandlingStrategy, message: string): void {
6465
if (type === 'ignore') {
6566
return;
6667
}
@@ -78,7 +79,7 @@ function createI18nDiagnostics(reporter: DiagnosticReporter | undefined): I18nDi
7879
this.add('warning', message);
7980
}
8081

81-
merge(other: I18nDiagnostics): void {
82+
merge(other: Diagnostics): void {
8283
for (const diagnostic of other.messages) {
8384
this.add(diagnostic.type, diagnostic.message);
8485
}

packages/angular_devkit/build_angular/src/builders/extract-i18n/index.ts

Lines changed: 15 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { BuilderContext, createBuilder, targetFromTargetString } from '@angular-
1010
import { BuildResult, runWebpack } from '@angular-devkit/build-webpack';
1111
import { JsonObject } from '@angular-devkit/core';
1212
import type { ɵParsedMessage as LocalizeMessage } from '@angular/localize';
13-
import type { Diagnostics } from '@angular/localize/src/tools/src/diagnostics';
13+
import type { Diagnostics } from '@angular/localize/tools';
1414
import * as fs from 'fs';
1515
import * as path from 'path';
1616
import webpack from 'webpack';
@@ -32,24 +32,6 @@ import { Format, Schema } from './schema';
3232

3333
export type ExtractI18nBuilderOptions = Schema & JsonObject;
3434

35-
/**
36-
* The manually constructed type for the `@angular/localize/tools` module.
37-
* This type only contains the exports that are need for this file.
38-
*
39-
* TODO_ESM: Remove once the `tools` entry point exists in a published package version
40-
*/
41-
interface LocalizeToolsModule {
42-
/* eslint-disable max-len */
43-
checkDuplicateMessages: typeof import('@angular/localize/src/tools/src/extract/duplicates').checkDuplicateMessages;
44-
XmbTranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/xmb_translation_serializer').XmbTranslationSerializer;
45-
SimpleJsonTranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/json_translation_serializer').SimpleJsonTranslationSerializer;
46-
Xliff1TranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer').Xliff1TranslationSerializer;
47-
Xliff2TranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer').Xliff2TranslationSerializer;
48-
ArbTranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/arb_translation_serializer').ArbTranslationSerializer;
49-
LegacyMessageIdMigrationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/legacy_message_id_migration_serializer').LegacyMessageIdMigrationSerializer;
50-
/* eslint-enable max-len */
51-
}
52-
5335
function getI18nOutfile(format: string | undefined) {
5436
switch (format) {
5537
case 'xmb':
@@ -71,67 +53,40 @@ function getI18nOutfile(format: string | undefined) {
7153
}
7254

7355
async function getSerializer(
74-
localizeToolsModule: LocalizeToolsModule | undefined,
56+
localizeToolsModule: typeof import('@angular/localize/tools'),
7557
format: Format,
7658
sourceLocale: string,
7759
basePath: string,
7860
useLegacyIds: boolean,
7961
diagnostics: Diagnostics,
8062
) {
63+
const {
64+
XmbTranslationSerializer,
65+
LegacyMessageIdMigrationSerializer,
66+
ArbTranslationSerializer,
67+
Xliff1TranslationSerializer,
68+
Xliff2TranslationSerializer,
69+
SimpleJsonTranslationSerializer,
70+
} = localizeToolsModule;
71+
8172
switch (format) {
8273
case Format.Xmb:
83-
const { XmbTranslationSerializer } =
84-
localizeToolsModule ??
85-
(await import(
86-
'@angular/localize/src/tools/src/extract/translation_files/xmb_translation_serializer'
87-
));
88-
8974
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9075
return new XmbTranslationSerializer(basePath as any, useLegacyIds);
9176
case Format.Xlf:
9277
case Format.Xlif:
9378
case Format.Xliff:
94-
const { Xliff1TranslationSerializer } =
95-
localizeToolsModule ??
96-
(await import(
97-
'@angular/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer'
98-
));
99-
10079
// eslint-disable-next-line @typescript-eslint/no-explicit-any
10180
return new Xliff1TranslationSerializer(sourceLocale, basePath as any, useLegacyIds, {});
10281
case Format.Xlf2:
10382
case Format.Xliff2:
104-
const { Xliff2TranslationSerializer } =
105-
localizeToolsModule ??
106-
(await import(
107-
'@angular/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer'
108-
));
109-
11083
// eslint-disable-next-line @typescript-eslint/no-explicit-any
11184
return new Xliff2TranslationSerializer(sourceLocale, basePath as any, useLegacyIds, {});
11285
case Format.Json:
113-
const { SimpleJsonTranslationSerializer } =
114-
localizeToolsModule ??
115-
(await import(
116-
'@angular/localize/src/tools/src/extract/translation_files/json_translation_serializer'
117-
));
118-
11986
return new SimpleJsonTranslationSerializer(sourceLocale);
12087
case Format.LegacyMigrate:
121-
const { LegacyMessageIdMigrationSerializer } =
122-
localizeToolsModule ??
123-
(await import(
124-
'@angular/localize/src/tools/src/extract/translation_files/legacy_message_id_migration_serializer'
125-
));
126-
12788
return new LegacyMessageIdMigrationSerializer(diagnostics);
12889
case Format.Arb:
129-
const { ArbTranslationSerializer } =
130-
localizeToolsModule ??
131-
(await import(
132-
'@angular/localize/src/tools/src/extract/translation_files/arb_translation_serializer'
133-
));
134-
13590
const fileSystem = {
13691
relative(from: string, to: string): string {
13792
return path.relative(from, to);
@@ -287,15 +242,9 @@ export async function execute(
287242

288243
// All the localize usages are setup to first try the ESM entry point then fallback to the deep imports.
289244
// This provides interim compatibility while the framework is transitioned to bundled ESM packages.
290-
// TODO_ESM: Remove all deep imports once `@angular/localize` is published with the `tools` entry point
291-
let localizeToolsModule;
292-
try {
293-
// Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround.
294-
// Once TypeScript provides support for keeping the dynamic import this workaround can be
295-
// changed to a direct dynamic import.
296-
localizeToolsModule = await loadEsmModule<LocalizeToolsModule>('@angular/localize/tools');
297-
} catch {}
298-
245+
const localizeToolsModule = await loadEsmModule<typeof import('@angular/localize/tools')>(
246+
'@angular/localize/tools',
247+
);
299248
const webpackResult = await runWebpack(
300249
(await transforms?.webpackConfiguration?.(config)) || config,
301250
context,
@@ -315,8 +264,7 @@ export async function execute(
315264

316265
const basePath = config.context || projectRoot;
317266

318-
const { checkDuplicateMessages } =
319-
localizeToolsModule ?? (await import('@angular/localize/src/tools/src/extract/duplicates'));
267+
const { checkDuplicateMessages } = localizeToolsModule;
320268

321269
// The filesystem is used to create a relative path for each file
322270
// from the basePath. This relative path is then used in the error message.

packages/angular_devkit/build_angular/src/builders/extract-i18n/ivy-extract-loader.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,13 @@ async function extract(
4747
// TODO_ESM: Remove all deep imports once `@angular/localize` is published with the `tools` entry point
4848
let MessageExtractor;
4949
try {
50-
try {
51-
// Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround.
52-
// Once TypeScript provides support for keeping the dynamic import this workaround can be
53-
// changed to a direct dynamic import.
54-
const localizeToolsModule = await loadEsmModule<
55-
typeof import('@angular/localize/src/tools/src/extract/extraction')
56-
>('@angular/localize/tools');
57-
MessageExtractor = localizeToolsModule.MessageExtractor;
58-
} catch {
59-
MessageExtractor = (await import('@angular/localize/src/tools/src/extract/extraction'))
60-
.MessageExtractor;
61-
}
50+
// Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround.
51+
// Once TypeScript provides support for keeping the dynamic import this workaround can be
52+
// changed to a direct dynamic import.
53+
const localizeToolsModule = await loadEsmModule<typeof import('@angular/localize/tools')>(
54+
'@angular/localize/tools',
55+
);
56+
MessageExtractor = localizeToolsModule.MessageExtractor;
6257
} catch {
6358
throw new Error(
6459
`Unable to load message extractor. Please ensure '@angular/localize' is installed.`,

packages/angular_devkit/build_angular/src/utils/load-esm.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,6 @@ import { URL } from 'url';
2020
* @param modulePath The path of the module to load.
2121
* @returns A Promise that resolves to the dynamically imported module.
2222
*/
23-
export async function loadEsmModule<T>(modulePath: string | URL): Promise<T> {
24-
try {
25-
return (await new Function('modulePath', `return import(modulePath);`)(modulePath)) as T;
26-
} catch (e) {
27-
// Temporary workaround to handle directory imports for current packages. ESM does not support
28-
// directory imports.
29-
// TODO_ESM: Remove once FW packages are fully ESM with defined `exports` package.json fields
30-
if (e.code !== 'ERR_UNSUPPORTED_DIR_IMPORT') {
31-
throw e;
32-
}
33-
34-
return (await new Function('modulePath', `return import(modulePath);`)(
35-
modulePath + '/index.js',
36-
)) as T;
37-
}
23+
export function loadEsmModule<T>(modulePath: string | URL): Promise<T> {
24+
return new Function('modulePath', `return import(modulePath);`)(modulePath) as Promise<T>;
3825
}

packages/angular_devkit/build_angular/src/utils/load-translations.ts

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import type { Diagnostics } from '@angular/localize/tools';
910
import { createHash } from 'crypto';
1011
import * as fs from 'fs';
1112
import { loadEsmModule } from './load-esm';
@@ -14,7 +15,7 @@ export type TranslationLoader = (path: string) => {
1415
translations: Record<string, import('@angular/localize').ɵParsedTranslation>;
1516
format: string;
1617
locale?: string;
17-
diagnostics: import('@angular/localize/src/tools/src/diagnostics').Diagnostics;
18+
diagnostics: Diagnostics;
1819
integrity: string;
1920
};
2021

@@ -60,64 +61,27 @@ export async function createTranslationLoader(): Promise<TranslationLoader> {
6061
}
6162

6263
async function importParsers() {
63-
// All the localize usages are setup to first try the ESM entry point then fallback to the deep imports.
64-
// This provides interim compatibility while the framework is transitioned to bundled ESM packages.
65-
// TODO_ESM: Remove all deep imports once `@angular/localize` is published with the `tools` entry point
66-
let localizeToolsModule;
6764
try {
6865
// Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround.
6966
// Once TypeScript provides support for keeping the dynamic import this workaround can be
7067
// changed to a direct dynamic import.
71-
// TODO_ESM: The type needs to be manually constructed until the tools entry point exists
72-
localizeToolsModule = await loadEsmModule<{
73-
Diagnostics: typeof import('@angular/localize/src/tools/src/diagnostics').Diagnostics;
74-
/* eslint-disable max-len */
75-
ArbTranslationParser: typeof import('@angular/localize/src/tools/src/translate/translation_files/translation_parsers/arb_translation_parser').ArbTranslationParser;
76-
SimpleJsonTranslationParser: typeof import('@angular/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser').SimpleJsonTranslationParser;
77-
Xliff1TranslationParser: typeof import('@angular/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser').Xliff1TranslationParser;
78-
Xliff2TranslationParser: typeof import('@angular/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser').Xliff2TranslationParser;
79-
XtbTranslationParser: typeof import('@angular/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser').XtbTranslationParser;
80-
/* eslint-enable max-len */
81-
}>('@angular/localize/tools');
82-
} catch {}
68+
const {
69+
Diagnostics,
70+
ArbTranslationParser,
71+
SimpleJsonTranslationParser,
72+
Xliff1TranslationParser,
73+
Xliff2TranslationParser,
74+
XtbTranslationParser,
75+
} = await loadEsmModule<typeof import('@angular/localize/tools')>('@angular/localize/tools');
8376

84-
try {
85-
const { Diagnostics } =
86-
localizeToolsModule ?? (await import('@angular/localize/src/tools/src/diagnostics'));
8777
const diagnostics = new Diagnostics();
88-
8978
const parsers = {
90-
arb: new (
91-
localizeToolsModule ??
92-
(await import(
93-
'@angular/localize/src/tools/src/translate/translation_files/translation_parsers/arb_translation_parser'
94-
))
95-
).ArbTranslationParser(),
96-
json: new (
97-
localizeToolsModule ??
98-
(await import(
99-
'@angular/localize/src/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser'
100-
))
101-
).SimpleJsonTranslationParser(),
102-
xlf: new (
103-
localizeToolsModule ??
104-
(await import(
105-
'@angular/localize/src/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser'
106-
))
107-
).Xliff1TranslationParser(),
108-
xlf2: new (
109-
localizeToolsModule ??
110-
(await import(
111-
'@angular/localize/src/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser'
112-
))
113-
).Xliff2TranslationParser(),
79+
arb: new ArbTranslationParser(),
80+
json: new SimpleJsonTranslationParser(),
81+
xlf: new Xliff1TranslationParser(),
82+
xlf2: new Xliff2TranslationParser(),
11483
// The name ('xmb') needs to match the AOT compiler option
115-
xmb: new (
116-
localizeToolsModule ??
117-
(await import(
118-
'@angular/localize/src/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser'
119-
))
120-
).XtbTranslationParser(),
84+
xmb: new XtbTranslationParser(),
12185
};
12286

12387
return { parsers, diagnostics };

0 commit comments

Comments
 (0)