@angular-devkit/build-angular 15.0.0-rc.1 → 15.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "15.0.0-rc.1",
3
+ "version": "15.0.0-rc.2",
4
4
  "description": "Angular Webpack Build Facade",
5
5
  "main": "src/index.js",
6
6
  "typings": "src/index.d.ts",
7
7
  "builders": "builders.json",
8
8
  "dependencies": {
9
9
  "@ampproject/remapping": "2.2.0",
10
- "@angular-devkit/architect": "0.1500.0-rc.1",
11
- "@angular-devkit/build-webpack": "0.1500.0-rc.1",
12
- "@angular-devkit/core": "15.0.0-rc.1",
10
+ "@angular-devkit/architect": "0.1500.0-rc.2",
11
+ "@angular-devkit/build-webpack": "0.1500.0-rc.2",
12
+ "@angular-devkit/core": "15.0.0-rc.2",
13
13
  "@babel/core": "7.19.6",
14
- "@babel/generator": "7.19.6",
14
+ "@babel/generator": "7.20.1",
15
15
  "@babel/helper-annotate-as-pure": "7.18.6",
16
- "@babel/plugin-proposal-async-generator-functions": "7.19.1",
16
+ "@babel/plugin-proposal-async-generator-functions": "7.20.1",
17
17
  "@babel/plugin-transform-async-to-generator": "7.18.6",
18
18
  "@babel/plugin-transform-runtime": "7.19.6",
19
19
  "@babel/preset-env": "7.19.4",
20
- "@babel/runtime": "7.19.4",
20
+ "@babel/runtime": "7.20.1",
21
21
  "@babel/template": "7.18.10",
22
22
  "@discoveryjs/json-ext": "0.5.7",
23
- "@ngtools/webpack": "15.0.0-rc.1",
23
+ "@ngtools/webpack": "15.0.0-rc.2",
24
24
  "ansi-colors": "4.1.3",
25
- "autoprefixer": "10.4.12",
26
- "babel-loader": "8.2.5",
25
+ "autoprefixer": "10.4.13",
26
+ "babel-loader": "9.0.1",
27
27
  "babel-plugin-istanbul": "6.1.1",
28
28
  "browserslist": "^4.9.1",
29
29
  "cacache": "17.0.1",
@@ -60,7 +60,7 @@
60
60
  "terser": "5.15.1",
61
61
  "text-table": "0.2.0",
62
62
  "tree-kill": "1.2.2",
63
- "tslib": "2.4.0",
63
+ "tslib": "2.4.1",
64
64
  "webpack": "5.74.0",
65
65
  "webpack-dev-middleware": "5.3.3",
66
66
  "webpack-dev-server": "4.11.1",
@@ -188,8 +188,8 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
188
188
  location: { file: pluginOptions.tsconfig },
189
189
  notes: [
190
190
  {
191
- text: `To control ECMA version and features use the Browerslist configuration. ' +
192
- 'For more information, see https://angular.io/guide/build#configuring-browser-compatibility'`,
191
+ text: 'To control ECMA version and features use the Browerslist configuration. ' +
192
+ 'For more information, see https://angular.io/guide/build#configuring-browser-compatibility',
193
193
  },
194
194
  ],
195
195
  });
@@ -46,6 +46,7 @@ const compiler_plugin_1 = require("./compiler-plugin");
46
46
  const esbuild_1 = require("./esbuild");
47
47
  const experimental_warnings_1 = require("./experimental-warnings");
48
48
  const options_1 = require("./options");
49
+ const sass_plugin_1 = require("./sass-plugin");
49
50
  const stylesheets_1 = require("./stylesheets");
50
51
  const watcher_1 = require("./watcher");
51
52
  /**
@@ -208,6 +209,11 @@ function createCodeBundleOptions(options, target, sourceFileCache) {
208
209
  // loader to perform the downlevel transformation.
209
210
  // NOTE: If esbuild adds support in the future, the babel support for async generators can be disabled.
210
211
  'async-await': false,
212
+ // V8 currently has a performance defect involving object spread operations that can cause signficant
213
+ // degradation in runtime performance. By not supporting the language feature here, a downlevel form
214
+ // will be used instead which provides a workaround for the performance issue.
215
+ // For more details: https://bugs.chromium.org/p/v8/issues/detail?id=11536
216
+ 'object-rest-spread': false,
211
217
  },
212
218
  mainFields: ['es2020', 'browser', 'module', 'main'],
213
219
  conditions: ['es2020', 'es2015', 'module'],
@@ -345,6 +351,7 @@ async function* buildEsbuildBrowser(initialOptions, context) {
345
351
  yield result.output;
346
352
  // Finish if watch mode is not enabled
347
353
  if (!initialOptions.watch) {
354
+ (0, sass_plugin_1.shutdownSassWorkerPool)();
348
355
  return;
349
356
  }
350
357
  context.logger.info('Watch mode enabled. Watching for file changes...');
@@ -378,6 +385,7 @@ async function* buildEsbuildBrowser(initialOptions, context) {
378
385
  await watcher.close();
379
386
  // Cleanup incremental rebuild state
380
387
  result.dispose();
388
+ (0, sass_plugin_1.shutdownSassWorkerPool)();
381
389
  }
382
390
  }
383
391
  exports.buildEsbuildBrowser = buildEsbuildBrowser;
@@ -6,6 +6,7 @@
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;
9
10
  export declare function createSassPlugin(options: {
10
11
  sourcemap: boolean;
11
12
  loadPaths?: string[];
@@ -6,54 +6,49 @@
6
6
  * Use of this source code is governed by an MIT-style license that can be
7
7
  * found in the LICENSE file at https://angular.io/license
8
8
  */
9
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- var desc = Object.getOwnPropertyDescriptor(m, k);
12
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
- desc = { enumerable: true, get: function() { return m[k]; } };
14
- }
15
- Object.defineProperty(o, k2, desc);
16
- }) : (function(o, m, k, k2) {
17
- if (k2 === undefined) k2 = k;
18
- o[k2] = m[k];
19
- }));
20
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
- Object.defineProperty(o, "default", { enumerable: true, value: v });
22
- }) : function(o, v) {
23
- o["default"] = v;
24
- });
25
- var __importStar = (this && this.__importStar) || function (mod) {
26
- if (mod && mod.__esModule) return mod;
27
- var result = {};
28
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29
- __setModuleDefault(result, mod);
30
- return result;
31
- };
32
9
  Object.defineProperty(exports, "__esModule", { value: true });
33
- exports.createSassPlugin = void 0;
10
+ exports.createSassPlugin = exports.shutdownSassWorkerPool = void 0;
11
+ const promises_1 = require("node:fs/promises");
34
12
  const node_path_1 = require("node:path");
35
13
  const node_url_1 = require("node:url");
14
+ const sass_service_1 = require("../../sass/sass-service");
15
+ let sassWorkerPool;
16
+ function isSassException(error) {
17
+ return !!error && typeof error === 'object' && 'sassMessage' in error;
18
+ }
19
+ function shutdownSassWorkerPool() {
20
+ sassWorkerPool === null || sassWorkerPool === void 0 ? void 0 : sassWorkerPool.close();
21
+ }
22
+ exports.shutdownSassWorkerPool = shutdownSassWorkerPool;
36
23
  function createSassPlugin(options) {
37
24
  return {
38
25
  name: 'angular-sass',
39
26
  setup(build) {
40
- let sass;
41
27
  build.onLoad({ filter: /\.s[ac]ss$/ }, async (args) => {
42
28
  // Lazily load Sass when a Sass file is found
43
- sass !== null && sass !== void 0 ? sass : (sass = await Promise.resolve().then(() => __importStar(require('sass'))));
29
+ sassWorkerPool !== null && sassWorkerPool !== void 0 ? sassWorkerPool : (sassWorkerPool = new sass_service_1.SassWorkerImplementation());
30
+ const warnings = [];
44
31
  try {
45
- const warnings = [];
46
- // Use sync version as async version is slower.
47
- const { css, sourceMap, loadedUrls } = sass.compile(args.path, {
32
+ const data = await (0, promises_1.readFile)(args.path, 'utf-8');
33
+ const { css, sourceMap, loadedUrls } = await sassWorkerPool.compileStringAsync(data, {
34
+ url: (0, node_url_1.pathToFileURL)(args.path),
48
35
  style: 'expanded',
49
36
  loadPaths: options.loadPaths,
50
37
  sourceMap: options.sourcemap,
51
38
  sourceMapIncludeSources: options.sourcemap,
52
39
  quietDeps: true,
53
40
  logger: {
54
- warn: (text, _options) => {
41
+ warn: (text, { deprecation, span }) => {
55
42
  warnings.push({
56
- text,
43
+ text: deprecation ? 'Deprecation' : text,
44
+ location: span && {
45
+ file: span.url && (0, node_url_1.fileURLToPath)(span.url),
46
+ lineText: span.context,
47
+ // Sass line numbers are 0-based while esbuild's are 1-based
48
+ line: span.start.line + 1,
49
+ column: span.start.column,
50
+ },
51
+ notes: deprecation ? [{ text }] : undefined,
57
52
  });
58
53
  },
59
54
  },
@@ -68,15 +63,16 @@ function createSassPlugin(options) {
68
63
  };
69
64
  }
70
65
  catch (error) {
71
- if (error instanceof sass.Exception) {
66
+ if (isSassException(error)) {
72
67
  const file = error.span.url ? (0, node_url_1.fileURLToPath)(error.span.url) : undefined;
73
68
  return {
74
69
  loader: 'css',
75
70
  errors: [
76
71
  {
77
- text: error.toString(),
72
+ text: error.message,
78
73
  },
79
74
  ],
75
+ warnings,
80
76
  watchFiles: file ? [file] : undefined,
81
77
  };
82
78
  }
@@ -33,6 +33,7 @@ function createWatcher(options) {
33
33
  });
34
34
  const nextQueue = [];
35
35
  let currentChanges;
36
+ let nextWaitTimeout;
36
37
  watcher.on('all', (event, path) => {
37
38
  switch (event) {
38
39
  case 'add':
@@ -50,11 +51,18 @@ function createWatcher(options) {
50
51
  default:
51
52
  return;
52
53
  }
53
- const next = nextQueue.shift();
54
- if (next) {
55
- const value = currentChanges;
56
- currentChanges = undefined;
57
- next(value);
54
+ // Wait 250ms from next change to better capture groups of file save operations.
55
+ if (!nextWaitTimeout) {
56
+ nextWaitTimeout = setTimeout(() => {
57
+ nextWaitTimeout = undefined;
58
+ const next = nextQueue.shift();
59
+ if (next) {
60
+ const value = currentChanges;
61
+ currentChanges = undefined;
62
+ next(value);
63
+ }
64
+ }, 250);
65
+ nextWaitTimeout === null || nextWaitTimeout === void 0 ? void 0 : nextWaitTimeout.unref();
58
66
  }
59
67
  });
60
68
  return {
@@ -80,6 +88,9 @@ function createWatcher(options) {
80
88
  async close() {
81
89
  try {
82
90
  await watcher.close();
91
+ if (nextWaitTimeout) {
92
+ clearTimeout(nextWaitTimeout);
93
+ }
83
94
  }
84
95
  finally {
85
96
  let next;
@@ -87,12 +87,13 @@ class SassWorkerImplementation {
87
87
  }
88
88
  resolve(result);
89
89
  };
90
- const request = this.createRequest(workerIndex, callback, importers);
90
+ const request = this.createRequest(workerIndex, callback, logger, importers);
91
91
  this.requests.set(request.id, request);
92
92
  this.workers[workerIndex].postMessage({
93
93
  id: request.id,
94
94
  source,
95
95
  hasImporter: !!(importers === null || importers === void 0 ? void 0 : importers.length),
96
+ hasLogger: !!logger,
96
97
  options: {
97
98
  ...serializableOptions,
98
99
  // URL is not serializable so to convert to string here and back to URL in the worker.
@@ -122,12 +123,24 @@ class SassWorkerImplementation {
122
123
  transferList: [workerImporterPort],
123
124
  });
124
125
  worker.on('message', (response) => {
126
+ var _a;
125
127
  const request = this.requests.get(response.id);
126
128
  if (!request) {
127
129
  return;
128
130
  }
129
131
  this.requests.delete(response.id);
130
132
  this.availableWorkers.push(request.workerIndex);
133
+ if (response.warnings && ((_a = request.logger) === null || _a === void 0 ? void 0 : _a.warn)) {
134
+ for (const { message, span, ...options } of response.warnings) {
135
+ request.logger.warn(message, {
136
+ ...options,
137
+ span: span && {
138
+ ...span,
139
+ url: span.url ? (0, node_url_1.pathToFileURL)(span.url) : undefined,
140
+ },
141
+ });
142
+ }
143
+ }
131
144
  if (response.result) {
132
145
  request.callback(undefined, {
133
146
  ...response.result,
@@ -184,11 +197,12 @@ class SassWorkerImplementation {
184
197
  }
185
198
  return null;
186
199
  }
187
- createRequest(workerIndex, callback, importers) {
200
+ createRequest(workerIndex, callback, logger, importers) {
188
201
  return {
189
202
  id: this.idCounter++,
190
203
  workerIndex,
191
204
  callback,
205
+ logger,
192
206
  importers,
193
207
  };
194
208
  }
@@ -15,10 +15,12 @@ if (!worker_threads_1.parentPort || !worker_threads_1.workerData) {
15
15
  }
16
16
  // The importer variables are used to proxy import requests to the main thread
17
17
  const { workerImporterPort, importerSignal } = worker_threads_1.workerData;
18
- worker_threads_1.parentPort.on('message', ({ id, hasImporter, source, options }) => {
18
+ worker_threads_1.parentPort.on('message', (message) => {
19
19
  if (!worker_threads_1.parentPort) {
20
20
  throw new Error('"parentPort" is not defined. Sass worker must be executed as a Worker.');
21
21
  }
22
+ const { id, hasImporter, hasLogger, source, options } = message;
23
+ let warnings;
22
24
  try {
23
25
  if (hasImporter) {
24
26
  // When a custom importer function is present, the importer request must be proxied
@@ -44,9 +46,23 @@ worker_threads_1.parentPort.on('message', ({ id, hasImporter, source, options })
44
46
  ...options,
45
47
  // URL is not serializable so to convert to string in the parent and back to URL here.
46
48
  url: options.url ? (0, url_1.pathToFileURL)(options.url) : undefined,
49
+ logger: hasLogger
50
+ ? {
51
+ warn(message, { deprecation, span, stack }) {
52
+ warnings !== null && warnings !== void 0 ? warnings : (warnings = []);
53
+ warnings.push({
54
+ message,
55
+ deprecation,
56
+ stack,
57
+ span: span && convertSourceSpan(span),
58
+ });
59
+ },
60
+ }
61
+ : undefined,
47
62
  });
48
63
  worker_threads_1.parentPort.postMessage({
49
64
  id,
65
+ warnings,
50
66
  result: {
51
67
  ...result,
52
68
  // URL is not serializable so to convert to string here and back to URL in the parent.
@@ -60,22 +76,9 @@ worker_threads_1.parentPort.on('message', ({ id, hasImporter, source, options })
60
76
  const { span, message, stack, sassMessage, sassStack } = error;
61
77
  worker_threads_1.parentPort.postMessage({
62
78
  id,
79
+ warnings,
63
80
  error: {
64
- span: {
65
- text: span.text,
66
- context: span.context,
67
- end: {
68
- column: span.end.column,
69
- offset: span.end.offset,
70
- line: span.end.line,
71
- },
72
- start: {
73
- column: span.start.column,
74
- offset: span.start.offset,
75
- line: span.start.line,
76
- },
77
- url: span.url ? (0, url_1.fileURLToPath)(span.url) : undefined,
78
- },
81
+ span: convertSourceSpan(span),
79
82
  message,
80
83
  stack,
81
84
  sassMessage,
@@ -85,10 +88,40 @@ worker_threads_1.parentPort.on('message', ({ id, hasImporter, source, options })
85
88
  }
86
89
  else if (error instanceof Error) {
87
90
  const { message, stack } = error;
88
- worker_threads_1.parentPort.postMessage({ id, error: { message, stack } });
91
+ worker_threads_1.parentPort.postMessage({ id, warnings, error: { message, stack } });
89
92
  }
90
93
  else {
91
- worker_threads_1.parentPort.postMessage({ id, error: { message: 'An unknown error has occurred.' } });
94
+ worker_threads_1.parentPort.postMessage({
95
+ id,
96
+ warnings,
97
+ error: { message: 'An unknown error has occurred.' },
98
+ });
92
99
  }
93
100
  }
94
101
  });
102
+ /**
103
+ * Converts a Sass SourceSpan object into a serializable form.
104
+ * The SourceSpan object contains a URL property which must be converted into a string.
105
+ * Also, most of the interface's properties are get accessors and are not automatically
106
+ * serialized when sent back from the worker.
107
+ *
108
+ * @param span The Sass SourceSpan object to convert.
109
+ * @returns A serializable form of the SourceSpan object.
110
+ */
111
+ function convertSourceSpan(span) {
112
+ return {
113
+ text: span.text,
114
+ context: span.context,
115
+ end: {
116
+ column: span.end.column,
117
+ offset: span.end.offset,
118
+ line: span.end.line,
119
+ },
120
+ start: {
121
+ column: span.start.column,
122
+ offset: span.start.offset,
123
+ line: span.start.line,
124
+ },
125
+ url: span.url ? (0, url_1.fileURLToPath)(span.url) : undefined,
126
+ };
127
+ }