@angular-devkit/build-angular 14.2.0-next.0 → 14.2.0-next.1

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.
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "14.2.0-next.0",
3
+ "version": "14.2.0-next.1",
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.0",
10
- "@angular-devkit/architect": "0.1402.0-next.0",
11
- "@angular-devkit/build-webpack": "0.1402.0-next.0",
12
- "@angular-devkit/core": "14.2.0-next.0",
10
+ "@angular-devkit/architect": "0.1402.0-next.1",
11
+ "@angular-devkit/build-webpack": "0.1402.0-next.1",
12
+ "@angular-devkit/core": "14.2.0-next.1",
13
13
  "@babel/core": "7.18.10",
14
- "@babel/generator": "7.18.10",
14
+ "@babel/generator": "7.18.12",
15
15
  "@babel/helper-annotate-as-pure": "7.18.6",
16
16
  "@babel/plugin-proposal-async-generator-functions": "7.18.10",
17
17
  "@babel/plugin-transform-async-to-generator": "7.18.6",
@@ -20,7 +20,7 @@
20
20
  "@babel/runtime": "7.18.9",
21
21
  "@babel/template": "7.18.10",
22
22
  "@discoveryjs/json-ext": "0.5.7",
23
- "@ngtools/webpack": "14.2.0-next.0",
23
+ "@ngtools/webpack": "14.2.0-next.1",
24
24
  "ansi-colors": "4.1.3",
25
25
  "babel-loader": "8.2.5",
26
26
  "babel-plugin-istanbul": "6.1.1",
@@ -29,7 +29,7 @@
29
29
  "copy-webpack-plugin": "11.0.0",
30
30
  "critters": "0.0.16",
31
31
  "css-loader": "6.7.1",
32
- "esbuild-wasm": "0.14.53",
32
+ "esbuild-wasm": "0.15.1",
33
33
  "glob": "8.0.3",
34
34
  "https-proxy-agent": "5.0.1",
35
35
  "inquirer": "8.2.4",
@@ -45,14 +45,14 @@
45
45
  "ora": "5.4.1",
46
46
  "parse5-html-rewriting-stream": "6.0.1",
47
47
  "piscina": "3.2.0",
48
- "postcss": "8.4.14",
48
+ "postcss": "8.4.16",
49
49
  "postcss-import": "14.1.0",
50
50
  "postcss-loader": "7.0.1",
51
51
  "postcss-preset-env": "7.7.2",
52
52
  "regenerator-runtime": "0.13.9",
53
53
  "resolve-url-loader": "5.0.0",
54
54
  "rxjs": "6.6.7",
55
- "sass": "1.54.1",
55
+ "sass": "1.54.4",
56
56
  "sass-loader": "13.0.2",
57
57
  "semver": "7.3.7",
58
58
  "source-map-loader": "4.0.0",
@@ -65,12 +65,12 @@
65
65
  "tslib": "2.4.0",
66
66
  "webpack": "5.74.0",
67
67
  "webpack-dev-middleware": "5.3.3",
68
- "webpack-dev-server": "4.9.3",
68
+ "webpack-dev-server": "4.10.0",
69
69
  "webpack-merge": "5.8.0",
70
70
  "webpack-subresource-integrity": "5.1.0"
71
71
  },
72
72
  "optionalDependencies": {
73
- "esbuild": "0.14.53"
73
+ "esbuild": "0.15.1"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@angular/compiler-cli": "^14.0.0 || ^14.0.0-next || ^14.1.0-next",
@@ -162,9 +162,13 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
162
162
  }
163
163
  // The file emitter created during `onStart` that will be used during the build in `onLoad` callbacks for TS files
164
164
  let fileEmitter;
165
+ // The stylesheet resources from component stylesheets that will be added to the build results output files
166
+ let stylesheetResourceFiles;
165
167
  build.onStart(async () => {
166
168
  var _a, _b;
167
169
  const result = {};
170
+ // Reset stylesheet resource output files
171
+ stylesheetResourceFiles = [];
168
172
  // Create TypeScript compiler host
169
173
  const host = typescript_1.default.createIncrementalCompilerHost(compilerOptions);
170
174
  // Temporarily process external resources via readResource.
@@ -176,9 +180,10 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
176
180
  if (fileName.endsWith('.html')) {
177
181
  return (_a = this.readFile(fileName)) !== null && _a !== void 0 ? _a : '';
178
182
  }
179
- const { contents, errors, warnings } = await (0, stylesheets_1.bundleStylesheetFile)(fileName, styleOptions);
183
+ const { contents, resourceFiles, errors, warnings } = await (0, stylesheets_1.bundleStylesheetFile)(fileName, styleOptions);
180
184
  ((_b = result.errors) !== null && _b !== void 0 ? _b : (result.errors = [])).push(...errors);
181
185
  ((_c = result.warnings) !== null && _c !== void 0 ? _c : (result.warnings = [])).push(...warnings);
186
+ stylesheetResourceFiles.push(...resourceFiles);
182
187
  return contents;
183
188
  };
184
189
  // Add an AOT compiler resource transform hook
@@ -191,12 +196,13 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
191
196
  // The file with the resource content will either be an actual file (resourceFile)
192
197
  // or the file containing the inline component style text (containingFile).
193
198
  const file = (_a = context.resourceFile) !== null && _a !== void 0 ? _a : context.containingFile;
194
- const { contents, errors, warnings } = await (0, stylesheets_1.bundleStylesheetText)(data, {
199
+ const { contents, resourceFiles, errors, warnings } = await (0, stylesheets_1.bundleStylesheetText)(data, {
195
200
  resolvePath: path.dirname(file),
196
201
  virtualName: file,
197
202
  }, styleOptions);
198
203
  ((_b = result.errors) !== null && _b !== void 0 ? _b : (result.errors = [])).push(...errors);
199
204
  ((_c = result.warnings) !== null && _c !== void 0 ? _c : (result.warnings = [])).push(...warnings);
205
+ stylesheetResourceFiles.push(...resourceFiles);
200
206
  return { content: contents };
201
207
  };
202
208
  // Create the Angular specific program that contains the Angular compiler
@@ -263,9 +269,20 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
263
269
  ],
264
270
  };
265
271
  }
272
+ const data = (_a = typescriptResult.content) !== null && _a !== void 0 ? _a : '';
273
+ const forceAsyncTransformation = /for\s+await\s*\(|async\s+function\s*\*/.test(data);
266
274
  const useInputSourcemap = pluginOptions.sourcemap &&
267
275
  (!!pluginOptions.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(args.path));
268
- const data = (_a = typescriptResult.content) !== null && _a !== void 0 ? _a : '';
276
+ // If no additional transformations are needed, return the TypeScript output directly
277
+ if (!forceAsyncTransformation && !pluginOptions.advancedOptimizations) {
278
+ return {
279
+ // Strip sourcemaps if they should not be used
280
+ contents: useInputSourcemap
281
+ ? data
282
+ : data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, ''),
283
+ loader: 'js',
284
+ };
285
+ }
269
286
  const babelResult = await (0, core_1.transformAsync)(data, {
270
287
  filename: args.path,
271
288
  inputSourceMap: (useInputSourcemap ? undefined : false),
@@ -279,7 +296,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
279
296
  [
280
297
  application_1.default,
281
298
  {
282
- forceAsyncTransformation: data.includes('async'),
299
+ forceAsyncTransformation,
283
300
  optimize: pluginOptions.advancedOptimizations && {},
284
301
  },
285
302
  ],
@@ -292,11 +309,24 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
292
309
  });
293
310
  build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
294
311
  var _a;
295
- const angularPackage = /[\\/]node_modules[\\/]@angular[\\/]/.test(args.path);
296
- const linkerPluginCreator = (await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli/linker/babel')).createEs2015LinkerPlugin;
312
+ const data = await fs_1.promises.readFile(args.path, 'utf-8');
313
+ const forceAsyncTransformation = !/[\\/][_f]?esm2015[\\/]/.test(args.path) &&
314
+ /for\s+await\s*\(|async\s+function\s*\*/.test(data);
315
+ const shouldLink = await (0, webpack_loader_1.requiresLinking)(args.path, data);
297
316
  const useInputSourcemap = pluginOptions.sourcemap &&
298
317
  (!!pluginOptions.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(args.path));
299
- const data = await fs_1.promises.readFile(args.path, 'utf-8');
318
+ // If no additional transformations are needed, return the TypeScript output directly
319
+ if (!forceAsyncTransformation && !pluginOptions.advancedOptimizations && !shouldLink) {
320
+ return {
321
+ // Strip sourcemaps if they should not be used
322
+ contents: useInputSourcemap
323
+ ? data
324
+ : data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, ''),
325
+ loader: 'js',
326
+ };
327
+ }
328
+ const angularPackage = /[\\/]node_modules[\\/]@angular[\\/]/.test(args.path);
329
+ const linkerPluginCreator = (await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli/linker/babel')).createEs2015LinkerPlugin;
300
330
  const result = await (0, core_1.transformAsync)(data, {
301
331
  filename: args.path,
302
332
  inputSourceMap: (useInputSourcemap ? undefined : false),
@@ -311,11 +341,11 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
311
341
  application_1.default,
312
342
  {
313
343
  angularLinker: {
314
- shouldLink: await (0, webpack_loader_1.requiresLinking)(args.path, data),
344
+ shouldLink,
315
345
  jitMode: false,
316
346
  linkerPluginCreator,
317
347
  },
318
- forceAsyncTransformation: !/[\\/][_f]?esm2015[\\/]/.test(args.path) && data.includes('async'),
348
+ forceAsyncTransformation,
319
349
  optimize: pluginOptions.advancedOptimizations && {
320
350
  looseEnums: angularPackage,
321
351
  pureTopLevel: angularPackage,
@@ -329,6 +359,12 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
329
359
  loader: 'js',
330
360
  };
331
361
  });
362
+ build.onEnd((result) => {
363
+ var _a;
364
+ if (stylesheetResourceFiles.length) {
365
+ (_a = result.outputFiles) === null || _a === void 0 ? void 0 : _a.push(...stylesheetResourceFiles);
366
+ }
367
+ });
332
368
  },
333
369
  };
334
370
  }
@@ -0,0 +1,17 @@
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
+ import type { Plugin } from 'esbuild';
9
+ /**
10
+ * Creates an esbuild {@link Plugin} that loads all CSS url token references using the
11
+ * built-in esbuild `file` loader. A plugin is used to allow for all file extensions
12
+ * and types to be supported without needing to manually specify all extensions
13
+ * within the build configuration.
14
+ *
15
+ * @returns An esbuild {@link Plugin} instance.
16
+ */
17
+ export declare function createCssResourcePlugin(): Plugin;
@@ -0,0 +1,59 @@
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.createCssResourcePlugin = void 0;
11
+ const promises_1 = require("fs/promises");
12
+ /**
13
+ * Symbol marker used to indicate CSS resource resolution is being attempted.
14
+ * This is used to prevent an infinite loop within the plugin's resolve hook.
15
+ */
16
+ const CSS_RESOURCE_RESOLUTION = Symbol('CSS_RESOURCE_RESOLUTION');
17
+ /**
18
+ * Creates an esbuild {@link Plugin} that loads all CSS url token references using the
19
+ * built-in esbuild `file` loader. A plugin is used to allow for all file extensions
20
+ * and types to be supported without needing to manually specify all extensions
21
+ * within the build configuration.
22
+ *
23
+ * @returns An esbuild {@link Plugin} instance.
24
+ */
25
+ function createCssResourcePlugin() {
26
+ return {
27
+ name: 'angular-css-resource',
28
+ setup(build) {
29
+ build.onResolve({ filter: /.*/ }, async (args) => {
30
+ var _a;
31
+ // Only attempt to resolve url tokens which only exist inside CSS.
32
+ // Also, skip this plugin if already attempting to resolve the url-token.
33
+ if (args.kind !== 'url-token' || ((_a = args.pluginData) === null || _a === void 0 ? void 0 : _a[CSS_RESOURCE_RESOLUTION])) {
34
+ return null;
35
+ }
36
+ const { importer, kind, resolveDir, namespace, pluginData = {} } = args;
37
+ pluginData[CSS_RESOURCE_RESOLUTION] = true;
38
+ const result = await build.resolve(args.path, {
39
+ importer,
40
+ kind,
41
+ namespace,
42
+ pluginData,
43
+ resolveDir,
44
+ });
45
+ return {
46
+ ...result,
47
+ namespace: 'css-resource',
48
+ };
49
+ });
50
+ build.onLoad({ filter: /.*/, namespace: 'css-resource' }, async (args) => {
51
+ return {
52
+ contents: await (0, promises_1.readFile)(args.path),
53
+ loader: 'file',
54
+ };
55
+ });
56
+ },
57
+ };
58
+ }
59
+ exports.createCssResourcePlugin = createCssResourcePlugin;
@@ -140,6 +140,7 @@ async function buildEsbuildBrowser(options, context) {
140
140
  outputNames: noInjectNames.includes(name) ? { media: outputNames.media } : outputNames,
141
141
  includePaths: (_e = options.stylePreprocessorOptions) === null || _e === void 0 ? void 0 : _e.includePaths,
142
142
  preserveSymlinks: options.preserveSymlinks,
143
+ externalDependencies: options.externalDependencies,
143
144
  });
144
145
  await (0, esbuild_1.logMessages)(context, sheetResult);
145
146
  if (!sheetResult.path) {
@@ -244,6 +245,18 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
244
245
  entryNames: outputNames.bundles,
245
246
  assetNames: outputNames.media,
246
247
  target: 'es2020',
248
+ supported: {
249
+ // Native async/await is not supported with Zone.js. Disabling support here will cause
250
+ // esbuild to downlevel async/await to a Zone.js supported form.
251
+ 'async-await': false,
252
+ // Zone.js also does not support async generators or async iterators. However, esbuild does
253
+ // not currently support downleveling either of them. Instead babel is used within the JS/TS
254
+ // loader to perform the downlevel transformation. They are both disabled here to allow
255
+ // esbuild to handle them in the future if support is ever added.
256
+ // NOTE: If esbuild adds support in the future, the babel support for these can be disabled.
257
+ 'async-generator': false,
258
+ 'for-await': false,
259
+ },
247
260
  mainFields: ['es2020', 'browser', 'module', 'main'],
248
261
  conditions: ['es2020', 'es2015', 'module'],
249
262
  resolveExtensions: ['.ts', '.tsx', '.mjs', '.js'],
@@ -279,6 +292,7 @@ async function bundleCode(workspaceRoot, entryPoints, outputNames, options, opti
279
292
  !!sourcemapOptions.styles && (sourcemapOptions.hidden ? false : 'inline'),
280
293
  outputNames,
281
294
  includePaths: (_a = options.stylePreprocessorOptions) === null || _a === void 0 ? void 0 : _a.includePaths,
295
+ externalDependencies: options.externalDependencies,
282
296
  }),
283
297
  ],
284
298
  define: {
@@ -8,5 +8,5 @@
8
8
  import type { Plugin } from 'esbuild';
9
9
  export declare function createSassPlugin(options: {
10
10
  sourcemap: boolean;
11
- includePaths?: string[];
11
+ loadPaths?: string[];
12
12
  }): Plugin;
@@ -6,47 +6,90 @@
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
+ };
9
32
  Object.defineProperty(exports, "__esModule", { value: true });
10
33
  exports.createSassPlugin = void 0;
11
- const sass_service_1 = require("../../sass/sass-service");
34
+ const url_1 = require("url");
12
35
  function createSassPlugin(options) {
13
36
  return {
14
37
  name: 'angular-sass',
15
38
  setup(build) {
16
39
  let sass;
17
- build.onStart(() => {
18
- sass = new sass_service_1.SassWorkerImplementation();
40
+ build.onStart(async () => {
41
+ // Lazily load Sass
42
+ sass = await Promise.resolve().then(() => __importStar(require('sass')));
19
43
  });
20
- build.onEnd(() => {
21
- sass === null || sass === void 0 ? void 0 : sass.close();
22
- });
23
- build.onLoad({ filter: /\.s[ac]ss$/ }, async (args) => {
24
- const result = await new Promise((resolve, reject) => {
25
- sass.render({
26
- file: args.path,
27
- includePaths: options.includePaths,
28
- indentedSyntax: args.path.endsWith('.sass'),
29
- outputStyle: 'expanded',
44
+ build.onLoad({ filter: /\.s[ac]ss$/ }, (args) => {
45
+ try {
46
+ const warnings = [];
47
+ // Use sync version as async version is slower.
48
+ const { css, sourceMap, loadedUrls } = sass.compile(args.path, {
49
+ style: 'expanded',
50
+ loadPaths: options.loadPaths,
30
51
  sourceMap: options.sourcemap,
31
- sourceMapContents: options.sourcemap,
32
- sourceMapEmbed: options.sourcemap,
52
+ sourceMapIncludeSources: options.sourcemap,
33
53
  quietDeps: true,
34
- }, (error, result) => {
35
- if (error) {
36
- reject(error);
37
- }
38
- if (result) {
39
- resolve(result);
40
- }
54
+ logger: {
55
+ warn: (text, _options) => {
56
+ warnings.push({
57
+ text,
58
+ });
59
+ },
60
+ },
41
61
  });
42
- });
43
- return {
44
- contents: result.css,
45
- loader: 'css',
46
- watchFiles: result.stats.includedFiles,
47
- };
62
+ return {
63
+ loader: 'css',
64
+ contents: `${css}\n${sourceMapToUrlComment(sourceMap)}`,
65
+ watchFiles: loadedUrls.map((url) => (0, url_1.fileURLToPath)(url)),
66
+ warnings,
67
+ };
68
+ }
69
+ catch (error) {
70
+ if (error instanceof sass.Exception) {
71
+ const file = error.span.url ? (0, url_1.fileURLToPath)(error.span.url) : undefined;
72
+ return {
73
+ loader: 'css',
74
+ errors: [
75
+ {
76
+ text: error.toString(),
77
+ },
78
+ ],
79
+ watchFiles: file ? [file] : undefined,
80
+ };
81
+ }
82
+ throw error;
83
+ }
48
84
  });
49
85
  },
50
86
  };
51
87
  }
52
88
  exports.createSassPlugin = createSassPlugin;
89
+ function sourceMapToUrlComment(sourceMap) {
90
+ if (!sourceMap) {
91
+ return '';
92
+ }
93
+ const urlSourceMap = Buffer.from(JSON.stringify(sourceMap), 'utf-8').toString('base64');
94
+ return `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${urlSourceMap} */`;
95
+ }
@@ -16,6 +16,7 @@ export interface BundleStylesheetOptions {
16
16
  media?: string;
17
17
  };
18
18
  includePaths?: string[];
19
+ externalDependencies?: string[];
19
20
  }
20
21
  /**
21
22
  * Bundle a stylesheet that exists as a file on the filesystem.
@@ -32,17 +32,21 @@ var __importStar = (this && this.__importStar) || function (mod) {
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.bundleStylesheetText = exports.bundleStylesheetFile = void 0;
34
34
  const path = __importStar(require("path"));
35
+ const css_resource_plugin_1 = require("./css-resource-plugin");
35
36
  const esbuild_1 = require("./esbuild");
36
37
  const sass_plugin_1 = require("./sass-plugin");
37
38
  async function bundleStylesheet(entry, options) {
38
- var _a, _b;
39
+ var _a, _b, _c;
40
+ const loadPaths = (_a = options.includePaths) !== null && _a !== void 0 ? _a : [];
41
+ // Needed to resolve node packages.
42
+ loadPaths.push(path.join(options.workspaceRoot, 'node_modules'));
39
43
  // Execute esbuild
40
44
  const result = await (0, esbuild_1.bundle)({
41
45
  ...entry,
42
46
  absWorkingDir: options.workspaceRoot,
43
47
  bundle: true,
44
- entryNames: (_a = options.outputNames) === null || _a === void 0 ? void 0 : _a.bundles,
45
- assetNames: (_b = options.outputNames) === null || _b === void 0 ? void 0 : _b.media,
48
+ entryNames: (_b = options.outputNames) === null || _b === void 0 ? void 0 : _b.bundles,
49
+ assetNames: (_c = options.outputNames) === null || _c === void 0 ? void 0 : _c.media,
46
50
  logLevel: 'silent',
47
51
  minify: options.optimization,
48
52
  sourcemap: options.sourcemap,
@@ -50,10 +54,12 @@ async function bundleStylesheet(entry, options) {
50
54
  write: false,
51
55
  platform: 'browser',
52
56
  preserveSymlinks: options.preserveSymlinks,
57
+ external: options.externalDependencies,
53
58
  conditions: ['style', 'sass'],
54
59
  mainFields: ['style', 'sass'],
55
60
  plugins: [
56
- (0, sass_plugin_1.createSassPlugin)({ sourcemap: !!options.sourcemap, includePaths: options.includePaths }),
61
+ (0, sass_plugin_1.createSassPlugin)({ sourcemap: !!options.sourcemap, loadPaths }),
62
+ (0, css_resource_plugin_1.createCssResourcePlugin)(),
57
63
  ],
58
64
  });
59
65
  // Extract the result of the bundling from the output files
@@ -51,6 +51,7 @@ const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
51
51
  const webpack_diagnostics_1 = require("../../utils/webpack-diagnostics");
52
52
  const configs_1 = require("../../webpack/configs");
53
53
  const index_html_webpack_plugin_1 = require("../../webpack/plugins/index-html-webpack-plugin");
54
+ const service_worker_plugin_1 = require("../../webpack/plugins/service-worker-plugin");
54
55
  const stats_1 = require("../../webpack/utils/stats");
55
56
  const schema_1 = require("../browser/schema");
56
57
  /**
@@ -160,6 +161,7 @@ function serveWebpackBrowser(options, context, transforms = {}) {
160
161
  if (transforms.webpackConfiguration) {
161
162
  webpackConfig = await transforms.webpackConfiguration(webpackConfig);
162
163
  }
164
+ (_c = webpackConfig.plugins) !== null && _c !== void 0 ? _c : (webpackConfig.plugins = []);
163
165
  if (browserOptions.index) {
164
166
  const { scripts = [], styles = [], baseHref } = browserOptions;
165
167
  const entrypoints = (0, package_chunk_sort_1.generateEntryPoints)({
@@ -168,9 +170,8 @@ function serveWebpackBrowser(options, context, transforms = {}) {
168
170
  // The below is needed as otherwise HMR for CSS will break.
169
171
  // styles.js and runtime.js needs to be loaded as a non-module scripts as otherwise `document.currentScript` will be null.
170
172
  // https://github.com/webpack-contrib/mini-css-extract-plugin/blob/90445dd1d81da0c10b9b0e8a17b417d0651816b8/src/hmr/hotModuleReplacement.js#L39
171
- isHMREnabled: !!((_c = webpackConfig.devServer) === null || _c === void 0 ? void 0 : _c.hot),
173
+ isHMREnabled: !!((_d = webpackConfig.devServer) === null || _d === void 0 ? void 0 : _d.hot),
172
174
  });
173
- (_d = webpackConfig.plugins) !== null && _d !== void 0 ? _d : (webpackConfig.plugins = []);
174
175
  webpackConfig.plugins.push(new index_html_webpack_plugin_1.IndexHtmlWebpackPlugin({
175
176
  indexPath: path.resolve(workspaceRoot, (0, webpack_browser_config_1.getIndexInputFile)(browserOptions.index)),
176
177
  outputPath: (0, webpack_browser_config_1.getIndexOutputFile)(browserOptions.index),
@@ -185,6 +186,15 @@ function serveWebpackBrowser(options, context, transforms = {}) {
185
186
  lang: locale,
186
187
  }));
187
188
  }
189
+ if (browserOptions.serviceWorker) {
190
+ webpackConfig.plugins.push(new service_worker_plugin_1.ServiceWorkerPlugin({
191
+ baseHref: browserOptions.baseHref,
192
+ root: context.workspaceRoot,
193
+ projectRoot,
194
+ outputPath: path.join(context.workspaceRoot, browserOptions.outputPath),
195
+ ngswConfigPath: browserOptions.ngswConfigPath,
196
+ }));
197
+ }
188
198
  return {
189
199
  browserOptions,
190
200
  webpackConfig,
@@ -5,4 +5,6 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
- export declare function augmentAppWithServiceWorker(appRoot: string, workspaceRoot: string, outputPath: string, baseHref: string, ngswConfigPath?: string): Promise<void>;
8
+ /// <reference types="node" />
9
+ import { promises as fsPromises } from 'fs';
10
+ export declare function augmentAppWithServiceWorker(appRoot: string, workspaceRoot: string, outputPath: string, baseHref: string, ngswConfigPath?: string, inputputFileSystem?: typeof fsPromises, outputFileSystem?: typeof fsPromises): Promise<void>;
@@ -34,40 +34,42 @@ exports.augmentAppWithServiceWorker = void 0;
34
34
  const crypto = __importStar(require("crypto"));
35
35
  const fs_1 = require("fs");
36
36
  const path = __importStar(require("path"));
37
- const stream_1 = require("stream");
38
37
  const error_1 = require("./error");
39
38
  const load_esm_1 = require("./load-esm");
40
39
  class CliFilesystem {
41
- constructor(base) {
40
+ constructor(fs, base) {
41
+ this.fs = fs;
42
42
  this.base = base;
43
43
  }
44
44
  list(dir) {
45
45
  return this._recursiveList(this._resolve(dir), []);
46
46
  }
47
47
  read(file) {
48
- return fs_1.promises.readFile(this._resolve(file), 'utf-8');
48
+ return this.fs.readFile(this._resolve(file), 'utf-8');
49
49
  }
50
- hash(file) {
51
- return new Promise((resolve, reject) => {
52
- const hash = crypto.createHash('sha1').setEncoding('hex');
53
- (0, stream_1.pipeline)((0, fs_1.createReadStream)(this._resolve(file)), hash, (error) => error ? reject(error) : resolve(hash.read()));
54
- });
50
+ async hash(file) {
51
+ return crypto
52
+ .createHash('sha1')
53
+ .update(await this.fs.readFile(this._resolve(file)))
54
+ .digest('hex');
55
55
  }
56
- write(file, content) {
57
- return fs_1.promises.writeFile(this._resolve(file), content);
56
+ write(_file, _content) {
57
+ throw new Error('This should never happen.');
58
58
  }
59
59
  _resolve(file) {
60
60
  return path.join(this.base, file);
61
61
  }
62
62
  async _recursiveList(dir, items) {
63
63
  const subdirectories = [];
64
- for await (const entry of await fs_1.promises.opendir(dir)) {
65
- if (entry.isFile()) {
64
+ for (const entry of await this.fs.readdir(dir)) {
65
+ const entryPath = path.join(dir, entry);
66
+ const stats = await this.fs.stat(entryPath);
67
+ if (stats.isFile()) {
66
68
  // Uses posix paths since the service worker expects URLs
67
- items.push('/' + path.relative(this.base, path.join(dir, entry.name)).replace(/\\/g, '/'));
69
+ items.push('/' + path.relative(this.base, entryPath).replace(/\\/g, '/'));
68
70
  }
69
- else if (entry.isDirectory()) {
70
- subdirectories.push(path.join(dir, entry.name));
71
+ else if (stats.isDirectory()) {
72
+ subdirectories.push(entryPath);
71
73
  }
72
74
  }
73
75
  for (const subdirectory of subdirectories) {
@@ -76,7 +78,7 @@ class CliFilesystem {
76
78
  return items;
77
79
  }
78
80
  }
79
- async function augmentAppWithServiceWorker(appRoot, workspaceRoot, outputPath, baseHref, ngswConfigPath) {
81
+ async function augmentAppWithServiceWorker(appRoot, workspaceRoot, outputPath, baseHref, ngswConfigPath, inputputFileSystem = fs_1.promises, outputFileSystem = fs_1.promises) {
80
82
  // Determine the configuration file path
81
83
  const configPath = ngswConfigPath
82
84
  ? path.join(workspaceRoot, ngswConfigPath)
@@ -84,7 +86,7 @@ async function augmentAppWithServiceWorker(appRoot, workspaceRoot, outputPath, b
84
86
  // Read the configuration file
85
87
  let config;
86
88
  try {
87
- const configurationData = await fs_1.promises.readFile(configPath, 'utf-8');
89
+ const configurationData = await inputputFileSystem.readFile(configPath, 'utf-8');
88
90
  config = JSON.parse(configurationData);
89
91
  }
90
92
  catch (error) {
@@ -103,20 +105,28 @@ async function augmentAppWithServiceWorker(appRoot, workspaceRoot, outputPath, b
103
105
  // changed to a direct dynamic import.
104
106
  const GeneratorConstructor = (await (0, load_esm_1.loadEsmModule)('@angular/service-worker/config')).Generator;
105
107
  // Generate the manifest
106
- const generator = new GeneratorConstructor(new CliFilesystem(outputPath), baseHref);
108
+ const generator = new GeneratorConstructor(new CliFilesystem(outputFileSystem, outputPath), baseHref);
107
109
  const output = await generator.process(config);
108
110
  // Write the manifest
109
111
  const manifest = JSON.stringify(output, null, 2);
110
- await fs_1.promises.writeFile(path.join(outputPath, 'ngsw.json'), manifest);
112
+ await outputFileSystem.writeFile(path.join(outputPath, 'ngsw.json'), manifest);
111
113
  // Find the service worker package
112
114
  const workerPath = require.resolve('@angular/service-worker/ngsw-worker.js');
115
+ const copy = async (src, dest) => {
116
+ const resolvedDest = path.join(outputPath, dest);
117
+ return inputputFileSystem === outputFileSystem
118
+ ? // Native FS (Builder).
119
+ inputputFileSystem.copyFile(workerPath, resolvedDest, fs_1.constants.COPYFILE_FICLONE)
120
+ : // memfs (Webpack): Read the file from the input FS (disk) and write it to the output FS (memory).
121
+ outputFileSystem.writeFile(resolvedDest, await inputputFileSystem.readFile(src));
122
+ };
113
123
  // Write the worker code
114
- await fs_1.promises.copyFile(workerPath, path.join(outputPath, 'ngsw-worker.js'), fs_1.constants.COPYFILE_FICLONE);
124
+ await copy(workerPath, 'ngsw-worker.js');
115
125
  // If present, write the safety worker code
116
- const safetyPath = path.join(path.dirname(workerPath), 'safety-worker.js');
117
126
  try {
118
- await fs_1.promises.copyFile(safetyPath, path.join(outputPath, 'worker-basic.min.js'), fs_1.constants.COPYFILE_FICLONE);
119
- await fs_1.promises.copyFile(safetyPath, path.join(outputPath, 'safety-worker.js'), fs_1.constants.COPYFILE_FICLONE);
127
+ const safetyPath = path.join(path.dirname(workerPath), 'safety-worker.js');
128
+ await copy(safetyPath, 'worker-basic.min.js');
129
+ await copy(safetyPath, 'safety-worker.js');
120
130
  }
121
131
  catch (error) {
122
132
  (0, error_1.assertIsError)(error);
@@ -98,23 +98,6 @@ async function getDevServerConfig(wco) {
98
98
  publicPath: servePath,
99
99
  stats: false,
100
100
  },
101
- setupMiddlewares: (middlewares, _devServer) => {
102
- // Temporary workaround for https://github.com/webpack/webpack-dev-server/issues/4180
103
- middlewares.push({
104
- name: 'options-request-response',
105
- path: '*',
106
- middleware: (req, res, next) => {
107
- if (req.method === 'OPTIONS') {
108
- res.statusCode = 204;
109
- res.setHeader('Content-Length', 0);
110
- res.end();
111
- return;
112
- }
113
- next();
114
- },
115
- });
116
- return middlewares;
117
- },
118
101
  liveReload,
119
102
  hot: hmr && !liveReload ? 'only' : hmr,
120
103
  proxy: await addProxyConfig(root, proxyConfig),
@@ -157,7 +157,7 @@ class NgBuildAnalyticsPlugin {
157
157
  var _a, _b;
158
158
  const chunkAssets = new Set();
159
159
  for (const chunk of compilation.chunks) {
160
- if (!chunk.rendered) {
160
+ if (!chunk.rendered || chunk.files.size === 0) {
161
161
  continue;
162
162
  }
163
163
  const firstFile = Array.from(chunk.files)[0];
@@ -131,7 +131,7 @@ function default_1(options) {
131
131
  return;
132
132
  }
133
133
  const value = decl.value;
134
- const urlRegex = /url\(\s*(?:"([^"]+)"|'([^']+)'|(.+?))\s*\)/g;
134
+ const urlRegex = /url(?:\(\s*['"]?)(.*?)(?:['"]?\s*\))/g;
135
135
  const segments = [];
136
136
  let match;
137
137
  let lastIndex = 0;
@@ -141,7 +141,7 @@ function default_1(options) {
141
141
  const context = (inputFile && path.dirname(inputFile)) || loader.context;
142
142
  // eslint-disable-next-line no-cond-assign
143
143
  while ((match = urlRegex.exec(value))) {
144
- const originalUrl = match[1] || match[2] || match[3];
144
+ const originalUrl = match[1];
145
145
  let processedUrl;
146
146
  try {
147
147
  processedUrl = await process(originalUrl, context, resourceCache);
@@ -0,0 +1,20 @@
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
+ import type { Compiler } from 'webpack';
9
+ export interface ServiceWorkerPluginOptions {
10
+ projectRoot: string;
11
+ root: string;
12
+ outputPath: string;
13
+ baseHref?: string;
14
+ ngswConfigPath?: string;
15
+ }
16
+ export declare class ServiceWorkerPlugin {
17
+ private readonly options;
18
+ constructor(options: ServiceWorkerPluginOptions);
19
+ apply(compiler: Compiler): void;
20
+ }
@@ -0,0 +1,27 @@
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.ServiceWorkerPlugin = void 0;
11
+ const service_worker_1 = require("../../utils/service-worker");
12
+ class ServiceWorkerPlugin {
13
+ constructor(options) {
14
+ this.options = options;
15
+ }
16
+ apply(compiler) {
17
+ compiler.hooks.done.tapPromise('angular-service-worker', async (_compilation) => {
18
+ const { projectRoot, root, baseHref = '', ngswConfigPath, outputPath } = this.options;
19
+ await (0, service_worker_1.augmentAppWithServiceWorker)(projectRoot, root, outputPath, baseHref, ngswConfigPath,
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ compiler.inputFileSystem.promises,
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ compiler.outputFileSystem.promises);
24
+ });
25
+ }
26
+ }
27
+ exports.ServiceWorkerPlugin = ServiceWorkerPlugin;