@@ -38,62 +38,64 @@ export function createBrowserCodeBundleOptions(
3838 target : string [ ] ,
3939 sourceFileCache : SourceFileCache ,
4040 stylesheetBundler : ComponentStylesheetBundler ,
41- ) : BuildOptions {
42- const { entryPoints, outputNames, polyfills } = options ;
43-
44- const pluginOptions = createCompilerPluginOptions ( options , sourceFileCache ) ;
45-
46- const zoneless = isZonelessApp ( polyfills ) ;
47-
48- const buildOptions : BuildOptions = {
49- ...getEsBuildCommonOptions ( options ) ,
50- platform : 'browser' ,
51- // Note: `es2015` is needed for RxJS v6. If not specified, `module` would
52- // match and the ES5 distribution would be bundled and ends up breaking at
53- // runtime with the RxJS testing library.
54- // More details: https://github.com/angular/angular-cli/issues/25405.
55- mainFields : [ 'es2020' , 'es2015' , 'browser' , 'module' , 'main' ] ,
56- entryNames : outputNames . bundles ,
57- entryPoints,
58- target,
59- supported : getFeatureSupport ( target , zoneless ) ,
60- plugins : [
61- createLoaderImportAttributePlugin ( ) ,
62- createWasmPlugin ( { allowAsync : zoneless , cache : sourceFileCache ?. loadResultCache } ) ,
63- createSourcemapIgnorelistPlugin ( ) ,
64- createCompilerPlugin (
65- // JS/TS options
66- pluginOptions ,
67- // Component stylesheet bundler
68- stylesheetBundler ,
69- ) ,
70- ] ,
71- } ;
41+ ) : BundlerOptionsFactory {
42+ return ( loadCache ) => {
43+ const { entryPoints, outputNames, polyfills } = options ;
44+
45+ const pluginOptions = createCompilerPluginOptions ( options , sourceFileCache , loadCache ) ;
46+
47+ const zoneless = isZonelessApp ( polyfills ) ;
48+
49+ const buildOptions : BuildOptions = {
50+ ...getEsBuildCommonOptions ( options ) ,
51+ platform : 'browser' ,
52+ // Note: `es2015` is needed for RxJS v6. If not specified, `module` would
53+ // match and the ES5 distribution would be bundled and ends up breaking at
54+ // runtime with the RxJS testing library.
55+ // More details: https://github.com/angular/angular-cli/issues/25405.
56+ mainFields : [ 'es2020' , 'es2015' , 'browser' , 'module' , 'main' ] ,
57+ entryNames : outputNames . bundles ,
58+ entryPoints,
59+ target,
60+ supported : getFeatureSupport ( target , zoneless ) ,
61+ plugins : [
62+ createLoaderImportAttributePlugin ( ) ,
63+ createWasmPlugin ( { allowAsync : zoneless , cache : loadCache } ) ,
64+ createSourcemapIgnorelistPlugin ( ) ,
65+ createCompilerPlugin (
66+ // JS/TS options
67+ pluginOptions ,
68+ // Component stylesheet bundler
69+ stylesheetBundler ,
70+ ) ,
71+ ] ,
72+ } ;
7273
73- if ( options . plugins ) {
74- buildOptions . plugins ?. push ( ...options . plugins ) ;
75- }
74+ if ( options . plugins ) {
75+ buildOptions . plugins ?. push ( ...options . plugins ) ;
76+ }
7677
77- if ( options . externalPackages ) {
78- // Package files affected by a customized loader should not be implicitly marked as external
79- if (
80- options . loaderExtensions ||
81- options . plugins ||
82- typeof options . externalPackages === 'object'
83- ) {
84- // Plugin must be added after custom plugins to ensure any added loader options are considered
85- buildOptions . plugins ?. push (
86- createExternalPackagesPlugin (
87- options . externalPackages !== true ? options . externalPackages : undefined ,
88- ) ,
89- ) ;
90- } else {
91- // Safe to use the packages external option directly
92- buildOptions . packages = 'external' ;
78+ if ( options . externalPackages ) {
79+ // Package files affected by a customized loader should not be implicitly marked as external
80+ if (
81+ options . loaderExtensions ||
82+ options . plugins ||
83+ typeof options . externalPackages === 'object'
84+ ) {
85+ // Plugin must be added after custom plugins to ensure any added loader options are considered
86+ buildOptions . plugins ?. push (
87+ createExternalPackagesPlugin (
88+ options . externalPackages !== true ? options . externalPackages : undefined ,
89+ ) ,
90+ ) ;
91+ } else {
92+ // Safe to use the packages external option directly
93+ buildOptions . packages = 'external' ;
94+ }
9395 }
94- }
9596
96- return buildOptions ;
97+ return buildOptions ;
98+ } ;
9799}
98100
99101export function createBrowserPolyfillBundleOptions (
@@ -158,7 +160,7 @@ export function createBrowserPolyfillBundleOptions(
158160export function createServerPolyfillBundleOptions (
159161 options : NormalizedApplicationBuildOptions ,
160162 target : string [ ] ,
161- sourceFileCache ?: SourceFileCache ,
163+ loadResultCache : LoadResultCache | undefined ,
162164) : BundlerOptionsFactory | undefined {
163165 const serverPolyfills : string [ ] = [ ] ;
164166 const polyfillsFromConfig = new Set ( options . polyfills ) ;
@@ -185,7 +187,7 @@ export function createServerPolyfillBundleOptions(
185187 } ,
186188 namespace ,
187189 false ,
188- sourceFileCache ?. loadResultCache ,
190+ loadResultCache ,
189191 ) ;
190192
191193 if ( ! polyfillBundleOptions ) {
@@ -372,137 +374,139 @@ export function createSsrEntryCodeBundleOptions(
372374 target : string [ ] ,
373375 sourceFileCache : SourceFileCache ,
374376 stylesheetBundler : ComponentStylesheetBundler ,
375- ) : BuildOptions {
377+ ) : BundlerOptionsFactory {
376378 const { workspaceRoot, ssrOptions, externalPackages } = options ;
377379 const serverEntryPoint = ssrOptions ?. entry ;
378380 assert (
379381 serverEntryPoint ,
380382 'createSsrEntryCodeBundleOptions should not be called without a defined serverEntryPoint.' ,
381383 ) ;
382384
383- const pluginOptions = createCompilerPluginOptions ( options , sourceFileCache ) ;
384-
385- const ssrEntryNamespace = 'angular:ssr-entry' ;
386- const ssrInjectManifestNamespace = 'angular:ssr-entry-inject-manifest' ;
387- const ssrInjectRequireNamespace = 'angular:ssr-entry-inject-require' ;
388- const isNodePlatform = options . ssrOptions ?. platform !== ExperimentalPlatform . Neutral ;
389-
390- const inject : string [ ] = [ ssrInjectManifestNamespace ] ;
391- if ( isNodePlatform ) {
392- inject . unshift ( ssrInjectRequireNamespace ) ;
393- }
394-
395- const buildOptions : BuildOptions = {
396- ...getEsBuildServerCommonOptions ( options ) ,
397- target,
398- entryPoints : {
399- // TODO: consider renaming to index
400- 'server' : ssrEntryNamespace ,
401- } ,
402- supported : getFeatureSupport ( target , true ) ,
403- plugins : [
404- createSourcemapIgnorelistPlugin ( ) ,
405- createCompilerPlugin (
406- // JS/TS options
407- { ...pluginOptions , noopTypeScriptCompilation : true } ,
408- // Component stylesheet bundler
409- stylesheetBundler ,
410- ) ,
411- ] ,
412- inject,
413- } ;
414-
415- buildOptions . plugins ??= [ ] ;
385+ return ( loadResultCache ) => {
386+ const pluginOptions = createCompilerPluginOptions ( options , sourceFileCache , loadResultCache ) ;
416387
417- if ( externalPackages ) {
418- buildOptions . packages = 'external' ;
419- } else {
420- buildOptions . plugins . push ( createRxjsEsmResolutionPlugin ( ) ) ;
421- }
388+ const ssrEntryNamespace = 'angular:ssr-entry' ;
389+ const ssrInjectManifestNamespace = 'angular:ssr-entry-inject-manifest' ;
390+ const ssrInjectRequireNamespace = 'angular:ssr-entry-inject-require' ;
391+ const isNodePlatform = options . ssrOptions ?. platform !== ExperimentalPlatform . Neutral ;
422392
423- // Mark manifest file as external. As this will be generated later on.
424- ( buildOptions . external ??= [ ] ) . push ( '*/main.server.mjs' , ...SERVER_GENERATED_EXTERNALS ) ;
393+ const inject : string [ ] = [ ssrInjectManifestNamespace ] ;
394+ if ( isNodePlatform ) {
395+ inject . unshift ( ssrInjectRequireNamespace ) ;
396+ }
425397
426- if ( ! isNodePlatform ) {
427- // `@angular/platform-server` lazily depends on `xhr2` for XHR usage with the HTTP client.
428- // Since `xhr2` has Node.js dependencies, it cannot be used when targeting non-Node.js platforms.
429- // Note: The framework already issues a warning when using XHR with SSR.
430- buildOptions . external . push ( 'xhr2' ) ;
431- }
398+ const buildOptions : BuildOptions = {
399+ ...getEsBuildServerCommonOptions ( options ) ,
400+ target,
401+ entryPoints : {
402+ // TODO: consider renaming to index
403+ 'server' : ssrEntryNamespace ,
404+ } ,
405+ supported : getFeatureSupport ( target , true ) ,
406+ plugins : [
407+ createSourcemapIgnorelistPlugin ( ) ,
408+ createCompilerPlugin (
409+ // JS/TS options
410+ { ...pluginOptions , noopTypeScriptCompilation : true } ,
411+ // Component stylesheet bundler
412+ stylesheetBundler ,
413+ ) ,
414+ ] ,
415+ inject,
416+ } ;
432417
433- buildOptions . plugins . push (
434- createServerBundleMetadata ( { ssrEntryBundle : true } ) ,
435- createVirtualModulePlugin ( {
436- namespace : ssrInjectRequireNamespace ,
437- cache : sourceFileCache ?. loadResultCache ,
438- loadContent : ( ) => {
439- const contents : string [ ] = [
440- // Note: Needed as esbuild does not provide require shims / proxy from ESModules.
441- // See: https://github.com/evanw/esbuild/issues/1921.
442- `import { createRequire } from 'node:module';` ,
443- `globalThis['require'] ??= createRequire(import.meta.url);` ,
444- ] ;
418+ buildOptions . plugins ??= [ ] ;
445419
446- return {
447- contents : contents . join ( '\n' ) ,
448- loader : 'js' ,
449- resolveDir : workspaceRoot ,
450- } ;
451- } ,
452- } ) ,
453- createVirtualModulePlugin ( {
454- namespace : ssrInjectManifestNamespace ,
455- cache : sourceFileCache ?. loadResultCache ,
456- loadContent : ( ) => {
457- const contents : string [ ] = [
458- // Configure `@angular/ssr` app engine manifest.
459- `import manifest from './${ SERVER_APP_ENGINE_MANIFEST_FILENAME } ';` ,
460- `import { ɵsetAngularAppEngineManifest } from '@angular/ssr';` ,
461- `ɵsetAngularAppEngineManifest(manifest);` ,
462- ] ;
420+ if ( externalPackages ) {
421+ buildOptions . packages = 'external' ;
422+ } else {
423+ buildOptions . plugins . push ( createRxjsEsmResolutionPlugin ( ) ) ;
424+ }
463425
464- return {
465- contents : contents . join ( '\n' ) ,
466- loader : 'js' ,
467- resolveDir : workspaceRoot ,
468- } ;
469- } ,
470- } ) ,
471- createVirtualModulePlugin ( {
472- namespace : ssrEntryNamespace ,
473- cache : sourceFileCache ?. loadResultCache ,
474- loadContent : ( ) => {
475- const serverEntryPointJsImport = entryFileToWorkspaceRelative (
476- workspaceRoot ,
477- serverEntryPoint ,
478- ) ;
479- const contents : string [ ] = [
480- // Re-export all symbols including default export
481- `import * as server from '${ serverEntryPointJsImport } ';` ,
482- `export * from '${ serverEntryPointJsImport } ';` ,
483- // The below is needed to avoid
484- // `Import "default" will always be undefined because there is no matching export` warning when no default is present.
485- `const defaultExportName = 'default';` ,
486- `export default server[defaultExportName]` ,
426+ // Mark manifest file as external. As this will be generated later on.
427+ ( buildOptions . external ??= [ ] ) . push ( '*/main.server.mjs' , ...SERVER_GENERATED_EXTERNALS ) ;
487428
488- // Add @angular /ssr exports
489- `export { AngularAppEngine } from '@angular/ssr';` ,
490- ] ;
429+ if ( ! isNodePlatform ) {
430+ // `@angular/platform-server` lazily depends on `xhr2` for XHR usage with the HTTP client.
431+ // Since `xhr2` has Node.js dependencies, it cannot be used when targeting non-Node.js platforms.
432+ // Note: The framework already issues a warning when using XHR with SSR.
433+ buildOptions . external . push ( 'xhr2' ) ;
434+ }
491435
492- return {
493- contents : contents . join ( '\n' ) ,
494- loader : 'js' ,
495- resolveDir : workspaceRoot ,
496- } ;
497- } ,
498- } ) ,
499- ) ;
436+ buildOptions . plugins . push (
437+ createServerBundleMetadata ( { ssrEntryBundle : true } ) ,
438+ createVirtualModulePlugin ( {
439+ namespace : ssrInjectRequireNamespace ,
440+ cache : loadResultCache ,
441+ loadContent : ( ) => {
442+ const contents : string [ ] = [
443+ // Note: Needed as esbuild does not provide require shims / proxy from ESModules.
444+ // See: https://github.com/evanw/esbuild/issues/1921.
445+ `import { createRequire } from 'node:module';` ,
446+ `globalThis['require'] ??= createRequire(import.meta.url);` ,
447+ ] ;
448+
449+ return {
450+ contents : contents . join ( '\n' ) ,
451+ loader : 'js' ,
452+ resolveDir : workspaceRoot ,
453+ } ;
454+ } ,
455+ } ) ,
456+ createVirtualModulePlugin ( {
457+ namespace : ssrInjectManifestNamespace ,
458+ cache : loadResultCache ,
459+ loadContent : ( ) => {
460+ const contents : string [ ] = [
461+ // Configure `@angular/ssr` app engine manifest.
462+ `import manifest from './${ SERVER_APP_ENGINE_MANIFEST_FILENAME } ';` ,
463+ `import { ɵsetAngularAppEngineManifest } from '@angular/ssr';` ,
464+ `ɵsetAngularAppEngineManifest(manifest);` ,
465+ ] ;
466+
467+ return {
468+ contents : contents . join ( '\n' ) ,
469+ loader : 'js' ,
470+ resolveDir : workspaceRoot ,
471+ } ;
472+ } ,
473+ } ) ,
474+ createVirtualModulePlugin ( {
475+ namespace : ssrEntryNamespace ,
476+ cache : loadResultCache ,
477+ loadContent : ( ) => {
478+ const serverEntryPointJsImport = entryFileToWorkspaceRelative (
479+ workspaceRoot ,
480+ serverEntryPoint ,
481+ ) ;
482+ const contents : string [ ] = [
483+ // Re-export all symbols including default export
484+ `import * as server from '${ serverEntryPointJsImport } ';` ,
485+ `export * from '${ serverEntryPointJsImport } ';` ,
486+ // The below is needed to avoid
487+ // `Import "default" will always be undefined because there is no matching export` warning when no default is present.
488+ `const defaultExportName = 'default';` ,
489+ `export default server[defaultExportName]` ,
490+
491+ // Add @angular /ssr exports
492+ `export { AngularAppEngine } from '@angular/ssr';` ,
493+ ] ;
494+
495+ return {
496+ contents : contents . join ( '\n' ) ,
497+ loader : 'js' ,
498+ resolveDir : workspaceRoot ,
499+ } ;
500+ } ,
501+ } ) ,
502+ ) ;
500503
501- if ( options . plugins ) {
502- buildOptions . plugins . push ( ...options . plugins ) ;
503- }
504+ if ( options . plugins ) {
505+ buildOptions . plugins . push ( ...options . plugins ) ;
506+ }
504507
505- return buildOptions ;
508+ return buildOptions ;
509+ } ;
506510}
507511
508512function getEsBuildServerCommonOptions ( options : NormalizedApplicationBuildOptions ) : BuildOptions {
0 commit comments