@angular-devkit/build-angular 15.0.0-next.3 → 15.0.0-next.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.
@@ -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 };
68
- }
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 };
54
+ class ExecutionResult {
55
+ constructor(success, codeRebuild, codeBundleCache) {
56
+ this.success = success;
57
+ this.codeRebuild = codeRebuild;
58
+ this.codeBundleCache = codeBundleCache;
76
59
  }
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);
60
+ get output() {
61
+ return {
62
+ success: this.success,
63
+ };
81
64
  }
82
- // Create output directory if needed
83
- try {
84
- await fs_1.promises.mkdir(outputPath, { recursive: true });
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
+ };
85
73
  }
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 = Date.now();
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,28 @@ 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
180
  context.logger.info(`Complete. [${(Date.now() - startTime) / 1000} seconds]`);
189
- return { success: true };
181
+ return new ExecutionResult(true, codeResults.rebuild, codeBundleCache);
190
182
  }
191
- exports.buildEsbuildBrowser = buildEsbuildBrowser;
192
183
  function createOutputFileFromText(path, text) {
193
184
  return {
194
185
  path,
@@ -198,18 +189,12 @@ function createOutputFileFromText(path, text) {
198
189
  },
199
190
  };
200
191
  }
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)({
192
+ function createCodeBundleOptions(options, target, sourceFileCache) {
193
+ const { workspaceRoot, entryPoints, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, } = options;
194
+ return {
211
195
  absWorkingDir: workspaceRoot,
212
196
  bundle: true,
197
+ incremental: options.watch,
213
198
  format: 'esm',
214
199
  entryPoints,
215
200
  entryNames: outputNames.bundles,
@@ -234,10 +219,10 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
234
219
  sourcemap: sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
235
220
  splitting: true,
236
221
  tsconfig,
237
- external: options.externalDependencies,
222
+ external: externalDependencies,
238
223
  write: false,
239
224
  platform: 'browser',
240
- preserveSymlinks: options.preserveSymlinks,
225
+ preserveSymlinks,
241
226
  plugins: [
242
227
  (0, compiler_plugin_1.createCompilerPlugin)(
243
228
  // JS/TS options
@@ -245,8 +230,9 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
245
230
  sourcemap: !!sourcemapOptions.scripts,
246
231
  thirdPartySourcemaps: sourcemapOptions.vendor,
247
232
  tsconfig,
248
- advancedOptimizations: options.buildOptimizer,
233
+ advancedOptimizations,
249
234
  fileReplacements,
235
+ sourceFileCache,
250
236
  },
251
237
  // Component stylesheet options
252
238
  {
@@ -258,8 +244,8 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
258
244
  // of sourcemap processing.
259
245
  !!sourcemapOptions.styles && (sourcemapOptions.hidden ? false : 'inline'),
260
246
  outputNames,
261
- includePaths: (_a = options.stylePreprocessorOptions) === null || _a === void 0 ? void 0 : _a.includePaths,
262
- externalDependencies: options.externalDependencies,
247
+ includePaths: stylePreprocessorOptions === null || stylePreprocessorOptions === void 0 ? void 0 : stylePreprocessorOptions.includePaths,
248
+ externalDependencies,
263
249
  target,
264
250
  }),
265
251
  ],
@@ -267,21 +253,15 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
267
253
  ...(optimizationOptions.scripts ? { 'ngDevMode': 'false' } : undefined),
268
254
  'ngJitMode': 'false',
269
255
  },
270
- });
256
+ };
271
257
  }
272
- async function bundleGlobalStylesheets(workspaceRoot, outputNames, options, optimizationOptions, sourcemapOptions, target) {
273
- var _a;
258
+ async function bundleGlobalStylesheets(options, target) {
259
+ const { workspaceRoot, optimizationOptions, sourcemapOptions, outputNames, globalStyles, preserveSymlinks, externalDependencies, stylePreprocessorOptions, } = options;
274
260
  const outputFiles = [];
275
261
  const initialFiles = [];
276
262
  const errors = [];
277
263
  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)) {
264
+ for (const { name, files, initial } of globalStyles) {
285
265
  const virtualEntryData = files
286
266
  .map((file) => `@import '${file.replace(/\\/g, '/')}';`)
287
267
  .join('\n');
@@ -289,10 +269,10 @@ async function bundleGlobalStylesheets(workspaceRoot, outputNames, options, opti
289
269
  workspaceRoot,
290
270
  optimization: !!optimizationOptions.styles.minify,
291
271
  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,
272
+ outputNames: initial ? outputNames : { media: outputNames.media },
273
+ includePaths: stylePreprocessorOptions === null || stylePreprocessorOptions === void 0 ? void 0 : stylePreprocessorOptions.includePaths,
274
+ preserveSymlinks,
275
+ externalDependencies,
296
276
  target,
297
277
  });
298
278
  errors.push(...sheetResult.errors);
@@ -312,7 +292,7 @@ async function bundleGlobalStylesheets(workspaceRoot, outputNames, options, opti
312
292
  sheetContents = sheetContents.replace('sourceMappingURL=stdin.css.map', `sourceMappingURL=${name}.css.map`);
313
293
  }
314
294
  outputFiles.push(createOutputFileFromText(sheetPath, sheetContents));
315
- if (!noInjectNames.includes(name)) {
295
+ if (initial) {
316
296
  initialFiles.push({
317
297
  file: sheetPath,
318
298
  name,
@@ -323,4 +303,81 @@ async function bundleGlobalStylesheets(workspaceRoot, outputNames, options, opti
323
303
  }
324
304
  return { outputFiles, initialFiles, errors, warnings };
325
305
  }
306
+ /**
307
+ * Main execution function for the esbuild-based application builder.
308
+ * The options are compatible with the Webpack-based builder.
309
+ * @param initialOptions The browser builder options to use when setting up the application build
310
+ * @param context The Architect builder context object
311
+ * @returns An async iterable with the builder result output
312
+ */
313
+ async function* buildEsbuildBrowser(initialOptions, context) {
314
+ var _a;
315
+ // Only AOT is currently supported
316
+ if (initialOptions.aot !== true) {
317
+ context.logger.error('JIT mode is currently not supported by this experimental builder. AOT mode must be used.');
318
+ return { success: false };
319
+ }
320
+ // Inform user of experimental status of builder and options
321
+ (0, experimental_warnings_1.logExperimentalWarnings)(initialOptions, context);
322
+ // Determine project name from builder context target
323
+ const projectName = (_a = context.target) === null || _a === void 0 ? void 0 : _a.project;
324
+ if (!projectName) {
325
+ context.logger.error(`The 'browser-esbuild' builder requires a target to be specified.`);
326
+ return { success: false };
327
+ }
328
+ const normalizedOptions = await (0, options_1.normalizeOptions)(context, projectName, initialOptions);
329
+ // Clean output path if enabled
330
+ if (initialOptions.deleteOutputPath) {
331
+ (0, utils_1.deleteOutputDir)(normalizedOptions.workspaceRoot, initialOptions.outputPath);
332
+ }
333
+ // Create output directory if needed
334
+ try {
335
+ await fs.mkdir(normalizedOptions.outputPath, { recursive: true });
336
+ }
337
+ catch (e) {
338
+ (0, error_1.assertIsError)(e);
339
+ context.logger.error('Unable to create output directory: ' + e.message);
340
+ return { success: false };
341
+ }
342
+ // Initial build
343
+ let result = await execute(normalizedOptions, context);
344
+ yield result.output;
345
+ // Finish if watch mode is not enabled
346
+ if (!initialOptions.watch) {
347
+ return;
348
+ }
349
+ context.logger.info('Watch mode enabled. Watching for file changes...');
350
+ // Setup a watcher
351
+ const watcher = (0, watcher_1.createWatcher)({
352
+ polling: typeof initialOptions.poll === 'number',
353
+ interval: initialOptions.poll,
354
+ // Ignore the output path to avoid infinite rebuild cycles
355
+ ignored: [normalizedOptions.outputPath],
356
+ });
357
+ // Temporarily watch the entire project
358
+ watcher.add(normalizedOptions.projectRoot);
359
+ // Watch workspace root node modules
360
+ // Includes Yarn PnP manifest files (https://yarnpkg.com/advanced/pnp-spec/)
361
+ watcher.add(path.join(normalizedOptions.workspaceRoot, 'node_modules'));
362
+ watcher.add(path.join(normalizedOptions.workspaceRoot, '.pnp.cjs'));
363
+ watcher.add(path.join(normalizedOptions.workspaceRoot, '.pnp.data.json'));
364
+ // Wait for changes and rebuild as needed
365
+ try {
366
+ for await (const changes of watcher) {
367
+ context.logger.info('Changes detected. Rebuilding...');
368
+ if (initialOptions.verbose) {
369
+ context.logger.info(changes.toDebugString());
370
+ }
371
+ result = await execute(normalizedOptions, context, result.createRebuildState(changes));
372
+ yield result.output;
373
+ }
374
+ }
375
+ finally {
376
+ // Stop the watcher
377
+ await watcher.close();
378
+ // Cleanup incremental rebuild state
379
+ result.dispose();
380
+ }
381
+ }
382
+ exports.buildEsbuildBrowser = buildEsbuildBrowser;
326
383
  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,23 @@
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 class ChangedFiles {
9
+ readonly added: Set<string>;
10
+ readonly modified: Set<string>;
11
+ readonly removed: Set<string>;
12
+ toDebugString(): string;
13
+ }
14
+ export interface BuildWatcher extends AsyncIterableIterator<ChangedFiles> {
15
+ add(paths: string | string[]): void;
16
+ remove(paths: string | string[]): void;
17
+ close(): Promise<void>;
18
+ }
19
+ export declare function createWatcher(options?: {
20
+ polling?: boolean;
21
+ interval?: number;
22
+ ignored?: string[];
23
+ }): BuildWatcher;
@@ -0,0 +1,93 @@
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.createWatcher = exports.ChangedFiles = void 0;
11
+ const chokidar_1 = require("chokidar");
12
+ class ChangedFiles {
13
+ constructor() {
14
+ this.added = new Set();
15
+ this.modified = new Set();
16
+ this.removed = new Set();
17
+ }
18
+ toDebugString() {
19
+ const content = {
20
+ added: Array.from(this.added),
21
+ modified: Array.from(this.modified),
22
+ removed: Array.from(this.removed),
23
+ };
24
+ return JSON.stringify(content, null, 2);
25
+ }
26
+ }
27
+ exports.ChangedFiles = ChangedFiles;
28
+ function createWatcher(options) {
29
+ const watcher = new chokidar_1.FSWatcher({
30
+ ...options,
31
+ disableGlobbing: true,
32
+ ignoreInitial: true,
33
+ });
34
+ const nextQueue = [];
35
+ let currentChanges;
36
+ watcher.on('all', (event, path) => {
37
+ switch (event) {
38
+ case 'add':
39
+ currentChanges !== null && currentChanges !== void 0 ? currentChanges : (currentChanges = new ChangedFiles());
40
+ currentChanges.added.add(path);
41
+ break;
42
+ case 'change':
43
+ currentChanges !== null && currentChanges !== void 0 ? currentChanges : (currentChanges = new ChangedFiles());
44
+ currentChanges.modified.add(path);
45
+ break;
46
+ case 'unlink':
47
+ currentChanges !== null && currentChanges !== void 0 ? currentChanges : (currentChanges = new ChangedFiles());
48
+ currentChanges.removed.add(path);
49
+ break;
50
+ default:
51
+ return;
52
+ }
53
+ const next = nextQueue.shift();
54
+ if (next) {
55
+ const value = currentChanges;
56
+ currentChanges = undefined;
57
+ next(value);
58
+ }
59
+ });
60
+ return {
61
+ [Symbol.asyncIterator]() {
62
+ return this;
63
+ },
64
+ async next() {
65
+ if (currentChanges && nextQueue.length === 0) {
66
+ const result = { value: currentChanges };
67
+ currentChanges = undefined;
68
+ return result;
69
+ }
70
+ return new Promise((resolve) => {
71
+ nextQueue.push((value) => resolve(value ? { value } : { done: true, value }));
72
+ });
73
+ },
74
+ add(paths) {
75
+ watcher.add(paths);
76
+ },
77
+ remove(paths) {
78
+ watcher.unwatch(paths);
79
+ },
80
+ async close() {
81
+ try {
82
+ await watcher.close();
83
+ }
84
+ finally {
85
+ let next;
86
+ while ((next = nextQueue.shift()) !== undefined) {
87
+ next();
88
+ }
89
+ }
90
+ },
91
+ };
92
+ }
93
+ exports.createWatcher = createWatcher;
@@ -23,5 +23,5 @@ export declare function execute(options: KarmaBuilderOptions, context: BuilderCo
23
23
  karmaOptions?: (options: KarmaConfigOptions) => KarmaConfigOptions;
24
24
  }): Observable<BuilderOutput>;
25
25
  export { KarmaBuilderOptions };
26
- declare const _default: import("@angular-devkit/architect/src/internal").Builder<Record<string, string> & KarmaBuilderOptions & import("../../../../core/src").JsonObject>;
26
+ declare const _default: import("@angular-devkit/architect/src/internal").Builder<Record<string, string> & KarmaBuilderOptions & import("@angular-devkit/core").JsonObject>;
27
27
  export default _default;