@angular-devkit/build-angular 15.0.0-rc.0 → 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 +16 -16
- package/src/builders/browser-esbuild/compiler-plugin.d.ts +1 -0
- package/src/builders/browser-esbuild/compiler-plugin.js +58 -38
- package/src/builders/browser-esbuild/index.js +10 -2
- package/src/builders/browser-esbuild/options.d.ts +1 -0
- package/src/builders/browser-esbuild/options.js +3 -1
- package/src/builders/browser-esbuild/profiling.js +19 -4
- package/src/builders/browser-esbuild/sass-plugin.d.ts +1 -0
- package/src/builders/browser-esbuild/sass-plugin.js +29 -33
- package/src/builders/browser-esbuild/watcher.js +16 -5
- package/src/builders/dev-server/index.js +6 -0
- package/src/sass/sass-service.js +16 -2
- package/src/sass/worker.js +51 -18
- package/src/webpack/plugins/typescript.js +1 -1
package/package.json
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-devkit/build-angular",
|
|
3
|
-
"version": "15.0.0-rc.
|
|
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.
|
|
11
|
-
"@angular-devkit/build-webpack": "0.1500.0-rc.
|
|
12
|
-
"@angular-devkit/core": "15.0.0-rc.
|
|
13
|
-
"@babel/core": "7.19.
|
|
14
|
-
"@babel/generator": "7.
|
|
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
|
+
"@babel/core": "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.
|
|
16
|
+
"@babel/plugin-proposal-async-generator-functions": "7.20.1",
|
|
17
17
|
"@babel/plugin-transform-async-to-generator": "7.18.6",
|
|
18
|
-
"@babel/plugin-transform-runtime": "7.19.
|
|
18
|
+
"@babel/plugin-transform-runtime": "7.19.6",
|
|
19
19
|
"@babel/preset-env": "7.19.4",
|
|
20
|
-
"@babel/runtime": "7.
|
|
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.
|
|
23
|
+
"@ngtools/webpack": "15.0.0-rc.2",
|
|
24
24
|
"ansi-colors": "4.1.3",
|
|
25
|
-
"autoprefixer": "10.4.
|
|
26
|
-
"babel-loader": "
|
|
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
|
-
"cacache": "17.0.
|
|
29
|
+
"cacache": "17.0.1",
|
|
30
30
|
"chokidar": "3.5.3",
|
|
31
31
|
"copy-webpack-plugin": "11.0.0",
|
|
32
32
|
"critters": "0.0.16",
|
|
33
33
|
"css-loader": "6.7.1",
|
|
34
|
-
"esbuild-wasm": "0.15.
|
|
34
|
+
"esbuild-wasm": "0.15.12",
|
|
35
35
|
"glob": "8.0.3",
|
|
36
36
|
"https-proxy-agent": "5.0.1",
|
|
37
37
|
"inquirer": "8.2.4",
|
|
@@ -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.
|
|
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",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"webpack-subresource-integrity": "5.1.0"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
|
-
"esbuild": "0.15.
|
|
71
|
+
"esbuild": "0.15.12"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"@angular/compiler-cli": "^15.0.0-next",
|
|
@@ -11,6 +11,7 @@ import { BundleStylesheetOptions } from './stylesheets';
|
|
|
11
11
|
export declare class SourceFileCache extends Map<string, ts.SourceFile> {
|
|
12
12
|
readonly modifiedFiles: Set<string>;
|
|
13
13
|
readonly babelFileCache: Map<string, Uint8Array>;
|
|
14
|
+
readonly typeScriptFileCache: Map<string, Uint8Array>;
|
|
14
15
|
invalidate(files: Iterable<string>): void;
|
|
15
16
|
}
|
|
16
17
|
export interface CompilerPluginOptions {
|
|
@@ -35,10 +35,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.createCompilerPlugin = exports.SourceFileCache = void 0;
|
|
37
37
|
const core_1 = require("@babel/core");
|
|
38
|
-
const assert = __importStar(require("assert"));
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const path = __importStar(require("path"));
|
|
38
|
+
const assert = __importStar(require("node:assert"));
|
|
39
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
40
|
+
const node_os_1 = require("node:os");
|
|
41
|
+
const path = __importStar(require("node:path"));
|
|
42
|
+
const node_url_1 = require("node:url");
|
|
42
43
|
const typescript_1 = __importDefault(require("typescript"));
|
|
43
44
|
const application_1 = __importDefault(require("../../babel/presets/application"));
|
|
44
45
|
const webpack_loader_1 = require("../../babel/webpack-loader");
|
|
@@ -108,18 +109,20 @@ function convertTypeScriptDiagnostic(diagnostic, host) {
|
|
|
108
109
|
}
|
|
109
110
|
return message;
|
|
110
111
|
}
|
|
111
|
-
const USING_WINDOWS = (0,
|
|
112
|
+
const USING_WINDOWS = (0, node_os_1.platform)() === 'win32';
|
|
112
113
|
const WINDOWS_SEP_REGEXP = new RegExp(`\\${path.win32.sep}`, 'g');
|
|
113
114
|
class SourceFileCache extends Map {
|
|
114
115
|
constructor() {
|
|
115
116
|
super(...arguments);
|
|
116
117
|
this.modifiedFiles = new Set();
|
|
117
118
|
this.babelFileCache = new Map();
|
|
119
|
+
this.typeScriptFileCache = new Map();
|
|
118
120
|
}
|
|
119
121
|
invalidate(files) {
|
|
120
122
|
this.modifiedFiles.clear();
|
|
121
123
|
for (let file of files) {
|
|
122
124
|
this.babelFileCache.delete(file);
|
|
125
|
+
this.typeScriptFileCache.delete((0, node_url_1.pathToFileURL)(file).href);
|
|
123
126
|
// Normalize separators to allow matching TypeScript Host paths
|
|
124
127
|
if (USING_WINDOWS) {
|
|
125
128
|
file = file.replace(WINDOWS_SEP_REGEXP, path.posix.sep);
|
|
@@ -185,8 +188,8 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
185
188
|
location: { file: pluginOptions.tsconfig },
|
|
186
189
|
notes: [
|
|
187
190
|
{
|
|
188
|
-
text:
|
|
189
|
-
|
|
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',
|
|
190
193
|
},
|
|
191
194
|
],
|
|
192
195
|
});
|
|
@@ -269,6 +272,12 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
269
272
|
const builder = typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram(typeScriptProgram, host, previousBuilder, configurationDiagnostics);
|
|
270
273
|
previousBuilder = builder;
|
|
271
274
|
await (0, profiling_1.profileAsync)('NG_ANALYZE_PROGRAM', () => angularCompiler.analyzeAsync());
|
|
275
|
+
const affectedFiles = (0, profiling_1.profileSync)('NG_FIND_AFFECTED', () => findAffectedFiles(builder, angularCompiler));
|
|
276
|
+
if (pluginOptions.sourceFileCache) {
|
|
277
|
+
for (const affected of affectedFiles) {
|
|
278
|
+
pluginOptions.sourceFileCache.typeScriptFileCache.delete((0, node_url_1.pathToFileURL)(affected.fileName).href);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
272
281
|
function* collectDiagnostics() {
|
|
273
282
|
// Collect program level diagnostics
|
|
274
283
|
yield* builder.getConfigFileParsingDiagnostics();
|
|
@@ -276,7 +285,6 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
276
285
|
yield* builder.getOptionsDiagnostics();
|
|
277
286
|
yield* builder.getGlobalDiagnostics();
|
|
278
287
|
// Collect source file specific diagnostics
|
|
279
|
-
const affectedFiles = findAffectedFiles(builder, angularCompiler);
|
|
280
288
|
const optimizeFor = affectedFiles.size > 1 ? OptimizeFor.WholeProgram : OptimizeFor.SingleFile;
|
|
281
289
|
for (const sourceFile of builder.getSourceFiles()) {
|
|
282
290
|
if (angularCompiler.ignoreForDiagnostics.has(sourceFile)) {
|
|
@@ -286,6 +294,10 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
286
294
|
// changed or affected for this build when using incremental building.
|
|
287
295
|
yield* (0, profiling_1.profileSync)('NG_DIAGNOSTICS_SYNTACTIC', () => builder.getSyntacticDiagnostics(sourceFile), true);
|
|
288
296
|
yield* (0, profiling_1.profileSync)('NG_DIAGNOSTICS_SEMANTIC', () => builder.getSemanticDiagnostics(sourceFile), true);
|
|
297
|
+
// Declaration files cannot have template diagnostics
|
|
298
|
+
if (sourceFile.isDeclarationFile) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
289
301
|
// Only request Angular template diagnostics for affected files to avoid
|
|
290
302
|
// overhead of template diagnostics for unchanged files.
|
|
291
303
|
if (affectedFiles.has(sourceFile)) {
|
|
@@ -319,39 +331,47 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
319
331
|
return result;
|
|
320
332
|
});
|
|
321
333
|
build.onLoad({ filter: compilerOptions.allowJs ? /\.[cm]?[jt]sx?$/ : /\.[cm]?tsx?$/ }, (args) => (0, profiling_1.profileAsync)('NG_EMIT_TS*', async () => {
|
|
322
|
-
var _a, _b, _c;
|
|
334
|
+
var _a, _b, _c, _d, _e;
|
|
323
335
|
assert.ok(fileEmitter, 'Invalid plugin execution order');
|
|
324
|
-
|
|
325
|
-
if (!typescriptResult) {
|
|
326
|
-
// No TS result indicates the file is not part of the TypeScript program.
|
|
327
|
-
// If allowJs is enabled and the file is JS then defer to the next load hook.
|
|
328
|
-
if (compilerOptions.allowJs && /\.[cm]?js$/.test(args.path)) {
|
|
329
|
-
return undefined;
|
|
330
|
-
}
|
|
331
|
-
// Otherwise return an error
|
|
332
|
-
return {
|
|
333
|
-
errors: [
|
|
334
|
-
{
|
|
335
|
-
text: `File '${args.path}' is missing from the TypeScript compilation.`,
|
|
336
|
-
notes: [
|
|
337
|
-
{
|
|
338
|
-
text: `Ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
|
|
339
|
-
},
|
|
340
|
-
],
|
|
341
|
-
},
|
|
342
|
-
],
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
const data = (_c = typescriptResult.content) !== null && _c !== void 0 ? _c : '';
|
|
346
|
-
// The pre-transformed data is used as a cache key. Since the cache is memory only,
|
|
336
|
+
// The filename is currently used as a cache key. Since the cache is memory only,
|
|
347
337
|
// the options cannot change and do not need to be represented in the key. If the
|
|
348
338
|
// cache is later stored to disk, then the options that affect transform output
|
|
349
|
-
// would need to be added to the key as well.
|
|
350
|
-
let contents =
|
|
339
|
+
// would need to be added to the key as well as a check for any change of content.
|
|
340
|
+
let contents = (_a = pluginOptions.sourceFileCache) === null || _a === void 0 ? void 0 : _a.typeScriptFileCache.get((0, node_url_1.pathToFileURL)(args.path).href);
|
|
351
341
|
if (contents === undefined) {
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
342
|
+
const typescriptResult = await fileEmitter((_c = (_b = pluginOptions.fileReplacements) === null || _b === void 0 ? void 0 : _b[args.path]) !== null && _c !== void 0 ? _c : args.path);
|
|
343
|
+
if (!typescriptResult) {
|
|
344
|
+
// No TS result indicates the file is not part of the TypeScript program.
|
|
345
|
+
// If allowJs is enabled and the file is JS then defer to the next load hook.
|
|
346
|
+
if (compilerOptions.allowJs && /\.[cm]?js$/.test(args.path)) {
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
// Otherwise return an error
|
|
350
|
+
return {
|
|
351
|
+
errors: [
|
|
352
|
+
{
|
|
353
|
+
text: `File '${args.path}' is missing from the TypeScript compilation.`,
|
|
354
|
+
notes: [
|
|
355
|
+
{
|
|
356
|
+
text: `Ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
const data = (_d = typescriptResult.content) !== null && _d !== void 0 ? _d : '';
|
|
364
|
+
// The pre-transformed data is used as a cache key. Since the cache is memory only,
|
|
365
|
+
// the options cannot change and do not need to be represented in the key. If the
|
|
366
|
+
// cache is later stored to disk, then the options that affect transform output
|
|
367
|
+
// would need to be added to the key as well.
|
|
368
|
+
contents = babelDataCache.get(data);
|
|
369
|
+
if (contents === undefined) {
|
|
370
|
+
const transformedData = await transformWithBabel(args.path, data, pluginOptions);
|
|
371
|
+
contents = Buffer.from(transformedData, 'utf-8');
|
|
372
|
+
babelDataCache.set(data, contents);
|
|
373
|
+
}
|
|
374
|
+
(_e = pluginOptions.sourceFileCache) === null || _e === void 0 ? void 0 : _e.typeScriptFileCache.set((0, node_url_1.pathToFileURL)(args.path).href, contents);
|
|
355
375
|
}
|
|
356
376
|
return {
|
|
357
377
|
contents,
|
|
@@ -366,7 +386,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
366
386
|
// would need to be added to the key as well as a check for any change of content.
|
|
367
387
|
let contents = (_a = pluginOptions.sourceFileCache) === null || _a === void 0 ? void 0 : _a.babelFileCache.get(args.path);
|
|
368
388
|
if (contents === undefined) {
|
|
369
|
-
const data = await
|
|
389
|
+
const data = await fs.readFile(args.path, 'utf-8');
|
|
370
390
|
const transformedData = await transformWithBabel(args.path, data, pluginOptions);
|
|
371
391
|
contents = Buffer.from(transformedData, 'utf-8');
|
|
372
392
|
(_b = pluginOptions.sourceFileCache) === null || _b === void 0 ? void 0 : _b.babelFileCache.set(args.path, contents);
|
|
@@ -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...');
|
|
@@ -352,8 +359,8 @@ async function* buildEsbuildBrowser(initialOptions, context) {
|
|
|
352
359
|
const watcher = (0, watcher_1.createWatcher)({
|
|
353
360
|
polling: typeof initialOptions.poll === 'number',
|
|
354
361
|
interval: initialOptions.poll,
|
|
355
|
-
// Ignore the output
|
|
356
|
-
ignored: [normalizedOptions.outputPath],
|
|
362
|
+
// Ignore the output and cache paths to avoid infinite rebuild cycles
|
|
363
|
+
ignored: [normalizedOptions.outputPath, normalizedOptions.cacheOptions.basePath],
|
|
357
364
|
});
|
|
358
365
|
// Temporarily watch the entire project
|
|
359
366
|
watcher.add(normalizedOptions.projectRoot);
|
|
@@ -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;
|
|
@@ -21,6 +21,7 @@ export declare type NormalizedBrowserOptions = Awaited<ReturnType<typeof normali
|
|
|
21
21
|
export declare function normalizeOptions(context: BuilderContext, projectName: string, options: BrowserBuilderOptions): Promise<{
|
|
22
22
|
advancedOptimizations: boolean | undefined;
|
|
23
23
|
baseHref: string | undefined;
|
|
24
|
+
cacheOptions: import("../../utils/normalize-cache").NormalizedCachedOptions;
|
|
24
25
|
crossOrigin: import("./schema").CrossOrigin | undefined;
|
|
25
26
|
externalDependencies: string[] | undefined;
|
|
26
27
|
poll: number | undefined;
|
|
@@ -33,6 +33,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
33
33
|
exports.normalizeOptions = void 0;
|
|
34
34
|
const path = __importStar(require("path"));
|
|
35
35
|
const utils_1 = require("../../utils");
|
|
36
|
+
const normalize_cache_1 = require("../../utils/normalize-cache");
|
|
36
37
|
const normalize_polyfills_1 = require("../../utils/normalize-polyfills");
|
|
37
38
|
const package_chunk_sort_1 = require("../../utils/package-chunk-sort");
|
|
38
39
|
const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
|
|
@@ -54,7 +55,7 @@ async function normalizeOptions(context, projectName, options) {
|
|
|
54
55
|
const projectMetadata = await context.getProjectMetadata(projectName);
|
|
55
56
|
const projectRoot = path.join(workspaceRoot, (_a = projectMetadata.root) !== null && _a !== void 0 ? _a : '');
|
|
56
57
|
const projectSourceRoot = path.join(workspaceRoot, (_b = projectMetadata.sourceRoot) !== null && _b !== void 0 ? _b : 'src');
|
|
57
|
-
|
|
58
|
+
const cacheOptions = (0, normalize_cache_1.normalizeCacheOptions)(projectMetadata, workspaceRoot);
|
|
58
59
|
const mainEntryPoint = path.join(workspaceRoot, options.main);
|
|
59
60
|
// Currently esbuild do not support multiple files per entry-point
|
|
60
61
|
const [polyfillsEntryPoint, ...remainingPolyfills] = (0, normalize_polyfills_1.normalizePolyfills)(options.polyfills, workspaceRoot);
|
|
@@ -126,6 +127,7 @@ async function normalizeOptions(context, projectName, options) {
|
|
|
126
127
|
return {
|
|
127
128
|
advancedOptimizations: buildOptimizer,
|
|
128
129
|
baseHref,
|
|
130
|
+
cacheOptions,
|
|
129
131
|
crossOrigin,
|
|
130
132
|
externalDependencies,
|
|
131
133
|
poll,
|
|
@@ -18,9 +18,22 @@ function logCumulativeDurations() {
|
|
|
18
18
|
if (!environment_options_1.debugPerformance || !cumulativeDurations) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
-
for (const [name,
|
|
21
|
+
for (const [name, durations] of cumulativeDurations) {
|
|
22
|
+
let total = 0;
|
|
23
|
+
let min;
|
|
24
|
+
let max;
|
|
25
|
+
for (const duration of durations) {
|
|
26
|
+
total += duration;
|
|
27
|
+
if (min === undefined || duration < min) {
|
|
28
|
+
min = duration;
|
|
29
|
+
}
|
|
30
|
+
if (max === undefined || duration > max) {
|
|
31
|
+
max = duration;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const average = total / durations.length;
|
|
22
35
|
// eslint-disable-next-line no-console
|
|
23
|
-
console.log(`DURATION[${name}]: ${
|
|
36
|
+
console.log(`DURATION[${name}]: ${total.toFixed(9)}s [count: ${durations.length}; avg: ${average.toFixed(9)}s; min: ${min === null || min === void 0 ? void 0 : min.toFixed(9)}s; max: ${max === null || max === void 0 ? void 0 : max.toFixed(9)}s]`);
|
|
24
37
|
}
|
|
25
38
|
}
|
|
26
39
|
exports.logCumulativeDurations = logCumulativeDurations;
|
|
@@ -29,11 +42,13 @@ function recordDuration(name, startTime, cumulative) {
|
|
|
29
42
|
const duration = Number(process.hrtime.bigint() - startTime) / 10 ** 9;
|
|
30
43
|
if (cumulative) {
|
|
31
44
|
cumulativeDurations !== null && cumulativeDurations !== void 0 ? cumulativeDurations : (cumulativeDurations = new Map());
|
|
32
|
-
|
|
45
|
+
const durations = (_a = cumulativeDurations.get(name)) !== null && _a !== void 0 ? _a : [];
|
|
46
|
+
durations.push(duration);
|
|
47
|
+
cumulativeDurations.set(name, durations);
|
|
33
48
|
}
|
|
34
49
|
else {
|
|
35
50
|
// eslint-disable-next-line no-console
|
|
36
|
-
console.log(`DURATION[${name}]: ${duration.toFixed(9)}
|
|
51
|
+
console.log(`DURATION[${name}]: ${duration.toFixed(9)}s`);
|
|
37
52
|
}
|
|
38
53
|
}
|
|
39
54
|
async function profileAsync(name, action, cumulative) {
|
|
@@ -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
|
-
|
|
29
|
+
sassWorkerPool !== null && sassWorkerPool !== void 0 ? sassWorkerPool : (sassWorkerPool = new sass_service_1.SassWorkerImplementation());
|
|
30
|
+
const warnings = [];
|
|
44
31
|
try {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
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,
|
|
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
|
|
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.
|
|
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
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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;
|
|
@@ -114,6 +114,12 @@ function serveWebpackBrowser(options, context, transforms = {}) {
|
|
|
114
114
|
const metadata = await context.getProjectMetadata(projectName);
|
|
115
115
|
const cacheOptions = (0, normalize_cache_1.normalizeCacheOptions)(metadata, context.workspaceRoot);
|
|
116
116
|
const browserName = await context.getBuilderNameForTarget(browserTarget);
|
|
117
|
+
// Issue a warning that the dev-server does not currently support the experimental esbuild-
|
|
118
|
+
// based builder and will use Webpack.
|
|
119
|
+
if (browserName === '@angular-devkit/build-angular:browser-esbuild') {
|
|
120
|
+
logger.warn('WARNING: The experimental esbuild-based builder is not currently supported ' +
|
|
121
|
+
'by the dev-server. The stable Webpack-based builder will be used instead.');
|
|
122
|
+
}
|
|
117
123
|
const browserOptions = (await context.validateOptions({
|
|
118
124
|
...rawBrowserOptions,
|
|
119
125
|
watch: options.watch,
|
package/src/sass/sass-service.js
CHANGED
|
@@ -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
|
}
|
package/src/sass/worker.js
CHANGED
|
@@ -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', (
|
|
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({
|
|
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
|
+
}
|
|
@@ -28,7 +28,7 @@ function createIvyPlugin(wco, aot, tsconfig) {
|
|
|
28
28
|
(_a = (_c = tsConfig.options).useDefineForClassFields) !== null && _a !== void 0 ? _a : (_c.useDefineForClassFields = false);
|
|
29
29
|
wco.logger.warn('TypeScript compiler options "target" and "useDefineForClassFields" are set to "ES2022" and ' +
|
|
30
30
|
'"false" respectively by the Angular CLI. To control ECMA version and features use the Browerslist configuration. ' +
|
|
31
|
-
'For more information, see https://
|
|
31
|
+
'For more information, see https://angular.io/guide/build#configuring-browser-compatibility');
|
|
32
32
|
}
|
|
33
33
|
if (buildOptions.preserveSymlinks !== undefined) {
|
|
34
34
|
compilerOptions.preserveSymlinks = buildOptions.preserveSymlinks;
|