@angular-devkit/build-angular 17.0.5 → 17.0.7
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 +6 -6
- package/src/builders/application/build-action.d.ts +1 -0
- package/src/builders/application/build-action.js +16 -11
- package/src/builders/application/execute-post-bundle.js +8 -4
- package/src/builders/application/index.js +1 -0
- package/src/builders/application/options.js +1 -1
- package/src/builders/browser-esbuild/builder-status-warnings.js +1 -3
- package/src/builders/dev-server/builder.js +5 -0
- package/src/builders/dev-server/vite-server.js +10 -3
- package/src/builders/ssr-dev-server/index.js +16 -17
- package/src/tools/esbuild/angular/compiler-plugin.js +1 -1
- package/src/tools/esbuild/angular/jit-plugin-callbacks.d.ts +2 -1
- package/src/tools/esbuild/angular/jit-plugin-callbacks.js +15 -15
- package/src/tools/esbuild/angular/source-file-cache.js +0 -1
- package/src/tools/esbuild/i18n-locale-plugin.d.ts +4 -0
- package/src/tools/esbuild/i18n-locale-plugin.js +48 -18
- package/src/tools/esbuild/watcher.d.ts +1 -0
- package/src/tools/esbuild/watcher.js +103 -27
- package/src/tools/sass/rebasing-importer.js +29 -4
- package/src/utils/check-port.js +15 -29
- package/src/utils/delete-output-dir.d.ts +1 -1
- package/src/utils/delete-output-dir.js +11 -2
- package/src/utils/environment-options.js +1 -3
- package/src/utils/index-file/inline-fonts.js +10 -1
- package/src/utils/server-rendering/esm-in-memory-loader/node-18-utils.js +6 -5
- package/src/utils/spinner.js +1 -1
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-devkit/build-angular",
|
|
3
|
-
"version": "17.0.
|
|
3
|
+
"version": "17.0.7",
|
|
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.1",
|
|
10
|
-
"@angular-devkit/architect": "0.1700.
|
|
11
|
-
"@angular-devkit/build-webpack": "0.1700.
|
|
12
|
-
"@angular-devkit/core": "17.0.
|
|
10
|
+
"@angular-devkit/architect": "0.1700.7",
|
|
11
|
+
"@angular-devkit/build-webpack": "0.1700.7",
|
|
12
|
+
"@angular-devkit/core": "17.0.7",
|
|
13
13
|
"@babel/core": "7.23.2",
|
|
14
14
|
"@babel/generator": "7.23.0",
|
|
15
15
|
"@babel/helper-annotate-as-pure": "7.22.5",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@babel/preset-env": "7.23.2",
|
|
21
21
|
"@babel/runtime": "7.23.2",
|
|
22
22
|
"@discoveryjs/json-ext": "0.5.7",
|
|
23
|
-
"@ngtools/webpack": "17.0.
|
|
23
|
+
"@ngtools/webpack": "17.0.7",
|
|
24
24
|
"@vitejs/plugin-basic-ssl": "1.0.1",
|
|
25
25
|
"ansi-colors": "4.1.3",
|
|
26
26
|
"autoprefixer": "10.4.16",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"tree-kill": "1.2.2",
|
|
66
66
|
"tslib": "2.6.2",
|
|
67
67
|
"undici": "5.27.2",
|
|
68
|
-
"vite": "4.5.
|
|
68
|
+
"vite": "4.5.1",
|
|
69
69
|
"webpack": "5.89.0",
|
|
70
70
|
"webpack-dev-middleware": "6.1.1",
|
|
71
71
|
"webpack-dev-server": "4.15.1",
|
|
@@ -24,4 +24,5 @@ export declare function runEsBuildBuildAction(action: (rebuildState?: RebuildSta
|
|
|
24
24
|
deleteOutputPath?: boolean;
|
|
25
25
|
poll?: number;
|
|
26
26
|
signal?: AbortSignal;
|
|
27
|
+
preserveSymlinks?: boolean;
|
|
27
28
|
}): AsyncIterable<(ExecutionResult['outputWithFiles'] | ExecutionResult['output']) & BuilderOutput>;
|
|
@@ -40,9 +40,9 @@ const utils_1 = require("../../tools/esbuild/utils");
|
|
|
40
40
|
const delete_output_dir_1 = require("../../utils/delete-output-dir");
|
|
41
41
|
const environment_options_1 = require("../../utils/environment-options");
|
|
42
42
|
async function* runEsBuildBuildAction(action, options) {
|
|
43
|
-
const { writeToFileSystemFilter, writeToFileSystem = true, watch, poll, logger, deleteOutputPath, cacheOptions, outputPath, verbose, projectRoot, workspaceRoot, progress, } = options;
|
|
43
|
+
const { writeToFileSystemFilter, writeToFileSystem = true, watch, poll, logger, deleteOutputPath, cacheOptions, outputPath, verbose, projectRoot, workspaceRoot, progress, preserveSymlinks, } = options;
|
|
44
44
|
if (deleteOutputPath && writeToFileSystem) {
|
|
45
|
-
await (0, delete_output_dir_1.deleteOutputDir)(workspaceRoot, outputPath);
|
|
45
|
+
await (0, delete_output_dir_1.deleteOutputDir)(workspaceRoot, outputPath, ['browser', 'server']);
|
|
46
46
|
}
|
|
47
47
|
const withProgress = progress ? utils_1.withSpinner : utils_1.withNoProgress;
|
|
48
48
|
// Initial build
|
|
@@ -62,20 +62,25 @@ async function* runEsBuildBuildAction(action, options) {
|
|
|
62
62
|
if (progress) {
|
|
63
63
|
logger.info('Watch mode enabled. Watching for file changes...');
|
|
64
64
|
}
|
|
65
|
+
const ignored = [
|
|
66
|
+
// Ignore the output and cache paths to avoid infinite rebuild cycles
|
|
67
|
+
outputPath,
|
|
68
|
+
cacheOptions.basePath,
|
|
69
|
+
`${workspaceRoot.replace(/\\/g, '/')}/**/.*/**`,
|
|
70
|
+
];
|
|
71
|
+
if (!preserveSymlinks) {
|
|
72
|
+
// Ignore all node modules directories to avoid excessive file watchers.
|
|
73
|
+
// Package changes are handled below by watching manifest and lock files.
|
|
74
|
+
// NOTE: this is not enable when preserveSymlinks is true as this would break `npm link` usages.
|
|
75
|
+
ignored.push('**/node_modules/**');
|
|
76
|
+
}
|
|
65
77
|
// Setup a watcher
|
|
66
78
|
const { createWatcher } = await Promise.resolve().then(() => __importStar(require('../../tools/esbuild/watcher')));
|
|
67
79
|
watcher = createWatcher({
|
|
68
80
|
polling: typeof poll === 'number',
|
|
69
81
|
interval: poll,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
outputPath,
|
|
73
|
-
cacheOptions.basePath,
|
|
74
|
-
// Ignore all node modules directories to avoid excessive file watchers.
|
|
75
|
-
// Package changes are handled below by watching manifest and lock files.
|
|
76
|
-
'**/node_modules/**',
|
|
77
|
-
`${workspaceRoot.replace(/\\/g, '/')}/**/.*/**`,
|
|
78
|
-
],
|
|
82
|
+
followSymlinks: preserveSymlinks,
|
|
83
|
+
ignored,
|
|
79
84
|
});
|
|
80
85
|
// Setup abort support
|
|
81
86
|
options.signal?.addEventListener('abort', () => void watcher?.close());
|
|
@@ -39,9 +39,11 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
|
|
|
39
39
|
* NOTE: we don't perform critical CSS inlining as this will be done during server rendering.
|
|
40
40
|
*/
|
|
41
41
|
let indexContentOutputNoCssInlining;
|
|
42
|
+
// When using prerender/app-shell the index HTML file can be regenerated.
|
|
43
|
+
// Thus, we use a Map so that we do not generate 2 files with the same filename.
|
|
44
|
+
const additionalHtmlOutputFiles = new Map();
|
|
42
45
|
// Generate index HTML file
|
|
43
46
|
// If localization is enabled, index generation is handled in the inlining process.
|
|
44
|
-
// NOTE: Localization with SSR is not currently supported.
|
|
45
47
|
if (indexHtmlOptions) {
|
|
46
48
|
const { content, contentWithoutCriticalCssInlined, errors, warnings } = await (0, index_html_generator_1.generateIndexHtml)(initialFiles, outputFiles, {
|
|
47
49
|
...options,
|
|
@@ -50,9 +52,10 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
|
|
|
50
52
|
indexContentOutputNoCssInlining = contentWithoutCriticalCssInlined;
|
|
51
53
|
allErrors.push(...errors);
|
|
52
54
|
allWarnings.push(...warnings);
|
|
53
|
-
|
|
55
|
+
additionalHtmlOutputFiles.set(indexHtmlOptions.output, (0, utils_1.createOutputFileFromText)(indexHtmlOptions.output, content, bundler_context_1.BuildOutputFileType.Browser));
|
|
54
56
|
if (ssrOptions) {
|
|
55
|
-
|
|
57
|
+
const serverIndexHtmlFilename = 'index.server.html';
|
|
58
|
+
additionalHtmlOutputFiles.set(serverIndexHtmlFilename, (0, utils_1.createOutputFileFromText)(serverIndexHtmlFilename, contentWithoutCriticalCssInlined, bundler_context_1.BuildOutputFileType.Server));
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
// Pre-render (SSG) and App-shell
|
|
@@ -64,9 +67,10 @@ async function executePostBundleSteps(options, outputFiles, assetFiles, initialF
|
|
|
64
67
|
allWarnings.push(...warnings);
|
|
65
68
|
prerenderedRoutes.push(...Array.from(generatedRoutes));
|
|
66
69
|
for (const [path, content] of Object.entries(output)) {
|
|
67
|
-
|
|
70
|
+
additionalHtmlOutputFiles.set(path, (0, utils_1.createOutputFileFromText)(path, content, bundler_context_1.BuildOutputFileType.Browser));
|
|
68
71
|
}
|
|
69
72
|
}
|
|
73
|
+
additionalOutputFiles.push(...additionalHtmlOutputFiles.values());
|
|
70
74
|
// Augment the application with service worker support
|
|
71
75
|
// If localization is enabled, service worker is handled in the inlining process.
|
|
72
76
|
if (serviceWorker) {
|
|
@@ -38,6 +38,7 @@ context, infrastructureSettings, plugins) {
|
|
|
38
38
|
return result;
|
|
39
39
|
}, {
|
|
40
40
|
watch: normalizedOptions.watch,
|
|
41
|
+
preserveSymlinks: normalizedOptions.preserveSymlinks,
|
|
41
42
|
poll: normalizedOptions.poll,
|
|
42
43
|
deleteOutputPath: normalizedOptions.deleteOutputPath,
|
|
43
44
|
cacheOptions: normalizedOptions.cacheOptions,
|
|
@@ -168,7 +168,7 @@ async function normalizeOptions(context, projectName, options, plugins) {
|
|
|
168
168
|
const { allowedCommonJsDependencies, aot, baseHref, crossOrigin, externalDependencies, extractLicenses, inlineStyleLanguage = 'css', outExtension, serviceWorker, poll, polyfills, statsJson, stylePreprocessorOptions, subresourceIntegrity, verbose, watch, progress = true, externalPackages, deleteOutputPath, namedChunks, budgets, deployUrl, } = options;
|
|
169
169
|
// Return all the normalized options
|
|
170
170
|
return {
|
|
171
|
-
advancedOptimizations: !!aot,
|
|
171
|
+
advancedOptimizations: !!aot && optimizationOptions.scripts,
|
|
172
172
|
allowedCommonJsDependencies,
|
|
173
173
|
baseHref,
|
|
174
174
|
cacheOptions,
|
|
@@ -30,9 +30,7 @@ function logBuilderStatusWarnings(options, { logger }) {
|
|
|
30
30
|
if (typeof value === 'object' && Object.keys(value).length === 0) {
|
|
31
31
|
continue;
|
|
32
32
|
}
|
|
33
|
-
if (unsupportedOption === 'vendorChunk' ||
|
|
34
|
-
unsupportedOption === 'resourcesOutputPath' ||
|
|
35
|
-
unsupportedOption === 'deployUrl') {
|
|
33
|
+
if (unsupportedOption === 'vendorChunk' || unsupportedOption === 'resourcesOutputPath') {
|
|
36
34
|
logger.warn(`The '${unsupportedOption}' option is not used by this builder and will be ignored.`);
|
|
37
35
|
continue;
|
|
38
36
|
}
|
|
@@ -65,6 +65,11 @@ function execute(options, context, transforms = {}, extensions) {
|
|
|
65
65
|
if (transforms?.logging || transforms?.webpackConfiguration) {
|
|
66
66
|
throw new Error('The `application` and `browser-esbuild` builders do not support Webpack transforms.');
|
|
67
67
|
}
|
|
68
|
+
if (normalizedOptions.forceEsbuild &&
|
|
69
|
+
builderName === '@angular-devkit/build-angular:browser') {
|
|
70
|
+
// The compatibility builder should be used if esbuild is force enabled with the official Webpack-based builder.
|
|
71
|
+
builderName = '@angular-devkit/build-angular:browser-esbuild';
|
|
72
|
+
}
|
|
68
73
|
return (0, rxjs_1.defer)(() => Promise.resolve().then(() => __importStar(require('./vite-server')))).pipe((0, rxjs_1.switchMap)(({ serveWithVite }) => serveWithVite(normalizedOptions, builderName, context, transforms, extensions)));
|
|
69
74
|
}
|
|
70
75
|
if (extensions?.buildPlugins?.length) {
|
|
@@ -73,8 +73,11 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
|
|
|
73
73
|
}
|
|
74
74
|
// Set all packages as external to support Vite's prebundle caching
|
|
75
75
|
browserOptions.externalPackages = serverOptions.cacheOptions.enabled;
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
const baseHref = browserOptions.baseHref;
|
|
77
|
+
if (serverOptions.servePath === undefined && baseHref !== undefined) {
|
|
78
|
+
// Remove trailing slash
|
|
79
|
+
serverOptions.servePath =
|
|
80
|
+
baseHref[baseHref.length - 1] === '/' ? baseHref.slice(0, -1) : baseHref;
|
|
78
81
|
}
|
|
79
82
|
// The development server currently only supports a single locale when localizing.
|
|
80
83
|
// This matches the behavior of the Webpack-based development server but could be expanded in the future.
|
|
@@ -329,7 +332,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
329
332
|
css: {
|
|
330
333
|
devSourcemap: true,
|
|
331
334
|
},
|
|
332
|
-
// Vite will normalize the `base` option by adding a leading
|
|
335
|
+
// Vite will normalize the `base` option by adding a leading slash.
|
|
333
336
|
base: serverOptions.servePath,
|
|
334
337
|
resolve: {
|
|
335
338
|
mainFields: ['es2020', 'browser', 'module', 'main'],
|
|
@@ -449,6 +452,10 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
|
|
|
449
452
|
// Rewrite all build assets to a vite raw fs URL
|
|
450
453
|
const assetSourcePath = assets.get(pathname);
|
|
451
454
|
if (assetSourcePath !== undefined) {
|
|
455
|
+
// Workaround to disable Vite transformer middleware.
|
|
456
|
+
// See: https://github.com/vitejs/vite/blob/746a1daab0395f98f0afbdee8f364cb6cf2f3b3f/packages/vite/src/node/server/middlewares/transform.ts#L201 and
|
|
457
|
+
// https://github.com/vitejs/vite/blob/746a1daab0395f98f0afbdee8f364cb6cf2f3b3f/packages/vite/src/node/server/transformRequest.ts#L204-L206
|
|
458
|
+
req.headers.accept = 'text/html';
|
|
452
459
|
// The encoding needs to match what happens in the vite static middleware.
|
|
453
460
|
// ref: https://github.com/vitejs/vite/blob/d4f13bd81468961c8c926438e815ab6b1c82735e/packages/vite/src/node/server/middlewares/static.ts#L163
|
|
454
461
|
req.url = `${server.config.base}@fs/${encodeURI(assetSourcePath)}`;
|
|
@@ -35,7 +35,6 @@ const architect_1 = require("@angular-devkit/architect");
|
|
|
35
35
|
const core_1 = require("@angular-devkit/core");
|
|
36
36
|
const path_1 = require("path");
|
|
37
37
|
const rxjs_1 = require("rxjs");
|
|
38
|
-
const operators_1 = require("rxjs/operators");
|
|
39
38
|
const url = __importStar(require("url"));
|
|
40
39
|
const error_1 = require("../../utils/error");
|
|
41
40
|
const utils_1 = require("./utils");
|
|
@@ -69,35 +68,35 @@ function execute(options, context) {
|
|
|
69
68
|
DON'T USE IT FOR PRODUCTION!
|
|
70
69
|
****************************************************************************************
|
|
71
70
|
`);
|
|
72
|
-
return (0, rxjs_1.zip)(browserTargetRun, serverTargetRun, (0, utils_1.getAvailablePort)()).pipe((0,
|
|
71
|
+
return (0, rxjs_1.zip)(browserTargetRun, serverTargetRun, (0, utils_1.getAvailablePort)()).pipe((0, rxjs_1.switchMap)(([br, sr, nodeServerPort]) => {
|
|
73
72
|
return (0, rxjs_1.combineLatest)([br.output, sr.output]).pipe(
|
|
74
73
|
// This is needed so that if both server and browser emit close to each other
|
|
75
74
|
// we only emit once. This typically happens on the first build.
|
|
76
|
-
(0,
|
|
75
|
+
(0, rxjs_1.debounceTime)(120), (0, rxjs_1.switchMap)(([b, s]) => {
|
|
77
76
|
if (!s.success || !b.success) {
|
|
78
77
|
return (0, rxjs_1.of)([b, s]);
|
|
79
78
|
}
|
|
80
|
-
return startNodeServer(s, nodeServerPort, context.logger, !!options.inspect).pipe((0,
|
|
79
|
+
return startNodeServer(s, nodeServerPort, context.logger, !!options.inspect).pipe((0, rxjs_1.map)(() => [b, s]), (0, rxjs_1.catchError)((err) => {
|
|
81
80
|
context.logger.error(`A server error has occurred.\n${mapErrorToMessage(err)}`);
|
|
82
81
|
return rxjs_1.EMPTY;
|
|
83
82
|
}));
|
|
84
|
-
}), (0,
|
|
83
|
+
}), (0, rxjs_1.map)(([b, s]) => [
|
|
85
84
|
{
|
|
86
85
|
success: b.success && s.success,
|
|
87
86
|
error: b.error || s.error,
|
|
88
87
|
},
|
|
89
88
|
nodeServerPort,
|
|
90
|
-
]), (0,
|
|
89
|
+
]), (0, rxjs_1.tap)(([builderOutput]) => {
|
|
91
90
|
if (builderOutput.success) {
|
|
92
91
|
context.logger.info('\nCompiled successfully.');
|
|
93
92
|
}
|
|
94
|
-
}), (0,
|
|
93
|
+
}), (0, rxjs_1.debounce)(([builderOutput]) => builderOutput.success && !options.inspect
|
|
95
94
|
? (0, utils_1.waitUntilServerIsListening)(nodeServerPort)
|
|
96
|
-
: rxjs_1.EMPTY), (0,
|
|
95
|
+
: rxjs_1.EMPTY), (0, rxjs_1.finalize)(() => {
|
|
97
96
|
void br.stop();
|
|
98
97
|
void sr.stop();
|
|
99
98
|
}));
|
|
100
|
-
}), (0,
|
|
99
|
+
}), (0, rxjs_1.concatMap)(([builderOutput, nodeServerPort]) => {
|
|
101
100
|
if (!builderOutput.success) {
|
|
102
101
|
return (0, rxjs_1.of)(builderOutput);
|
|
103
102
|
}
|
|
@@ -106,7 +105,7 @@ function execute(options, context) {
|
|
|
106
105
|
return (0, rxjs_1.of)(builderOutput);
|
|
107
106
|
}
|
|
108
107
|
else {
|
|
109
|
-
return (0, rxjs_1.from)(initBrowserSync(bsInstance, nodeServerPort, options, context)).pipe((0,
|
|
108
|
+
return (0, rxjs_1.from)(initBrowserSync(bsInstance, nodeServerPort, options, context)).pipe((0, rxjs_1.tap)((bs) => {
|
|
110
109
|
const baseUrl = getBaseUrl(bs);
|
|
111
110
|
context.logger.info(core_1.tags.oneLine `
|
|
112
111
|
**
|
|
@@ -114,19 +113,19 @@ function execute(options, context) {
|
|
|
114
113
|
open your browser on ${baseUrl}
|
|
115
114
|
**
|
|
116
115
|
`);
|
|
117
|
-
}), (0,
|
|
116
|
+
}), (0, rxjs_1.map)(() => builderOutput));
|
|
118
117
|
}
|
|
119
|
-
}), (0,
|
|
118
|
+
}), (0, rxjs_1.map)((builderOutput) => ({
|
|
120
119
|
success: builderOutput.success,
|
|
121
120
|
error: builderOutput.error,
|
|
122
121
|
baseUrl: getBaseUrl(bsInstance),
|
|
123
122
|
port: bsInstance.getOption('port'),
|
|
124
|
-
})), (0,
|
|
123
|
+
})), (0, rxjs_1.finalize)(() => {
|
|
125
124
|
if (bsInstance) {
|
|
126
125
|
bsInstance.exit();
|
|
127
126
|
bsInstance.cleanup();
|
|
128
127
|
}
|
|
129
|
-
}), (0,
|
|
128
|
+
}), (0, rxjs_1.catchError)((error) => (0, rxjs_1.of)({
|
|
130
129
|
success: false,
|
|
131
130
|
error: mapErrorToMessage(error),
|
|
132
131
|
})));
|
|
@@ -152,10 +151,10 @@ function startNodeServer(serverOutput, port, logger, inspectMode = false) {
|
|
|
152
151
|
if (inspectMode) {
|
|
153
152
|
args.unshift('--inspect-brk');
|
|
154
153
|
}
|
|
155
|
-
return (0, rxjs_1.of)(null).pipe((0,
|
|
156
|
-
(0,
|
|
154
|
+
return (0, rxjs_1.of)(null).pipe((0, rxjs_1.delay)(0), // Avoid EADDRINUSE error since it will cause the kill event to be finish.
|
|
155
|
+
(0, rxjs_1.switchMap)(() => (0, utils_1.spawnAsObservable)('node', args, { env, shell: true })), (0, rxjs_1.tap)((res) => log({ stderr: res.stderr, stdout: res.stdout }, logger)), (0, rxjs_1.ignoreElements)(),
|
|
157
156
|
// Emit a signal after the process has been started
|
|
158
|
-
(0,
|
|
157
|
+
(0, rxjs_1.startWith)(undefined));
|
|
159
158
|
}
|
|
160
159
|
async function initBrowserSync(browserSyncInstance, nodeServerPort, options, context) {
|
|
161
160
|
if (browserSyncInstance.active) {
|
|
@@ -308,7 +308,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
|
|
|
308
308
|
}));
|
|
309
309
|
// Setup bundling of component templates and stylesheets when in JIT mode
|
|
310
310
|
if (pluginOptions.jit) {
|
|
311
|
-
(0, jit_plugin_callbacks_1.setupJitPluginCallbacks)(build, stylesheetBundler, additionalResults, styleOptions.inlineStyleLanguage);
|
|
311
|
+
(0, jit_plugin_callbacks_1.setupJitPluginCallbacks)(build, stylesheetBundler, additionalResults, styleOptions.inlineStyleLanguage, pluginOptions.loadResultCache);
|
|
312
312
|
}
|
|
313
313
|
build.onEnd((result) => {
|
|
314
314
|
// Ensure other compilations are unblocked if the main compilation throws during start
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
8
|
import type { Metafile, OutputFile, PluginBuild } from 'esbuild';
|
|
9
|
+
import { LoadResultCache } from '../load-result-cache';
|
|
9
10
|
import { ComponentStylesheetBundler } from './component-stylesheets';
|
|
10
11
|
/**
|
|
11
12
|
* Sets up esbuild resolve and load callbacks to support Angular JIT mode processing
|
|
@@ -19,4 +20,4 @@ import { ComponentStylesheetBundler } from './component-stylesheets';
|
|
|
19
20
|
export declare function setupJitPluginCallbacks(build: PluginBuild, stylesheetBundler: ComponentStylesheetBundler, additionalResultFiles: Map<string, {
|
|
20
21
|
outputFiles?: OutputFile[];
|
|
21
22
|
metafile?: Metafile;
|
|
22
|
-
}>, inlineStyleLanguage: string): void;
|
|
23
|
+
}>, inlineStyleLanguage: string, loadCache?: LoadResultCache): void;
|
|
@@ -6,13 +6,11 @@
|
|
|
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
|
-
};
|
|
12
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
10
|
exports.setupJitPluginCallbacks = void 0;
|
|
14
11
|
const promises_1 = require("node:fs/promises");
|
|
15
|
-
const node_path_1 =
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
13
|
+
const load_result_cache_1 = require("../load-result-cache");
|
|
16
14
|
const uri_1 = require("./uri");
|
|
17
15
|
/**
|
|
18
16
|
* Loads/extracts the contents from a load callback Angular JIT entry.
|
|
@@ -27,7 +25,7 @@ const uri_1 = require("./uri");
|
|
|
27
25
|
*/
|
|
28
26
|
async function loadEntry(entry, root, skipRead) {
|
|
29
27
|
if (entry.startsWith('file:')) {
|
|
30
|
-
const specifier = node_path_1.
|
|
28
|
+
const specifier = (0, node_path_1.join)(root, entry.slice(5));
|
|
31
29
|
return {
|
|
32
30
|
path: specifier,
|
|
33
31
|
contents: skipRead ? undefined : await (0, promises_1.readFile)(specifier, 'utf-8'),
|
|
@@ -36,7 +34,7 @@ async function loadEntry(entry, root, skipRead) {
|
|
|
36
34
|
else if (entry.startsWith('inline:')) {
|
|
37
35
|
const [importer, data] = entry.slice(7).split(';', 2);
|
|
38
36
|
return {
|
|
39
|
-
path: node_path_1.
|
|
37
|
+
path: (0, node_path_1.join)(root, importer),
|
|
40
38
|
contents: Buffer.from(data, 'base64').toString(),
|
|
41
39
|
};
|
|
42
40
|
}
|
|
@@ -53,7 +51,7 @@ async function loadEntry(entry, root, skipRead) {
|
|
|
53
51
|
* @param styleOptions The options to use when bundling stylesheets.
|
|
54
52
|
* @param additionalResultFiles A Map where stylesheet resources will be added.
|
|
55
53
|
*/
|
|
56
|
-
function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles, inlineStyleLanguage) {
|
|
54
|
+
function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles, inlineStyleLanguage, loadCache) {
|
|
57
55
|
const root = build.initialOptions.absWorkingDir ?? '';
|
|
58
56
|
// Add a resolve callback to capture and parse any JIT URIs that were added by the
|
|
59
57
|
// JIT resource TypeScript transformer.
|
|
@@ -68,13 +66,13 @@ function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles
|
|
|
68
66
|
return {
|
|
69
67
|
// Use a relative path to prevent fully resolved paths in the metafile (JSON stats file).
|
|
70
68
|
// This is only necessary for custom namespaces. esbuild will handle the file namespace.
|
|
71
|
-
path: 'file:' + node_path_1.
|
|
69
|
+
path: 'file:' + (0, node_path_1.relative)(root, (0, node_path_1.join)((0, node_path_1.dirname)(args.importer), specifier)),
|
|
72
70
|
namespace,
|
|
73
71
|
};
|
|
74
72
|
}
|
|
75
73
|
else {
|
|
76
74
|
// Inline data may need the importer to resolve imports/references within the content
|
|
77
|
-
const importer = node_path_1.
|
|
75
|
+
const importer = (0, node_path_1.relative)(root, args.importer);
|
|
78
76
|
return {
|
|
79
77
|
path: `inline:${importer};${specifier}`,
|
|
80
78
|
namespace,
|
|
@@ -82,7 +80,7 @@ function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles
|
|
|
82
80
|
}
|
|
83
81
|
});
|
|
84
82
|
// Add a load callback to handle Component stylesheets (both inline and external)
|
|
85
|
-
build.onLoad({ filter: /./, namespace: uri_1.JIT_STYLE_NAMESPACE }, async (args) => {
|
|
83
|
+
build.onLoad({ filter: /./, namespace: uri_1.JIT_STYLE_NAMESPACE }, (0, load_result_cache_1.createCachedLoad)(loadCache, async (args) => {
|
|
86
84
|
// skipRead is used here because the stylesheet bundling will read a file stylesheet
|
|
87
85
|
// directly either via a preprocessor or esbuild itself.
|
|
88
86
|
const entry = await loadEntry(args.path, root, true /* skipRead */);
|
|
@@ -94,24 +92,26 @@ function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles
|
|
|
94
92
|
else {
|
|
95
93
|
stylesheetResult = await stylesheetBundler.bundleInline(entry.contents, entry.path, inlineStyleLanguage);
|
|
96
94
|
}
|
|
97
|
-
const { contents, resourceFiles, errors, warnings, metafile } = stylesheetResult;
|
|
95
|
+
const { contents, resourceFiles, errors, warnings, metafile, referencedFiles } = stylesheetResult;
|
|
98
96
|
additionalResultFiles.set(entry.path, { outputFiles: resourceFiles, metafile });
|
|
99
97
|
return {
|
|
100
98
|
errors,
|
|
101
99
|
warnings,
|
|
102
100
|
contents,
|
|
103
101
|
loader: 'text',
|
|
102
|
+
watchFiles: referencedFiles && [...referencedFiles],
|
|
104
103
|
};
|
|
105
|
-
});
|
|
104
|
+
}));
|
|
106
105
|
// Add a load callback to handle Component templates
|
|
107
106
|
// NOTE: While this callback supports both inline and external templates, the transformer
|
|
108
107
|
// currently only supports generating URIs for external templates.
|
|
109
|
-
build.onLoad({ filter: /./, namespace: uri_1.JIT_TEMPLATE_NAMESPACE }, async (args) => {
|
|
110
|
-
const { contents } = await loadEntry(args.path, root);
|
|
108
|
+
build.onLoad({ filter: /./, namespace: uri_1.JIT_TEMPLATE_NAMESPACE }, (0, load_result_cache_1.createCachedLoad)(loadCache, async (args) => {
|
|
109
|
+
const { contents, path } = await loadEntry(args.path, root);
|
|
111
110
|
return {
|
|
112
111
|
contents,
|
|
113
112
|
loader: 'text',
|
|
113
|
+
watchFiles: [path],
|
|
114
114
|
};
|
|
115
|
-
});
|
|
115
|
+
}));
|
|
116
116
|
}
|
|
117
117
|
exports.setupJitPluginCallbacks = setupJitPluginCallbacks;
|
|
@@ -52,7 +52,6 @@ class SourceFileCache extends Map {
|
|
|
52
52
|
}
|
|
53
53
|
for (let file of files) {
|
|
54
54
|
file = path.normalize(file);
|
|
55
|
-
this.typeScriptFileCache.delete(file);
|
|
56
55
|
this.loadResultCache.invalidate(file);
|
|
57
56
|
// Normalize separators to allow matching TypeScript Host paths
|
|
58
57
|
if (USING_WINDOWS) {
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
8
|
import type { Plugin } from 'esbuild';
|
|
9
|
+
/**
|
|
10
|
+
* The internal namespace used by generated locale import statements and Angular locale data plugin.
|
|
11
|
+
*/
|
|
12
|
+
export declare const LOCALE_DATA_NAMESPACE = "angular:locale/data";
|
|
9
13
|
/**
|
|
10
14
|
* The base module location used to search for locale specific data.
|
|
11
15
|
*/
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
* found in the LICENSE file at https://angular.io/license
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.createAngularLocaleDataPlugin = exports.LOCALE_DATA_BASE_MODULE = void 0;
|
|
10
|
+
exports.createAngularLocaleDataPlugin = exports.LOCALE_DATA_BASE_MODULE = exports.LOCALE_DATA_NAMESPACE = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* The internal namespace used by generated locale import statements and Angular locale data plugin.
|
|
13
|
+
*/
|
|
14
|
+
exports.LOCALE_DATA_NAMESPACE = 'angular:locale/data';
|
|
11
15
|
/**
|
|
12
16
|
* The base module location used to search for locale specific data.
|
|
13
17
|
*/
|
|
@@ -33,12 +37,37 @@ function createAngularLocaleDataPlugin() {
|
|
|
33
37
|
}
|
|
34
38
|
build.onResolve({ filter: /^angular:locale\/data:/ }, async ({ path }) => {
|
|
35
39
|
// Extract the locale from the path
|
|
36
|
-
const
|
|
37
|
-
//
|
|
38
|
-
let
|
|
40
|
+
const rawLocaleTag = path.split(':', 3)[2];
|
|
41
|
+
// Extract and normalize the base name of the raw locale tag
|
|
42
|
+
let partialLocaleTag;
|
|
43
|
+
try {
|
|
44
|
+
const locale = new Intl.Locale(rawLocaleTag);
|
|
45
|
+
partialLocaleTag = locale.baseName;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return {
|
|
49
|
+
path: rawLocaleTag,
|
|
50
|
+
namespace: exports.LOCALE_DATA_NAMESPACE,
|
|
51
|
+
errors: [
|
|
52
|
+
{
|
|
53
|
+
text: `Invalid or unsupported locale provided in configuration: "${rawLocaleTag}"`,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
39
58
|
let exact = true;
|
|
40
|
-
while (
|
|
41
|
-
|
|
59
|
+
while (partialLocaleTag) {
|
|
60
|
+
// Angular embeds the `en`/`en-US` locale into the framework and it does not need to be included again here.
|
|
61
|
+
// The onLoad hook below for the locale data namespace has an `empty` loader that will prevent inclusion.
|
|
62
|
+
// Angular does not contain exact locale data for `en-US` but `en` is equivalent.
|
|
63
|
+
if (partialLocaleTag === 'en' || partialLocaleTag === 'en-US') {
|
|
64
|
+
return {
|
|
65
|
+
path: rawLocaleTag,
|
|
66
|
+
namespace: exports.LOCALE_DATA_NAMESPACE,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Attempt to resolve the locale tag data within the Angular base module location
|
|
70
|
+
const potentialPath = `${exports.LOCALE_DATA_BASE_MODULE}/${partialLocaleTag}`;
|
|
42
71
|
const result = await build.resolve(potentialPath, {
|
|
43
72
|
kind: 'import-statement',
|
|
44
73
|
resolveDir: build.initialOptions.absWorkingDir,
|
|
@@ -54,36 +83,37 @@ function createAngularLocaleDataPlugin() {
|
|
|
54
83
|
...result.warnings,
|
|
55
84
|
{
|
|
56
85
|
location: null,
|
|
57
|
-
text: `Locale data for '${
|
|
86
|
+
text: `Locale data for '${rawLocaleTag}' cannot be found. Using locale data for '${partialLocaleTag}'.`,
|
|
58
87
|
},
|
|
59
88
|
],
|
|
60
89
|
};
|
|
61
90
|
}
|
|
62
91
|
}
|
|
63
|
-
// Remove the last subtag and try again with a less specific locale
|
|
64
|
-
|
|
65
|
-
|
|
92
|
+
// Remove the last subtag and try again with a less specific locale.
|
|
93
|
+
// Usually the match is exact so the string splitting here is not done until actually needed after the exact
|
|
94
|
+
// match fails to resolve.
|
|
95
|
+
const parts = partialLocaleTag.split('-');
|
|
96
|
+
partialLocaleTag = parts.slice(0, -1).join('-');
|
|
66
97
|
exact = false;
|
|
67
|
-
// The locales "en" and "en-US" are considered exact to retain existing behavior
|
|
68
|
-
if (originalLocale === 'en-US' && partialLocale === 'en') {
|
|
69
|
-
exact = true;
|
|
70
|
-
}
|
|
71
98
|
}
|
|
72
99
|
// Not found so issue a warning and use an empty loader. Framework built-in `en-US` data will be used.
|
|
73
100
|
// This retains existing behavior as in the Webpack-based builder.
|
|
74
101
|
return {
|
|
75
|
-
path:
|
|
76
|
-
namespace:
|
|
102
|
+
path: rawLocaleTag,
|
|
103
|
+
namespace: exports.LOCALE_DATA_NAMESPACE,
|
|
77
104
|
warnings: [
|
|
78
105
|
{
|
|
79
106
|
location: null,
|
|
80
|
-
text: `Locale data for '${
|
|
107
|
+
text: `Locale data for '${rawLocaleTag}' cannot be found. No locale data will be included for this locale.`,
|
|
81
108
|
},
|
|
82
109
|
],
|
|
83
110
|
};
|
|
84
111
|
});
|
|
85
112
|
// Locales that cannot be found will be loaded as empty content with a warning from the resolve step
|
|
86
|
-
build.onLoad({ filter: /./, namespace:
|
|
113
|
+
build.onLoad({ filter: /./, namespace: exports.LOCALE_DATA_NAMESPACE }, () => ({
|
|
114
|
+
contents: '',
|
|
115
|
+
loader: 'empty',
|
|
116
|
+
}));
|
|
87
117
|
},
|
|
88
118
|
};
|
|
89
119
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.createWatcher = exports.ChangedFiles = void 0;
|
|
11
11
|
const chokidar_1 = require("chokidar");
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
12
13
|
class ChangedFiles {
|
|
13
14
|
added = new Set();
|
|
14
15
|
modified = new Set();
|
|
@@ -31,43 +32,75 @@ function createWatcher(options) {
|
|
|
31
32
|
usePolling: options?.polling,
|
|
32
33
|
interval: options?.interval,
|
|
33
34
|
ignored: options?.ignored,
|
|
35
|
+
followSymlinks: options?.followSymlinks,
|
|
34
36
|
disableGlobbing: true,
|
|
35
37
|
ignoreInitial: true,
|
|
36
38
|
});
|
|
37
39
|
const nextQueue = [];
|
|
38
40
|
let currentChanges;
|
|
39
41
|
let nextWaitTimeout;
|
|
40
|
-
|
|
42
|
+
/**
|
|
43
|
+
* We group the current events in a map as on Windows with certain IDE a file contents change can trigger multiple events.
|
|
44
|
+
*
|
|
45
|
+
* Example:
|
|
46
|
+
* rename | 'C:/../src/app/app.component.css'
|
|
47
|
+
* rename | 'C:/../src/app/app.component.css'
|
|
48
|
+
* change | 'C:/../src/app/app.component.css'
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
let currentEvents;
|
|
52
|
+
/**
|
|
53
|
+
* Using `watcher.on('all')` does not capture some of events fired when using Visual studio and this does not happen all the time,
|
|
54
|
+
* but only after a file has been changed 3 or more times.
|
|
55
|
+
*
|
|
56
|
+
* Also, some IDEs such as Visual Studio (not VS Code) will fire a rename event instead of unlink when a file is renamed or changed.
|
|
57
|
+
*
|
|
58
|
+
* Example:
|
|
59
|
+
* ```
|
|
60
|
+
* watcher.on('raw')
|
|
61
|
+
* Change 1
|
|
62
|
+
* rename | 'C:/../src/app/app.component.css'
|
|
63
|
+
* rename | 'C:/../src/app/app.component.css'
|
|
64
|
+
* change | 'C:/../src/app/app.component.css'
|
|
65
|
+
*
|
|
66
|
+
* Change 2
|
|
67
|
+
* rename | 'C:/../src/app/app.component.css'
|
|
68
|
+
* rename | 'C:/../src/app/app.component.css'
|
|
69
|
+
* change | 'C:/../src/app/app.component.css'
|
|
70
|
+
*
|
|
71
|
+
* Change 3
|
|
72
|
+
* rename | 'C:/../src/app/app.component.css'
|
|
73
|
+
* rename | 'C:/../src/app/app.component.css'
|
|
74
|
+
* change | 'C:/../src/app/app.component.css'
|
|
75
|
+
*
|
|
76
|
+
* watcher.on('all')
|
|
77
|
+
* Change 1
|
|
78
|
+
* change | 'C:\\..\\src\\app\\app.component.css'
|
|
79
|
+
*
|
|
80
|
+
* Change 2
|
|
81
|
+
* unlink | 'C:\\..\\src\\app\\app.component.css'
|
|
82
|
+
*
|
|
83
|
+
* Change 3
|
|
84
|
+
* ... (Nothing)
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
watcher
|
|
88
|
+
.on('raw', (event, path, { watchedPath }) => {
|
|
89
|
+
if (watchedPath && !(0, node_path_1.extname)(watchedPath)) {
|
|
90
|
+
// Ignore directories, file changes in directories will be fired seperatly.
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
41
93
|
switch (event) {
|
|
42
|
-
case '
|
|
43
|
-
currentChanges ??= new ChangedFiles();
|
|
44
|
-
currentChanges.added.add(path);
|
|
45
|
-
break;
|
|
94
|
+
case 'rename':
|
|
46
95
|
case 'change':
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
currentChanges ??= new ChangedFiles();
|
|
52
|
-
currentChanges.removed.add(path);
|
|
96
|
+
// When polling is enabled `watchedPath` can be undefined.
|
|
97
|
+
// `path` is always normalized unlike `watchedPath`.
|
|
98
|
+
const changedPath = watchedPath ? (0, node_path_1.normalize)(watchedPath) : path;
|
|
99
|
+
handleFileChange(event, changedPath);
|
|
53
100
|
break;
|
|
54
|
-
default:
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
// Wait 250ms from next change to better capture groups of file save operations.
|
|
58
|
-
if (!nextWaitTimeout) {
|
|
59
|
-
nextWaitTimeout = setTimeout(() => {
|
|
60
|
-
nextWaitTimeout = undefined;
|
|
61
|
-
const next = nextQueue.shift();
|
|
62
|
-
if (next) {
|
|
63
|
-
const value = currentChanges;
|
|
64
|
-
currentChanges = undefined;
|
|
65
|
-
next(value);
|
|
66
|
-
}
|
|
67
|
-
}, 250);
|
|
68
|
-
nextWaitTimeout?.unref();
|
|
69
101
|
}
|
|
70
|
-
})
|
|
102
|
+
})
|
|
103
|
+
.on('all', handleFileChange);
|
|
71
104
|
return {
|
|
72
105
|
[Symbol.asyncIterator]() {
|
|
73
106
|
return this;
|
|
@@ -103,5 +136,48 @@ function createWatcher(options) {
|
|
|
103
136
|
}
|
|
104
137
|
},
|
|
105
138
|
};
|
|
139
|
+
function handleFileChange(event, path) {
|
|
140
|
+
switch (event) {
|
|
141
|
+
case 'add':
|
|
142
|
+
case 'change':
|
|
143
|
+
// When using Visual Studio the rename event is fired before a change event when the contents of the file changed
|
|
144
|
+
// or instead of `unlink` when the file has been renamed.
|
|
145
|
+
case 'unlink':
|
|
146
|
+
case 'rename':
|
|
147
|
+
currentEvents ??= new Map();
|
|
148
|
+
currentEvents.set(path, event);
|
|
149
|
+
break;
|
|
150
|
+
default:
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Wait 250ms from next change to better capture groups of file save operations.
|
|
154
|
+
if (!nextWaitTimeout) {
|
|
155
|
+
nextWaitTimeout = setTimeout(() => {
|
|
156
|
+
nextWaitTimeout = undefined;
|
|
157
|
+
const next = nextQueue.shift();
|
|
158
|
+
if (next && currentEvents) {
|
|
159
|
+
const events = currentEvents;
|
|
160
|
+
currentEvents = undefined;
|
|
161
|
+
const currentChanges = new ChangedFiles();
|
|
162
|
+
for (const [path, event] of events) {
|
|
163
|
+
switch (event) {
|
|
164
|
+
case 'add':
|
|
165
|
+
currentChanges.added.add(path);
|
|
166
|
+
break;
|
|
167
|
+
case 'change':
|
|
168
|
+
currentChanges.modified.add(path);
|
|
169
|
+
break;
|
|
170
|
+
case 'unlink':
|
|
171
|
+
case 'rename':
|
|
172
|
+
currentChanges.removed.add(path);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
next(currentChanges);
|
|
177
|
+
}
|
|
178
|
+
}, 250);
|
|
179
|
+
nextWaitTimeout?.unref();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
106
182
|
}
|
|
107
183
|
exports.createWatcher = createWatcher;
|
|
@@ -16,6 +16,25 @@ const node_fs_1 = require("node:fs");
|
|
|
16
16
|
const node_path_1 = require("node:path");
|
|
17
17
|
const node_url_1 = require("node:url");
|
|
18
18
|
const lexer_1 = require("./lexer");
|
|
19
|
+
/**
|
|
20
|
+
* Ensures that a bare specifier URL path that is intended to be treated as
|
|
21
|
+
* a relative path has a leading `./` or `../` prefix.
|
|
22
|
+
*
|
|
23
|
+
* @param url A bare specifier URL path that should be considered relative.
|
|
24
|
+
* @returns
|
|
25
|
+
*/
|
|
26
|
+
function ensureRelative(url) {
|
|
27
|
+
// Empty
|
|
28
|
+
if (!url) {
|
|
29
|
+
return url;
|
|
30
|
+
}
|
|
31
|
+
// Already relative
|
|
32
|
+
if (url[0] === '.' && (url[1] === '/' || (url[1] === '.' && url[2] === '/'))) {
|
|
33
|
+
return url;
|
|
34
|
+
}
|
|
35
|
+
// Needs prefix
|
|
36
|
+
return './' + url;
|
|
37
|
+
}
|
|
19
38
|
/**
|
|
20
39
|
* A Sass Importer base class that provides the load logic to rebase all `url()` functions
|
|
21
40
|
* within a stylesheet. The rebasing will ensure that the URLs in the output of the Sass compiler
|
|
@@ -46,8 +65,13 @@ class UrlRebasingImporter {
|
|
|
46
65
|
// Rebase any URLs that are found
|
|
47
66
|
let updatedContents;
|
|
48
67
|
for (const { start, end, value } of (0, lexer_1.findUrls)(contents)) {
|
|
49
|
-
// Skip if value is empty
|
|
50
|
-
if (value.length === 0 || value[0] === '
|
|
68
|
+
// Skip if value is empty or Webpack-specific prefix
|
|
69
|
+
if (value.length === 0 || value[0] === '~' || value[0] === '^') {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// Skip if value is a Sass variable.
|
|
73
|
+
// Sass variable usage either starts with a `$` or contains a namespace and a `.$`
|
|
74
|
+
if (value[0] === '$' || /^\w+\.\$/.test(value)) {
|
|
51
75
|
continue;
|
|
52
76
|
}
|
|
53
77
|
// Skip if root-relative, absolute or protocol relative url
|
|
@@ -57,9 +81,10 @@ class UrlRebasingImporter {
|
|
|
57
81
|
const rebasedPath = (0, node_path_1.relative)(this.entryDirectory, (0, node_path_1.join)(stylesheetDirectory, value));
|
|
58
82
|
// Normalize path separators and escape characters
|
|
59
83
|
// https://developer.mozilla.org/en-US/docs/Web/CSS/url#syntax
|
|
60
|
-
const rebasedUrl =
|
|
84
|
+
const rebasedUrl = ensureRelative(rebasedPath.replace(/\\/g, '/').replace(/[()\s'"]/g, '\\$&'));
|
|
61
85
|
updatedContents ??= new magic_string_1.default(contents);
|
|
62
|
-
|
|
86
|
+
// Always quote the URL to avoid potential downstream parsing problems
|
|
87
|
+
updatedContents.update(start, end, `"${rebasedUrl}"`);
|
|
63
88
|
}
|
|
64
89
|
if (updatedContents) {
|
|
65
90
|
contents = updatedContents.toString();
|
package/src/utils/check-port.js
CHANGED
|
@@ -6,43 +6,26 @@
|
|
|
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
|
|
10
|
-
|
|
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;
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
31
11
|
};
|
|
32
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
13
|
exports.checkPort = void 0;
|
|
34
|
-
const
|
|
14
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
15
|
+
const node_net_1 = require("node:net");
|
|
35
16
|
const load_esm_1 = require("./load-esm");
|
|
36
17
|
const tty_1 = require("./tty");
|
|
37
18
|
function createInUseError(port) {
|
|
38
19
|
return new Error(`Port ${port} is already in use. Use '--port' to specify a different port.`);
|
|
39
20
|
}
|
|
40
21
|
async function checkPort(port, host) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
22
|
+
// Disabled due to Vite not handling port 0 and instead always using the default value (5173)
|
|
23
|
+
// TODO: Enable this again once Vite is fixed
|
|
24
|
+
// if (port === 0) {
|
|
25
|
+
// return 0;
|
|
26
|
+
// }
|
|
44
27
|
return new Promise((resolve, reject) => {
|
|
45
|
-
const server =
|
|
28
|
+
const server = (0, node_net_1.createServer)();
|
|
46
29
|
server
|
|
47
30
|
.once('error', (err) => {
|
|
48
31
|
if (err.code !== 'EADDRINUSE') {
|
|
@@ -60,11 +43,14 @@ async function checkPort(port, host) {
|
|
|
60
43
|
message: `Port ${port} is already in use.\nWould you like to use a different port?`,
|
|
61
44
|
default: true,
|
|
62
45
|
}))
|
|
63
|
-
.then((answers) =>
|
|
46
|
+
.then((answers) => answers.useDifferent ? resolve(checkPort(0, host)) : reject(createInUseError(port)), () => reject(createInUseError(port)));
|
|
64
47
|
})
|
|
65
48
|
.once('listening', () => {
|
|
49
|
+
// Get the actual address from the listening server instance
|
|
50
|
+
const address = server.address();
|
|
51
|
+
(0, node_assert_1.default)(address && typeof address !== 'string', 'Port check server address should always be an object.');
|
|
66
52
|
server.close();
|
|
67
|
-
resolve(port);
|
|
53
|
+
resolve(address.port);
|
|
68
54
|
})
|
|
69
55
|
.listen(port, host);
|
|
70
56
|
});
|
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
/**
|
|
9
9
|
* Delete an output directory, but error out if it's the root of the project.
|
|
10
10
|
*/
|
|
11
|
-
export declare function deleteOutputDir(root: string, outputPath: string): Promise<void>;
|
|
11
|
+
export declare function deleteOutputDir(root: string, outputPath: string, emptyOnlyDirectories?: string[]): Promise<void>;
|
|
@@ -13,11 +13,14 @@ const node_path_1 = require("node:path");
|
|
|
13
13
|
/**
|
|
14
14
|
* Delete an output directory, but error out if it's the root of the project.
|
|
15
15
|
*/
|
|
16
|
-
async function deleteOutputDir(root, outputPath) {
|
|
16
|
+
async function deleteOutputDir(root, outputPath, emptyOnlyDirectories) {
|
|
17
17
|
const resolvedOutputPath = (0, node_path_1.resolve)(root, outputPath);
|
|
18
18
|
if (resolvedOutputPath === root) {
|
|
19
19
|
throw new Error('Output path MUST not be project root directory!');
|
|
20
20
|
}
|
|
21
|
+
const directoriesToEmpty = emptyOnlyDirectories
|
|
22
|
+
? new Set(emptyOnlyDirectories.map((directory) => (0, node_path_1.join)(resolvedOutputPath, directory)))
|
|
23
|
+
: undefined;
|
|
21
24
|
// Avoid removing the actual directory to avoid errors in cases where the output
|
|
22
25
|
// directory is mounted or symlinked. Instead the contents are removed.
|
|
23
26
|
let entries;
|
|
@@ -31,7 +34,13 @@ async function deleteOutputDir(root, outputPath) {
|
|
|
31
34
|
throw error;
|
|
32
35
|
}
|
|
33
36
|
for (const entry of entries) {
|
|
34
|
-
|
|
37
|
+
const fullEntry = (0, node_path_1.join)(resolvedOutputPath, entry);
|
|
38
|
+
// Leave requested directories. This allows symlinks to continue to function.
|
|
39
|
+
if (directoriesToEmpty?.has(fullEntry)) {
|
|
40
|
+
await deleteOutputDir(resolvedOutputPath, fullEntry);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
await (0, promises_1.rm)(fullEntry, { force: true, recursive: true, maxRetries: 3 });
|
|
35
44
|
}
|
|
36
45
|
}
|
|
37
46
|
exports.deleteOutputDir = deleteOutputDir;
|
|
@@ -82,6 +82,4 @@ exports.useLegacySass = (() => {
|
|
|
82
82
|
const debugPerfVariable = process.env['NG_BUILD_DEBUG_PERF'];
|
|
83
83
|
exports.debugPerformance = isPresent(debugPerfVariable) && isEnabled(debugPerfVariable);
|
|
84
84
|
const watchRootVariable = process.env['NG_BUILD_WATCH_ROOT'];
|
|
85
|
-
exports.shouldWatchRoot =
|
|
86
|
-
? !isPresent(watchRootVariable) || !isDisabled(watchRootVariable)
|
|
87
|
-
: isPresent(watchRootVariable) && isEnabled(watchRootVariable);
|
|
85
|
+
exports.shouldWatchRoot = isPresent(watchRootVariable) && isEnabled(watchRootVariable);
|
|
@@ -195,7 +195,16 @@ class InlineFontsProcessor {
|
|
|
195
195
|
.get(url, {
|
|
196
196
|
agent,
|
|
197
197
|
headers: {
|
|
198
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Always use a Windows UA. This is because Google fonts will including hinting in fonts for Windows.
|
|
200
|
+
* Hinting is a technique used with Windows files to improve appearance however
|
|
201
|
+
* results in 20-50% larger file sizes.
|
|
202
|
+
*
|
|
203
|
+
* @see http://google3/java/com/google/fonts/css/OpenSansWebFontsCssBuilder.java?l=22
|
|
204
|
+
* @see https://fonts.google.com/knowledge/glossary/hinting (short)
|
|
205
|
+
* @see https://glyphsapp.com/learn/hinting-manual-truetype-hinting (deep dive)
|
|
206
|
+
*/
|
|
207
|
+
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
199
208
|
},
|
|
200
209
|
}, (res) => {
|
|
201
210
|
if (res.statusCode !== 200) {
|
|
@@ -11,19 +11,20 @@ exports.getESMLoaderArgs = exports.callInitializeIfNeeded = void 0;
|
|
|
11
11
|
const node_path_1 = require("node:path");
|
|
12
12
|
const node_url_1 = require("node:url");
|
|
13
13
|
const node_worker_threads_1 = require("node:worker_threads");
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const semver_1 = require("semver");
|
|
15
|
+
let SUPPORTS_IMPORT_FLAG;
|
|
16
|
+
function supportsImportFlag() {
|
|
17
|
+
return (SUPPORTS_IMPORT_FLAG ??= (0, semver_1.satisfies)(process.versions.node, '>= 18.19'));
|
|
17
18
|
}
|
|
18
19
|
/** Call the initialize hook when running on Node.js 18 */
|
|
19
20
|
function callInitializeIfNeeded(initialize) {
|
|
20
|
-
if (
|
|
21
|
+
if (!supportsImportFlag()) {
|
|
21
22
|
initialize(node_worker_threads_1.workerData);
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
exports.callInitializeIfNeeded = callInitializeIfNeeded;
|
|
25
26
|
function getESMLoaderArgs() {
|
|
26
|
-
if (
|
|
27
|
+
if (!supportsImportFlag()) {
|
|
27
28
|
return [
|
|
28
29
|
'--no-warnings',
|
|
29
30
|
'--loader',
|
package/src/utils/spinner.js
CHANGED
|
@@ -21,7 +21,7 @@ class Spinner {
|
|
|
21
21
|
#isTTY = (0, tty_1.isTTY)();
|
|
22
22
|
constructor(text) {
|
|
23
23
|
this.spinner = (0, ora_1.default)({
|
|
24
|
-
text,
|
|
24
|
+
text: text === undefined ? undefined : text + '\n',
|
|
25
25
|
// The below 2 options are needed because otherwise CTRL+C will be delayed
|
|
26
26
|
// when the underlying process is sync.
|
|
27
27
|
hideCursor: false,
|