Skip to content

Commit f9da163

Browse files
alan-agius4clydin
authored andcommittedNov 25, 2024·
fix(@angular/build): minimize reliance on esbuild inject to prevent code reordering
Resolved an issue where the use of `esbuild`'s `inject` feature caused incorrect reordering of class structures during bundling. This reordering affected extended classes, as illustrated below: ```js class e extends Ur { constructor(n, r, i) { super(n, r, i); } ngOnDestroy() { this.flush(); } static ɵfac = function (r) { return new (r || e)(pe(Xe), pe(Ti), pe(Di)); }; static ɵprov = oe({ token: e, factory: e.ɵfac }); } var Ur = class { // Class properties and methods omitted for brevity }; ``` By reducing the reliance on `inject`, we ensure that the ordering of class properties and methods remains consistent, preserving the expected behavior. Closes #28941 (cherry picked from commit 8f9fc59)
1 parent 5ac03f4 commit f9da163

File tree

2 files changed

+33
-46
lines changed

2 files changed

+33
-46
lines changed
 

Diff for: ‎packages/angular/build/src/tools/esbuild/application-code-bundle.ts

+28-46
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,7 @@ export function createServerMainCodeBundleOptions(
254254

255255
return (loadResultCache) => {
256256
const pluginOptions = createCompilerPluginOptions(options, sourceFileCache, loadResultCache);
257-
258257
const mainServerNamespace = 'angular:main-server';
259-
const mainServerInjectPolyfillsNamespace = 'angular:main-server-inject-polyfills';
260258
const mainServerInjectManifestNamespace = 'angular:main-server-inject-manifest';
261259
const zoneless = isZonelessApp(polyfills);
262260
const entryPoints: Record<string, string> = {
@@ -275,7 +273,9 @@ export function createServerMainCodeBundleOptions(
275273
const buildOptions: BuildOptions = {
276274
...getEsBuildServerCommonOptions(options),
277275
target,
278-
inject: [mainServerInjectPolyfillsNamespace, mainServerInjectManifestNamespace],
276+
banner: {
277+
js: `import './polyfills.server.mjs';`,
278+
},
279279
entryPoints,
280280
supported: getFeatureSupport(target, zoneless),
281281
plugins: [
@@ -311,18 +311,10 @@ export function createServerMainCodeBundleOptions(
311311

312312
buildOptions.plugins.push(
313313
createServerBundleMetadata(),
314-
createVirtualModulePlugin({
315-
namespace: mainServerInjectPolyfillsNamespace,
316-
cache: loadResultCache,
317-
loadContent: () => ({
318-
contents: `import './polyfills.server.mjs';`,
319-
loader: 'js',
320-
resolveDir: workspaceRoot,
321-
}),
322-
}),
323314
createVirtualModulePlugin({
324315
namespace: mainServerInjectManifestNamespace,
325316
cache: loadResultCache,
317+
entryPointOnly: false,
326318
loadContent: async () => {
327319
const contents: string[] = [
328320
// Configure `@angular/ssr` manifest.
@@ -348,16 +340,19 @@ export function createServerMainCodeBundleOptions(
348340
);
349341

350342
const contents: string[] = [
351-
// Re-export all symbols including default export from 'main.server.ts'
352-
`export { default } from '${mainServerEntryPointJsImport}';`,
353-
`export * from '${mainServerEntryPointJsImport}';`,
343+
// Inject manifest
344+
`import '${mainServerInjectManifestNamespace}';`,
354345

355346
// Add @angular/ssr exports
356347
`export {
357-
ɵdestroyAngularServerApp,
358-
ɵextractRoutesAndCreateRouteTree,
359-
ɵgetOrCreateAngularServerApp,
360-
} from '@angular/ssr';`,
348+
ɵdestroyAngularServerApp,
349+
ɵextractRoutesAndCreateRouteTree,
350+
ɵgetOrCreateAngularServerApp,
351+
} from '@angular/ssr';`,
352+
353+
// Re-export all symbols including default export from 'main.server.ts'
354+
`export { default } from '${mainServerEntryPointJsImport}';`,
355+
`export * from '${mainServerEntryPointJsImport}';`,
361356
];
362357

363358
return {
@@ -392,22 +387,24 @@ export function createSsrEntryCodeBundleOptions(
392387

393388
return (loadResultCache) => {
394389
const pluginOptions = createCompilerPluginOptions(options, sourceFileCache, loadResultCache);
395-
396390
const ssrEntryNamespace = 'angular:ssr-entry';
397391
const ssrInjectManifestNamespace = 'angular:ssr-entry-inject-manifest';
398-
const ssrInjectRequireNamespace = 'angular:ssr-entry-inject-require';
399392
const isNodePlatform = options.ssrOptions?.platform !== ExperimentalPlatform.Neutral;
400393

401-
const inject: string[] = [ssrInjectManifestNamespace];
402-
if (isNodePlatform) {
403-
inject.unshift(ssrInjectRequireNamespace);
404-
}
405-
406394
const buildOptions: BuildOptions = {
407395
...getEsBuildServerCommonOptions(options),
408396
target,
397+
banner: isNodePlatform
398+
? {
399+
js: [
400+
// Note: Needed as esbuild does not provide require shims / proxy from ESModules.
401+
// See: https://github.com/evanw/esbuild/issues/1921.
402+
`import { createRequire } from 'node:module';`,
403+
`globalThis['require'] ??= createRequire(import.meta.url);`,
404+
].join('\n'),
405+
}
406+
: undefined,
409407
entryPoints: {
410-
// TODO: consider renaming to index
411408
'server': ssrEntryNamespace,
412409
},
413410
supported: getFeatureSupport(target, true),
@@ -420,7 +417,6 @@ export function createSsrEntryCodeBundleOptions(
420417
stylesheetBundler,
421418
),
422419
],
423-
inject,
424420
};
425421

426422
buildOptions.plugins ??= [];
@@ -443,27 +439,10 @@ export function createSsrEntryCodeBundleOptions(
443439

444440
buildOptions.plugins.push(
445441
createServerBundleMetadata({ ssrEntryBundle: true }),
446-
createVirtualModulePlugin({
447-
namespace: ssrInjectRequireNamespace,
448-
cache: loadResultCache,
449-
loadContent: () => {
450-
const contents: string[] = [
451-
// Note: Needed as esbuild does not provide require shims / proxy from ESModules.
452-
// See: https://github.com/evanw/esbuild/issues/1921.
453-
`import { createRequire } from 'node:module';`,
454-
`globalThis['require'] ??= createRequire(import.meta.url);`,
455-
];
456-
457-
return {
458-
contents: contents.join('\n'),
459-
loader: 'js',
460-
resolveDir: workspaceRoot,
461-
};
462-
},
463-
}),
464442
createVirtualModulePlugin({
465443
namespace: ssrInjectManifestNamespace,
466444
cache: loadResultCache,
445+
entryPointOnly: false,
467446
loadContent: () => {
468447
const contents: string[] = [
469448
// Configure `@angular/ssr` app engine manifest.
@@ -488,6 +467,9 @@ export function createSsrEntryCodeBundleOptions(
488467
serverEntryPoint,
489468
);
490469
const contents: string[] = [
470+
// Configure `@angular/ssr` app engine manifest.
471+
`import '${ssrInjectManifestNamespace}';`,
472+
491473
// Re-export all symbols including default export
492474
`import * as server from '${serverEntryPointJsImport}';`,
493475
`export * from '${serverEntryPointJsImport}';`,

Diff for: ‎tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { updateJsonFile, useSha } from '../../../utils/project';
1212
import { getGlobalVariable } from '../../../utils/env';
1313
import { findFreePort } from '../../../utils/network';
14+
import { readFile } from 'node:fs/promises';
1415

1516
export default async function () {
1617
assert(
@@ -68,6 +69,10 @@ export default async function () {
6869
},
6970
];
7071
`,
72+
'src/app/app.config.ts': `
73+
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
74+
${(await readFile('src/app/app.config.ts', 'utf8')).replace('provideRouter(routes),', 'provideAnimationsAsync(), provideRouter(routes),')}
75+
`,
7176
'src/server.ts': `
7277
import { AngularAppEngine, createRequestHandler } from '@angular/ssr';
7378
import { createApp, createRouter, toWebHandler, defineEventHandler, toWebRequest } from 'h3';

0 commit comments

Comments
 (0)
Please sign in to comment.