@angular-devkit/build-angular 15.1.0-next.1 → 15.1.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/package.json +14 -14
  2. package/src/babel/presets/application.d.ts +0 -1
  3. package/src/babel/presets/application.js +29 -1
  4. package/src/babel/webpack-loader.js +5 -11
  5. package/src/builders/browser-esbuild/compiler-plugin.d.ts +3 -1
  6. package/src/builders/browser-esbuild/compiler-plugin.js +11 -12
  7. package/src/builders/browser-esbuild/experimental-warnings.js +3 -7
  8. package/src/builders/browser-esbuild/index.js +57 -14
  9. package/src/builders/browser-esbuild/javascript-transformer-worker.js +1 -1
  10. package/src/builders/browser-esbuild/javascript-transformer.d.ts +0 -1
  11. package/src/builders/browser-esbuild/javascript-transformer.js +15 -8
  12. package/src/builders/browser-esbuild/options.d.ts +2 -1
  13. package/src/builders/browser-esbuild/options.js +4 -2
  14. package/src/builders/browser-esbuild/sass-plugin.d.ts +5 -3
  15. package/src/builders/browser-esbuild/sass-plugin.js +87 -74
  16. package/src/builders/browser-esbuild/stylesheets.d.ts +13 -25
  17. package/src/builders/browser-esbuild/stylesheets.js +57 -40
  18. package/src/builders/browser-esbuild/worker-angular-compilation.d.ts +0 -0
  19. package/src/builders/browser-esbuild/worker-angular-compilation.js +59 -0
  20. package/src/builders/karma/index.js +3 -1
  21. package/src/utils/esbuild-targets.js +17 -2
  22. package/src/utils/i18n-inlining.js +1 -1
  23. package/src/utils/index-file/augment-index-html.js +1 -1
  24. package/src/utils/index-file/html-rewriting-stream.d.ts +1 -1
  25. package/src/utils/index-file/html-rewriting-stream.js +24 -20
  26. package/src/utils/index-file/inline-fonts.js +6 -2
  27. package/src/utils/process-bundle.js +1 -1
  28. package/src/webpack/configs/styles.js +1 -0
  29. package/src/webpack/plugins/javascript-optimizer-worker.js +3 -2
  30. package/src/webpack/plugins/styles-webpack-plugin.js +26 -24
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "15.1.0-next.1",
3
+ "version": "15.1.0-next.3",
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.1501.0-next.1",
11
- "@angular-devkit/build-webpack": "0.1501.0-next.1",
12
- "@angular-devkit/core": "15.1.0-next.1",
10
+ "@angular-devkit/architect": "0.1501.0-next.3",
11
+ "@angular-devkit/build-webpack": "0.1501.0-next.3",
12
+ "@angular-devkit/core": "15.1.0-next.3",
13
13
  "@babel/core": "7.20.5",
14
14
  "@babel/generator": "7.20.5",
15
15
  "@babel/helper-annotate-as-pure": "7.18.6",
@@ -20,18 +20,18 @@
20
20
  "@babel/runtime": "7.20.6",
21
21
  "@babel/template": "7.18.10",
22
22
  "@discoveryjs/json-ext": "0.5.7",
23
- "@ngtools/webpack": "15.1.0-next.1",
23
+ "@ngtools/webpack": "15.1.0-next.3",
24
24
  "ansi-colors": "4.1.3",
25
25
  "autoprefixer": "10.4.13",
26
26
  "babel-loader": "9.1.0",
27
27
  "babel-plugin-istanbul": "6.1.1",
28
28
  "browserslist": "4.21.4",
29
- "cacache": "17.0.2",
29
+ "cacache": "17.0.3",
30
30
  "chokidar": "3.5.3",
31
31
  "copy-webpack-plugin": "11.0.0",
32
32
  "critters": "0.0.16",
33
- "css-loader": "6.7.2",
34
- "esbuild-wasm": "0.15.16",
33
+ "css-loader": "6.7.3",
34
+ "esbuild-wasm": "0.16.6",
35
35
  "glob": "8.0.3",
36
36
  "https-proxy-agent": "5.0.1",
37
37
  "inquirer": "8.2.4",
@@ -41,22 +41,22 @@
41
41
  "less-loader": "11.1.0",
42
42
  "license-webpack-plugin": "4.0.2",
43
43
  "loader-utils": "3.2.1",
44
- "magic-string": "0.26.7",
45
- "mini-css-extract-plugin": "2.7.1",
44
+ "magic-string": "0.27.0",
45
+ "mini-css-extract-plugin": "2.7.2",
46
46
  "open": "8.4.0",
47
47
  "ora": "5.4.1",
48
48
  "parse5-html-rewriting-stream": "6.0.1",
49
49
  "piscina": "3.2.0",
50
- "postcss": "8.4.19",
50
+ "postcss": "8.4.20",
51
51
  "postcss-loader": "7.0.2",
52
52
  "resolve-url-loader": "5.0.0",
53
53
  "rxjs": "6.6.7",
54
- "sass": "1.56.1",
54
+ "sass": "1.56.2",
55
55
  "sass-loader": "13.2.0",
56
56
  "semver": "7.3.8",
57
57
  "source-map-loader": "4.0.1",
58
58
  "source-map-support": "0.5.21",
59
- "terser": "5.16.0",
59
+ "terser": "5.16.1",
60
60
  "text-table": "0.2.0",
61
61
  "tree-kill": "1.2.2",
62
62
  "tslib": "2.4.1",
@@ -67,7 +67,7 @@
67
67
  "webpack-subresource-integrity": "5.1.0"
68
68
  },
69
69
  "optionalDependencies": {
70
- "esbuild": "0.15.16"
70
+ "esbuild": "0.16.6"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@angular/compiler-cli": "^15.0.0-next",
@@ -31,7 +31,6 @@ export interface ApplicationPresetOptions {
31
31
  jitMode: boolean;
32
32
  linkerPluginCreator: typeof import('@angular/compiler-cli/linker/babel').createEs2015LinkerPlugin;
33
33
  };
34
- forcePresetEnv?: boolean;
35
34
  forceAsyncTransformation?: boolean;
36
35
  instrumentCode?: {
37
36
  includedBasePath: string;
@@ -29,10 +29,27 @@ var __importStar = (this && this.__importStar) || function (mod) {
29
29
  __setModuleDefault(result, mod);
30
30
  return result;
31
31
  };
32
+ var __importDefault = (this && this.__importDefault) || function (mod) {
33
+ return (mod && mod.__esModule) ? mod : { "default": mod };
34
+ };
32
35
  Object.defineProperty(exports, "__esModule", { value: true });
33
36
  const assert_1 = require("assert");
37
+ const browserslist_1 = __importDefault(require("browserslist"));
34
38
  const fs = __importStar(require("fs"));
35
39
  const path = __importStar(require("path"));
40
+ /**
41
+ * List of browsers which are affected by a WebKit bug where class field
42
+ * initializers might have incorrect variable scopes.
43
+ *
44
+ * See: https://github.com/angular/angular-cli/issues/24355#issuecomment-1333477033
45
+ * See: https://github.com/WebKit/WebKit/commit/e8788a34b3d5f5b4edd7ff6450b80936bff396f2
46
+ */
47
+ const safariClassFieldScopeBugBrowsers = new Set((0, browserslist_1.default)([
48
+ // Safari <15 is technically not supported via https://angular.io/guide/browser-support,
49
+ // but we apply the workaround if forcibly selected.
50
+ 'Safari <=15',
51
+ 'iOS <=15',
52
+ ]));
36
53
  function createI18nDiagnostics(reporter) {
37
54
  const diagnostics = new (class {
38
55
  constructor() {
@@ -113,13 +130,24 @@ function default_1(api, options) {
113
130
  },
114
131
  }));
115
132
  }
116
- if (options.forcePresetEnv) {
133
+ // Applications code ES version can be controlled using TypeScript's `target` option.
134
+ // However, this doesn't effect libraries and hence we use preset-env to downlevel ES features
135
+ // based on the supported browsers in browserslist.
136
+ if (options.supportedBrowsers) {
137
+ const includePlugins = [];
138
+ // If a Safari browser affected by the class field scope bug is selected, we
139
+ // downlevel class properties by ensuring the class properties Babel plugin
140
+ // is always included- regardless of the preset-env targets.
141
+ if (options.supportedBrowsers.some((b) => safariClassFieldScopeBugBrowsers.has(b))) {
142
+ includePlugins.push('@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-private-methods');
143
+ }
117
144
  presets.push([
118
145
  require('@babel/preset-env').default,
119
146
  {
120
147
  bugfixes: true,
121
148
  modules: false,
122
149
  targets: options.supportedBrowsers,
150
+ include: includePlugins,
123
151
  exclude: ['transform-typeof-symbol'],
124
152
  },
125
153
  ]);
@@ -51,13 +51,12 @@ exports.default = (0, babel_loader_1.custom)(() => {
51
51
  });
52
52
  return {
53
53
  async customOptions(options, { source, map }) {
54
- var _a, _b, _c;
54
+ var _a, _b;
55
55
  const { i18n, aot, optimize, instrumentCode, supportedBrowsers, ...rawOptions } = options;
56
56
  // Must process file if plugins are added
57
57
  let shouldProcess = Array.isArray(rawOptions.plugins) && rawOptions.plugins.length > 0;
58
58
  const customOptions = {
59
59
  forceAsyncTransformation: false,
60
- forcePresetEnv: false,
61
60
  angularLinker: undefined,
62
61
  i18n: undefined,
63
62
  instrumentCode: undefined,
@@ -76,20 +75,15 @@ exports.default = (0, babel_loader_1.custom)(() => {
76
75
  };
77
76
  shouldProcess = true;
78
77
  }
79
- // Analyze for ES target processing
80
- if ((_a = customOptions.supportedBrowsers) === null || _a === void 0 ? void 0 : _a.length) {
81
- // Applications code ES version can be controlled using TypeScript's `target` option.
82
- // However, this doesn't effect libraries and hence we use preset-env to downlevel ES fetaures
83
- // based on the supported browsers in browserlist.
84
- customOptions.forcePresetEnv = true;
85
- }
86
78
  // Application code (TS files) will only contain native async if target is ES2017+.
87
79
  // However, third-party libraries can regardless of the target option.
88
80
  // APF packages with code in [f]esm2015 directories is downlevelled to ES2015 and
89
81
  // will not have native async.
90
82
  customOptions.forceAsyncTransformation =
91
83
  !/[\\/][_f]?esm2015[\\/]/.test(this.resourcePath) && source.includes('async');
92
- shouldProcess || (shouldProcess = customOptions.forceAsyncTransformation || customOptions.forcePresetEnv || false);
84
+ shouldProcess || (shouldProcess = customOptions.forceAsyncTransformation ||
85
+ customOptions.supportedBrowsers !== undefined ||
86
+ false);
93
87
  // Analyze for i18n inlining
94
88
  if (i18n &&
95
89
  !/[\\/]@angular[\\/](?:compiler|localize)/.test(this.resourcePath) &&
@@ -128,7 +122,7 @@ exports.default = (0, babel_loader_1.custom)(() => {
128
122
  pureTopLevel: angularPackage,
129
123
  // JavaScript modules that are marked as side effect free are considered to have
130
124
  // no decorators that contain non-local effects.
131
- wrapDecorators: !!((_c = (_b = this._module) === null || _b === void 0 ? void 0 : _b.factoryMeta) === null || _c === void 0 ? void 0 : _c.sideEffectFree),
125
+ wrapDecorators: !!((_b = (_a = this._module) === null || _a === void 0 ? void 0 : _a.factoryMeta) === null || _b === void 0 ? void 0 : _b.sideEffectFree),
132
126
  };
133
127
  shouldProcess = true;
134
128
  }
@@ -22,4 +22,6 @@ export interface CompilerPluginOptions {
22
22
  fileReplacements?: Record<string, string>;
23
23
  sourceFileCache?: SourceFileCache;
24
24
  }
25
- export declare function createCompilerPlugin(pluginOptions: CompilerPluginOptions, styleOptions: BundleStylesheetOptions): Plugin;
25
+ export declare function createCompilerPlugin(pluginOptions: CompilerPluginOptions, styleOptions: BundleStylesheetOptions & {
26
+ inlineStyleLanguage: string;
27
+ }): Plugin;
@@ -44,6 +44,10 @@ const angular_compilation_1 = require("./angular-compilation");
44
44
  const javascript_transformer_1 = require("./javascript-transformer");
45
45
  const profiling_1 = require("./profiling");
46
46
  const stylesheets_1 = require("./stylesheets");
47
+ /**
48
+ * A counter for component styles used to generate unique build-time identifiers for each stylesheet.
49
+ */
50
+ let componentStyleCounter = 0;
47
51
  /**
48
52
  * Converts TypeScript Diagnostic related information into an esbuild compatible note object.
49
53
  * Related information is a subset of a full TypeScript Diagnostic and also used for diagnostic
@@ -149,6 +153,10 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
149
153
  // Skip keys that have been manually provided
150
154
  continue;
151
155
  }
156
+ if (key === 'ngDevMode') {
157
+ // ngDevMode is already set based on the builder's script optimization option
158
+ continue;
159
+ }
152
160
  // esbuild requires values to be a string (actual strings need to be quoted).
153
161
  // In this case, all provided values are booleans.
154
162
  build.initialOptions.define[key] = value.toString();
@@ -213,18 +221,9 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
213
221
  var _a, _b;
214
222
  // Stylesheet file only exists for external stylesheets
215
223
  const filename = stylesheetFile !== null && stylesheetFile !== void 0 ? stylesheetFile : containingFile;
216
- // Temporary workaround for lack of virtual file support in the Sass plugin.
217
- // External Sass stylesheets are transformed using the file instead of the already read content.
218
- let stylesheetResult;
219
- if (filename.endsWith('.scss') || filename.endsWith('.sass')) {
220
- stylesheetResult = await (0, stylesheets_1.bundleStylesheetFile)(filename, styleOptions);
221
- }
222
- else {
223
- stylesheetResult = await (0, stylesheets_1.bundleStylesheetText)(data, {
224
- resolvePath: path.dirname(filename),
225
- virtualName: filename,
226
- }, styleOptions);
227
- }
224
+ const stylesheetResult = await (0, stylesheets_1.bundleComponentStylesheet)(
225
+ // TODO: Evaluate usage of a fast hash instead
226
+ `${++componentStyleCounter}`, styleOptions.inlineStyleLanguage, data, filename, !stylesheetFile, styleOptions);
228
227
  const { contents, resourceFiles, errors, warnings } = stylesheetResult;
229
228
  ((_a = result.errors) !== null && _a !== void 0 ? _a : (result.errors = [])).push(...errors);
230
229
  ((_b = result.warnings) !== null && _b !== void 0 ? _b : (result.warnings = [])).push(...warnings);
@@ -20,10 +20,6 @@ const UNSUPPORTED_OPTIONS = [
20
20
  // The following two have no effect when localize is not enabled
21
21
  // 'i18nDuplicateTranslation',
22
22
  // 'i18nMissingTranslation',
23
- // * Stylesheet preprocessor support
24
- 'inlineStyleLanguage',
25
- // The following option has no effect until preprocessors are supported
26
- // 'stylePreprocessorOptions',
27
23
  // * Deprecated
28
24
  'deployUrl',
29
25
  // * Always enabled with esbuild
@@ -49,10 +45,10 @@ function logExperimentalWarnings(options, context) {
49
45
  if (typeof value === 'object' && Object.keys(value).length === 0) {
50
46
  continue;
51
47
  }
52
- if (unsupportedOption === 'inlineStyleLanguage' && value === 'css') {
53
- continue;
54
- }
55
48
  context.logger.warn(`The '${unsupportedOption}' option is currently unsupported by this experimental builder and will be ignored.`);
56
49
  }
50
+ if (options.inlineStyleLanguage === 'less') {
51
+ context.logger.warn('The less stylesheet preprocessor is not currently supported.');
52
+ }
57
53
  }
58
54
  exports.logExperimentalWarnings = logExperimentalWarnings;
@@ -180,7 +180,7 @@ function createOutputFileFromText(path, text) {
180
180
  };
181
181
  }
182
182
  function createCodeBundleOptions(options, target, sourceFileCache) {
183
- const { workspaceRoot, entryPoints, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, } = options;
183
+ const { workspaceRoot, entryPoints, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, inlineStyleLanguage, } = options;
184
184
  return {
185
185
  absWorkingDir: workspaceRoot,
186
186
  bundle: true,
@@ -190,19 +190,7 @@ function createCodeBundleOptions(options, target, sourceFileCache) {
190
190
  entryNames: outputNames.bundles,
191
191
  assetNames: outputNames.media,
192
192
  target,
193
- supported: {
194
- // Native async/await is not supported with Zone.js. Disabling support here will cause
195
- // esbuild to downlevel async/await and for await...of to a Zone.js supported form. However, esbuild
196
- // does not currently support downleveling async generators. Instead babel is used within the JS/TS
197
- // loader to perform the downlevel transformation.
198
- // NOTE: If esbuild adds support in the future, the babel support for async generators can be disabled.
199
- 'async-await': false,
200
- // V8 currently has a performance defect involving object spread operations that can cause signficant
201
- // degradation in runtime performance. By not supporting the language feature here, a downlevel form
202
- // will be used instead which provides a workaround for the performance issue.
203
- // For more details: https://bugs.chromium.org/p/v8/issues/detail?id=11536
204
- 'object-rest-spread': false,
205
- },
193
+ supported: getFeatureSupport(target),
206
194
  mainFields: ['es2020', 'browser', 'module', 'main'],
207
195
  conditions: ['es2020', 'es2015', 'module'],
208
196
  resolveExtensions: ['.ts', '.tsx', '.mjs', '.js'],
@@ -241,14 +229,69 @@ function createCodeBundleOptions(options, target, sourceFileCache) {
241
229
  includePaths: stylePreprocessorOptions === null || stylePreprocessorOptions === void 0 ? void 0 : stylePreprocessorOptions.includePaths,
242
230
  externalDependencies,
243
231
  target,
232
+ inlineStyleLanguage,
244
233
  }),
245
234
  ],
246
235
  define: {
236
+ // Only set to false when script optimizations are enabled. It should not be set to true because
237
+ // Angular turns `ngDevMode` into an object for development debugging purposes when not defined
238
+ // which a constant true value would break.
247
239
  ...(optimizationOptions.scripts ? { 'ngDevMode': 'false' } : undefined),
240
+ // Only AOT mode is supported currently
248
241
  'ngJitMode': 'false',
249
242
  },
250
243
  };
251
244
  }
245
+ /**
246
+ * Generates a syntax feature object map for Angular applications based on a list of targets.
247
+ * A full set of feature names can be found here: https://esbuild.github.io/api/#supported
248
+ * @param target An array of browser/engine targets in the format accepted by the esbuild `target` option.
249
+ * @returns An object that can be used with the esbuild build `supported` option.
250
+ */
251
+ function getFeatureSupport(target) {
252
+ const supported = {
253
+ // Native async/await is not supported with Zone.js. Disabling support here will cause
254
+ // esbuild to downlevel async/await and for await...of to a Zone.js supported form. However, esbuild
255
+ // does not currently support downleveling async generators. Instead babel is used within the JS/TS
256
+ // loader to perform the downlevel transformation.
257
+ // NOTE: If esbuild adds support in the future, the babel support for async generators can be disabled.
258
+ 'async-await': false,
259
+ // V8 currently has a performance defect involving object spread operations that can cause signficant
260
+ // degradation in runtime performance. By not supporting the language feature here, a downlevel form
261
+ // will be used instead which provides a workaround for the performance issue.
262
+ // For more details: https://bugs.chromium.org/p/v8/issues/detail?id=11536
263
+ 'object-rest-spread': false,
264
+ };
265
+ // Detect Safari browser versions that have a class field behavior bug
266
+ // See: https://github.com/angular/angular-cli/issues/24355#issuecomment-1333477033
267
+ // See: https://github.com/WebKit/WebKit/commit/e8788a34b3d5f5b4edd7ff6450b80936bff396f2
268
+ let safariClassFieldScopeBug = false;
269
+ for (const browser of target) {
270
+ let majorVersion;
271
+ if (browser.startsWith('ios')) {
272
+ majorVersion = Number(browser.slice(3, 5));
273
+ }
274
+ else if (browser.startsWith('safari')) {
275
+ majorVersion = Number(browser.slice(6, 8));
276
+ }
277
+ else {
278
+ continue;
279
+ }
280
+ // Technically, 14.0 is not broken but rather does not have support. However, the behavior
281
+ // is identical since it would be set to false by esbuild if present as a target.
282
+ if (majorVersion === 14 || majorVersion === 15) {
283
+ safariClassFieldScopeBug = true;
284
+ break;
285
+ }
286
+ }
287
+ // If class field support cannot be used set to false; otherwise leave undefined to allow
288
+ // esbuild to use `target` to determine support.
289
+ if (safariClassFieldScopeBug) {
290
+ supported['class-field'] = false;
291
+ supported['class-static-field'] = false;
292
+ }
293
+ return supported;
294
+ }
252
295
  function createGlobalStylesBundleOptions(options, target) {
253
296
  const { workspaceRoot, optimizationOptions, sourcemapOptions, outputNames, globalStyles, preserveSymlinks, externalDependencies, stylePreprocessorOptions, watch, } = options;
254
297
  const buildOptions = (0, stylesheets_1.createStylesheetBundleOptions)({
@@ -25,7 +25,7 @@ exports.default = transformJavaScript;
25
25
  let linkerPluginCreator;
26
26
  async function transformWithBabel({ filename, data, ...options }) {
27
27
  var _a, _b;
28
- const forceAsyncTransformation = (_a = options.forceAsyncTransformation) !== null && _a !== void 0 ? _a : (!/[\\/][_f]?esm2015[\\/]/.test(filename) && /async\s+function\s*\*/.test(data));
28
+ const forceAsyncTransformation = (_a = options.forceAsyncTransformation) !== null && _a !== void 0 ? _a : (!/[\\/][_f]?esm2015[\\/]/.test(filename) && /async(?:\s+function)?\s*\*/.test(data));
29
29
  const shouldLink = !options.skipLinker && (await (0, webpack_loader_1.requiresLinking)(filename, data));
30
30
  const useInputSourcemap = options.sourcemap &&
31
31
  (!!options.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename));
@@ -22,7 +22,6 @@ export interface JavaScriptTransformerOptions {
22
22
  */
23
23
  export declare class JavaScriptTransformer {
24
24
  #private;
25
- private options;
26
25
  constructor(options: JavaScriptTransformerOptions, maxThreads?: number);
27
26
  /**
28
27
  * Performs JavaScript transformations on a file from the filesystem.
@@ -20,7 +20,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
20
20
  var __importDefault = (this && this.__importDefault) || function (mod) {
21
21
  return (mod && mod.__esModule) ? mod : { "default": mod };
22
22
  };
23
- var _JavaScriptTransformer_workerPool;
23
+ var _JavaScriptTransformer_workerPool, _JavaScriptTransformer_commonOptions;
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.JavaScriptTransformer = void 0;
26
26
  const piscina_1 = __importDefault(require("piscina"));
@@ -33,12 +33,19 @@ const piscina_1 = __importDefault(require("piscina"));
33
33
  */
34
34
  class JavaScriptTransformer {
35
35
  constructor(options, maxThreads) {
36
- this.options = options;
37
36
  _JavaScriptTransformer_workerPool.set(this, void 0);
37
+ _JavaScriptTransformer_commonOptions.set(this, void 0);
38
38
  __classPrivateFieldSet(this, _JavaScriptTransformer_workerPool, new piscina_1.default({
39
39
  filename: require.resolve('./javascript-transformer-worker'),
40
40
  maxThreads,
41
41
  }), "f");
42
+ // Extract options to ensure only the named options are serialized and sent to the worker
43
+ const { sourcemap, thirdPartySourcemaps = false, advancedOptimizations = false } = options;
44
+ __classPrivateFieldSet(this, _JavaScriptTransformer_commonOptions, {
45
+ sourcemap,
46
+ thirdPartySourcemaps,
47
+ advancedOptimizations,
48
+ }, "f");
42
49
  }
43
50
  /**
44
51
  * Performs JavaScript transformations on a file from the filesystem.
@@ -51,7 +58,7 @@ class JavaScriptTransformer {
51
58
  // they may need linking. The data is also not yet available to perform most transformation checks.
52
59
  return __classPrivateFieldGet(this, _JavaScriptTransformer_workerPool, "f").run({
53
60
  filename,
54
- ...this.options,
61
+ ...__classPrivateFieldGet(this, _JavaScriptTransformer_commonOptions, "f"),
55
62
  });
56
63
  }
57
64
  /**
@@ -66,10 +73,10 @@ class JavaScriptTransformer {
66
73
  // Perform a quick test to determine if the data needs any transformations.
67
74
  // This allows directly returning the data without the worker communication overhead.
68
75
  let forceAsyncTransformation;
69
- if (skipLinker && !this.options.advancedOptimizations) {
76
+ if (skipLinker && !__classPrivateFieldGet(this, _JavaScriptTransformer_commonOptions, "f").advancedOptimizations) {
70
77
  // If the linker is being skipped and no optimizations are needed, only async transformation is left.
71
- // This checks for async generator functions. All other async transformation is handled by esbuild.
72
- forceAsyncTransformation = data.includes('async') && /async\s+function\s*\*/.test(data);
78
+ // This checks for async generator functions and class methods. All other async transformation is handled by esbuild.
79
+ forceAsyncTransformation = data.includes('async') && /async(?:\s+function)?\s*\*/.test(data);
73
80
  if (!forceAsyncTransformation) {
74
81
  return Buffer.from(data, 'utf-8');
75
82
  }
@@ -80,7 +87,7 @@ class JavaScriptTransformer {
80
87
  // Send the async check result if present to avoid rechecking in the worker
81
88
  forceAsyncTransformation,
82
89
  skipLinker,
83
- ...this.options,
90
+ ...__classPrivateFieldGet(this, _JavaScriptTransformer_commonOptions, "f"),
84
91
  });
85
92
  }
86
93
  /**
@@ -92,4 +99,4 @@ class JavaScriptTransformer {
92
99
  }
93
100
  }
94
101
  exports.JavaScriptTransformer = JavaScriptTransformer;
95
- _JavaScriptTransformer_workerPool = new WeakMap();
102
+ _JavaScriptTransformer_workerPool = new WeakMap(), _JavaScriptTransformer_commonOptions = new WeakMap();
@@ -24,8 +24,9 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
24
24
  cacheOptions: import("../../utils/normalize-cache").NormalizedCachedOptions;
25
25
  crossOrigin: import("./schema").CrossOrigin | undefined;
26
26
  externalDependencies: string[] | undefined;
27
+ inlineStyleLanguage: string;
27
28
  poll: number | undefined;
28
- preserveSymlinks: boolean | undefined;
29
+ preserveSymlinks: boolean;
29
30
  stylePreprocessorOptions: import("./schema").StylePreprocessorOptions | undefined;
30
31
  subresourceIntegrity: boolean | undefined;
31
32
  verbose: boolean | undefined;
@@ -122,7 +122,7 @@ async function normalizeOptions(context, projectName, options) {
122
122
  };
123
123
  }
124
124
  // Initial options to keep
125
- const { baseHref, buildOptimizer, crossOrigin, externalDependencies, poll, preserveSymlinks, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, } = options;
125
+ const { baseHref, buildOptimizer, crossOrigin, externalDependencies, inlineStyleLanguage = 'css', poll, preserveSymlinks, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, } = options;
126
126
  // Return all the normalized options
127
127
  return {
128
128
  advancedOptimizations: buildOptimizer,
@@ -130,8 +130,10 @@ async function normalizeOptions(context, projectName, options) {
130
130
  cacheOptions,
131
131
  crossOrigin,
132
132
  externalDependencies,
133
+ inlineStyleLanguage,
133
134
  poll,
134
- preserveSymlinks,
135
+ // If not explicitly set, default to the Node.js process argument
136
+ preserveSymlinks: preserveSymlinks !== null && preserveSymlinks !== void 0 ? preserveSymlinks : process.execArgv.includes('--preserve-symlinks'),
135
137
  stylePreprocessorOptions,
136
138
  subresourceIntegrity,
137
139
  verbose,
@@ -6,8 +6,10 @@
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
8
  import type { Plugin } from 'esbuild';
9
- export declare function shutdownSassWorkerPool(): void;
10
- export declare function createSassPlugin(options: {
9
+ export interface SassPluginOptions {
11
10
  sourcemap: boolean;
12
11
  loadPaths?: string[];
13
- }): Plugin;
12
+ inlineComponentData?: Record<string, string>;
13
+ }
14
+ export declare function shutdownSassWorkerPool(): void;
15
+ export declare function createSassPlugin(options: SassPluginOptions): Plugin;
@@ -6,8 +6,12 @@
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 __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
9
12
  Object.defineProperty(exports, "__esModule", { value: true });
10
13
  exports.createSassPlugin = exports.shutdownSassWorkerPool = void 0;
14
+ const node_assert_1 = __importDefault(require("node:assert"));
11
15
  const promises_1 = require("node:fs/promises");
12
16
  const node_path_1 = require("node:path");
13
17
  const node_url_1 = require("node:url");
@@ -46,86 +50,95 @@ function createSassPlugin(options) {
46
50
  }
47
51
  return result;
48
52
  };
53
+ build.onLoad({ filter: /^angular:styles\/component;s[ac]ss;/, namespace: 'angular:styles/component' }, async (args) => {
54
+ var _a;
55
+ const data = (_a = options.inlineComponentData) === null || _a === void 0 ? void 0 : _a[args.path];
56
+ (0, node_assert_1.default)(data, `component style name should always be found [${args.path}]`);
57
+ const [, language, , filePath] = args.path.split(';', 4);
58
+ const syntax = language === 'sass' ? 'indented' : 'scss';
59
+ return compileString(data, filePath, syntax, options, resolveUrl);
60
+ });
49
61
  build.onLoad({ filter: /\.s[ac]ss$/ }, async (args) => {
50
- // Lazily load Sass when a Sass file is found
51
- sassWorkerPool !== null && sassWorkerPool !== void 0 ? sassWorkerPool : (sassWorkerPool = new sass_service_1.SassWorkerImplementation(true));
52
- const warnings = [];
53
- try {
54
- const data = await (0, promises_1.readFile)(args.path, 'utf-8');
55
- const { css, sourceMap, loadedUrls } = await sassWorkerPool.compileStringAsync(data, {
56
- url: (0, node_url_1.pathToFileURL)(args.path),
57
- style: 'expanded',
58
- loadPaths: options.loadPaths,
59
- sourceMap: options.sourcemap,
60
- sourceMapIncludeSources: options.sourcemap,
61
- quietDeps: true,
62
- importers: [
63
- {
64
- findFileUrl: async (url, { previousResolvedModules }) => {
65
- const result = await resolveUrl(url, previousResolvedModules);
66
- // Check for package deep imports
67
- if (!result.path) {
68
- const parts = url.split('/');
69
- const hasScope = parts.length >= 2 && parts[0].startsWith('@');
70
- const [nameOrScope, nameOrFirstPath, ...pathPart] = parts;
71
- const packageName = hasScope
72
- ? `${nameOrScope}/${nameOrFirstPath}`
73
- : nameOrScope;
74
- const packageResult = await resolveUrl(packageName + '/package.json', previousResolvedModules);
75
- if (packageResult.path) {
76
- return (0, node_url_1.pathToFileURL)((0, node_path_1.join)((0, node_path_1.dirname)(packageResult.path), !hasScope ? nameOrFirstPath : '', ...pathPart));
77
- }
78
- }
79
- return result.path ? (0, node_url_1.pathToFileURL)(result.path) : null;
80
- },
81
- },
82
- ],
83
- logger: {
84
- warn: (text, { deprecation, span }) => {
85
- warnings.push({
86
- text: deprecation ? 'Deprecation' : text,
87
- location: span && {
88
- file: span.url && (0, node_url_1.fileURLToPath)(span.url),
89
- lineText: span.context,
90
- // Sass line numbers are 0-based while esbuild's are 1-based
91
- line: span.start.line + 1,
92
- column: span.start.column,
93
- },
94
- notes: deprecation ? [{ text }] : undefined,
95
- });
96
- },
97
- },
98
- });
99
- return {
100
- loader: 'css',
101
- contents: sourceMap
102
- ? `${css}\n${sourceMapToUrlComment(sourceMap, (0, node_path_1.dirname)(args.path))}`
103
- : css,
104
- watchFiles: loadedUrls.map((url) => (0, node_url_1.fileURLToPath)(url)),
105
- warnings,
106
- };
107
- }
108
- catch (error) {
109
- if (isSassException(error)) {
110
- const file = error.span.url ? (0, node_url_1.fileURLToPath)(error.span.url) : undefined;
111
- return {
112
- loader: 'css',
113
- errors: [
114
- {
115
- text: error.message,
116
- },
117
- ],
118
- warnings,
119
- watchFiles: file ? [file] : undefined,
120
- };
121
- }
122
- throw error;
123
- }
62
+ const data = await (0, promises_1.readFile)(args.path, 'utf-8');
63
+ const syntax = (0, node_path_1.extname)(args.path).toLowerCase() === '.sass' ? 'indented' : 'scss';
64
+ return compileString(data, args.path, syntax, options, resolveUrl);
124
65
  });
125
66
  },
126
67
  };
127
68
  }
128
69
  exports.createSassPlugin = createSassPlugin;
70
+ async function compileString(data, filePath, syntax, options, resolveUrl) {
71
+ // Lazily load Sass when a Sass file is found
72
+ sassWorkerPool !== null && sassWorkerPool !== void 0 ? sassWorkerPool : (sassWorkerPool = new sass_service_1.SassWorkerImplementation(true));
73
+ const warnings = [];
74
+ try {
75
+ const { css, sourceMap, loadedUrls } = await sassWorkerPool.compileStringAsync(data, {
76
+ url: (0, node_url_1.pathToFileURL)(filePath),
77
+ style: 'expanded',
78
+ syntax,
79
+ loadPaths: options.loadPaths,
80
+ sourceMap: options.sourcemap,
81
+ sourceMapIncludeSources: options.sourcemap,
82
+ quietDeps: true,
83
+ importers: [
84
+ {
85
+ findFileUrl: async (url, { previousResolvedModules }) => {
86
+ const result = await resolveUrl(url, previousResolvedModules);
87
+ // Check for package deep imports
88
+ if (!result.path) {
89
+ const parts = url.split('/');
90
+ const hasScope = parts.length >= 2 && parts[0].startsWith('@');
91
+ const [nameOrScope, nameOrFirstPath, ...pathPart] = parts;
92
+ const packageName = hasScope ? `${nameOrScope}/${nameOrFirstPath}` : nameOrScope;
93
+ const packageResult = await resolveUrl(packageName + '/package.json', previousResolvedModules);
94
+ if (packageResult.path) {
95
+ return (0, node_url_1.pathToFileURL)((0, node_path_1.join)((0, node_path_1.dirname)(packageResult.path), !hasScope ? nameOrFirstPath : '', ...pathPart));
96
+ }
97
+ }
98
+ return result.path ? (0, node_url_1.pathToFileURL)(result.path) : null;
99
+ },
100
+ },
101
+ ],
102
+ logger: {
103
+ warn: (text, { deprecation, span }) => {
104
+ warnings.push({
105
+ text: deprecation ? 'Deprecation' : text,
106
+ location: span && {
107
+ file: span.url && (0, node_url_1.fileURLToPath)(span.url),
108
+ lineText: span.context,
109
+ // Sass line numbers are 0-based while esbuild's are 1-based
110
+ line: span.start.line + 1,
111
+ column: span.start.column,
112
+ },
113
+ notes: deprecation ? [{ text }] : undefined,
114
+ });
115
+ },
116
+ },
117
+ });
118
+ return {
119
+ loader: 'css',
120
+ contents: sourceMap ? `${css}\n${sourceMapToUrlComment(sourceMap, (0, node_path_1.dirname)(filePath))}` : css,
121
+ watchFiles: loadedUrls.map((url) => (0, node_url_1.fileURLToPath)(url)),
122
+ warnings,
123
+ };
124
+ }
125
+ catch (error) {
126
+ if (isSassException(error)) {
127
+ const file = error.span.url ? (0, node_url_1.fileURLToPath)(error.span.url) : undefined;
128
+ return {
129
+ loader: 'css',
130
+ errors: [
131
+ {
132
+ text: error.message,
133
+ },
134
+ ],
135
+ warnings,
136
+ watchFiles: file ? [file] : undefined,
137
+ };
138
+ }
139
+ throw error;
140
+ }
141
+ }
129
142
  function sourceMapToUrlComment(sourceMap, root) {
130
143
  // Remove `file` protocol from all sourcemap sources and adjust to be relative to the input file.
131
144
  // This allows esbuild to correctly process the paths.
@@ -19,36 +19,24 @@ export interface BundleStylesheetOptions {
19
19
  externalDependencies?: string[];
20
20
  target: string[];
21
21
  }
22
- export declare function createStylesheetBundleOptions(options: BundleStylesheetOptions): BuildOptions & {
22
+ export declare function createStylesheetBundleOptions(options: BundleStylesheetOptions, inlineComponentData?: Record<string, string>): BuildOptions & {
23
23
  plugins: NonNullable<BuildOptions['plugins']>;
24
24
  };
25
25
  /**
26
- * Bundle a stylesheet that exists as a file on the filesystem.
26
+ * Bundles a component stylesheet. The stylesheet can be either an inline stylesheet that
27
+ * is contained within the Component's metadata definition or an external file referenced
28
+ * from the Component's metadata definition.
27
29
  *
28
- * @param filename The path to the file to bundle.
29
- * @param options The stylesheet bundling options to use.
30
- * @returns The bundle result object.
30
+ * @param identifier A unique string identifier for the component stylesheet.
31
+ * @param language The language of the stylesheet such as `css` or `scss`.
32
+ * @param data The string content of the stylesheet.
33
+ * @param filename The filename representing the source of the stylesheet content.
34
+ * @param inline If true, the stylesheet source is within the component metadata;
35
+ * if false, the source is a stylesheet file.
36
+ * @param options An object containing the stylesheet bundling options.
37
+ * @returns An object containing the output of the bundling operation.
31
38
  */
32
- export declare function bundleStylesheetFile(filename: string, options: BundleStylesheetOptions): Promise<{
33
- errors: import("esbuild").Message[];
34
- warnings: import("esbuild").Message[];
35
- contents: string;
36
- map: string | undefined;
37
- path: string | undefined;
38
- resourceFiles: OutputFile[];
39
- }>;
40
- /**
41
- * Bundle stylesheet text data from a string.
42
- *
43
- * @param data The string content of a stylesheet to bundle.
44
- * @param dataOptions The options to use to resolve references and name output of the stylesheet data.
45
- * @param bundleOptions The stylesheet bundling options to use.
46
- * @returns The bundle result object.
47
- */
48
- export declare function bundleStylesheetText(data: string, dataOptions: {
49
- resolvePath: string;
50
- virtualName?: string;
51
- }, bundleOptions: BundleStylesheetOptions): Promise<{
39
+ export declare function bundleComponentStylesheet(identifier: string, language: string, data: string, filename: string, inline: boolean, options: BundleStylesheetOptions): Promise<{
52
40
  errors: import("esbuild").Message[];
53
41
  warnings: import("esbuild").Message[];
54
42
  contents: string;
@@ -30,12 +30,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
30
30
  return result;
31
31
  };
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
- exports.bundleStylesheetText = exports.bundleStylesheetFile = exports.createStylesheetBundleOptions = void 0;
34
- const path = __importStar(require("path"));
33
+ exports.bundleComponentStylesheet = exports.createStylesheetBundleOptions = void 0;
34
+ const path = __importStar(require("node:path"));
35
35
  const css_resource_plugin_1 = require("./css-resource-plugin");
36
36
  const esbuild_1 = require("./esbuild");
37
37
  const sass_plugin_1 = require("./sass-plugin");
38
- function createStylesheetBundleOptions(options) {
38
+ function createStylesheetBundleOptions(options, inlineComponentData) {
39
39
  var _a, _b;
40
40
  return {
41
41
  absWorkingDir: options.workspaceRoot,
@@ -54,18 +54,65 @@ function createStylesheetBundleOptions(options) {
54
54
  conditions: ['style', 'sass'],
55
55
  mainFields: ['style', 'sass'],
56
56
  plugins: [
57
- (0, sass_plugin_1.createSassPlugin)({ sourcemap: !!options.sourcemap, loadPaths: options.includePaths }),
57
+ (0, sass_plugin_1.createSassPlugin)({
58
+ sourcemap: !!options.sourcemap,
59
+ loadPaths: options.includePaths,
60
+ inlineComponentData,
61
+ }),
58
62
  (0, css_resource_plugin_1.createCssResourcePlugin)(),
59
63
  ],
60
64
  };
61
65
  }
62
66
  exports.createStylesheetBundleOptions = createStylesheetBundleOptions;
63
- async function bundleStylesheet(entry, options) {
64
- // Execute esbuild
65
- const result = await (0, esbuild_1.bundle)(options.workspaceRoot, {
66
- ...createStylesheetBundleOptions(options),
67
- ...entry,
67
+ /**
68
+ * Bundles a component stylesheet. The stylesheet can be either an inline stylesheet that
69
+ * is contained within the Component's metadata definition or an external file referenced
70
+ * from the Component's metadata definition.
71
+ *
72
+ * @param identifier A unique string identifier for the component stylesheet.
73
+ * @param language The language of the stylesheet such as `css` or `scss`.
74
+ * @param data The string content of the stylesheet.
75
+ * @param filename The filename representing the source of the stylesheet content.
76
+ * @param inline If true, the stylesheet source is within the component metadata;
77
+ * if false, the source is a stylesheet file.
78
+ * @param options An object containing the stylesheet bundling options.
79
+ * @returns An object containing the output of the bundling operation.
80
+ */
81
+ async function bundleComponentStylesheet(identifier, language, data, filename, inline, options) {
82
+ const namespace = 'angular:styles/component';
83
+ const entry = [namespace, language, identifier, filename].join(';');
84
+ const buildOptions = createStylesheetBundleOptions(options, { [entry]: data });
85
+ buildOptions.entryPoints = [entry];
86
+ buildOptions.plugins.push({
87
+ name: 'angular-component-styles',
88
+ setup(build) {
89
+ build.onResolve({ filter: /^angular:styles\/component;/ }, (args) => {
90
+ if (args.kind !== 'entry-point') {
91
+ return null;
92
+ }
93
+ if (inline) {
94
+ return {
95
+ path: args.path,
96
+ namespace,
97
+ };
98
+ }
99
+ else {
100
+ return {
101
+ path: filename,
102
+ };
103
+ }
104
+ });
105
+ build.onLoad({ filter: /^angular:styles\/component;css;/, namespace }, async () => {
106
+ return {
107
+ contents: data,
108
+ loader: 'css',
109
+ resolveDir: path.dirname(filename),
110
+ };
111
+ });
112
+ },
68
113
  });
114
+ // Execute esbuild
115
+ const result = await (0, esbuild_1.bundle)(options.workspaceRoot, buildOptions);
69
116
  // Extract the result of the bundling from the output files
70
117
  let contents = '';
71
118
  let map;
@@ -96,34 +143,4 @@ async function bundleStylesheet(entry, options) {
96
143
  resourceFiles,
97
144
  };
98
145
  }
99
- /**
100
- * Bundle a stylesheet that exists as a file on the filesystem.
101
- *
102
- * @param filename The path to the file to bundle.
103
- * @param options The stylesheet bundling options to use.
104
- * @returns The bundle result object.
105
- */
106
- async function bundleStylesheetFile(filename, options) {
107
- return bundleStylesheet({ entryPoints: [filename] }, options);
108
- }
109
- exports.bundleStylesheetFile = bundleStylesheetFile;
110
- /**
111
- * Bundle stylesheet text data from a string.
112
- *
113
- * @param data The string content of a stylesheet to bundle.
114
- * @param dataOptions The options to use to resolve references and name output of the stylesheet data.
115
- * @param bundleOptions The stylesheet bundling options to use.
116
- * @returns The bundle result object.
117
- */
118
- async function bundleStylesheetText(data, dataOptions, bundleOptions) {
119
- const result = bundleStylesheet({
120
- stdin: {
121
- contents: data,
122
- sourcefile: dataOptions.virtualName,
123
- resolveDir: dataOptions.resolvePath,
124
- loader: 'css',
125
- },
126
- }, bundleOptions);
127
- return result;
128
- }
129
- exports.bundleStylesheetText = bundleStylesheetText;
146
+ exports.bundleComponentStylesheet = bundleComponentStylesheet;
@@ -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
+ // import type ng from '@angular/compiler-cli';
10
+ // import { Worker } from 'node:worker_threads';
11
+ // import { AngularHostOptions } from './angular-host';
12
+ // interface Message {
13
+ // id: number;
14
+ // }
15
+ // interface InitializeRequest extends Message {
16
+ // tsconfig: string;
17
+ // tsCallbackId: number;
18
+ // tcoCallbackId?: number;
19
+ // }
20
+ // // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ // type ResponseCallback = (...args: any[]) => void;
22
+ // let callbackIds = 0;
23
+ // export class WorkerAngularCompilation {
24
+ // #worker: Worker;
25
+ // #callbacks = new Map<number, ResponseCallback>();
26
+ // constructor() {
27
+ // this.#worker = new Worker(require.resolve('./compilation-worker'));
28
+ // }
29
+ // async initialize(
30
+ // tsconfig: string,
31
+ // hostOptions: AngularHostOptions,
32
+ // transformCompilerOptions?: (compilerOptions: ng.CompilerOptions) => ng.CompilerOptions,
33
+ // ): Promise<{ compilerOptions: ng.CompilerOptions }> {
34
+ // const request: InitializeRequest = {
35
+ // id: ++callbackIds,
36
+ // tsconfig,
37
+ // tsCallbackId: ++callbackIds,
38
+ // };
39
+ // this.#callbacks.set(request.tsCallbackId, () => {
40
+ // hostOptions.transformStylesheet();
41
+ // });
42
+ // if (transformCompilerOptions) {
43
+ // request.tcoCallbackId = ++callbackIds;
44
+ // this.#callbacks.set(request.tcoCallbackId, (id: number, compilerOptions) => {
45
+ // try {
46
+ // transformCompilerOptions(compilerOptions);
47
+ // } catch (e) {
48
+ // }
49
+ // });
50
+ // }
51
+ // const result = new Promise<{ compilerOptions: ng.CompilerOptions }>((resolve, reject) => {
52
+ // this.#callbacks.set(request.id, () => {
53
+ // resolve({});
54
+ // });
55
+ // });
56
+ // this.#worker.postMessage(request);
57
+ // return result;
58
+ // }
59
+ // }
@@ -149,7 +149,9 @@ function execute(options, context, transforms = {}) {
149
149
  });
150
150
  const karmaStart = karmaServer.start();
151
151
  // Cleanup, signal Karma to exit.
152
- return () => karmaStart.then(() => karmaServer.stop());
152
+ return () => {
153
+ void karmaStart.then(() => karmaServer.stop());
154
+ };
153
155
  })), (0, operators_1.defaultIfEmpty)({ success: false }));
154
156
  }
155
157
  exports.execute = execute;
@@ -15,9 +15,18 @@ exports.transformSupportedBrowsersToTargets = void 0;
15
15
  function transformSupportedBrowsersToTargets(supportedBrowsers) {
16
16
  const transformed = [];
17
17
  // https://esbuild.github.io/api/#target
18
- const esBuildSupportedBrowsers = new Set(['safari', 'firefox', 'edge', 'chrome', 'ios', 'node']);
18
+ const esBuildSupportedBrowsers = new Set([
19
+ 'chrome',
20
+ 'edge',
21
+ 'firefox',
22
+ 'ie',
23
+ 'ios',
24
+ 'node',
25
+ 'opera',
26
+ 'safari',
27
+ ]);
19
28
  for (const browser of supportedBrowsers) {
20
- let [browserName, version] = browser.split(' ');
29
+ let [browserName, version] = browser.toLowerCase().split(' ');
21
30
  // browserslist uses the name `ios_saf` for iOS Safari whereas esbuild uses `ios`
22
31
  if (browserName === 'ios_saf') {
23
32
  browserName = 'ios';
@@ -31,6 +40,12 @@ function transformSupportedBrowsersToTargets(supportedBrowsers) {
31
40
  // a Technology Preview (TP) of Safari is assumed to support all currently known features.
32
41
  version = '999';
33
42
  }
43
+ else if (!version.includes('.')) {
44
+ // A lone major version is considered by esbuild to include all minor versions. However,
45
+ // browserslist does not and is also inconsistent in its `.0` version naming. For example,
46
+ // Safari 15.0 is named `safari 15` but Safari 16.0 is named `safari 16.0`.
47
+ version += '.0';
48
+ }
34
49
  transformed.push(browserName + version);
35
50
  }
36
51
  }
@@ -52,7 +52,7 @@ function emittedFilesToInlineOptions(emittedFiles, scriptsEntryPointName, emitte
52
52
  code: fs.readFileSync(originalPath, 'utf8'),
53
53
  outputPath,
54
54
  missingTranslation,
55
- setLocale: emittedFile.name === 'main' || emittedFile.name === 'vendor',
55
+ setLocale: emittedFile.name === 'main',
56
56
  };
57
57
  originalFiles.push(originalPath);
58
58
  try {
@@ -125,7 +125,7 @@ async function augmentIndexHtml(params) {
125
125
  }
126
126
  rewriter.emitEndTag(tag);
127
127
  });
128
- const content = await transformedContent;
128
+ const content = await transformedContent();
129
129
  return {
130
130
  content: linkTags.length || scriptTags.length
131
131
  ? // In case no body/head tags are not present (dotnet partial templates)
@@ -7,5 +7,5 @@
7
7
  */
8
8
  export declare function htmlRewritingStream(content: string): Promise<{
9
9
  rewriter: import('parse5-html-rewriting-stream');
10
- transformedContent: Promise<string>;
10
+ transformedContent: () => Promise<string>;
11
11
  }>;
@@ -37,26 +37,30 @@ async function htmlRewritingStream(content) {
37
37
  const rewriter = new (await Promise.resolve().then(() => __importStar(require('parse5-html-rewriting-stream')))).default();
38
38
  return {
39
39
  rewriter,
40
- transformedContent: new Promise((resolve) => {
41
- new stream_1.Readable({
42
- encoding: 'utf8',
43
- read() {
44
- this.push(Buffer.from(content));
45
- this.push(null);
46
- },
47
- })
48
- .pipe(rewriter)
49
- .pipe(new stream_1.Writable({
50
- write(chunk, encoding, callback) {
51
- chunks.push(typeof chunk === 'string' ? Buffer.from(chunk, encoding) : chunk);
52
- callback();
53
- },
54
- final(callback) {
55
- callback();
56
- resolve(Buffer.concat(chunks).toString());
57
- },
58
- }));
59
- }),
40
+ transformedContent: () => {
41
+ return new Promise((resolve) => {
42
+ new stream_1.Readable({
43
+ encoding: 'utf8',
44
+ read() {
45
+ this.push(Buffer.from(content));
46
+ this.push(null);
47
+ },
48
+ })
49
+ .pipe(rewriter)
50
+ .pipe(new stream_1.Writable({
51
+ write(chunk, encoding, callback) {
52
+ chunks.push(typeof chunk === 'string'
53
+ ? Buffer.from(chunk, encoding)
54
+ : chunk);
55
+ callback();
56
+ },
57
+ final(callback) {
58
+ callback();
59
+ resolve(Buffer.concat(chunks).toString());
60
+ },
61
+ }));
62
+ });
63
+ },
60
64
  };
61
65
  }
62
66
  exports.htmlRewritingStream = htmlRewritingStream;
@@ -63,7 +63,7 @@ class InlineFontsProcessor {
63
63
  const hrefList = [];
64
64
  const existingPreconnect = new Set();
65
65
  // Collector link tags with href
66
- const { rewriter: collectorStream } = await (0, html_rewriting_stream_1.htmlRewritingStream)(content);
66
+ const { rewriter: collectorStream, transformedContent: initCollectorStream } = await (0, html_rewriting_stream_1.htmlRewritingStream)(content);
67
67
  collectorStream.on('startTag', (tag) => {
68
68
  const { tagName, attrs } = tag;
69
69
  if (tagName !== 'link') {
@@ -95,6 +95,10 @@ class InlineFontsProcessor {
95
95
  }
96
96
  }
97
97
  });
98
+ initCollectorStream().catch(() => {
99
+ // We don't really care about any errors here because it just initializes
100
+ // the rewriting stream, as we are waiting for `finish` below.
101
+ });
98
102
  await new Promise((resolve) => collectorStream.on('finish', resolve));
99
103
  // Download stylesheets
100
104
  const hrefsContent = new Map();
@@ -146,7 +150,7 @@ class InlineFontsProcessor {
146
150
  break;
147
151
  }
148
152
  });
149
- return transformedContent;
153
+ return transformedContent();
150
154
  }
151
155
  async getResponse(url) {
152
156
  var _a;
@@ -211,7 +211,7 @@ async function inlineLocalesDirect(ast, options) {
211
211
  }
212
212
  let outputSource = content;
213
213
  if (options.setLocale) {
214
- const setLocaleText = `var $localize=Object.assign(void 0===$localize?{}:$localize,{locale:"${locale}"});\n`;
214
+ const setLocaleText = `globalThis.$localize=Object.assign(globalThis.$localize || {},{locale:"${locale}"});\n`;
215
215
  // If locale data is provided, load it and prepend to file
216
216
  let localeDataSource;
217
217
  const localeDataPath = i18n.locales[locale] && i18n.locales[locale].dataPath;
@@ -320,6 +320,7 @@ function getSassLoaderOptions(root, implementation, includePaths, indentedSyntax
320
320
  quietDeps: !verbose,
321
321
  verbose,
322
322
  syntax: indentedSyntax ? 'indented' : 'scss',
323
+ sourceMapIncludeSources: true,
323
324
  }),
324
325
  }
325
326
  : {
@@ -90,8 +90,9 @@ async function optimizeWithTerser(name, code, sourcemaps, advanced) {
90
90
  passes: advanced ? 2 : 1,
91
91
  pure_getters: advanced,
92
92
  },
93
- // terser only supports up to ES2020
94
- ecma: 2020,
93
+ // Set to ES2015 to prevent higher level features from being introduced when browserslist
94
+ // contains older browsers. The build system requires browsers to support ES2015 at a minimum.
95
+ ecma: 2015,
95
96
  // esbuild in the first pass is used to minify identifiers instead of mangle here
96
97
  mangle: false,
97
98
  // esbuild in the first pass is used to minify function names
@@ -24,8 +24,6 @@ class StylesWebpackPlugin {
24
24
  }
25
25
  apply(compiler) {
26
26
  const { entryPoints, preserveSymlinks, root } = this.options;
27
- const webpackOptions = compiler.options;
28
- const entry = typeof webpackOptions.entry === 'function' ? webpackOptions.entry() : webpackOptions.entry;
29
27
  const resolver = compiler.resolverFactory.get('global-styles', {
30
28
  conditionNames: ['sass', 'less', 'style'],
31
29
  mainFields: ['sass', 'less', 'style', 'main', '...'],
@@ -36,33 +34,37 @@ class StylesWebpackPlugin {
36
34
  symlinks: !preserveSymlinks,
37
35
  fileSystem: compiler.inputFileSystem,
38
36
  });
39
- webpackOptions.entry = async () => {
40
- var _a, _b;
41
- var _c;
42
- const entrypoints = await entry;
43
- for (const [bundleName, paths] of Object.entries(entryPoints)) {
44
- (_a = entrypoints[bundleName]) !== null && _a !== void 0 ? _a : (entrypoints[bundleName] = {});
45
- const entryImport = ((_b = (_c = entrypoints[bundleName]).import) !== null && _b !== void 0 ? _b : (_c.import = []));
46
- for (const path of paths) {
47
- try {
48
- const resolvedPath = resolver.resolveSync({}, root, path);
49
- if (resolvedPath) {
50
- entryImport.push(`${resolvedPath}?ngGlobalStyle`);
37
+ const webpackOptions = compiler.options;
38
+ compiler.hooks.environment.tap(PLUGIN_NAME, () => {
39
+ const entry = typeof webpackOptions.entry === 'function' ? webpackOptions.entry() : webpackOptions.entry;
40
+ webpackOptions.entry = async () => {
41
+ var _a, _b;
42
+ var _c;
43
+ const entrypoints = await entry;
44
+ for (const [bundleName, paths] of Object.entries(entryPoints)) {
45
+ (_a = entrypoints[bundleName]) !== null && _a !== void 0 ? _a : (entrypoints[bundleName] = {});
46
+ const entryImport = ((_b = (_c = entrypoints[bundleName]).import) !== null && _b !== void 0 ? _b : (_c.import = []));
47
+ for (const path of paths) {
48
+ try {
49
+ const resolvedPath = resolver.resolveSync({}, root, path);
50
+ if (resolvedPath) {
51
+ entryImport.push(`${resolvedPath}?ngGlobalStyle`);
52
+ }
53
+ else {
54
+ (0, assert_1.default)(this.compilation, 'Compilation cannot be undefined.');
55
+ (0, webpack_diagnostics_1.addError)(this.compilation, `Cannot resolve '${path}'.`);
56
+ }
51
57
  }
52
- else {
58
+ catch (error) {
53
59
  (0, assert_1.default)(this.compilation, 'Compilation cannot be undefined.');
54
- (0, webpack_diagnostics_1.addError)(this.compilation, `Cannot resolve '${path}'.`);
60
+ (0, error_1.assertIsError)(error);
61
+ (0, webpack_diagnostics_1.addError)(this.compilation, error.message);
55
62
  }
56
63
  }
57
- catch (error) {
58
- (0, assert_1.default)(this.compilation, 'Compilation cannot be undefined.');
59
- (0, error_1.assertIsError)(error);
60
- (0, webpack_diagnostics_1.addError)(this.compilation, error.message);
61
- }
62
64
  }
63
- }
64
- return entrypoints;
65
- };
65
+ return entrypoints;
66
+ };
67
+ });
66
68
  compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
67
69
  this.compilation = compilation;
68
70
  });