@angular-devkit/build-angular 17.0.0-rc.2 → 17.0.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/package.json +9 -8
  2. package/src/builders/app-shell/render-worker.d.ts +1 -1
  3. package/src/builders/app-shell/render-worker.js +16 -9
  4. package/src/builders/application/build-action.js +5 -2
  5. package/src/builders/application/execute-build.js +49 -30
  6. package/src/builders/dev-server/builder.d.ts +12 -1
  7. package/src/builders/dev-server/builder.js +13 -4
  8. package/src/builders/dev-server/vite-server.d.ts +9 -6
  9. package/src/builders/dev-server/vite-server.js +167 -63
  10. package/src/builders/extract-i18n/application-extraction.js +1 -0
  11. package/src/builders/prerender/routes-extractor-worker.js +1 -1
  12. package/src/builders/ssr-dev-server/index.d.ts +1 -1
  13. package/src/index.d.ts +1 -0
  14. package/src/index.js +3 -1
  15. package/src/tools/babel/plugins/elide-angular-metadata.js +14 -2
  16. package/src/tools/esbuild/angular/compiler-plugin.js +104 -57
  17. package/src/tools/esbuild/application-code-bundle.d.ts +3 -2
  18. package/src/tools/esbuild/application-code-bundle.js +7 -19
  19. package/src/tools/esbuild/bundler-context.d.ts +4 -1
  20. package/src/tools/esbuild/bundler-context.js +13 -8
  21. package/src/tools/esbuild/bundler-execution-result.d.ts +16 -14
  22. package/src/tools/esbuild/bundler-execution-result.js +18 -3
  23. package/src/tools/esbuild/commonjs-checker.js +12 -7
  24. package/src/tools/esbuild/global-scripts.d.ts +1 -1
  25. package/src/tools/esbuild/global-scripts.js +2 -1
  26. package/src/tools/esbuild/javascript-transformer.d.ts +2 -1
  27. package/src/tools/esbuild/javascript-transformer.js +44 -20
  28. package/src/tools/esbuild/utils.d.ts +1 -1
  29. package/src/tools/esbuild/utils.js +18 -4
  30. package/src/utils/bundle-calculator.js +1 -1
  31. package/src/utils/environment-options.d.ts +1 -0
  32. package/src/utils/environment-options.js +7 -2
  33. package/src/utils/load-esm.js +6 -1
  34. package/src/utils/routes-extractor/extractor.d.ts +1 -1
  35. package/src/utils/routes-extractor/extractor.js +2 -2
  36. package/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.d.ts +0 -4
  37. package/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.js +78 -28
  38. package/src/utils/server-rendering/fetch-patch.d.ts +8 -0
  39. package/src/utils/server-rendering/fetch-patch.js +66 -0
  40. package/src/utils/server-rendering/prerender.js +25 -30
  41. package/src/utils/server-rendering/render-worker.d.ts +4 -2
  42. package/src/utils/server-rendering/render-worker.js +10 -4
  43. package/src/utils/server-rendering/routes-extractor-worker.d.ts +5 -3
  44. package/src/utils/server-rendering/routes-extractor-worker.js +12 -6
  45. package/src/utils/server-rendering/prerender-server.d.ts +0 -21
  46. package/src/utils/server-rendering/prerender-server.js +0 -102
@@ -78,6 +78,8 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
78
78
  const compilation = pluginOptions.noopTypeScriptCompilation
79
79
  ? new compilation_1.NoopCompilation()
80
80
  : await (0, compilation_1.createAngularCompilation)(!!pluginOptions.jit);
81
+ // Compilation is initially assumed to have errors until emitted
82
+ let hasCompilationErrors = true;
81
83
  // Determines if TypeScript should process JavaScript files based on tsconfig `allowJs` option
82
84
  let shouldTsIgnoreJs = true;
83
85
  // Track incremental component stylesheet builds
@@ -172,77 +174,64 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
172
174
  // Return bundled worker file entry name to be used in the built output
173
175
  const workerCodeFile = workerResult.outputFiles.find((file) => file.path.endsWith('.js'));
174
176
  (0, node_assert_1.default)(workerCodeFile, 'Web Worker bundled code file should always be present.');
175
- return path.relative(build.initialOptions.outdir ?? '', workerCodeFile.path);
177
+ const workerCodePath = path.relative(build.initialOptions.outdir ?? '', workerCodeFile.path);
178
+ return workerCodePath.replaceAll('\\', '/');
176
179
  },
177
180
  };
178
181
  // Initialize the Angular compilation for the current build.
179
182
  // In watch mode, previous build state will be reused.
180
- const { compilerOptions: { allowJs }, referencedFiles, } = await compilation.initialize(tsconfigPath, hostOptions, (compilerOptions) => {
181
- // target of 9 is ES2022 (using the number avoids an expensive import of typescript just for an enum)
182
- if (compilerOptions.target === undefined || compilerOptions.target < 9) {
183
- // If 'useDefineForClassFields' is already defined in the users project leave the value as is.
184
- // Otherwise fallback to false due to https://github.com/microsoft/TypeScript/issues/45995
185
- // which breaks the deprecated `@Effects` NGRX decorator and potentially other existing code as well.
186
- compilerOptions.target = 9;
187
- compilerOptions.useDefineForClassFields ??= false;
188
- // Only add the warning on the initial build
189
- setupWarnings?.push({
190
- text: 'TypeScript compiler options "target" and "useDefineForClassFields" are set to "ES2022" and ' +
191
- '"false" respectively by the Angular CLI.',
192
- location: { file: pluginOptions.tsconfig },
193
- notes: [
194
- {
195
- text: 'To control ECMA version and features use the Browerslist configuration. ' +
196
- 'For more information, see https://angular.io/guide/build#configuring-browser-compatibility',
197
- },
198
- ],
199
- });
200
- }
201
- if (compilerOptions.compilationMode === 'partial') {
202
- setupWarnings?.push({
203
- text: 'Angular partial compilation mode is not supported when building applications.',
204
- location: null,
205
- notes: [{ text: 'Full compilation mode will be used instead.' }],
206
- });
207
- compilerOptions.compilationMode = 'full';
208
- }
209
- // Enable incremental compilation by default if caching is enabled
210
- if (pluginOptions.sourceFileCache?.persistentCachePath) {
211
- compilerOptions.incremental ??= true;
212
- // Set the build info file location to the configured cache directory
213
- compilerOptions.tsBuildInfoFile = path.join(pluginOptions.sourceFileCache?.persistentCachePath, '.tsbuildinfo');
214
- }
215
- else {
216
- compilerOptions.incremental = false;
217
- }
218
- return {
219
- ...compilerOptions,
220
- noEmitOnError: false,
221
- inlineSources: pluginOptions.sourcemap,
222
- inlineSourceMap: pluginOptions.sourcemap,
223
- mapRoot: undefined,
224
- sourceRoot: undefined,
225
- preserveSymlinks,
226
- };
227
- });
228
- shouldTsIgnoreJs = !allowJs;
183
+ let referencedFiles;
184
+ try {
185
+ const initializationResult = await compilation.initialize(tsconfigPath, hostOptions, createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks));
186
+ shouldTsIgnoreJs = !initializationResult.compilerOptions.allowJs;
187
+ referencedFiles = initializationResult.referencedFiles;
188
+ }
189
+ catch (error) {
190
+ (result.errors ??= []).push({
191
+ text: 'Angular compilation initialization failed.',
192
+ location: null,
193
+ notes: [
194
+ {
195
+ text: error instanceof Error ? error.stack ?? error.message : `${error}`,
196
+ location: null,
197
+ },
198
+ ],
199
+ });
200
+ // Initialization failure prevents further compilation steps
201
+ hasCompilationErrors = true;
202
+ return result;
203
+ }
229
204
  if (compilation instanceof compilation_1.NoopCompilation) {
230
205
  await sharedTSCompilationState.waitUntilReady;
231
206
  return result;
232
207
  }
233
208
  const diagnostics = await compilation.diagnoseFiles();
234
- if (diagnostics.errors) {
209
+ if (diagnostics.errors?.length) {
235
210
  (result.errors ??= []).push(...diagnostics.errors);
236
211
  }
237
- if (diagnostics.warnings) {
212
+ if (diagnostics.warnings?.length) {
238
213
  (result.warnings ??= []).push(...diagnostics.warnings);
239
214
  }
240
215
  // Update TypeScript file output cache for all affected files
241
- await (0, profiling_1.profileAsync)('NG_EMIT_TS', async () => {
242
- for (const { filename, contents } of await compilation.emitAffectedFiles()) {
243
- typeScriptFileCache.set((0, node_url_1.pathToFileURL)(filename).href, contents);
244
- }
245
- });
216
+ try {
217
+ await (0, profiling_1.profileAsync)('NG_EMIT_TS', async () => {
218
+ for (const { filename, contents } of await compilation.emitAffectedFiles()) {
219
+ typeScriptFileCache.set((0, node_url_1.pathToFileURL)(filename).href, contents);
220
+ }
221
+ });
222
+ }
223
+ catch (error) {
224
+ (result.errors ??= []).push({
225
+ text: 'Angular compilation emit failed.',
226
+ location: null,
227
+ notes: [
228
+ {
229
+ text: error instanceof Error ? error.stack ?? error.message : `${error}`,
230
+ location: null,
231
+ },
232
+ ],
233
+ });
234
+ }
246
235
  // Add errors from failed additional results.
247
236
  // This must be done after emit to capture latest web worker results.
248
237
  for (const { errors } of additionalResults.values()) {
@@ -257,6 +246,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
257
246
  ...referencedFileTracker.referencedFiles,
258
247
  ];
259
248
  }
249
+ hasCompilationErrors = !!result.errors?.length;
260
250
  // Reset the setup warnings so that they are only shown during the first build.
261
251
  setupWarnings = undefined;
262
252
  sharedTSCompilationState.markAsReady();
@@ -274,6 +264,11 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
274
264
  // would need to be added to the key as well as a check for any change of content.
275
265
  let contents = typeScriptFileCache.get((0, node_url_1.pathToFileURL)(request).href);
276
266
  if (contents === undefined) {
267
+ // If the Angular compilation had errors the file may not have been emitted.
268
+ // To avoid additional errors about missing files, return empty contents.
269
+ if (hasCompilationErrors) {
270
+ return { contents: '', loader: 'js' };
271
+ }
277
272
  // No TS result indicates the file is not part of the TypeScript program.
278
273
  // If allowJs is enabled and the file is JS then defer to the next load hook.
279
274
  if (!shouldTsIgnoreJs && /\.[cm]?js$/.test(request)) {
@@ -317,6 +312,8 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
317
312
  (0, jit_plugin_callbacks_1.setupJitPluginCallbacks)(build, stylesheetBundler, additionalResults, styleOptions.inlineStyleLanguage);
318
313
  }
319
314
  build.onEnd((result) => {
315
+ // Ensure other compilations are unblocked if the main compilation throws during start
316
+ sharedTSCompilationState?.markAsReady();
320
317
  for (const { outputFiles, metafile } of additionalResults.values()) {
321
318
  // Add any additional output files to the main output files
322
319
  if (outputFiles?.length) {
@@ -339,6 +336,56 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
339
336
  };
340
337
  }
341
338
  exports.createCompilerPlugin = createCompilerPlugin;
339
+ function createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks) {
340
+ return (compilerOptions) => {
341
+ // target of 9 is ES2022 (using the number avoids an expensive import of typescript just for an enum)
342
+ if (compilerOptions.target === undefined || compilerOptions.target < 9) {
343
+ // If 'useDefineForClassFields' is already defined in the users project leave the value as is.
344
+ // Otherwise fallback to false due to https://github.com/microsoft/TypeScript/issues/45995
345
+ // which breaks the deprecated `@Effects` NGRX decorator and potentially other existing code as well.
346
+ compilerOptions.target = 9;
347
+ compilerOptions.useDefineForClassFields ??= false;
348
+ // Only add the warning on the initial build
349
+ setupWarnings?.push({
350
+ text: 'TypeScript compiler options "target" and "useDefineForClassFields" are set to "ES2022" and ' +
351
+ '"false" respectively by the Angular CLI.',
352
+ location: { file: pluginOptions.tsconfig },
353
+ notes: [
354
+ {
355
+ text: 'To control ECMA version and features use the Browerslist configuration. ' +
356
+ 'For more information, see https://angular.io/guide/build#configuring-browser-compatibility',
357
+ },
358
+ ],
359
+ });
360
+ }
361
+ if (compilerOptions.compilationMode === 'partial') {
362
+ setupWarnings?.push({
363
+ text: 'Angular partial compilation mode is not supported when building applications.',
364
+ location: null,
365
+ notes: [{ text: 'Full compilation mode will be used instead.' }],
366
+ });
367
+ compilerOptions.compilationMode = 'full';
368
+ }
369
+ // Enable incremental compilation by default if caching is enabled
370
+ if (pluginOptions.sourceFileCache?.persistentCachePath) {
371
+ compilerOptions.incremental ??= true;
372
+ // Set the build info file location to the configured cache directory
373
+ compilerOptions.tsBuildInfoFile = path.join(pluginOptions.sourceFileCache?.persistentCachePath, '.tsbuildinfo');
374
+ }
375
+ else {
376
+ compilerOptions.incremental = false;
377
+ }
378
+ return {
379
+ ...compilerOptions,
380
+ noEmitOnError: false,
381
+ inlineSources: pluginOptions.sourcemap,
382
+ inlineSourceMap: pluginOptions.sourcemap,
383
+ mapRoot: undefined,
384
+ sourceRoot: undefined,
385
+ preserveSymlinks,
386
+ };
387
+ };
388
+ }
342
389
  function bundleWebWorker(build, pluginOptions, workerFile) {
343
390
  try {
344
391
  return build.esbuild.buildSync({
@@ -8,12 +8,13 @@
8
8
  import type { BuildOptions } from 'esbuild';
9
9
  import type { NormalizedApplicationBuildOptions } from '../../builders/application/options';
10
10
  import { SourceFileCache } from './angular/source-file-cache';
11
+ import { BundlerOptionsFactory } from './bundler-context';
11
12
  export declare function createBrowserCodeBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions;
12
- export declare function createBrowserPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions | undefined;
13
+ export declare function createBrowserPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions | BundlerOptionsFactory | undefined;
13
14
  /**
14
15
  * Create an esbuild 'build' options object for the server bundle.
15
16
  * @param options The builder's user-provider normalized options.
16
17
  * @returns An esbuild BuildOptions object.
17
18
  */
18
19
  export declare function createServerCodeBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache: SourceFileCache): BuildOptions;
19
- export declare function createServerPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions | undefined;
20
+ export declare function createServerPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BundlerOptionsFactory | undefined;
@@ -63,6 +63,7 @@ function createBrowserPolyfillBundleOptions(options, target, sourceFileCache) {
63
63
  return;
64
64
  }
65
65
  const { outputNames, polyfills } = options;
66
+ const hasTypeScriptEntries = polyfills?.some((entry) => /\.[cm]?tsx?$/.test(entry));
66
67
  const buildOptions = {
67
68
  ...polyfillBundleOptions,
68
69
  platform: 'browser',
@@ -78,7 +79,6 @@ function createBrowserPolyfillBundleOptions(options, target, sourceFileCache) {
78
79
  },
79
80
  };
80
81
  // Only add the Angular TypeScript compiler if TypeScript files are provided in the polyfills
81
- const hasTypeScriptEntries = polyfills?.some((entry) => /\.[cm]?tsx?$/.test(entry));
82
82
  if (hasTypeScriptEntries) {
83
83
  buildOptions.plugins ??= [];
84
84
  const { pluginOptions, styleOptions } = (0, compiler_plugin_options_1.createCompilerPluginOptions)(options, target, sourceFileCache);
@@ -88,7 +88,10 @@ function createBrowserPolyfillBundleOptions(options, target, sourceFileCache) {
88
88
  // Component stylesheet options are unused for polyfills but required by the plugin
89
89
  styleOptions));
90
90
  }
91
- return buildOptions;
91
+ // Use an options factory to allow fully incremental bundling when no TypeScript files are present.
92
+ // The TypeScript compilation is not currently integrated into the bundler invalidation so
93
+ // cannot be used with fully incremental bundling yet.
94
+ return hasTypeScriptEntries ? buildOptions : () => buildOptions;
92
95
  }
93
96
  exports.createBrowserPolyfillBundleOptions = createBrowserPolyfillBundleOptions;
94
97
  /**
@@ -174,12 +177,9 @@ function createServerCodeBundleOptions(options, target, sourceFileCache) {
174
177
  exports.createServerCodeBundleOptions = createServerCodeBundleOptions;
175
178
  function createServerPolyfillBundleOptions(options, target, sourceFileCache) {
176
179
  const polyfills = [];
177
- const zoneFlagsNamespace = 'angular:zone-flags/placeholder';
178
180
  const polyfillsFromConfig = new Set(options.polyfills);
179
- let hasZoneJs = false;
180
181
  if (polyfillsFromConfig.has('zone.js')) {
181
- hasZoneJs = true;
182
- polyfills.push(zoneFlagsNamespace, 'zone.js/node');
182
+ polyfills.push('zone.js/node');
183
183
  }
184
184
  if (polyfillsFromConfig.has('@angular/localize') ||
185
185
  polyfillsFromConfig.has('@angular/localize/init')) {
@@ -219,20 +219,8 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) {
219
219
  },
220
220
  };
221
221
  buildOptions.plugins ??= [];
222
- // Disable Zone.js uncaught promise rejections to provide cleaner stacktraces.
223
- if (hasZoneJs) {
224
- buildOptions.plugins.unshift((0, virtual_module_plugin_1.createVirtualModulePlugin)({
225
- namespace: zoneFlagsNamespace,
226
- entryPointOnly: false,
227
- loadContent: () => ({
228
- contents: `globalThis.__zone_symbol__DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION = true;`,
229
- loader: 'js',
230
- resolveDir: workspaceRoot,
231
- }),
232
- }));
233
- }
234
222
  buildOptions.plugins.push((0, rxjs_esm_resolution_plugin_1.createRxjsEsmResolutionPlugin)());
235
- return buildOptions;
223
+ return () => buildOptions;
236
224
  }
237
225
  exports.createServerPolyfillBundleOptions = createServerPolyfillBundleOptions;
238
226
  function getEsBuildCommonOptions(options) {
@@ -16,7 +16,10 @@ export type BundleContextResult = {
16
16
  metafile: Metafile;
17
17
  outputFiles: BuildOutputFile[];
18
18
  initialFiles: Map<string, InitialFileRecord>;
19
- externalImports: Set<string>;
19
+ externalImports: {
20
+ server?: Set<string>;
21
+ browser?: Set<string>;
22
+ };
20
23
  };
21
24
  export interface InitialFileRecord {
22
25
  entrypoint: boolean;
@@ -68,7 +68,8 @@ class BundlerContext {
68
68
  const warnings = [];
69
69
  const metafile = { inputs: {}, outputs: {} };
70
70
  const initialFiles = new Map();
71
- const externalImports = new Set();
71
+ const externalImportsBrowser = new Set();
72
+ const externalImportsServer = new Set();
72
73
  const outputFiles = [];
73
74
  for (const result of individualResults) {
74
75
  warnings.push(...result.warnings);
@@ -84,7 +85,8 @@ class BundlerContext {
84
85
  }
85
86
  result.initialFiles.forEach((value, key) => initialFiles.set(key, value));
86
87
  outputFiles.push(...result.outputFiles);
87
- result.externalImports.forEach((value) => externalImports.add(value));
88
+ result.externalImports.browser?.forEach((value) => externalImportsBrowser.add(value));
89
+ result.externalImports.server?.forEach((value) => externalImportsServer.add(value));
88
90
  }
89
91
  if (errors !== undefined) {
90
92
  return { errors, warnings };
@@ -95,7 +97,10 @@ class BundlerContext {
95
97
  metafile,
96
98
  initialFiles,
97
99
  outputFiles,
98
- externalImports,
100
+ externalImports: {
101
+ browser: externalImportsBrowser,
102
+ server: externalImportsServer,
103
+ },
99
104
  };
100
105
  }
101
106
  /**
@@ -241,16 +246,14 @@ class BundlerContext {
241
246
  externalImports.add(importData.path);
242
247
  }
243
248
  }
249
+ const platformIsServer = this.#esbuildOptions?.platform === 'node';
244
250
  const outputFiles = result.outputFiles.map((file) => {
245
251
  let fileType;
246
252
  if ((0, node_path_1.dirname)(file.path) === 'media') {
247
253
  fileType = BuildOutputFileType.Media;
248
254
  }
249
255
  else {
250
- fileType =
251
- this.#esbuildOptions?.platform === 'node'
252
- ? BuildOutputFileType.Server
253
- : BuildOutputFileType.Browser;
256
+ fileType = platformIsServer ? BuildOutputFileType.Server : BuildOutputFileType.Browser;
254
257
  }
255
258
  return (0, utils_1.convertOutputFile)(file, fileType);
256
259
  });
@@ -259,7 +262,9 @@ class BundlerContext {
259
262
  ...result,
260
263
  outputFiles,
261
264
  initialFiles,
262
- externalImports,
265
+ externalImports: {
266
+ [platformIsServer ? 'server' : 'browser']: externalImports,
267
+ },
263
268
  errors: undefined,
264
269
  };
265
270
  }
@@ -5,7 +5,7 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
- import type { Message } from 'esbuild';
8
+ import type { Message, PartialMessage } from 'esbuild';
9
9
  import type { ChangedFiles } from '../../tools/esbuild/watcher';
10
10
  import type { SourceFileCache } from './angular/source-file-cache';
11
11
  import type { BuildOutputFile, BuildOutputFileType, BundlerContext } from './bundler-context';
@@ -17,6 +17,12 @@ export interface RebuildState {
17
17
  rebuildContexts: BundlerContext[];
18
18
  codeBundleCache?: SourceFileCache;
19
19
  fileChanges: ChangedFiles;
20
+ previousOutputHashes: Map<string, string>;
21
+ }
22
+ export interface ExternalResultMetadata {
23
+ implicitBrowser: string[];
24
+ implicitServer: string[];
25
+ explicit: string[];
20
26
  }
21
27
  /**
22
28
  * Represents the result of a single builder execute call.
@@ -26,22 +32,20 @@ export declare class ExecutionResult {
26
32
  private codeBundleCache?;
27
33
  outputFiles: BuildOutputFile[];
28
34
  assetFiles: BuildOutputAsset[];
29
- errors: Message[];
30
- externalMetadata?: {
31
- implicit: string[];
32
- explicit?: string[];
33
- };
35
+ errors: (Message | PartialMessage)[];
36
+ externalMetadata?: ExternalResultMetadata;
34
37
  constructor(rebuildContexts: BundlerContext[], codeBundleCache?: SourceFileCache | undefined);
35
38
  addOutputFile(path: string, content: string, type: BuildOutputFileType): void;
36
39
  addAssets(assets: BuildOutputAsset[]): void;
37
- addErrors(errors: Message[]): void;
40
+ addErrors(errors: (Message | PartialMessage)[]): void;
38
41
  /**
39
42
  * Add external JavaScript import metadata to the result. This is currently used
40
43
  * by the development server to optimize the prebundling process.
41
- * @param implicit External dependencies due to the external packages option.
44
+ * @param implicitBrowser External dependencies for the browser bundles due to the external packages option.
45
+ * @param implicitServer External dependencies for the server bundles due to the external packages option.
42
46
  * @param explicit External dependencies due to explicit project configuration.
43
47
  */
44
- setExternalMetadata(implicit: string[], explicit: string[] | undefined): void;
48
+ setExternalMetadata(implicitBrowser: string[], implicitServer: string[], explicit: string[] | undefined): void;
45
49
  get output(): {
46
50
  success: boolean;
47
51
  };
@@ -49,13 +53,11 @@ export declare class ExecutionResult {
49
53
  success: boolean;
50
54
  outputFiles: BuildOutputFile[];
51
55
  assetFiles: BuildOutputAsset[];
52
- errors: Message[];
53
- externalMetadata: {
54
- implicit: string[];
55
- explicit?: string[] | undefined;
56
- } | undefined;
56
+ errors: (PartialMessage | Message)[];
57
+ externalMetadata: ExternalResultMetadata | undefined;
57
58
  };
58
59
  get watchFiles(): string[];
59
60
  createRebuildState(fileChanges: ChangedFiles): RebuildState;
61
+ findChangedFiles(previousOutputHashes: Map<string, string>): Set<string>;
60
62
  dispose(): Promise<void>;
61
63
  }
@@ -35,11 +35,12 @@ class ExecutionResult {
35
35
  /**
36
36
  * Add external JavaScript import metadata to the result. This is currently used
37
37
  * by the development server to optimize the prebundling process.
38
- * @param implicit External dependencies due to the external packages option.
38
+ * @param implicitBrowser External dependencies for the browser bundles due to the external packages option.
39
+ * @param implicitServer External dependencies for the server bundles due to the external packages option.
39
40
  * @param explicit External dependencies due to explicit project configuration.
40
41
  */
41
- setExternalMetadata(implicit, explicit) {
42
- this.externalMetadata = { implicit, explicit };
42
+ setExternalMetadata(implicitBrowser, implicitServer, explicit) {
43
+ this.externalMetadata = { implicitBrowser, implicitServer, explicit: explicit ?? [] };
43
44
  }
44
45
  get output() {
45
46
  return {
@@ -60,6 +61,9 @@ class ExecutionResult {
60
61
  if (this.codeBundleCache?.referencedFiles) {
61
62
  files.push(...this.codeBundleCache.referencedFiles);
62
63
  }
64
+ if (this.codeBundleCache?.loadResultCache) {
65
+ files.push(...this.codeBundleCache.loadResultCache.watchFiles);
66
+ }
63
67
  return files;
64
68
  }
65
69
  createRebuildState(fileChanges) {
@@ -68,8 +72,19 @@ class ExecutionResult {
68
72
  rebuildContexts: this.rebuildContexts,
69
73
  codeBundleCache: this.codeBundleCache,
70
74
  fileChanges,
75
+ previousOutputHashes: new Map(this.outputFiles.map((file) => [file.path, file.hash])),
71
76
  };
72
77
  }
78
+ findChangedFiles(previousOutputHashes) {
79
+ const changed = new Set();
80
+ for (const file of this.outputFiles) {
81
+ const previousHash = previousOutputHashes.get(file.path);
82
+ if (previousHash === undefined || previousHash !== file.hash) {
83
+ changed.add(file.path);
84
+ }
85
+ }
86
+ return changed;
87
+ }
73
88
  async dispose() {
74
89
  await Promise.allSettled(this.rebuildContexts.map((context) => context.dispose()));
75
90
  }
@@ -38,6 +38,10 @@ function checkCommonJSModules(metafile, allowedCommonJsDependencies) {
38
38
  // Used by '@angular/platform-server' and is in a seperate chunk that is unused when
39
39
  // using `provideHttpClient(withFetch())`.
40
40
  allowedRequests.add('xhr2');
41
+ // Packages used by @angular/ssr.
42
+ // While critters is ESM it has a number of direct and transtive CJS deps.
43
+ allowedRequests.add('express');
44
+ allowedRequests.add('critters');
41
45
  // Find all entry points that contain code (JS/TS)
42
46
  const files = [];
43
47
  for (const { entryPoint } of Object.values(metafile.outputs)) {
@@ -62,6 +66,10 @@ function checkCommonJSModules(metafile, allowedCommonJsDependencies) {
62
66
  continue;
63
67
  }
64
68
  seenFiles.add(imported.path);
69
+ // If the dependency is allowed ignore all other checks
70
+ if (allowedRequests.has(imported.original)) {
71
+ continue;
72
+ }
65
73
  // Only check actual code files
66
74
  if (!isPathCode(imported.path)) {
67
75
  continue;
@@ -84,11 +92,11 @@ function checkCommonJSModules(metafile, allowedCommonJsDependencies) {
84
92
  }
85
93
  }
86
94
  if (notAllowed) {
87
- // Issue a diagnostic message and skip all descendants since they are also most
88
- // likely not ESM but solved by addressing this import.
95
+ // Issue a diagnostic message for CommonJS module
89
96
  messages.push(createCommonJSModuleError(request, currentFile));
90
- continue;
91
97
  }
98
+ // Skip all descendants since they are also most likely not ESM but solved by addressing this import
99
+ continue;
92
100
  }
93
101
  // Add the path so that its imports can be checked
94
102
  files.push(imported.path);
@@ -117,10 +125,7 @@ function isPathCode(name) {
117
125
  * @returns True, if specifier is potentially relative; false, otherwise.
118
126
  */
119
127
  function isPotentialRelative(specifier) {
120
- if (specifier[0] === '.') {
121
- return true;
122
- }
123
- return false;
128
+ return specifier[0] === '.';
124
129
  }
125
130
  /**
126
131
  * Creates an esbuild diagnostic message for a given non-ESM module request.
@@ -13,4 +13,4 @@ import { BundlerOptionsFactory } from './bundler-context';
13
13
  * @param options The builder's user-provider normalized options.
14
14
  * @returns An esbuild BuildOptions object.
15
15
  */
16
- export declare function createGlobalScriptsBundleOptions(options: NormalizedApplicationBuildOptions, initial: boolean): BundlerOptionsFactory | undefined;
16
+ export declare function createGlobalScriptsBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], initial: boolean): BundlerOptionsFactory | undefined;
@@ -48,7 +48,7 @@ const virtual_module_plugin_1 = require("./virtual-module-plugin");
48
48
  * @param options The builder's user-provider normalized options.
49
49
  * @returns An esbuild BuildOptions object.
50
50
  */
51
- function createGlobalScriptsBundleOptions(options, initial) {
51
+ function createGlobalScriptsBundleOptions(options, target, initial) {
52
52
  const { globalScripts, optimizationOptions, outputNames, preserveSymlinks, sourcemapOptions, workspaceRoot, } = options;
53
53
  const namespace = 'angular:script/global';
54
54
  const entryPoints = {};
@@ -81,6 +81,7 @@ function createGlobalScriptsBundleOptions(options, initial) {
81
81
  sourcemap: sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
82
82
  write: false,
83
83
  platform: 'neutral',
84
+ target,
84
85
  preserveSymlinks,
85
86
  plugins: [
86
87
  (0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)(),
@@ -23,7 +23,8 @@ export interface JavaScriptTransformerOptions {
23
23
  */
24
24
  export declare class JavaScriptTransformer {
25
25
  #private;
26
- constructor(options: JavaScriptTransformerOptions, maxThreads: number);
26
+ readonly maxThreads: number;
27
+ constructor(options: JavaScriptTransformerOptions, maxThreads: number, reuseResults?: boolean);
27
28
  /**
28
29
  * Performs JavaScript transformations on a file from the filesystem.
29
30
  * If no transformations are required, the data for the original file will be returned.