Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/dev-infra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: angular/dev-infra/github-actions/breaking-changes-label@e638278dafb51a09bb06929dbfa44b3ac26e4030
- uses: angular/dev-infra/github-actions/breaking-changes-label@792204aa7a4e88dab92636d34f5efb3546d69a9e
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
2 changes: 1 addition & 1 deletion .github/workflows/lock-closed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ jobs:
lock_closed:
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/lock-closed@e638278dafb51a09bb06929dbfa44b3ac26e4030
- uses: angular/dev-infra/github-actions/lock-closed@792204aa7a4e88dab92636d34f5efb3546d69a9e
with:
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,21 @@
},
"devDependencies": {
"@ampproject/remapping": "1.0.1",
"@angular/animations": "13.0.0-next.9",
"@angular/animations": "13.0.0-next.11",
"@angular/cdk": "13.0.0-next.7",
"@angular/common": "13.0.0-next.9",
"@angular/compiler": "13.0.0-next.9",
"@angular/compiler-cli": "13.0.0-next.9",
"@angular/core": "13.0.0-next.9",
"@angular/dev-infra-private": "https://github.com/angular/dev-infra-private-builds.git#60593b6c234682d0bb9f009e47316c26bb5a94a3",
"@angular/forms": "13.0.0-next.9",
"@angular/localize": "13.0.0-next.9",
"@angular/common": "13.0.0-next.11",
"@angular/compiler": "13.0.0-next.11",
"@angular/compiler-cli": "13.0.0-next.11",
"@angular/core": "13.0.0-next.11",
"@angular/dev-infra-private": "https://github.com/angular/dev-infra-private-builds.git#d008ca5d1aeebd192c7dca25d6eb09b9e69140c6",
"@angular/forms": "13.0.0-next.11",
"@angular/localize": "13.0.0-next.11",
"@angular/material": "13.0.0-next.7",
"@angular/platform-browser": "13.0.0-next.9",
"@angular/platform-browser-dynamic": "13.0.0-next.9",
"@angular/platform-server": "13.0.0-next.9",
"@angular/router": "13.0.0-next.9",
"@angular/service-worker": "13.0.0-next.9",
"@angular/platform-browser": "13.0.0-next.11",
"@angular/platform-browser-dynamic": "13.0.0-next.11",
"@angular/platform-server": "13.0.0-next.11",
"@angular/router": "13.0.0-next.11",
"@angular/service-worker": "13.0.0-next.11",
"@babel/core": "7.15.5",
"@babel/generator": "7.15.4",
"@babel/helper-annotate-as-pure": "7.15.4",
Expand Down Expand Up @@ -178,7 +178,7 @@
"mini-css-extract-plugin": "2.3.0",
"minimatch": "3.0.4",
"minimist": "^1.2.0",
"ng-packagr": "13.0.0-next.5",
"ng-packagr": "13.0.0-next.6",
"node-fetch": "^2.2.0",
"open": "8.2.1",
"ora": "5.4.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/

import type {
DiagnosticHandlingStrategy,
Diagnostics,
makeEs2015TranslatePlugin,
makeEs5TranslatePlugin,
makeLocalePlugin,
} from '@angular/localize/tools';
import { strict as assert } from 'assert';
import * as fs from 'fs';
import * as path from 'path';
Expand All @@ -17,14 +24,11 @@ export type DiagnosticReporter = (type: 'error' | 'warning' | 'info', message: s
* This must be provided for the ESM imports since dynamic imports are required to be asynchronous and
* Babel presets currently can only be synchronous.
*
* TODO_ESM: Remove all deep imports once `@angular/localize` is published with the `tools` entry point
*/
export interface I18nPluginCreators {
/* eslint-disable max-len */
makeEs2015TranslatePlugin: typeof import('@angular/localize/src/tools/src/translate/source_files/es2015_translate_plugin').makeEs2015TranslatePlugin;
makeEs5TranslatePlugin: typeof import('@angular/localize/src/tools/src/translate/source_files/es5_translate_plugin').makeEs5TranslatePlugin;
makeLocalePlugin: typeof import('@angular/localize/src/tools/src/translate/source_files/locale_plugin').makeLocalePlugin;
/* eslint-enable max-len */
makeEs2015TranslatePlugin: typeof makeEs2015TranslatePlugin;
makeEs5TranslatePlugin: typeof makeEs5TranslatePlugin;
makeLocalePlugin: typeof makeLocalePlugin;
}

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

type I18nDiagnostics = import('@angular/localize/src/tools/src/diagnostics').Diagnostics;
type I18nDiagnosticsHandlingStrategy =
import('@angular/localize/src/tools/src/diagnostics').DiagnosticHandlingStrategy;
function createI18nDiagnostics(reporter: DiagnosticReporter | undefined): I18nDiagnostics {
const diagnostics: I18nDiagnostics = new (class {
readonly messages: I18nDiagnostics['messages'] = [];
function createI18nDiagnostics(reporter: DiagnosticReporter | undefined): Diagnostics {
const diagnostics: Diagnostics = new (class {
readonly messages: Diagnostics['messages'] = [];
hasErrors = false;

add(type: I18nDiagnosticsHandlingStrategy, message: string): void {
add(type: DiagnosticHandlingStrategy, message: string): void {
if (type === 'ignore') {
return;
}
Expand All @@ -78,7 +79,7 @@ function createI18nDiagnostics(reporter: DiagnosticReporter | undefined): I18nDi
this.add('warning', message);
}

merge(other: I18nDiagnostics): void {
merge(other: Diagnostics): void {
for (const diagnostic of other.messages) {
this.add(diagnostic.type, diagnostic.message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,7 @@ describe('Browser Builder lazy modules', () => {
const { files } = await browserBuild(architect, host, target);
expect(files['src_one_ts.js']).not.toBeUndefined();
expect(files['src_two_ts.js']).not.toBeUndefined();
expect(
files['default-node_modules_angular_common___ivy_ngcc___fesm2015_http_js.js'],
).toBeDefined();
expect(files['default-node_modules_angular_common_fesm2020_http_mjs.js']).toBeDefined();
});

it(`supports disabling the common bundle`, async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { BuilderContext, createBuilder, targetFromTargetString } from '@angular-
import { BuildResult, runWebpack } from '@angular-devkit/build-webpack';
import { JsonObject } from '@angular-devkit/core';
import type { Ι΅ParsedMessage as LocalizeMessage } from '@angular/localize';
import type { Diagnostics } from '@angular/localize/src/tools/src/diagnostics';
import type { Diagnostics } from '@angular/localize/tools';
import * as fs from 'fs';
import * as path from 'path';
import webpack from 'webpack';
Expand All @@ -32,24 +32,6 @@ import { Format, Schema } from './schema';

export type ExtractI18nBuilderOptions = Schema & JsonObject;

/**
* The manually constructed type for the `@angular/localize/tools` module.
* This type only contains the exports that are need for this file.
*
* TODO_ESM: Remove once the `tools` entry point exists in a published package version
*/
interface LocalizeToolsModule {
/* eslint-disable max-len */
checkDuplicateMessages: typeof import('@angular/localize/src/tools/src/extract/duplicates').checkDuplicateMessages;
XmbTranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/xmb_translation_serializer').XmbTranslationSerializer;
SimpleJsonTranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/json_translation_serializer').SimpleJsonTranslationSerializer;
Xliff1TranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer').Xliff1TranslationSerializer;
Xliff2TranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer').Xliff2TranslationSerializer;
ArbTranslationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/arb_translation_serializer').ArbTranslationSerializer;
LegacyMessageIdMigrationSerializer: typeof import('@angular/localize/src/tools/src/extract/translation_files/legacy_message_id_migration_serializer').LegacyMessageIdMigrationSerializer;
/* eslint-enable max-len */
}

function getI18nOutfile(format: string | undefined) {
switch (format) {
case 'xmb':
Expand All @@ -71,67 +53,40 @@ function getI18nOutfile(format: string | undefined) {
}

async function getSerializer(
localizeToolsModule: LocalizeToolsModule | undefined,
localizeToolsModule: typeof import('@angular/localize/tools'),
format: Format,
sourceLocale: string,
basePath: string,
useLegacyIds: boolean,
diagnostics: Diagnostics,
) {
const {
XmbTranslationSerializer,
LegacyMessageIdMigrationSerializer,
ArbTranslationSerializer,
Xliff1TranslationSerializer,
Xliff2TranslationSerializer,
SimpleJsonTranslationSerializer,
} = localizeToolsModule;

switch (format) {
case Format.Xmb:
const { XmbTranslationSerializer } =
localizeToolsModule ??
(await import(
'@angular/localize/src/tools/src/extract/translation_files/xmb_translation_serializer'
));

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return new XmbTranslationSerializer(basePath as any, useLegacyIds);
case Format.Xlf:
case Format.Xlif:
case Format.Xliff:
const { Xliff1TranslationSerializer } =
localizeToolsModule ??
(await import(
'@angular/localize/src/tools/src/extract/translation_files/xliff1_translation_serializer'
));

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return new Xliff1TranslationSerializer(sourceLocale, basePath as any, useLegacyIds, {});
case Format.Xlf2:
case Format.Xliff2:
const { Xliff2TranslationSerializer } =
localizeToolsModule ??
(await import(
'@angular/localize/src/tools/src/extract/translation_files/xliff2_translation_serializer'
));

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return new Xliff2TranslationSerializer(sourceLocale, basePath as any, useLegacyIds, {});
case Format.Json:
const { SimpleJsonTranslationSerializer } =
localizeToolsModule ??
(await import(
'@angular/localize/src/tools/src/extract/translation_files/json_translation_serializer'
));

return new SimpleJsonTranslationSerializer(sourceLocale);
case Format.LegacyMigrate:
const { LegacyMessageIdMigrationSerializer } =
localizeToolsModule ??
(await import(
'@angular/localize/src/tools/src/extract/translation_files/legacy_message_id_migration_serializer'
));

return new LegacyMessageIdMigrationSerializer(diagnostics);
case Format.Arb:
const { ArbTranslationSerializer } =
localizeToolsModule ??
(await import(
'@angular/localize/src/tools/src/extract/translation_files/arb_translation_serializer'
));

const fileSystem = {
relative(from: string, to: string): string {
return path.relative(from, to);
Expand Down Expand Up @@ -287,15 +242,9 @@ export async function execute(

// All the localize usages are setup to first try the ESM entry point then fallback to the deep imports.
// This provides interim compatibility while the framework is transitioned to bundled ESM packages.
// TODO_ESM: Remove all deep imports once `@angular/localize` is published with the `tools` entry point
let localizeToolsModule;
try {
// Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround.
// Once TypeScript provides support for keeping the dynamic import this workaround can be
// changed to a direct dynamic import.
localizeToolsModule = await loadEsmModule<LocalizeToolsModule>('@angular/localize/tools');
} catch {}

const localizeToolsModule = await loadEsmModule<typeof import('@angular/localize/tools')>(
'@angular/localize/tools',
);
const webpackResult = await runWebpack(
(await transforms?.webpackConfiguration?.(config)) || config,
context,
Expand All @@ -315,8 +264,7 @@ export async function execute(

const basePath = config.context || projectRoot;

const { checkDuplicateMessages } =
localizeToolsModule ?? (await import('@angular/localize/src/tools/src/extract/duplicates'));
const { checkDuplicateMessages } = localizeToolsModule;

// The filesystem is used to create a relative path for each file
// from the basePath. This relative path is then used in the error message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,13 @@ async function extract(
// TODO_ESM: Remove all deep imports once `@angular/localize` is published with the `tools` entry point
let MessageExtractor;
try {
try {
// Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround.
// Once TypeScript provides support for keeping the dynamic import this workaround can be
// changed to a direct dynamic import.
const localizeToolsModule = await loadEsmModule<
typeof import('@angular/localize/src/tools/src/extract/extraction')
>('@angular/localize/tools');
MessageExtractor = localizeToolsModule.MessageExtractor;
} catch {
MessageExtractor = (await import('@angular/localize/src/tools/src/extract/extraction'))
.MessageExtractor;
}
// Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround.
// Once TypeScript provides support for keeping the dynamic import this workaround can be
// changed to a direct dynamic import.
const localizeToolsModule = await loadEsmModule<typeof import('@angular/localize/tools')>(
'@angular/localize/tools',
);
MessageExtractor = localizeToolsModule.MessageExtractor;
} catch {
throw new Error(
`Unable to load message extractor. Please ensure '@angular/localize' is installed.`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ describe('NgPackagr Builder', () => {

await run.stop();

expect(host.scopedSync().exists(normalize('./dist/lib/fesm2015/lib.js'))).toBe(true);
expect(host.scopedSync().exists(normalize('./dist/lib/fesm2015/lib.mjs'))).toBe(true);
const content = virtualFs.fileBufferToString(
host.scopedSync().read(normalize('./dist/lib/fesm2015/lib.js')),
host.scopedSync().read(normalize('./dist/lib/fesm2015/lib.mjs')),
);
expect(content).toContain('lib works');

Expand Down Expand Up @@ -90,7 +90,7 @@ describe('NgPackagr Builder', () => {
tap((buildEvent) => expect(buildEvent.success).toBe(true)),
debounceTime(1000),
map(() => {
const fileName = './dist/lib/fesm2015/lib.js';
const fileName = './dist/lib/fesm2015/lib.mjs';
const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));

return content;
Expand Down
17 changes: 2 additions & 15 deletions packages/angular_devkit/build_angular/src/utils/load-esm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,6 @@ import { URL } from 'url';
* @param modulePath The path of the module to load.
* @returns A Promise that resolves to the dynamically imported module.
*/
export async function loadEsmModule<T>(modulePath: string | URL): Promise<T> {
try {
return (await new Function('modulePath', `return import(modulePath);`)(modulePath)) as T;
} catch (e) {
// Temporary workaround to handle directory imports for current packages. ESM does not support
// directory imports.
// TODO_ESM: Remove once FW packages are fully ESM with defined `exports` package.json fields
if (e.code !== 'ERR_UNSUPPORTED_DIR_IMPORT') {
throw e;
}

return (await new Function('modulePath', `return import(modulePath);`)(
modulePath + '/index.js',
)) as T;
}
export function loadEsmModule<T>(modulePath: string | URL): Promise<T> {
return new Function('modulePath', `return import(modulePath);`)(modulePath) as Promise<T>;
}
Loading