@angular-devkit/build-angular 17.0.0 → 17.0.2

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 (30) hide show
  1. package/package.json +5 -5
  2. package/src/builders/application/build-action.js +20 -27
  3. package/src/builders/application/i18n.js +8 -2
  4. package/src/builders/application/options.js +9 -1
  5. package/src/builders/browser/index.js +1 -1
  6. package/src/builders/browser-esbuild/builder-status-warnings.js +0 -2
  7. package/src/builders/browser-esbuild/index.js +6 -1
  8. package/src/builders/dev-server/index.d.ts +1 -1
  9. package/src/builders/dev-server/index.js +2 -2
  10. package/src/builders/dev-server/vite-server.d.ts +1 -1
  11. package/src/builders/dev-server/vite-server.js +28 -27
  12. package/src/builders/server/index.js +1 -1
  13. package/src/tools/esbuild/angular/compilation/parallel-compilation.js +3 -0
  14. package/src/tools/esbuild/angular/compilation-state.d.ts +2 -2
  15. package/src/tools/esbuild/angular/compilation-state.js +6 -4
  16. package/src/tools/esbuild/angular/compiler-plugin.js +22 -27
  17. package/src/tools/esbuild/angular/file-reference-tracker.js +10 -6
  18. package/src/tools/esbuild/angular/source-file-cache.d.ts +0 -1
  19. package/src/tools/esbuild/angular/source-file-cache.js +2 -4
  20. package/src/tools/esbuild/application-code-bundle.js +0 -3
  21. package/src/tools/esbuild/bundler-context.js +17 -10
  22. package/src/tools/esbuild/bundler-execution-result.d.ts +1 -1
  23. package/src/tools/esbuild/bundler-execution-result.js +6 -1
  24. package/src/tools/esbuild/load-result-cache.js +10 -3
  25. package/src/tools/esbuild/stylesheets/less-language.js +4 -2
  26. package/src/tools/esbuild/stylesheets/sass-language.js +2 -2
  27. package/src/utils/delete-output-dir.d.ts +1 -1
  28. package/src/utils/delete-output-dir.js +19 -28
  29. package/src/utils/environment-options.js +1 -4
  30. package/src/utils/routes-extractor/extractor.js +9 -3
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "17.0.0",
3
+ "version": "17.0.2",
4
4
  "description": "Angular Webpack Build Facade",
5
5
  "main": "src/index.js",
6
6
  "typings": "src/index.d.ts",
7
7
  "builders": "builders.json",
8
8
  "dependencies": {
9
9
  "@ampproject/remapping": "2.2.1",
10
- "@angular-devkit/architect": "0.1700.0",
11
- "@angular-devkit/build-webpack": "0.1700.0",
12
- "@angular-devkit/core": "17.0.0",
10
+ "@angular-devkit/architect": "0.1700.2",
11
+ "@angular-devkit/build-webpack": "0.1700.2",
12
+ "@angular-devkit/core": "17.0.2",
13
13
  "@babel/core": "7.23.2",
14
14
  "@babel/generator": "7.23.0",
15
15
  "@babel/helper-annotate-as-pure": "7.22.5",
@@ -20,7 +20,7 @@
20
20
  "@babel/preset-env": "7.23.2",
21
21
  "@babel/runtime": "7.23.2",
22
22
  "@discoveryjs/json-ext": "0.5.7",
23
- "@ngtools/webpack": "17.0.0",
23
+ "@ngtools/webpack": "17.0.2",
24
24
  "@vitejs/plugin-basic-ssl": "1.0.1",
25
25
  "ansi-colors": "4.1.3",
26
26
  "autoprefixer": "10.4.16",
@@ -34,32 +34,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.runEsBuildBuildAction = void 0;
37
- const promises_1 = __importDefault(require("node:fs/promises"));
38
37
  const node_path_1 = __importDefault(require("node:path"));
39
38
  const sass_language_1 = require("../../tools/esbuild/stylesheets/sass-language");
40
39
  const utils_1 = require("../../tools/esbuild/utils");
40
+ const delete_output_dir_1 = require("../../utils/delete-output-dir");
41
41
  const environment_options_1 = require("../../utils/environment-options");
42
- const error_1 = require("../../utils/error");
43
42
  async function* runEsBuildBuildAction(action, options) {
44
43
  const { writeToFileSystemFilter, writeToFileSystem = true, watch, poll, logger, deleteOutputPath, cacheOptions, outputPath, verbose, projectRoot, workspaceRoot, progress, } = options;
45
- if (writeToFileSystem) {
46
- // Clean output path if enabled
47
- if (deleteOutputPath) {
48
- if (outputPath === workspaceRoot) {
49
- logger.error('Output path MUST not be workspace root directory!');
50
- return;
51
- }
52
- await promises_1.default.rm(outputPath, { force: true, recursive: true, maxRetries: 3 });
53
- }
54
- // Create output directory if needed
55
- try {
56
- await promises_1.default.mkdir(outputPath, { recursive: true });
57
- }
58
- catch (e) {
59
- (0, error_1.assertIsError)(e);
60
- logger.error('Unable to create output directory: ' + e.message);
61
- return;
62
- }
44
+ if (deleteOutputPath && writeToFileSystem) {
45
+ await (0, delete_output_dir_1.deleteOutputDir)(workspaceRoot, outputPath);
63
46
  }
64
47
  const withProgress = progress ? utils_1.withSpinner : utils_1.withNoProgress;
65
48
  // Initial build
@@ -135,7 +118,7 @@ async function* runEsBuildBuildAction(action, options) {
135
118
  return;
136
119
  }
137
120
  // Wait for changes and rebuild as needed
138
- let previousWatchFiles = new Set(result.watchFiles);
121
+ const currentWatchFiles = new Set(result.watchFiles);
139
122
  try {
140
123
  for await (const changes of watcher) {
141
124
  if (options.signal?.aborted) {
@@ -146,12 +129,22 @@ async function* runEsBuildBuildAction(action, options) {
146
129
  }
147
130
  result = await withProgress('Changes detected. Rebuilding...', () => action(result.createRebuildState(changes)));
148
131
  // Update watched locations provided by the new build result.
149
- // Add any new locations
150
- watcher.add(result.watchFiles.filter((watchFile) => !previousWatchFiles.has(watchFile)));
151
- const newWatchFiles = new Set(result.watchFiles);
152
- // Remove any old locations
153
- watcher.remove([...previousWatchFiles].filter((watchFile) => !newWatchFiles.has(watchFile)));
154
- previousWatchFiles = newWatchFiles;
132
+ // Keep watching all previous files if there are any errors; otherwise consider all
133
+ // files stale until confirmed present in the new result's watch files.
134
+ const staleWatchFiles = result.errors.length > 0 ? undefined : new Set(currentWatchFiles);
135
+ for (const watchFile of result.watchFiles) {
136
+ if (!currentWatchFiles.has(watchFile)) {
137
+ // Add new watch location
138
+ watcher.add(watchFile);
139
+ currentWatchFiles.add(watchFile);
140
+ }
141
+ // Present so remove from stale locations
142
+ staleWatchFiles?.delete(watchFile);
143
+ }
144
+ // Remove any stale locations if the build was successful
145
+ if (staleWatchFiles?.size) {
146
+ watcher.remove([...staleWatchFiles]);
147
+ }
155
148
  if (writeToFileSystem) {
156
149
  // Write output files
157
150
  const filesToWrite = writeToFileSystemFilter
@@ -9,6 +9,7 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.loadActiveTranslations = exports.inlineI18n = void 0;
11
11
  const node_path_1 = require("node:path");
12
+ const bundler_context_1 = require("../../tools/esbuild/bundler-context");
12
13
  const i18n_inliner_1 = require("../../tools/esbuild/i18n-inliner");
13
14
  const environment_options_1 = require("../../utils/environment-options");
14
15
  const i18n_options_1 = require("../../utils/i18n-options");
@@ -72,8 +73,13 @@ async function inlineI18n(options, executionResult, initialFiles) {
72
73
  finally {
73
74
  await inliner.close();
74
75
  }
75
- // Update the result with all localized files
76
- executionResult.outputFiles = updatedOutputFiles;
76
+ // Update the result with all localized files.
77
+ executionResult.outputFiles = [
78
+ // Root files are not modified.
79
+ ...executionResult.outputFiles.filter(({ type }) => type === bundler_context_1.BuildOutputFileType.Root),
80
+ // Updated files for each locale.
81
+ ...updatedOutputFiles,
82
+ ];
77
83
  // Assets are only changed if not using the flat output option
78
84
  if (options.i18nOptions.flatOutput !== true) {
79
85
  executionResult.assetFiles = updatedAssetFiles;
@@ -11,6 +11,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.normalizeOptions = void 0;
14
+ const promises_1 = require("node:fs/promises");
14
15
  const node_module_1 = require("node:module");
15
16
  const node_path_1 = __importDefault(require("node:path"));
16
17
  const helpers_1 = require("../../tools/webpack/utils/helpers");
@@ -67,8 +68,15 @@ async function normalizeOptions(context, projectName, options, plugins) {
67
68
  let fileReplacements;
68
69
  if (options.fileReplacements) {
69
70
  for (const replacement of options.fileReplacements) {
71
+ const fileReplaceWith = node_path_1.default.join(workspaceRoot, replacement.with);
72
+ try {
73
+ await (0, promises_1.access)(fileReplaceWith, promises_1.constants.F_OK);
74
+ }
75
+ catch {
76
+ throw new Error(`The ${fileReplaceWith} path in file replacements does not exist.`);
77
+ }
70
78
  fileReplacements ??= {};
71
- fileReplacements[node_path_1.default.join(workspaceRoot, replacement.replace)] = node_path_1.default.join(workspaceRoot, replacement.with);
79
+ fileReplacements[node_path_1.default.join(workspaceRoot, replacement.replace)] = fileReplaceWith;
72
80
  }
73
81
  }
74
82
  const globalStyles = [];
@@ -74,7 +74,7 @@ async function initialize(options, context, webpackConfigurationTransform) {
74
74
  transformedConfig = await webpackConfigurationTransform(config);
75
75
  }
76
76
  if (options.deleteOutputPath) {
77
- (0, utils_1.deleteOutputDir)(context.workspaceRoot, originalOutputPath);
77
+ await (0, utils_1.deleteOutputDir)(context.workspaceRoot, originalOutputPath);
78
78
  }
79
79
  return { config: transformedConfig || config, projectRoot, projectSourceRoot, i18n };
80
80
  }
@@ -18,8 +18,6 @@ const UNSUPPORTED_OPTIONS = [
18
18
  'webWorkerTsConfig',
19
19
  ];
20
20
  function logBuilderStatusWarnings(options, { logger }) {
21
- logger.warn(`The 'browser-esbuild' builder is a compatibility builder which will be removed in a future major ` +
22
- `version in favor of the 'application' builder.`);
23
21
  // Validate supported options
24
22
  for (const unsupportedOption of UNSUPPORTED_OPTIONS) {
25
23
  const value = options[unsupportedOption];
@@ -16,6 +16,7 @@ const node_fs_1 = require("node:fs");
16
16
  const promises_1 = __importDefault(require("node:fs/promises"));
17
17
  const node_path_1 = __importDefault(require("node:path"));
18
18
  const utils_1 = require("../../tools/esbuild/utils");
19
+ const utils_2 = require("../../utils");
19
20
  const application_1 = require("../application");
20
21
  const builder_status_warnings_1 = require("./builder-status-warnings");
21
22
  /**
@@ -29,7 +30,11 @@ async function* buildEsbuildBrowser(userOptions, context, infrastructureSettings
29
30
  // Inform user of status of builder and options
30
31
  (0, builder_status_warnings_1.logBuilderStatusWarnings)(userOptions, context);
31
32
  const normalizedOptions = normalizeOptions(userOptions);
32
- const fullOutputPath = node_path_1.default.join(context.workspaceRoot, normalizedOptions.outputPath);
33
+ const { deleteOutputPath, outputPath } = normalizedOptions;
34
+ const fullOutputPath = node_path_1.default.join(context.workspaceRoot, outputPath);
35
+ if (deleteOutputPath && infrastructureSettings?.write !== false) {
36
+ await (0, utils_2.deleteOutputDir)(context.workspaceRoot, outputPath);
37
+ }
33
38
  for await (const result of (0, application_1.buildApplicationInternal)(normalizedOptions, context, {
34
39
  write: false,
35
40
  }, plugins)) {
@@ -11,4 +11,4 @@ import { DevServerBuilderOutput } from './webpack-server';
11
11
  export { DevServerBuilderOptions, DevServerBuilderOutput, execute as executeDevServerBuilder };
12
12
  declare const _default: import("../../../../architect/src/internal").Builder<DevServerBuilderOptions & import("../../../../core/src").JsonObject>;
13
13
  export default _default;
14
- export { execute as serveWebpackBrowser };
14
+ export { execute as executeDevServer };
@@ -7,9 +7,9 @@
7
7
  * found in the LICENSE file at https://angular.io/license
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.serveWebpackBrowser = exports.executeDevServerBuilder = void 0;
10
+ exports.executeDevServer = exports.executeDevServerBuilder = void 0;
11
11
  const architect_1 = require("@angular-devkit/architect");
12
12
  const builder_1 = require("./builder");
13
13
  Object.defineProperty(exports, "executeDevServerBuilder", { enumerable: true, get: function () { return builder_1.execute; } });
14
- Object.defineProperty(exports, "serveWebpackBrowser", { enumerable: true, get: function () { return builder_1.execute; } });
14
+ Object.defineProperty(exports, "executeDevServer", { enumerable: true, get: function () { return builder_1.execute; } });
15
15
  exports.default = (0, architect_1.createBuilder)(builder_1.execute);
@@ -25,5 +25,5 @@ export declare function serveWithVite(serverOptions: NormalizedDevServerOptions,
25
25
  middleware?: Connect.NextHandleFunction[];
26
26
  buildPlugins?: Plugin[];
27
27
  }): AsyncIterableIterator<DevServerBuilderOutput>;
28
- export declare function setupServer(serverOptions: NormalizedDevServerOptions, outputFiles: Map<string, OutputFileRecord>, assets: Map<string, string>, preserveSymlinks: boolean | undefined, externalMetadata: ExternalResultMetadata, ssr: boolean, prebundleTransformer: JavaScriptTransformer, target: string[], extensionMiddleware?: Connect.NextHandleFunction[], indexHtmlTransformer?: (content: string) => Promise<string>): Promise<InlineConfig>;
28
+ export declare function setupServer(serverOptions: NormalizedDevServerOptions, outputFiles: Map<string, OutputFileRecord>, assets: Map<string, string>, preserveSymlinks: boolean | undefined, externalMetadata: ExternalResultMetadata, ssr: boolean, prebundleTransformer: JavaScriptTransformer, target: string[], extensionMiddleware?: Connect.NextHandleFunction[], indexHtmlTransformer?: (content: string) => Promise<string>, thirdPartySourcemaps?: boolean): Promise<InlineConfig>;
29
29
  export {};
@@ -44,6 +44,7 @@ const javascript_transformer_1 = require("../../tools/esbuild/javascript-transfo
44
44
  const rxjs_esm_resolution_plugin_1 = require("../../tools/esbuild/rxjs-esm-resolution-plugin");
45
45
  const utils_1 = require("../../tools/esbuild/utils");
46
46
  const i18n_locale_plugin_1 = require("../../tools/vite/i18n-locale-plugin");
47
+ const utils_2 = require("../../utils");
47
48
  const render_page_1 = require("../../utils/server-rendering/render-page");
48
49
  const supported_browsers_1 = require("../../utils/supported-browsers");
49
50
  const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
@@ -60,11 +61,12 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
60
61
  poll: serverOptions.poll,
61
62
  verbose: serverOptions.verbose,
62
63
  }, builderName));
63
- if (browserOptions.prerender) {
64
+ if (browserOptions.prerender || browserOptions.ssr) {
64
65
  // Disable prerendering if enabled and force SSR.
65
66
  // This is so instead of prerendering all the routes for every change, the page is "prerendered" when it is requested.
66
- browserOptions.ssr = true;
67
67
  browserOptions.prerender = false;
68
+ // Avoid bundling and processing the ssr entry-point as this is not used by the dev-server.
69
+ browserOptions.ssr = true;
68
70
  // https://nodejs.org/api/process.html#processsetsourcemapsenabledval
69
71
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
72
  process.setSourceMapsEnabled(true);
@@ -85,12 +87,13 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
85
87
  // When localization is enabled with a single locale, force a flat path to maintain behavior with the existing Webpack-based dev server.
86
88
  browserOptions.forceI18nFlatOutput = true;
87
89
  }
90
+ const { vendor: thirdPartySourcemaps } = (0, utils_2.normalizeSourceMaps)(browserOptions.sourceMap ?? false);
88
91
  // Setup the prebundling transformer that will be shared across Vite prebundling requests
89
92
  const prebundleTransformer = new javascript_transformer_1.JavaScriptTransformer(
90
93
  // Always enable JIT linking to support applications built with and without AOT.
91
94
  // In a development environment the additional scope information does not
92
95
  // have a negative effect unlike production where final output size is relevant.
93
- { sourcemap: true, jit: true }, 1, true);
96
+ { sourcemap: true, jit: true, thirdPartySourcemaps }, 1, true);
94
97
  // Extract output index from options
95
98
  // TODO: Provide this info from the build results
96
99
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -107,9 +110,16 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
107
110
  implicitServer: [],
108
111
  explicit: [],
109
112
  };
110
- const build = builderName === '@angular-devkit/build-angular:application'
111
- ? application_1.buildApplicationInternal
112
- : browser_esbuild_1.buildEsbuildBrowser;
113
+ // Add cleanup logic via a builder teardown.
114
+ let deferred;
115
+ context.addTeardown(async () => {
116
+ await server?.close();
117
+ await prebundleTransformer.close();
118
+ deferred?.();
119
+ });
120
+ const build = builderName === '@angular-devkit/build-angular:browser-esbuild'
121
+ ? browser_esbuild_1.buildEsbuildBrowser
122
+ : application_1.buildApplicationInternal;
113
123
  // TODO: Switch this to an architect schedule call when infrastructure settings are supported
114
124
  for await (const result of build(
115
125
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -178,7 +188,7 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
178
188
  const browsers = (0, supported_browsers_1.getSupportedBrowsers)(projectRoot, context.logger);
179
189
  const target = (0, utils_1.transformSupportedBrowsersToTargets)(browsers);
180
190
  // Setup server and start listening
181
- const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, !!browserOptions.ssr, prebundleTransformer, target, extensions?.middleware, transformers?.indexHtml);
191
+ const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, !!browserOptions.ssr, prebundleTransformer, target, extensions?.middleware, transformers?.indexHtml, thirdPartySourcemaps);
182
192
  server = await createServer(serverConfiguration);
183
193
  await server.listen();
184
194
  if (serverConfiguration.ssr?.optimizeDeps?.disabled === false) {
@@ -206,13 +216,6 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
206
216
  baseUrl: serverUrl?.href,
207
217
  };
208
218
  }
209
- // Add cleanup logic via a builder teardown
210
- let deferred;
211
- context.addTeardown(async () => {
212
- await server?.close();
213
- await prebundleTransformer.close();
214
- deferred?.();
215
- });
216
219
  await new Promise((resolve) => (deferred = resolve));
217
220
  }
218
221
  exports.serveWithVite = serveWithVite;
@@ -304,7 +307,7 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
304
307
  }
305
308
  }
306
309
  // eslint-disable-next-line max-lines-per-function
307
- async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata, ssr, prebundleTransformer, target, extensionMiddleware, indexHtmlTransformer) {
310
+ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata, ssr, prebundleTransformer, target, extensionMiddleware, indexHtmlTransformer, thirdPartySourcemaps = false) {
308
311
  const proxy = await (0, load_proxy_config_1.loadProxyConfiguration)(serverOptions.workspaceRoot, serverOptions.proxyConfig, true);
309
312
  // dynamically import Vite for ESM compatibility
310
313
  const { normalizePath } = await Promise.resolve().then(() => __importStar(require('vite')));
@@ -371,6 +374,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
371
374
  ssr: true,
372
375
  prebundleTransformer,
373
376
  target,
377
+ thirdPartySourcemaps,
374
378
  }),
375
379
  },
376
380
  plugins: [
@@ -421,19 +425,15 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
421
425
  const originalssrTransform = server.ssrTransform;
422
426
  server.ssrTransform = async (code, map, url, originalCode) => {
423
427
  const result = await originalssrTransform(code, null, url, originalCode);
424
- if (!result) {
425
- return null;
426
- }
427
- let transformedCode = result.code;
428
- if (result.map && map) {
429
- transformedCode +=
430
- `\n//# sourceMappingURL=` +
431
- `data:application/json;base64,${Buffer.from(JSON.stringify((0, remapping_1.default)([result.map, map], () => null))).toString('base64')}`;
428
+ if (!result || !result.map || !map) {
429
+ return result;
432
430
  }
431
+ const remappedMap = (0, remapping_1.default)([result.map, map], () => null);
432
+ // Set the sourcemap root to the workspace root. This is needed since we set a virtual path as root.
433
+ remappedMap.sourceRoot = normalizePath(serverOptions.workspaceRoot) + '/';
433
434
  return {
434
435
  ...result,
435
- map: null,
436
- code: transformedCode,
436
+ map: remappedMap,
437
437
  };
438
438
  };
439
439
  // Assets and resources get handled first
@@ -572,6 +572,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
572
572
  ssr: false,
573
573
  prebundleTransformer,
574
574
  target,
575
+ thirdPartySourcemaps,
575
576
  }),
576
577
  };
577
578
  if (serverOptions.ssl) {
@@ -620,10 +621,10 @@ function pathnameWithoutServePath(url, serverOptions) {
620
621
  }
621
622
  return pathname;
622
623
  }
623
- function getDepOptimizationConfig({ disabled, exclude, include, target, prebundleTransformer, ssr, }) {
624
+ function getDepOptimizationConfig({ disabled, exclude, include, target, prebundleTransformer, ssr, thirdPartySourcemaps, }) {
624
625
  const plugins = [
625
626
  {
626
- name: `angular-vite-optimize-deps${ssr ? '-ssr' : ''}`,
627
+ name: `angular-vite-optimize-deps${ssr ? '-ssr' : ''}${thirdPartySourcemaps ? '-vendor-sourcemap' : ''}`,
627
628
  setup(build) {
628
629
  build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
629
630
  return {
@@ -150,7 +150,7 @@ async function initialize(options, context, webpackConfigurationTransform) {
150
150
  return [getPlatformServerExportsConfig(wco), (0, configs_1.getCommonConfig)(wco), (0, configs_1.getStylesConfig)(wco)];
151
151
  });
152
152
  if (options.deleteOutputPath) {
153
- (0, utils_1.deleteOutputDir)(context.workspaceRoot, originalOutputPath);
153
+ await (0, utils_1.deleteOutputDir)(context.workspaceRoot, originalOutputPath);
154
154
  }
155
155
  const transformedConfig = (await webpackConfigurationTransform?.(config)) ?? config;
156
156
  return { config: transformedConfig, i18n, projectRoot, projectSourceRoot };
@@ -35,6 +35,9 @@ class ParallelCompilation extends angular_compilation_1.AngularCompilation {
35
35
  minThreads: 1,
36
36
  maxThreads: 1,
37
37
  idleTimeout: Infinity,
38
+ // Web containers do not support transferable objects with receiveOnMessagePort which
39
+ // is used when the Atomics based wait loop is enable.
40
+ useAtomics: !process.versions.webcontainer,
38
41
  filename: localRequire.resolve('./parallel-worker'),
39
42
  });
40
43
  }
@@ -7,8 +7,8 @@
7
7
  */
8
8
  export declare class SharedTSCompilationState {
9
9
  #private;
10
- get waitUntilReady(): Promise<void>;
11
- markAsReady(): void;
10
+ get waitUntilReady(): Promise<boolean>;
11
+ markAsReady(hasErrors: boolean): void;
12
12
  markAsInProgress(): void;
13
13
  dispose(): void;
14
14
  }
@@ -12,17 +12,19 @@ class SharedTSCompilationState {
12
12
  #pendingCompilation = true;
13
13
  #resolveCompilationReady;
14
14
  #compilationReadyPromise;
15
+ #hasErrors = true;
15
16
  get waitUntilReady() {
16
17
  if (!this.#pendingCompilation) {
17
- return Promise.resolve();
18
+ return Promise.resolve(this.#hasErrors);
18
19
  }
19
20
  this.#compilationReadyPromise ??= new Promise((resolve) => {
20
21
  this.#resolveCompilationReady = resolve;
21
22
  });
22
23
  return this.#compilationReadyPromise;
23
24
  }
24
- markAsReady() {
25
- this.#resolveCompilationReady?.();
25
+ markAsReady(hasErrors) {
26
+ this.#hasErrors = hasErrors;
27
+ this.#resolveCompilationReady?.(hasErrors);
26
28
  this.#compilationReadyPromise = undefined;
27
29
  this.#pendingCompilation = false;
28
30
  }
@@ -30,7 +32,7 @@ class SharedTSCompilationState {
30
32
  this.#pendingCompilation = true;
31
33
  }
32
34
  dispose() {
33
- this.markAsReady();
35
+ this.markAsReady(true);
34
36
  globalSharedCompilationState = undefined;
35
37
  }
36
38
  }
@@ -37,9 +37,9 @@ exports.createCompilerPlugin = void 0;
37
37
  const node_assert_1 = __importDefault(require("node:assert"));
38
38
  const promises_1 = require("node:fs/promises");
39
39
  const path = __importStar(require("node:path"));
40
- const node_url_1 = require("node:url");
41
40
  const environment_options_1 = require("../../../utils/environment-options");
42
41
  const javascript_transformer_1 = require("../javascript-transformer");
42
+ const load_result_cache_1 = require("../load-result-cache");
43
43
  const profiling_1 = require("../profiling");
44
44
  const compilation_1 = require("./compilation");
45
45
  const compilation_state_1 = require("./compilation-state");
@@ -202,8 +202,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
202
202
  return result;
203
203
  }
204
204
  if (compilation instanceof compilation_1.NoopCompilation) {
205
- await sharedTSCompilationState.waitUntilReady;
206
- hasCompilationErrors = false;
205
+ hasCompilationErrors = await sharedTSCompilationState.waitUntilReady;
207
206
  return result;
208
207
  }
209
208
  const diagnostics = await compilation.diagnoseFiles();
@@ -217,7 +216,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
217
216
  try {
218
217
  await (0, profiling_1.profileAsync)('NG_EMIT_TS', async () => {
219
218
  for (const { filename, contents } of await compilation.emitAffectedFiles()) {
220
- typeScriptFileCache.set((0, node_url_1.pathToFileURL)(filename).href, contents);
219
+ typeScriptFileCache.set(path.normalize(filename), contents);
221
220
  }
222
221
  });
223
222
  }
@@ -250,11 +249,11 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
250
249
  hasCompilationErrors = !!result.errors?.length;
251
250
  // Reset the setup warnings so that they are only shown during the first build.
252
251
  setupWarnings = undefined;
253
- sharedTSCompilationState.markAsReady();
252
+ sharedTSCompilationState.markAsReady(hasCompilationErrors);
254
253
  return result;
255
254
  });
256
255
  build.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async (args) => {
257
- const request = pluginOptions.fileReplacements?.[args.path] ?? args.path;
256
+ const request = path.normalize(pluginOptions.fileReplacements?.[path.normalize(args.path)] ?? args.path);
258
257
  // Skip TS load attempt if JS TypeScript compilation not enabled and file is JS
259
258
  if (shouldTsIgnoreJs && /\.[cm]?js$/.test(request)) {
260
259
  return undefined;
@@ -263,7 +262,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
263
262
  // the options cannot change and do not need to be represented in the key. If the
264
263
  // cache is later stored to disk, then the options that affect transform output
265
264
  // would need to be added to the key as well as a check for any change of content.
266
- let contents = typeScriptFileCache.get((0, node_url_1.pathToFileURL)(request).href);
265
+ let contents = typeScriptFileCache.get(request);
267
266
  if (contents === undefined) {
268
267
  // If the Angular compilation had errors the file may not have been emitted.
269
268
  // To avoid additional errors about missing files, return empty contents.
@@ -286,35 +285,29 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
286
285
  // A string indicates untransformed output from the TS/NG compiler
287
286
  contents = await javascriptTransformer.transformData(request, contents, true /* skipLinker */);
288
287
  // Store as the returned Uint8Array to allow caching the fully transformed code
289
- typeScriptFileCache.set((0, node_url_1.pathToFileURL)(request).href, contents);
288
+ typeScriptFileCache.set(request, contents);
290
289
  }
291
290
  return {
292
291
  contents,
293
292
  loader: 'js',
294
293
  };
295
294
  });
296
- build.onLoad({ filter: /\.[cm]?js$/ }, (args) => (0, profiling_1.profileAsync)('NG_EMIT_JS*', async () => {
297
- // The filename is currently used as a cache key. Since the cache is memory only,
298
- // the options cannot change and do not need to be represented in the key. If the
299
- // cache is later stored to disk, then the options that affect transform output
300
- // would need to be added to the key as well as a check for any change of content.
301
- let contents = pluginOptions.sourceFileCache?.babelFileCache.get(args.path);
302
- if (contents === undefined) {
303
- contents = await javascriptTransformer.transformFile(args.path, pluginOptions.jit);
304
- pluginOptions.sourceFileCache?.babelFileCache.set(args.path, contents);
305
- }
306
- return {
307
- contents,
308
- loader: 'js',
309
- };
310
- }, true));
295
+ build.onLoad({ filter: /\.[cm]?js$/ }, (0, load_result_cache_1.createCachedLoad)(pluginOptions.loadResultCache, async (args) => {
296
+ return (0, profiling_1.profileAsync)('NG_EMIT_JS*', async () => {
297
+ const contents = await javascriptTransformer.transformFile(args.path, pluginOptions.jit);
298
+ return {
299
+ contents,
300
+ loader: 'js',
301
+ };
302
+ }, true);
303
+ }));
311
304
  // Setup bundling of component templates and stylesheets when in JIT mode
312
305
  if (pluginOptions.jit) {
313
306
  (0, jit_plugin_callbacks_1.setupJitPluginCallbacks)(build, stylesheetBundler, additionalResults, styleOptions.inlineStyleLanguage);
314
307
  }
315
308
  build.onEnd((result) => {
316
309
  // Ensure other compilations are unblocked if the main compilation throws during start
317
- sharedTSCompilationState?.markAsReady();
310
+ sharedTSCompilationState?.markAsReady(hasCompilationErrors);
318
311
  for (const { outputFiles, metafile } of additionalResults.values()) {
319
312
  // Add any additional output files to the main output files
320
313
  if (outputFiles?.length) {
@@ -416,17 +409,19 @@ function bundleWebWorker(build, pluginOptions, workerFile) {
416
409
  }
417
410
  }
418
411
  function createMissingFileError(request, original, root) {
412
+ const relativeRequest = path.relative(root, request);
419
413
  const error = {
420
- text: `File '${path.relative(root, request)}' is missing from the TypeScript compilation.`,
414
+ text: `File '${relativeRequest}' is missing from the TypeScript compilation.`,
421
415
  notes: [
422
416
  {
423
417
  text: `Ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
424
418
  },
425
419
  ],
426
420
  };
427
- if (request !== original) {
421
+ const relativeOriginal = path.relative(root, original);
422
+ if (relativeRequest !== relativeOriginal) {
428
423
  error.notes.push({
429
- text: `File is requested from a file replacement of '${path.relative(root, original)}'.`,
424
+ text: `File is requested from a file replacement of '${relativeOriginal}'.`,
430
425
  });
431
426
  }
432
427
  return error;
@@ -8,23 +8,26 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.FileReferenceTracker = void 0;
11
+ const node_path_1 = require("node:path");
11
12
  class FileReferenceTracker {
12
13
  #referencingFiles = new Map();
13
14
  get referencedFiles() {
14
15
  return this.#referencingFiles.keys();
15
16
  }
16
17
  add(containingFile, referencedFiles) {
18
+ const normalizedContainingFile = (0, node_path_1.normalize)(containingFile);
17
19
  for (const file of referencedFiles) {
18
- if (file === containingFile) {
20
+ const normalizedReferencedFile = (0, node_path_1.normalize)(file);
21
+ if (normalizedReferencedFile === normalizedContainingFile) {
19
22
  // Containing file is already known to the AOT compiler
20
23
  continue;
21
24
  }
22
- const referencing = this.#referencingFiles.get(file);
25
+ const referencing = this.#referencingFiles.get(normalizedReferencedFile);
23
26
  if (referencing === undefined) {
24
- this.#referencingFiles.set(file, new Set([containingFile]));
27
+ this.#referencingFiles.set(normalizedReferencedFile, new Set([normalizedContainingFile]));
25
28
  }
26
29
  else {
27
- referencing.add(containingFile);
30
+ referencing.add(normalizedContainingFile);
28
31
  }
29
32
  }
30
33
  }
@@ -37,14 +40,15 @@ class FileReferenceTracker {
37
40
  let allChangedFiles;
38
41
  // Add referencing files to fully notify the AOT compiler of required component updates
39
42
  for (const modifiedFile of changed) {
40
- const referencing = this.#referencingFiles.get(modifiedFile);
43
+ const normalizedModifiedFile = (0, node_path_1.normalize)(modifiedFile);
44
+ const referencing = this.#referencingFiles.get(normalizedModifiedFile);
41
45
  if (referencing) {
42
46
  allChangedFiles ??= new Set(changed);
43
47
  for (const referencingFile of referencing) {
44
48
  allChangedFiles.add(referencingFile);
45
49
  }
46
50
  // Cleanup the stale record which will be updated by new resource transforms
47
- this.#referencingFiles.delete(modifiedFile);
51
+ this.#referencingFiles.delete(normalizedModifiedFile);
48
52
  }
49
53
  }
50
54
  return allChangedFiles ?? changed;
@@ -10,7 +10,6 @@ import { MemoryLoadResultCache } from '../load-result-cache';
10
10
  export declare class SourceFileCache extends Map<string, ts.SourceFile> {
11
11
  readonly persistentCachePath?: string | undefined;
12
12
  readonly modifiedFiles: Set<string>;
13
- readonly babelFileCache: Map<string, Uint8Array>;
14
13
  readonly typeScriptFileCache: Map<string, string | Uint8Array>;
15
14
  readonly loadResultCache: MemoryLoadResultCache;
16
15
  referencedFiles?: readonly string[];
@@ -33,14 +33,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.SourceFileCache = void 0;
34
34
  const node_os_1 = require("node:os");
35
35
  const path = __importStar(require("node:path"));
36
- const node_url_1 = require("node:url");
37
36
  const load_result_cache_1 = require("../load-result-cache");
38
37
  const USING_WINDOWS = (0, node_os_1.platform)() === 'win32';
39
38
  const WINDOWS_SEP_REGEXP = new RegExp(`\\${path.win32.sep}`, 'g');
40
39
  class SourceFileCache extends Map {
41
40
  persistentCachePath;
42
41
  modifiedFiles = new Set();
43
- babelFileCache = new Map();
44
42
  typeScriptFileCache = new Map();
45
43
  loadResultCache = new load_result_cache_1.MemoryLoadResultCache();
46
44
  referencedFiles;
@@ -53,8 +51,8 @@ class SourceFileCache extends Map {
53
51
  this.modifiedFiles.clear();
54
52
  }
55
53
  for (let file of files) {
56
- this.babelFileCache.delete(file);
57
- this.typeScriptFileCache.delete((0, node_url_1.pathToFileURL)(file).href);
54
+ file = path.normalize(file);
55
+ this.typeScriptFileCache.delete(file);
58
56
  this.loadResultCache.invalidate(file);
59
57
  // Normalize separators to allow matching TypeScript Host paths
60
58
  if (USING_WINDOWS) {
@@ -194,7 +194,6 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) {
194
194
  if (!polyfillBundleOptions) {
195
195
  return;
196
196
  }
197
- const { workspaceRoot } = options;
198
197
  const buildOptions = {
199
198
  ...polyfillBundleOptions,
200
199
  platform: 'node',
@@ -218,8 +217,6 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) {
218
217
  'polyfills.server': namespace,
219
218
  },
220
219
  };
221
- buildOptions.plugins ??= [];
222
- buildOptions.plugins.push((0, rxjs_esm_resolution_plugin_1.createRxjsEsmResolutionPlugin)());
223
220
  return () => buildOptions;
224
221
  }
225
222
  exports.createServerPolyfillBundleOptions = createServerPolyfillBundleOptions;
@@ -161,22 +161,26 @@ class BundlerContext {
161
161
  throw failure;
162
162
  }
163
163
  }
164
+ finally {
165
+ if (this.incremental) {
166
+ // When incremental always add any files from the load result cache
167
+ if (this.#loadCache) {
168
+ this.#loadCache.watchFiles
169
+ .filter((file) => !isInternalAngularFile(file))
170
+ // watch files are fully resolved paths
171
+ .forEach((file) => this.watchFiles.add(file));
172
+ }
173
+ }
174
+ }
164
175
  // Update files that should be watched.
165
176
  // While this should technically not be linked to incremental mode, incremental is only
166
177
  // currently enabled with watch mode where watch files are needed.
167
178
  if (this.incremental) {
168
179
  // Add input files except virtual angular files which do not exist on disk
169
180
  Object.keys(result.metafile.inputs)
170
- .filter((input) => !input.startsWith('angular:'))
181
+ .filter((input) => !isInternalAngularFile(input))
171
182
  // input file paths are always relative to the workspace root
172
183
  .forEach((input) => this.watchFiles.add((0, node_path_1.join)(this.workspaceRoot, input)));
173
- // Also add any files from the load result cache
174
- if (this.#loadCache) {
175
- this.#loadCache.watchFiles
176
- .filter((file) => !file.startsWith('angular:'))
177
- // watch files are fully resolved paths
178
- .forEach((file) => this.watchFiles.add(file));
179
- }
180
184
  }
181
185
  // Return if the build encountered any errors
182
186
  if (result.errors.length) {
@@ -271,12 +275,12 @@ class BundlerContext {
271
275
  #addErrorsToWatch(result) {
272
276
  for (const error of result.errors) {
273
277
  let file = error.location?.file;
274
- if (file) {
278
+ if (file && !isInternalAngularFile(file)) {
275
279
  this.watchFiles.add((0, node_path_1.join)(this.workspaceRoot, file));
276
280
  }
277
281
  for (const note of error.notes) {
278
282
  file = note.location?.file;
279
- if (file) {
283
+ if (file && !isInternalAngularFile(file)) {
280
284
  this.watchFiles.add((0, node_path_1.join)(this.workspaceRoot, file));
281
285
  }
282
286
  }
@@ -324,3 +328,6 @@ class BundlerContext {
324
328
  }
325
329
  }
326
330
  exports.BundlerContext = BundlerContext;
331
+ function isInternalAngularFile(file) {
332
+ return file.startsWith('angular:');
333
+ }
@@ -53,7 +53,7 @@ export declare class ExecutionResult {
53
53
  success: boolean;
54
54
  outputFiles: BuildOutputFile[];
55
55
  assetFiles: BuildOutputAsset[];
56
- errors: (PartialMessage | Message)[];
56
+ errors: (Message | PartialMessage)[];
57
57
  externalMetadata: ExternalResultMetadata | undefined;
58
58
  };
59
59
  get watchFiles(): string[];
@@ -8,6 +8,7 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.ExecutionResult = void 0;
11
+ const node_path_1 = require("node:path");
11
12
  const utils_1 = require("./utils");
12
13
  /**
13
14
  * Represents the result of a single builder execute call.
@@ -57,11 +58,15 @@ class ExecutionResult {
57
58
  };
58
59
  }
59
60
  get watchFiles() {
61
+ // Bundler contexts internally normalize file dependencies
60
62
  const files = this.rebuildContexts.flatMap((context) => [...context.watchFiles]);
61
63
  if (this.codeBundleCache?.referencedFiles) {
62
- files.push(...this.codeBundleCache.referencedFiles);
64
+ // These files originate from TS/NG and can have POSIX path separators even on Windows.
65
+ // To ensure path comparisons are valid, all these paths must be normalized.
66
+ files.push(...this.codeBundleCache.referencedFiles.map(node_path_1.normalize));
63
67
  }
64
68
  if (this.codeBundleCache?.loadResultCache) {
69
+ // Load result caches internally normalize file dependencies
65
70
  files.push(...this.codeBundleCache.loadResultCache.watchFiles);
66
71
  }
67
72
  return files;
@@ -18,8 +18,13 @@ function createCachedLoad(cache, callback) {
18
18
  let result = cache.get(loadCacheKey);
19
19
  if (result === undefined) {
20
20
  result = await callback(args);
21
- // Do not cache null or undefined or results with errors
22
- if (result && result.errors === undefined) {
21
+ // Do not cache null or undefined
22
+ if (result) {
23
+ // Ensure requested path is included if it was a resolved file
24
+ if (args.namespace === 'file') {
25
+ result.watchFiles ??= [];
26
+ result.watchFiles.push(args.path);
27
+ }
23
28
  await cache.put(loadCacheKey, result);
24
29
  }
25
30
  }
@@ -59,7 +64,9 @@ class MemoryLoadResultCache {
59
64
  return found;
60
65
  }
61
66
  get watchFiles() {
62
- return [...this.#loadResults.keys(), ...this.#fileDependencies.keys()];
67
+ // this.#loadResults.keys() is not included here because the keys
68
+ // are namespaced request paths and not disk-based file paths.
69
+ return [...this.#fileDependencies.keys()];
63
70
  }
64
71
  }
65
72
  exports.MemoryLoadResultCache = MemoryLoadResultCache;
@@ -107,6 +107,7 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava
107
107
  }
108
108
  catch (error) {
109
109
  if (isLessException(error)) {
110
+ const location = convertExceptionLocation(error);
110
111
  // Retry with a warning for less files requiring the deprecated inline JavaScript option
111
112
  if (error.message.includes('Inline JavaScript is not enabled.')) {
112
113
  const withJsResult = await compileString(data, filename, options, resolver,
@@ -114,7 +115,7 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava
114
115
  withJsResult.warnings = [
115
116
  {
116
117
  text: 'Deprecated inline execution of JavaScript has been enabled ("javascriptEnabled")',
117
- location: convertExceptionLocation(error),
118
+ location,
118
119
  notes: [
119
120
  {
120
121
  location: null,
@@ -133,10 +134,11 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava
133
134
  errors: [
134
135
  {
135
136
  text: error.message,
136
- location: convertExceptionLocation(error),
137
+ location,
137
138
  },
138
139
  ],
139
140
  loader: 'css',
141
+ watchFiles: location.file ? [filename, location.file] : [filename],
140
142
  };
141
143
  }
142
144
  throw error;
@@ -170,7 +170,7 @@ async function compileString(data, filePath, syntax, options, resolveUrl) {
170
170
  }
171
171
  catch (error) {
172
172
  if (isSassException(error)) {
173
- const file = error.span.url ? (0, node_url_1.fileURLToPath)(error.span.url) : undefined;
173
+ const fileWithError = error.span.url ? (0, node_url_1.fileURLToPath)(error.span.url) : undefined;
174
174
  return {
175
175
  loader: 'css',
176
176
  errors: [
@@ -179,7 +179,7 @@ async function compileString(data, filePath, syntax, options, resolveUrl) {
179
179
  },
180
180
  ],
181
181
  warnings,
182
- watchFiles: file ? [file] : undefined,
182
+ watchFiles: fileWithError ? [filePath, fileWithError] : [filePath],
183
183
  };
184
184
  }
185
185
  throw error;
@@ -8,4 +8,4 @@
8
8
  /**
9
9
  * Delete an output directory, but error out if it's the root of the project.
10
10
  */
11
- export declare function deleteOutputDir(root: string, outputPath: string): void;
11
+ export declare function deleteOutputDir(root: string, outputPath: string): Promise<void>;
@@ -6,41 +6,32 @@
6
6
  * Use of this source code is governed by an MIT-style license that can be
7
7
  * found in the LICENSE file at https://angular.io/license
8
8
  */
9
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- var desc = Object.getOwnPropertyDescriptor(m, k);
12
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
- desc = { enumerable: true, get: function() { return m[k]; } };
14
- }
15
- Object.defineProperty(o, k2, desc);
16
- }) : (function(o, m, k, k2) {
17
- if (k2 === undefined) k2 = k;
18
- o[k2] = m[k];
19
- }));
20
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
- Object.defineProperty(o, "default", { enumerable: true, value: v });
22
- }) : function(o, v) {
23
- o["default"] = v;
24
- });
25
- var __importStar = (this && this.__importStar) || function (mod) {
26
- if (mod && mod.__esModule) return mod;
27
- var result = {};
28
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29
- __setModuleDefault(result, mod);
30
- return result;
31
- };
32
9
  Object.defineProperty(exports, "__esModule", { value: true });
33
10
  exports.deleteOutputDir = void 0;
34
- const fs = __importStar(require("fs"));
35
- const path_1 = require("path");
11
+ const promises_1 = require("node:fs/promises");
12
+ const node_path_1 = require("node:path");
36
13
  /**
37
14
  * Delete an output directory, but error out if it's the root of the project.
38
15
  */
39
- function deleteOutputDir(root, outputPath) {
40
- const resolvedOutputPath = (0, path_1.resolve)(root, outputPath);
16
+ async function deleteOutputDir(root, outputPath) {
17
+ const resolvedOutputPath = (0, node_path_1.resolve)(root, outputPath);
41
18
  if (resolvedOutputPath === root) {
42
19
  throw new Error('Output path MUST not be project root directory!');
43
20
  }
44
- fs.rmSync(resolvedOutputPath, { force: true, recursive: true, maxRetries: 3 });
21
+ // Avoid removing the actual directory to avoid errors in cases where the output
22
+ // directory is mounted or symlinked. Instead the contents are removed.
23
+ let entries;
24
+ try {
25
+ entries = await (0, promises_1.readdir)(resolvedOutputPath);
26
+ }
27
+ catch (error) {
28
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
29
+ return;
30
+ }
31
+ throw error;
32
+ }
33
+ for (const entry of entries) {
34
+ await (0, promises_1.rm)((0, node_path_1.join)(resolvedOutputPath, entry), { force: true, recursive: true, maxRetries: 3 });
35
+ }
45
36
  }
46
37
  exports.deleteOutputDir = deleteOutputDir;
@@ -68,11 +68,8 @@ exports.allowMinify = debugOptimize.minify;
68
68
  */
69
69
  const maxWorkersVariable = process.env['NG_BUILD_MAX_WORKERS'];
70
70
  exports.maxWorkers = isPresent(maxWorkersVariable) ? +maxWorkersVariable : 4;
71
- // Default to enabled unless inside a Web Container which currently fails when transferring MessagePort objects
72
71
  const parallelTsVariable = process.env['NG_BUILD_PARALLEL_TS'];
73
- exports.useParallelTs = isPresent(parallelTsVariable)
74
- ? !isDisabled(parallelTsVariable)
75
- : !process.versions.webcontainer;
72
+ exports.useParallelTs = !isPresent(parallelTsVariable) || !isDisabled(parallelTsVariable);
76
73
  const legacySassVariable = process.env['NG_BUILD_LEGACY_SASS'];
77
74
  exports.useLegacySass = (() => {
78
75
  if (!isPresent(legacySassVariable)) {
@@ -74,9 +74,15 @@ export async function* extractRoutes(bootstrapAppFnOrModule, document) {
74
74
  await whenStable(applicationRef);
75
75
  const injector = applicationRef.injector;
76
76
  const router = injector.get(Router);
77
- const compiler = injector.get(Compiler);
78
- // Extract all the routes from the config.
79
- yield* getRoutesFromRouterConfig(router.config, compiler, injector);
77
+ if (router.config.length === 0) {
78
+ // In case there are no routes available
79
+ yield { route: '', success: true, redirect: false };
80
+ }
81
+ else {
82
+ const compiler = injector.get(Compiler);
83
+ // Extract all the routes from the config.
84
+ yield* getRoutesFromRouterConfig(router.config, compiler, injector);
85
+ }
80
86
  }
81
87
  finally {
82
88
  platformRef.destroy();