@angular-devkit/build-angular 15.0.0-next.3 → 15.0.0-next.5

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 (53) hide show
  1. package/package.json +19 -18
  2. package/src/builders/app-shell/index.js +39 -40
  3. package/src/builders/app-shell/render-worker.d.ts +36 -0
  4. package/src/builders/app-shell/render-worker.js +82 -0
  5. package/src/builders/browser/index.d.ts +2 -0
  6. package/src/builders/browser/index.js +38 -19
  7. package/src/builders/browser/schema.json +2 -2
  8. package/src/builders/browser-esbuild/compiler-plugin.d.ts +10 -2
  9. package/src/builders/browser-esbuild/compiler-plugin.js +211 -115
  10. package/src/builders/browser-esbuild/esbuild.d.ts +4 -3
  11. package/src/builders/browser-esbuild/esbuild.js +12 -6
  12. package/src/builders/browser-esbuild/experimental-warnings.js +0 -3
  13. package/src/builders/browser-esbuild/index.d.ts +3 -3
  14. package/src/builders/browser-esbuild/index.js +145 -87
  15. package/src/builders/browser-esbuild/options.d.ts +26 -4
  16. package/src/builders/browser-esbuild/options.js +56 -5
  17. package/src/builders/browser-esbuild/profiling.d.ts +11 -0
  18. package/src/builders/browser-esbuild/profiling.js +64 -0
  19. package/src/builders/browser-esbuild/sass-plugin.js +11 -5
  20. package/src/builders/browser-esbuild/schema.json +2 -2
  21. package/src/builders/browser-esbuild/watcher.d.ts +23 -0
  22. package/src/builders/browser-esbuild/watcher.js +93 -0
  23. package/src/builders/dev-server/index.d.ts +2 -0
  24. package/src/builders/dev-server/index.js +10 -7
  25. package/src/builders/karma/find-tests-plugin.js +1 -0
  26. package/src/builders/karma/index.d.ts +1 -1
  27. package/src/builders/karma/index.js +50 -9
  28. package/src/builders/karma/schema.d.ts +1 -1
  29. package/src/builders/karma/schema.json +1 -1
  30. package/src/builders/server/schema.json +1 -1
  31. package/src/utils/environment-options.d.ts +1 -0
  32. package/src/utils/environment-options.js +3 -1
  33. package/src/utils/process-bundle.js +1 -1
  34. package/src/utils/service-worker.d.ts +3 -0
  35. package/src/utils/service-worker.js +29 -2
  36. package/src/webpack/configs/common.js +31 -7
  37. package/src/webpack/configs/index.d.ts +0 -1
  38. package/src/webpack/configs/index.js +0 -1
  39. package/src/webpack/configs/styles.d.ts +1 -7
  40. package/src/webpack/configs/styles.js +70 -60
  41. package/src/webpack/plugins/occurrences-plugin.d.ts +18 -0
  42. package/src/webpack/plugins/occurrences-plugin.js +79 -0
  43. package/src/webpack/plugins/scripts-webpack-plugin.js +24 -5
  44. package/src/webpack/plugins/styles-webpack-plugin.d.ts +19 -0
  45. package/src/webpack/plugins/styles-webpack-plugin.js +71 -0
  46. package/src/webpack/utils/helpers.d.ts +5 -1
  47. package/src/webpack/utils/helpers.js +24 -14
  48. package/src/webpack/utils/stats.d.ts +13 -8
  49. package/src/webpack/utils/stats.js +57 -6
  50. package/src/webpack/configs/analytics.d.ts +0 -11
  51. package/src/webpack/configs/analytics.js +0 -27
  52. package/src/webpack/plugins/analytics.d.ts +0 -66
  53. package/src/webpack/plugins/analytics.js +0 -236
@@ -33,67 +33,62 @@ Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.buildEsbuildBrowser = void 0;
34
34
  const architect_1 = require("@angular-devkit/architect");
35
35
  const assert = __importStar(require("assert"));
36
- const fs_1 = require("fs");
36
+ const fs = __importStar(require("fs/promises"));
37
37
  const path = __importStar(require("path"));
38
38
  const utils_1 = require("../../utils");
39
39
  const copy_assets_1 = require("../../utils/copy-assets");
40
40
  const error_1 = require("../../utils/error");
41
41
  const esbuild_targets_1 = require("../../utils/esbuild-targets");
42
42
  const index_html_generator_1 = require("../../utils/index-file/index-html-generator");
43
- const package_chunk_sort_1 = require("../../utils/package-chunk-sort");
44
43
  const service_worker_1 = require("../../utils/service-worker");
45
44
  const supported_browsers_1 = require("../../utils/supported-browsers");
46
- const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
47
- const configs_1 = require("../../webpack/configs");
48
45
  const compiler_plugin_1 = require("./compiler-plugin");
49
46
  const esbuild_1 = require("./esbuild");
50
47
  const experimental_warnings_1 = require("./experimental-warnings");
51
48
  const options_1 = require("./options");
52
49
  const stylesheets_1 = require("./stylesheets");
50
+ const watcher_1 = require("./watcher");
53
51
  /**
54
- * Main execution function for the esbuild-based application builder.
55
- * The options are compatible with the Webpack-based builder.
56
- * @param options The browser builder options to use when setting up the application build
57
- * @param context The Architect builder context object
58
- * @returns A promise with the builder result output
52
+ * Represents the result of a single builder execute call.
59
53
  */
60
- // eslint-disable-next-line max-lines-per-function
61
- async function buildEsbuildBrowser(options, context) {
62
- var _a, _b, _c, _d, _e, _f;
63
- const startTime = Date.now();
64
- // Only AOT is currently supported
65
- if (options.aot !== true) {
66
- context.logger.error('JIT mode is currently not supported by this experimental builder. AOT mode must be used.');
67
- return { success: false };
54
+ class ExecutionResult {
55
+ constructor(success, codeRebuild, codeBundleCache) {
56
+ this.success = success;
57
+ this.codeRebuild = codeRebuild;
58
+ this.codeBundleCache = codeBundleCache;
68
59
  }
69
- // Inform user of experimental status of builder and options
70
- (0, experimental_warnings_1.logExperimentalWarnings)(options, context);
71
- // Determine project name from builder context target
72
- const projectName = (_a = context.target) === null || _a === void 0 ? void 0 : _a.project;
73
- if (!projectName) {
74
- context.logger.error(`The 'browser-esbuild' builder requires a target to be specified.`);
75
- return { success: false };
60
+ get output() {
61
+ return {
62
+ success: this.success,
63
+ };
76
64
  }
77
- const { projectRoot, workspaceRoot, entryPoints, entryPointNameLookup, optimizationOptions, outputPath, sourcemapOptions, tsconfig, assets, outputNames, } = await (0, options_1.normalizeOptions)(context, projectName, options);
78
- // Clean output path if enabled
79
- if (options.deleteOutputPath) {
80
- (0, utils_1.deleteOutputDir)(workspaceRoot, options.outputPath);
65
+ createRebuildState(fileChanges) {
66
+ var _a;
67
+ (_a = this.codeBundleCache) === null || _a === void 0 ? void 0 : _a.invalidate([...fileChanges.modified, ...fileChanges.removed]);
68
+ return {
69
+ codeRebuild: this.codeRebuild,
70
+ codeBundleCache: this.codeBundleCache,
71
+ fileChanges,
72
+ };
81
73
  }
82
- // Create output directory if needed
83
- try {
84
- await fs_1.promises.mkdir(outputPath, { recursive: true });
85
- }
86
- catch (e) {
87
- (0, error_1.assertIsError)(e);
88
- context.logger.error('Unable to create output directory: ' + e.message);
89
- return { success: false };
74
+ dispose() {
75
+ var _a;
76
+ (_a = this.codeRebuild) === null || _a === void 0 ? void 0 : _a.dispose();
90
77
  }
78
+ }
79
+ async function execute(options, context, rebuildState) {
80
+ var _a, _b, _c, _d;
81
+ const startTime = process.hrtime.bigint();
82
+ const { projectRoot, workspaceRoot, optimizationOptions, outputPath, assets, serviceWorkerOptions, indexHtmlOptions, } = options;
91
83
  const target = (0, esbuild_targets_1.transformSupportedBrowsersToTargets)((0, supported_browsers_1.getSupportedBrowsers)(projectRoot, context.logger));
84
+ const codeBundleCache = options.watch
85
+ ? (_a = rebuildState === null || rebuildState === void 0 ? void 0 : rebuildState.codeBundleCache) !== null && _a !== void 0 ? _a : new compiler_plugin_1.SourceFileCache()
86
+ : undefined;
92
87
  const [codeResults, styleResults] = await Promise.all([
93
88
  // Execute esbuild to bundle the application code
94
- bundleCode(workspaceRoot, entryPoints, outputNames, options, optimizationOptions, sourcemapOptions, tsconfig, target),
89
+ (0, esbuild_1.bundle)((_b = rebuildState === null || rebuildState === void 0 ? void 0 : rebuildState.codeRebuild) !== null && _b !== void 0 ? _b : createCodeBundleOptions(options, target, codeBundleCache)),
95
90
  // Execute esbuild to bundle the global stylesheets
96
- bundleGlobalStylesheets(workspaceRoot, outputNames, options, optimizationOptions, sourcemapOptions, target),
91
+ bundleGlobalStylesheets(options, target),
97
92
  ]);
98
93
  // Log all warnings and errors generated during bundling
99
94
  await (0, esbuild_1.logMessages)(context, {
@@ -102,7 +97,7 @@ async function buildEsbuildBrowser(options, context) {
102
97
  });
103
98
  // Return if the bundling failed to generate output files or there are errors
104
99
  if (!codeResults.outputFiles || codeResults.errors.length) {
105
- return { success: false };
100
+ return new ExecutionResult(false, rebuildState === null || rebuildState === void 0 ? void 0 : rebuildState.codeRebuild, codeBundleCache);
106
101
  }
107
102
  // Structure the code bundling output files
108
103
  const initialFiles = [];
@@ -110,13 +105,14 @@ async function buildEsbuildBrowser(options, context) {
110
105
  for (const outputFile of codeResults.outputFiles) {
111
106
  // Entries in the metafile are relative to the `absWorkingDir` option which is set to the workspaceRoot
112
107
  const relativeFilePath = path.relative(workspaceRoot, outputFile.path);
113
- const entryPoint = (_c = (_b = codeResults.metafile) === null || _b === void 0 ? void 0 : _b.outputs[relativeFilePath]) === null || _c === void 0 ? void 0 : _c.entryPoint;
108
+ const entryPoint = (_d = (_c = codeResults.metafile) === null || _c === void 0 ? void 0 : _c.outputs[relativeFilePath]) === null || _d === void 0 ? void 0 : _d.entryPoint;
114
109
  outputFile.path = relativeFilePath;
115
110
  if (entryPoint) {
116
111
  // An entryPoint value indicates an initial file
117
112
  initialFiles.push({
118
113
  file: outputFile.path,
119
- name: (_d = entryPointNameLookup.get(entryPoint)) !== null && _d !== void 0 ? _d : '',
114
+ // The first part of the filename is the name of file (e.g., "polyfills" for "polyfills.7S5G3MDY.js")
115
+ name: path.basename(outputFile.path).split('.')[0],
120
116
  extension: path.extname(outputFile.path),
121
117
  });
122
118
  }
@@ -127,18 +123,14 @@ async function buildEsbuildBrowser(options, context) {
127
123
  initialFiles.push(...styleResults.initialFiles);
128
124
  // Return if the global stylesheet bundling has errors
129
125
  if (styleResults.errors.length) {
130
- return { success: false };
126
+ return new ExecutionResult(false, codeResults.rebuild, codeBundleCache);
131
127
  }
132
128
  // Generate index HTML file
133
- if (options.index) {
134
- const entrypoints = (0, package_chunk_sort_1.generateEntryPoints)({
135
- scripts: (_e = options.scripts) !== null && _e !== void 0 ? _e : [],
136
- styles: (_f = options.styles) !== null && _f !== void 0 ? _f : [],
137
- });
129
+ if (indexHtmlOptions) {
138
130
  // Create an index HTML generator that reads from the in-memory output files
139
131
  const indexHtmlGenerator = new index_html_generator_1.IndexHtmlGenerator({
140
- indexPath: path.join(context.workspaceRoot, (0, webpack_browser_config_1.getIndexInputFile)(options.index)),
141
- entrypoints,
132
+ indexPath: indexHtmlOptions.input,
133
+ entrypoints: indexHtmlOptions.insertionOrder,
142
134
  sri: options.subresourceIntegrity,
143
135
  optimization: optimizationOptions,
144
136
  crossOrigin: options.crossOrigin,
@@ -166,29 +158,29 @@ async function buildEsbuildBrowser(options, context) {
166
158
  for (const warning of warnings) {
167
159
  context.logger.warn(warning);
168
160
  }
169
- outputFiles.push(createOutputFileFromText((0, webpack_browser_config_1.getIndexOutputFile)(options.index), content));
161
+ outputFiles.push(createOutputFileFromText(indexHtmlOptions.output, content));
170
162
  }
171
163
  // Copy assets
172
164
  if (assets) {
173
165
  await (0, copy_assets_1.copyAssets)(assets, [outputPath], workspaceRoot);
174
166
  }
175
167
  // Write output files
176
- await Promise.all(outputFiles.map((file) => fs_1.promises.writeFile(path.join(outputPath, file.path), file.contents)));
168
+ await Promise.all(outputFiles.map((file) => fs.writeFile(path.join(outputPath, file.path), file.contents)));
177
169
  // Augment the application with service worker support
178
170
  // TODO: This should eventually operate on the in-memory files prior to writing the output files
179
- if (options.serviceWorker) {
171
+ if (serviceWorkerOptions) {
180
172
  try {
181
- await (0, service_worker_1.augmentAppWithServiceWorker)(projectRoot, workspaceRoot, outputPath, options.baseHref || '/', options.ngswConfigPath);
173
+ await (0, service_worker_1.augmentAppWithServiceWorkerEsbuild)(workspaceRoot, serviceWorkerOptions, outputPath, options.baseHref || '/');
182
174
  }
183
175
  catch (error) {
184
176
  context.logger.error(error instanceof Error ? error.message : `${error}`);
185
- return { success: false };
177
+ return new ExecutionResult(false, codeResults.rebuild, codeBundleCache);
186
178
  }
187
179
  }
188
- context.logger.info(`Complete. [${(Date.now() - startTime) / 1000} seconds]`);
189
- return { success: true };
180
+ const buildTime = Number(process.hrtime.bigint() - startTime) / 10 ** 9;
181
+ context.logger.info(`Complete. [${buildTime.toFixed(3)} seconds]`);
182
+ return new ExecutionResult(true, codeResults.rebuild, codeBundleCache);
190
183
  }
191
- exports.buildEsbuildBrowser = buildEsbuildBrowser;
192
184
  function createOutputFileFromText(path, text) {
193
185
  return {
194
186
  path,
@@ -198,18 +190,12 @@ function createOutputFileFromText(path, text) {
198
190
  },
199
191
  };
200
192
  }
201
- async function bundleCode(workspaceRoot, entryPoints, outputNames, options, optimizationOptions, sourcemapOptions, tsconfig, target) {
202
- var _a;
203
- let fileReplacements;
204
- if (options.fileReplacements) {
205
- for (const replacement of options.fileReplacements) {
206
- fileReplacements !== null && fileReplacements !== void 0 ? fileReplacements : (fileReplacements = {});
207
- fileReplacements[path.join(workspaceRoot, replacement.replace)] = path.join(workspaceRoot, replacement.with);
208
- }
209
- }
210
- return (0, esbuild_1.bundle)({
193
+ function createCodeBundleOptions(options, target, sourceFileCache) {
194
+ const { workspaceRoot, entryPoints, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, } = options;
195
+ return {
211
196
  absWorkingDir: workspaceRoot,
212
197
  bundle: true,
198
+ incremental: options.watch,
213
199
  format: 'esm',
214
200
  entryPoints,
215
201
  entryNames: outputNames.bundles,
@@ -234,10 +220,10 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
234
220
  sourcemap: sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
235
221
  splitting: true,
236
222
  tsconfig,
237
- external: options.externalDependencies,
223
+ external: externalDependencies,
238
224
  write: false,
239
225
  platform: 'browser',
240
- preserveSymlinks: options.preserveSymlinks,
226
+ preserveSymlinks,
241
227
  plugins: [
242
228
  (0, compiler_plugin_1.createCompilerPlugin)(
243
229
  // JS/TS options
@@ -245,8 +231,9 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
245
231
  sourcemap: !!sourcemapOptions.scripts,
246
232
  thirdPartySourcemaps: sourcemapOptions.vendor,
247
233
  tsconfig,
248
- advancedOptimizations: options.buildOptimizer,
234
+ advancedOptimizations,
249
235
  fileReplacements,
236
+ sourceFileCache,
250
237
  },
251
238
  // Component stylesheet options
252
239
  {
@@ -258,8 +245,8 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
258
245
  // of sourcemap processing.
259
246
  !!sourcemapOptions.styles && (sourcemapOptions.hidden ? false : 'inline'),
260
247
  outputNames,
261
- includePaths: (_a = options.stylePreprocessorOptions) === null || _a === void 0 ? void 0 : _a.includePaths,
262
- externalDependencies: options.externalDependencies,
248
+ includePaths: stylePreprocessorOptions === null || stylePreprocessorOptions === void 0 ? void 0 : stylePreprocessorOptions.includePaths,
249
+ externalDependencies,
263
250
  target,
264
251
  }),
265
252
  ],
@@ -267,21 +254,15 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
267
254
  ...(optimizationOptions.scripts ? { 'ngDevMode': 'false' } : undefined),
268
255
  'ngJitMode': 'false',
269
256
  },
270
- });
257
+ };
271
258
  }
272
- async function bundleGlobalStylesheets(workspaceRoot, outputNames, options, optimizationOptions, sourcemapOptions, target) {
273
- var _a;
259
+ async function bundleGlobalStylesheets(options, target) {
260
+ const { workspaceRoot, optimizationOptions, sourcemapOptions, outputNames, globalStyles, preserveSymlinks, externalDependencies, stylePreprocessorOptions, } = options;
274
261
  const outputFiles = [];
275
262
  const initialFiles = [];
276
263
  const errors = [];
277
264
  const warnings = [];
278
- // resolveGlobalStyles is temporarily reused from the Webpack builder code
279
- const { entryPoints: stylesheetEntrypoints, noInjectNames } = (0, configs_1.resolveGlobalStyles)(options.styles || [], workspaceRoot,
280
- // preserveSymlinks is always true here to allow the bundler to handle the option
281
- true,
282
- // skipResolution to leverage the bundler's more comprehensive resolution
283
- true);
284
- for (const [name, files] of Object.entries(stylesheetEntrypoints)) {
265
+ for (const { name, files, initial } of globalStyles) {
285
266
  const virtualEntryData = files
286
267
  .map((file) => `@import '${file.replace(/\\/g, '/')}';`)
287
268
  .join('\n');
@@ -289,10 +270,10 @@ async function bundleGlobalStylesheets(workspaceRoot, outputNames, options, opti
289
270
  workspaceRoot,
290
271
  optimization: !!optimizationOptions.styles.minify,
291
272
  sourcemap: !!sourcemapOptions.styles && (sourcemapOptions.hidden ? 'external' : true),
292
- outputNames: noInjectNames.includes(name) ? { media: outputNames.media } : outputNames,
293
- includePaths: (_a = options.stylePreprocessorOptions) === null || _a === void 0 ? void 0 : _a.includePaths,
294
- preserveSymlinks: options.preserveSymlinks,
295
- externalDependencies: options.externalDependencies,
273
+ outputNames: initial ? outputNames : { media: outputNames.media },
274
+ includePaths: stylePreprocessorOptions === null || stylePreprocessorOptions === void 0 ? void 0 : stylePreprocessorOptions.includePaths,
275
+ preserveSymlinks,
276
+ externalDependencies,
296
277
  target,
297
278
  });
298
279
  errors.push(...sheetResult.errors);
@@ -312,7 +293,7 @@ async function bundleGlobalStylesheets(workspaceRoot, outputNames, options, opti
312
293
  sheetContents = sheetContents.replace('sourceMappingURL=stdin.css.map', `sourceMappingURL=${name}.css.map`);
313
294
  }
314
295
  outputFiles.push(createOutputFileFromText(sheetPath, sheetContents));
315
- if (!noInjectNames.includes(name)) {
296
+ if (initial) {
316
297
  initialFiles.push({
317
298
  file: sheetPath,
318
299
  name,
@@ -323,4 +304,81 @@ async function bundleGlobalStylesheets(workspaceRoot, outputNames, options, opti
323
304
  }
324
305
  return { outputFiles, initialFiles, errors, warnings };
325
306
  }
307
+ /**
308
+ * Main execution function for the esbuild-based application builder.
309
+ * The options are compatible with the Webpack-based builder.
310
+ * @param initialOptions The browser builder options to use when setting up the application build
311
+ * @param context The Architect builder context object
312
+ * @returns An async iterable with the builder result output
313
+ */
314
+ async function* buildEsbuildBrowser(initialOptions, context) {
315
+ var _a;
316
+ // Only AOT is currently supported
317
+ if (initialOptions.aot !== true) {
318
+ context.logger.error('JIT mode is currently not supported by this experimental builder. AOT mode must be used.');
319
+ return { success: false };
320
+ }
321
+ // Inform user of experimental status of builder and options
322
+ (0, experimental_warnings_1.logExperimentalWarnings)(initialOptions, context);
323
+ // Determine project name from builder context target
324
+ const projectName = (_a = context.target) === null || _a === void 0 ? void 0 : _a.project;
325
+ if (!projectName) {
326
+ context.logger.error(`The 'browser-esbuild' builder requires a target to be specified.`);
327
+ return { success: false };
328
+ }
329
+ const normalizedOptions = await (0, options_1.normalizeOptions)(context, projectName, initialOptions);
330
+ // Clean output path if enabled
331
+ if (initialOptions.deleteOutputPath) {
332
+ (0, utils_1.deleteOutputDir)(normalizedOptions.workspaceRoot, initialOptions.outputPath);
333
+ }
334
+ // Create output directory if needed
335
+ try {
336
+ await fs.mkdir(normalizedOptions.outputPath, { recursive: true });
337
+ }
338
+ catch (e) {
339
+ (0, error_1.assertIsError)(e);
340
+ context.logger.error('Unable to create output directory: ' + e.message);
341
+ return { success: false };
342
+ }
343
+ // Initial build
344
+ let result = await execute(normalizedOptions, context);
345
+ yield result.output;
346
+ // Finish if watch mode is not enabled
347
+ if (!initialOptions.watch) {
348
+ return;
349
+ }
350
+ context.logger.info('Watch mode enabled. Watching for file changes...');
351
+ // Setup a watcher
352
+ const watcher = (0, watcher_1.createWatcher)({
353
+ polling: typeof initialOptions.poll === 'number',
354
+ interval: initialOptions.poll,
355
+ // Ignore the output path to avoid infinite rebuild cycles
356
+ ignored: [normalizedOptions.outputPath],
357
+ });
358
+ // Temporarily watch the entire project
359
+ watcher.add(normalizedOptions.projectRoot);
360
+ // Watch workspace root node modules
361
+ // Includes Yarn PnP manifest files (https://yarnpkg.com/advanced/pnp-spec/)
362
+ watcher.add(path.join(normalizedOptions.workspaceRoot, 'node_modules'));
363
+ watcher.add(path.join(normalizedOptions.workspaceRoot, '.pnp.cjs'));
364
+ watcher.add(path.join(normalizedOptions.workspaceRoot, '.pnp.data.json'));
365
+ // Wait for changes and rebuild as needed
366
+ try {
367
+ for await (const changes of watcher) {
368
+ context.logger.info('Changes detected. Rebuilding...');
369
+ if (initialOptions.verbose) {
370
+ context.logger.info(changes.toDebugString());
371
+ }
372
+ result = await execute(normalizedOptions, context, result.createRebuildState(changes));
373
+ yield result.output;
374
+ }
375
+ }
376
+ finally {
377
+ // Stop the watcher
378
+ await watcher.close();
379
+ // Cleanup incremental rebuild state
380
+ result.dispose();
381
+ }
382
+ }
383
+ exports.buildEsbuildBrowser = buildEsbuildBrowser;
326
384
  exports.default = (0, architect_1.createBuilder)(buildEsbuildBrowser);
@@ -6,7 +6,8 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import { BuilderContext } from '@angular-devkit/architect';
9
- import { Schema as BrowserBuilderOptions } from '../browser/schema';
9
+ import { Schema as BrowserBuilderOptions } from './schema';
10
+ export declare type NormalizedBrowserOptions = Awaited<ReturnType<typeof normalizeOptions>>;
10
11
  /**
11
12
  * Normalize the user provided options by creating full paths for all path based options
12
13
  * and converting multi-form options into a single form that can be directly used
@@ -18,17 +19,38 @@ import { Schema as BrowserBuilderOptions } from '../browser/schema';
18
19
  * @returns An object containing normalized options required to perform the build.
19
20
  */
20
21
  export declare function normalizeOptions(context: BuilderContext, projectName: string, options: BrowserBuilderOptions): Promise<{
22
+ advancedOptimizations: boolean | undefined;
23
+ baseHref: string | undefined;
24
+ crossOrigin: import("./schema").CrossOrigin | undefined;
25
+ externalDependencies: string[] | undefined;
26
+ poll: number | undefined;
27
+ preserveSymlinks: boolean | undefined;
28
+ stylePreprocessorOptions: import("./schema").StylePreprocessorOptions | undefined;
29
+ subresourceIntegrity: boolean | undefined;
30
+ verbose: boolean | undefined;
31
+ watch: boolean | undefined;
21
32
  workspaceRoot: string;
22
33
  entryPoints: Record<string, string>;
23
- entryPointNameLookup: ReadonlyMap<string, string>;
24
34
  optimizationOptions: import("../../utils").NormalizedOptimizationOptions;
25
35
  outputPath: string;
26
- sourcemapOptions: import("../browser/schema").SourceMapClass;
36
+ sourcemapOptions: import("../..").SourceMapObject;
27
37
  tsconfig: string;
28
38
  projectRoot: string;
29
- assets: import("../browser/schema").AssetPatternClass[] | undefined;
39
+ assets: import("../..").AssetPatternObject[] | undefined;
30
40
  outputNames: {
31
41
  bundles: string;
32
42
  media: string;
33
43
  };
44
+ fileReplacements: Record<string, string> | undefined;
45
+ globalStyles: {
46
+ name: string;
47
+ files: string[];
48
+ initial: boolean;
49
+ }[];
50
+ serviceWorkerOptions: string | undefined;
51
+ indexHtmlOptions: {
52
+ input: string;
53
+ output: string;
54
+ insertionOrder: import("../../utils/package-chunk-sort").EntryPointsType[];
55
+ } | undefined;
34
56
  }>;
@@ -34,7 +34,10 @@ exports.normalizeOptions = void 0;
34
34
  const path = __importStar(require("path"));
35
35
  const utils_1 = require("../../utils");
36
36
  const normalize_polyfills_1 = require("../../utils/normalize-polyfills");
37
- const schema_1 = require("../browser/schema");
37
+ const package_chunk_sort_1 = require("../../utils/package-chunk-sort");
38
+ const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
39
+ const helpers_1 = require("../../webpack/utils/helpers");
40
+ const schema_1 = require("./schema");
38
41
  /**
39
42
  * Normalize the user provided options by creating full paths for all path based options
40
43
  * and converting multi-form options into a single form that can be directly used
@@ -46,7 +49,7 @@ const schema_1 = require("../browser/schema");
46
49
  * @returns An object containing normalized options required to perform the build.
47
50
  */
48
51
  async function normalizeOptions(context, projectName, options) {
49
- var _a, _b, _c, _d;
52
+ var _a, _b, _c, _d, _e, _f, _g;
50
53
  const workspaceRoot = context.workspaceRoot;
51
54
  const projectMetadata = await context.getProjectMetadata(projectName);
52
55
  const projectRoot = path.join(workspaceRoot, (_a = projectMetadata.root) !== null && _a !== void 0 ? _a : '');
@@ -76,6 +79,27 @@ async function normalizeOptions(context, projectName, options) {
76
79
  if (options.resourcesOutputPath) {
77
80
  outputNames.media = path.join(options.resourcesOutputPath, outputNames.media);
78
81
  }
82
+ let fileReplacements;
83
+ if (options.fileReplacements) {
84
+ for (const replacement of options.fileReplacements) {
85
+ fileReplacements !== null && fileReplacements !== void 0 ? fileReplacements : (fileReplacements = {});
86
+ fileReplacements[path.join(workspaceRoot, replacement.replace)] = path.join(workspaceRoot, replacement.with);
87
+ }
88
+ }
89
+ const globalStyles = [];
90
+ if ((_e = options.styles) === null || _e === void 0 ? void 0 : _e.length) {
91
+ const { entryPoints: stylesheetEntrypoints, noInjectNames } = (0, helpers_1.normalizeGlobalStyles)(options.styles || []);
92
+ for (const [name, files] of Object.entries(stylesheetEntrypoints)) {
93
+ globalStyles.push({ name, files, initial: !noInjectNames.includes(name) });
94
+ }
95
+ }
96
+ let serviceWorkerOptions;
97
+ if (options.serviceWorker) {
98
+ // If ngswConfigPath is not specified, the default is 'ngsw-config.json' within the project root
99
+ serviceWorkerOptions = options.ngswConfigPath
100
+ ? path.join(workspaceRoot, options.ngswConfigPath)
101
+ : path.join(projectRoot, 'ngsw-config.json');
102
+ }
79
103
  // Setup bundler entry points
80
104
  const entryPoints = {
81
105
  main: mainEntryPoint,
@@ -83,12 +107,35 @@ async function normalizeOptions(context, projectName, options) {
83
107
  if (polyfillsEntryPoint) {
84
108
  entryPoints['polyfills'] = polyfillsEntryPoint;
85
109
  }
86
- // Create reverse lookup used during index HTML generation
87
- const entryPointNameLookup = new Map(Object.entries(entryPoints).map(([name, filePath]) => [path.relative(workspaceRoot, filePath), name]));
110
+ let indexHtmlOptions;
111
+ if (options.index) {
112
+ indexHtmlOptions = {
113
+ input: path.join(workspaceRoot, (0, webpack_browser_config_1.getIndexInputFile)(options.index)),
114
+ // The output file will be created within the configured output path
115
+ output: (0, webpack_browser_config_1.getIndexOutputFile)(options.index),
116
+ // TODO: Use existing information from above to create the insertion order
117
+ insertionOrder: (0, package_chunk_sort_1.generateEntryPoints)({
118
+ scripts: (_f = options.scripts) !== null && _f !== void 0 ? _f : [],
119
+ styles: (_g = options.styles) !== null && _g !== void 0 ? _g : [],
120
+ }),
121
+ };
122
+ }
123
+ // Initial options to keep
124
+ const { baseHref, buildOptimizer, crossOrigin, externalDependencies, poll, preserveSymlinks, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, } = options;
125
+ // Return all the normalized options
88
126
  return {
127
+ advancedOptimizations: buildOptimizer,
128
+ baseHref,
129
+ crossOrigin,
130
+ externalDependencies,
131
+ poll,
132
+ preserveSymlinks,
133
+ stylePreprocessorOptions,
134
+ subresourceIntegrity,
135
+ verbose,
136
+ watch,
89
137
  workspaceRoot,
90
138
  entryPoints,
91
- entryPointNameLookup,
92
139
  optimizationOptions,
93
140
  outputPath,
94
141
  sourcemapOptions,
@@ -96,6 +143,10 @@ async function normalizeOptions(context, projectName, options) {
96
143
  projectRoot,
97
144
  assets,
98
145
  outputNames,
146
+ fileReplacements,
147
+ globalStyles,
148
+ serviceWorkerOptions,
149
+ indexHtmlOptions,
99
150
  };
100
151
  }
101
152
  exports.normalizeOptions = normalizeOptions;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.io/license
7
+ */
8
+ export declare function resetCumulativeDurations(): void;
9
+ export declare function logCumulativeDurations(): void;
10
+ export declare function profileAsync<T>(name: string, action: () => Promise<T>, cumulative?: boolean): Promise<T>;
11
+ export declare function profileSync<T>(name: string, action: () => T, cumulative?: boolean): T;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.io/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.profileSync = exports.profileAsync = exports.logCumulativeDurations = exports.resetCumulativeDurations = void 0;
11
+ const environment_options_1 = require("../../utils/environment-options");
12
+ let cumulativeDurations;
13
+ function resetCumulativeDurations() {
14
+ cumulativeDurations === null || cumulativeDurations === void 0 ? void 0 : cumulativeDurations.clear();
15
+ }
16
+ exports.resetCumulativeDurations = resetCumulativeDurations;
17
+ function logCumulativeDurations() {
18
+ if (!environment_options_1.debugPerformance || !cumulativeDurations) {
19
+ return;
20
+ }
21
+ for (const [name, duration] of cumulativeDurations) {
22
+ // eslint-disable-next-line no-console
23
+ console.log(`DURATION[${name}]: ${duration.toFixed(9)} seconds`);
24
+ }
25
+ }
26
+ exports.logCumulativeDurations = logCumulativeDurations;
27
+ function recordDuration(name, startTime, cumulative) {
28
+ var _a;
29
+ const duration = Number(process.hrtime.bigint() - startTime) / 10 ** 9;
30
+ if (cumulative) {
31
+ cumulativeDurations !== null && cumulativeDurations !== void 0 ? cumulativeDurations : (cumulativeDurations = new Map());
32
+ cumulativeDurations.set(name, ((_a = cumulativeDurations.get(name)) !== null && _a !== void 0 ? _a : 0) + duration);
33
+ }
34
+ else {
35
+ // eslint-disable-next-line no-console
36
+ console.log(`DURATION[${name}]: ${duration.toFixed(9)} seconds`);
37
+ }
38
+ }
39
+ async function profileAsync(name, action, cumulative) {
40
+ if (!environment_options_1.debugPerformance) {
41
+ return action();
42
+ }
43
+ const startTime = process.hrtime.bigint();
44
+ try {
45
+ return await action();
46
+ }
47
+ finally {
48
+ recordDuration(name, startTime, cumulative);
49
+ }
50
+ }
51
+ exports.profileAsync = profileAsync;
52
+ function profileSync(name, action, cumulative) {
53
+ if (!environment_options_1.debugPerformance) {
54
+ return action();
55
+ }
56
+ const startTime = process.hrtime.bigint();
57
+ try {
58
+ return action();
59
+ }
60
+ finally {
61
+ recordDuration(name, startTime, cumulative);
62
+ }
63
+ }
64
+ exports.profileSync = profileSync;
@@ -31,7 +31,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
31
31
  };
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.createSassPlugin = void 0;
34
- const url_1 = require("url");
34
+ const node_path_1 = require("node:path");
35
+ const node_url_1 = require("node:url");
35
36
  function createSassPlugin(options) {
36
37
  return {
37
38
  name: 'angular-sass',
@@ -59,14 +60,16 @@ function createSassPlugin(options) {
59
60
  });
60
61
  return {
61
62
  loader: 'css',
62
- contents: sourceMap ? `${css}\n${sourceMapToUrlComment(sourceMap)}` : css,
63
- watchFiles: loadedUrls.map((url) => (0, url_1.fileURLToPath)(url)),
63
+ contents: sourceMap
64
+ ? `${css}\n${sourceMapToUrlComment(sourceMap, (0, node_path_1.dirname)(args.path))}`
65
+ : css,
66
+ watchFiles: loadedUrls.map((url) => (0, node_url_1.fileURLToPath)(url)),
64
67
  warnings,
65
68
  };
66
69
  }
67
70
  catch (error) {
68
71
  if (error instanceof sass.Exception) {
69
- const file = error.span.url ? (0, url_1.fileURLToPath)(error.span.url) : undefined;
72
+ const file = error.span.url ? (0, node_url_1.fileURLToPath)(error.span.url) : undefined;
70
73
  return {
71
74
  loader: 'css',
72
75
  errors: [
@@ -84,7 +87,10 @@ function createSassPlugin(options) {
84
87
  };
85
88
  }
86
89
  exports.createSassPlugin = createSassPlugin;
87
- function sourceMapToUrlComment(sourceMap) {
90
+ function sourceMapToUrlComment(sourceMap, root) {
91
+ // Remove `file` protocol from all sourcemap sources and adjust to be relative to the input file.
92
+ // This allows esbuild to correctly process the paths.
93
+ sourceMap.sources = sourceMap.sources.map((source) => (0, node_path_1.relative)(root, (0, node_url_1.fileURLToPath)(source)));
88
94
  const urlSourceMap = Buffer.from(JSON.stringify(sourceMap), 'utf-8').toString('base64');
89
95
  return `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${urlSourceMap} */`;
90
96
  }