Skip to content

Commit b356bb1

Browse files
committed
refactor: replace loadEsmModule with dynamic import()
The custom `loadEsmModule` helper function was a workaround to prevent TypeScript from downleveling dynamic imports to `require()` calls, which would fail to load ESM modules. With modern TypeScript and Node.js versions, this workaround is no longer necessary. This commit removes the `loadEsmModule` utility and replaces all its usages with standard dynamic `import()` expressions. This simplifies the codebase and relies on the native module loading capabilities of the environment.
1 parent a25aa52 commit b356bb1

File tree

34 files changed

+90
-306
lines changed

34 files changed

+90
-306
lines changed

packages/angular/build/src/builders/dev-server/vite/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { ServerSsrMode } from '../../../tools/vite/plugins';
1717
import { EsbuildLoaderOption } from '../../../tools/vite/utils';
1818
import { normalizeSourceMaps } from '../../../utils';
1919
import { useComponentStyleHmr, useComponentTemplateHmr } from '../../../utils/environment-options';
20-
import { loadEsmModule } from '../../../utils/load-esm';
2120
import { Result, ResultKind } from '../../application/results';
2221
import { OutputHashing } from '../../application/schema';
2322
import {
@@ -175,8 +174,7 @@ export async function* serveWithVite(
175174
// The index HTML path will be updated from the build results if provided by the builder
176175
let htmlIndexPath = 'index.html';
177176

178-
// dynamically import Vite for ESM compatibility
179-
const { createServer, normalizePath } = await loadEsmModule<typeof import('vite')>('vite');
177+
const { createServer, normalizePath } = await import('vite');
180178

181179
let server: ViteDevServer | undefined;
182180
let serverUrl: URL | undefined;

packages/angular/build/src/builders/dev-server/vite/server.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
} from '../../../tools/vite/plugins';
2020
import { EsbuildLoaderOption, getDepOptimizationConfig } from '../../../tools/vite/utils';
2121
import { loadProxyConfiguration } from '../../../utils';
22-
import { loadEsmModule } from '../../../utils/load-esm';
2322
import { type ApplicationBuilderInternalOptions, JavaScriptTransformer } from '../internal';
2423
import type { NormalizedDevServerOptions } from '../options';
2524
import { DevServerExternalResultMetadata, OutputAssetRecord, OutputFileRecord } from './utils';
@@ -152,8 +151,7 @@ export async function setupServer(
152151
indexHtmlTransformer?: (content: string) => Promise<string>,
153152
thirdPartySourcemaps = false,
154153
): Promise<InlineConfig> {
155-
// dynamically import Vite for ESM compatibility
156-
const { normalizePath } = await loadEsmModule<typeof import('vite')>('vite');
154+
const { normalizePath } = await import('vite');
157155

158156
// Path will not exist on disk and only used to provide separate path for Vite requests
159157
const virtualProjectRoot = normalizePath(

packages/angular/build/src/builders/extract-i18n/builder.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import type { Diagnostics } from '@angular/localize/tools';
1010
import type { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
1111
import fs from 'node:fs';
1212
import path from 'node:path';
13-
import { loadEsmModule } from '../../utils/load-esm';
1413
import { assertCompatibleAngularVersion } from '../../utils/version';
1514
import type { ApplicationBuilderExtensions } from '../application/options';
1615
import { normalizeOptions } from './options';
@@ -51,8 +50,7 @@ export async function execute(
5150
// The package is a peer dependency and might not be present
5251
let localizeToolsModule;
5352
try {
54-
localizeToolsModule =
55-
await loadEsmModule<typeof import('@angular/localize/tools')>('@angular/localize/tools');
53+
localizeToolsModule = await import('@angular/localize/tools');
5654
} catch {
5755
return {
5856
success: false,

packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
*/
88

99
import { createRequire } from 'node:module';
10+
import type { BrowserBuiltinProvider, BrowserConfigOptions } from 'vitest/node';
1011

1112
export interface BrowserConfiguration {
12-
browser?: import('vitest/node').BrowserConfigOptions;
13+
browser?: BrowserConfigOptions;
1314
errors?: string[];
1415
}
1516

1617
function findBrowserProvider(
1718
projectResolver: NodeJS.RequireResolve,
18-
): import('vitest/node').BrowserBuiltinProvider | undefined {
19+
): BrowserBuiltinProvider | undefined {
1920
// One of these must be installed in the project to use browser testing
2021
const vitestBuiltinProviders = ['playwright', 'webdriverio'] as const;
2122

@@ -87,7 +88,7 @@ export function setupBrowserConfiguration(
8788
const isCI = !!process.env['CI'];
8889
const headless = isCI || browsers.some((name) => name.toLowerCase().includes('headless'));
8990

90-
const browser = {
91+
const browser: BrowserConfigOptions = {
9192
enabled: true,
9293
provider,
9394
headless,
@@ -96,7 +97,7 @@ export function setupBrowserConfiguration(
9697
instances: browsers.map((browserName) => ({
9798
browser: normalizeBrowserName(browserName),
9899
})),
99-
} satisfies import('vitest/node').BrowserConfigOptions;
100+
};
100101

101102
return { browser };
102103
}

packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import assert from 'node:assert';
1111
import path from 'node:path';
1212
import type { InlineConfig, Vitest } from 'vitest/node';
1313
import { assertIsError } from '../../../../utils/error';
14-
import { loadEsmModule } from '../../../../utils/load-esm';
1514
import { toPosixPath } from '../../../../utils/path';
1615
import {
1716
type FullResult,
@@ -141,7 +140,7 @@ export class VitestExecutor implements TestExecutor {
141140
} = this.options;
142141
let vitestNodeModule;
143142
try {
144-
vitestNodeModule = await loadEsmModule<typeof import('vitest/node')>('vitest/node');
143+
vitestNodeModule = await import('vitest/node');
145144
} catch (error: unknown) {
146145
assertIsError(error);
147146
if (error.code !== 'ERR_MODULE_NOT_FOUND') {
@@ -230,7 +229,7 @@ async function generateCoverageOption(
230229
let defaultExcludes: string[] = [];
231230
if (coverage.exclude) {
232231
try {
233-
const vitestConfig = await loadEsmModule<typeof import('vitest/config')>('vitest/config');
232+
const vitestConfig = await import('vitest/config');
234233
defaultExcludes = vitestConfig.coverageConfigDefaults.exclude;
235234
} catch {}
236235
}

packages/angular/build/src/tools/angular/compilation/angular-compilation.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import type ng from '@angular/compiler-cli';
1010
import type { PartialMessage } from 'esbuild';
1111
import type ts from 'typescript';
12-
import { loadEsmModule } from '../../../utils/load-esm';
1312
import { convertTypeScriptDiagnostic } from '../../esbuild/angular/diagnostics';
1413
import { profileAsync, profileSync } from '../../esbuild/profiling';
1514
import type { AngularHostOptions } from '../angular-host';
@@ -33,10 +32,7 @@ export abstract class AngularCompilation {
3332
static #typescriptModule?: typeof ts;
3433

3534
static async loadCompilerCli(): Promise<typeof ng> {
36-
// This uses a wrapped dynamic import to load `@angular/compiler-cli` which is ESM.
37-
// Once TypeScript provides support for retaining dynamic imports this workaround can be dropped.
38-
AngularCompilation.#angularCompilerCliModule ??=
39-
await loadEsmModule<typeof ng>('@angular/compiler-cli');
35+
AngularCompilation.#angularCompilerCliModule ??= await import('@angular/compiler-cli');
4036

4137
return AngularCompilation.#angularCompilerCliModule;
4238
}

packages/angular/build/src/tools/angular/compilation/jit-compilation.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import type ng from '@angular/compiler-cli';
1010
import assert from 'node:assert';
1111
import ts from 'typescript';
12-
import { loadEsmModule } from '../../../utils/load-esm';
1312
import { profileSync } from '../../esbuild/profiling';
1413
import { AngularHostOptions, createAngularCompilerHost } from '../angular-host';
1514
import { createJitResourceTransformer } from '../transformers/jit-resource-transformer';
@@ -44,9 +43,9 @@ export class JitCompilation extends AngularCompilation {
4443
referencedFiles: readonly string[];
4544
}> {
4645
// Dynamically load the Angular compiler CLI package
47-
const { constructorParametersDownlevelTransform } = await loadEsmModule<
48-
typeof import('@angular/compiler-cli/private/tooling')
49-
>('@angular/compiler-cli/private/tooling');
46+
const { constructorParametersDownlevelTransform } = await import(
47+
'@angular/compiler-cli/private/tooling'
48+
);
5049

5150
// Load the compiler configuration and transform as needed
5251
const {

packages/angular/build/src/tools/esbuild/i18n-inliner-worker.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { PluginObj, parseSync, transformFromAstAsync, types } from '@babel/core'
1111
import assert from 'node:assert';
1212
import { workerData } from 'node:worker_threads';
1313
import { assertIsError } from '../../utils/error';
14-
import { loadEsmModule } from '../../utils/load-esm';
1514

1615
/**
1716
* The options passed to the inliner for each file request
@@ -131,7 +130,7 @@ async function loadLocalizeTools(): Promise<LocalizeUtilityModule> {
131130
// Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround.
132131
// Once TypeScript provides support for keeping the dynamic import this workaround can be
133132
// changed to a direct dynamic import.
134-
localizeToolsModule ??= await loadEsmModule<LocalizeUtilityModule>('@angular/localize/tools');
133+
localizeToolsModule ??= await import('@angular/localize/tools');
135134

136135
return localizeToolsModule;
137136
}

packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { type PluginItem, transformAsync } from '@babel/core';
1010
import fs from 'node:fs';
1111
import path from 'node:path';
1212
import Piscina from 'piscina';
13-
import { loadEsmModule } from '../../utils/load-esm';
1413

1514
interface JavaScriptTransformRequest {
1615
filename: string;
@@ -133,11 +132,8 @@ async function requiresLinking(path: string, source: string): Promise<boolean> {
133132
}
134133

135134
async function createLinkerPlugin(options: Omit<JavaScriptTransformRequest, 'filename' | 'data'>) {
136-
linkerPluginCreator ??= (
137-
await loadEsmModule<typeof import('@angular/compiler-cli/linker/babel')>(
138-
'@angular/compiler-cli/linker/babel',
139-
)
140-
).createEs2015LinkerPlugin;
135+
linkerPluginCreator ??= (await import('@angular/compiler-cli/linker/babel'))
136+
.createEs2015LinkerPlugin;
141137

142138
const linkerPlugin = linkerPluginCreator({
143139
linkerJitMode: options.jit,

packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import type {
1010
AngularAppEngine as SSRAngularAppEngine,
1111
ɵgetOrCreateAngularServerApp as getOrCreateAngularServerApp,
1212
} from '@angular/ssr';
13-
import type * as AngularSsrNode from '@angular/ssr/node' with { 'resolution-mode': 'import' };
1413
import type { ServerResponse } from 'node:http';
1514
import type { Connect, ViteDevServer } from 'vite';
16-
import { loadEsmModule } from '../../../utils/load-esm';
1715
import {
1816
isSsrNodeRequestHandler,
1917
isSsrRequestHandler,
@@ -37,9 +35,11 @@ export function createAngularSsrInternalMiddleware(
3735
(async () => {
3836
// Load the compiler because `@angular/ssr/node` depends on `@angular/` packages,
3937
// which must be processed by the runtime linker, even if they are not used.
40-
await loadEsmModule('@angular/compiler');
41-
const { writeResponseToNodeResponse, createWebRequestFromNodeRequest } =
42-
await loadEsmModule<typeof AngularSsrNode>('@angular/ssr/node');
38+
await import('@angular/compiler');
39+
const { writeResponseToNodeResponse, createWebRequestFromNodeRequest } = (await import(
40+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
41+
'@angular/ssr/node' as any
42+
)) as typeof import('@angular/ssr/node', { with: { 'resolution-mode': 'import' } });
4343

4444
const { ɵgetOrCreateAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs')) as {
4545
ɵgetOrCreateAngularServerApp: typeof getOrCreateAngularServerApp;
@@ -91,10 +91,12 @@ export async function createAngularSsrExternalMiddleware(
9191

9292
// Load the compiler because `@angular/ssr/node` depends on `@angular/` packages,
9393
// which must be processed by the runtime linker, even if they are not used.
94-
await loadEsmModule('@angular/compiler');
94+
await import('@angular/compiler');
9595

96-
const { createWebRequestFromNodeRequest, writeResponseToNodeResponse } =
97-
await loadEsmModule<typeof AngularSsrNode>('@angular/ssr/node');
96+
const { createWebRequestFromNodeRequest, writeResponseToNodeResponse } = (await import(
97+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
98+
'@angular/ssr/node' as any
99+
)) as typeof import('@angular/ssr/node', { with: { 'resolution-mode': 'import' } });
98100

99101
return function angularSsrExternalMiddleware(
100102
req: Connect.IncomingMessage,

0 commit comments

Comments
 (0)