@angular/build 20.0.0-next.8 → 20.0.0-rc.0
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/.browserslistrc +3 -3
- package/package.json +12 -12
- package/src/builders/application/execute-build.js +3 -2
- package/src/builders/application/i18n.js +15 -0
- package/src/builders/application/options.js +10 -24
- package/src/builders/application/schema.d.ts +1 -1
- package/src/builders/application/schema.json +1 -1
- package/src/builders/karma/application_builder.js +4 -3
- package/src/builders/unit-test/builder.js +49 -2
- package/src/builders/unit-test/karma-bridge.d.ts +10 -0
- package/src/builders/unit-test/karma-bridge.js +74 -0
- package/src/builders/unit-test/options.d.ts +1 -0
- package/src/builders/unit-test/options.js +4 -3
- package/src/builders/unit-test/schema.d.ts +6 -0
- package/src/builders/unit-test/schema.js +1 -0
- package/src/builders/unit-test/schema.json +9 -1
- package/src/tools/angular/compilation/aot-compilation.js +1 -4
- package/src/tools/esbuild/application-code-bundle.js +53 -69
- package/src/tools/esbuild/bundler-context.js +18 -3
- package/src/tools/esbuild/i18n-inliner-worker.d.ts +38 -3
- package/src/tools/esbuild/i18n-inliner-worker.js +18 -3
- package/src/tools/esbuild/i18n-inliner.d.ts +5 -0
- package/src/tools/esbuild/i18n-inliner.js +26 -0
- package/src/tools/esbuild/utils.js +1 -1
- package/src/tools/vite/middlewares/chrome-devtools-middleware.js +1 -1
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/project-metadata.d.ts +21 -0
- package/src/utils/project-metadata.js +31 -0
- package/src/utils/worker-pool.js +1 -1
package/.browserslistrc
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/build",
|
|
3
|
-
"version": "20.0.0-
|
|
3
|
+
"version": "20.0.0-rc.0",
|
|
4
4
|
"description": "Official build system for Angular",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Angular CLI",
|
|
@@ -23,34 +23,34 @@
|
|
|
23
23
|
"builders": "builders.json",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@ampproject/remapping": "2.3.0",
|
|
26
|
-
"@angular-devkit/architect": "0.2000.0-
|
|
27
|
-
"@babel/core": "7.
|
|
28
|
-
"@babel/helper-annotate-as-pure": "7.
|
|
26
|
+
"@angular-devkit/architect": "0.2000.0-rc.0",
|
|
27
|
+
"@babel/core": "7.27.1",
|
|
28
|
+
"@babel/helper-annotate-as-pure": "7.27.1",
|
|
29
29
|
"@babel/helper-split-export-declaration": "7.24.7",
|
|
30
30
|
"@inquirer/confirm": "5.1.9",
|
|
31
31
|
"@vitejs/plugin-basic-ssl": "2.0.0",
|
|
32
32
|
"beasties": "0.3.3",
|
|
33
33
|
"browserslist": "^4.23.0",
|
|
34
|
-
"esbuild": "0.25.
|
|
34
|
+
"esbuild": "0.25.4",
|
|
35
35
|
"https-proxy-agent": "7.0.6",
|
|
36
36
|
"istanbul-lib-instrument": "6.0.3",
|
|
37
37
|
"jsonc-parser": "3.3.1",
|
|
38
|
-
"listr2": "8.3.
|
|
38
|
+
"listr2": "8.3.3",
|
|
39
39
|
"magic-string": "0.30.17",
|
|
40
40
|
"mrmime": "2.0.1",
|
|
41
41
|
"parse5-html-rewriting-stream": "7.1.0",
|
|
42
42
|
"picomatch": "4.0.2",
|
|
43
|
-
"piscina": "
|
|
44
|
-
"rollup": "4.40.
|
|
43
|
+
"piscina": "5.0.0",
|
|
44
|
+
"rollup": "4.40.2",
|
|
45
45
|
"sass": "1.87.0",
|
|
46
46
|
"semver": "7.7.1",
|
|
47
47
|
"source-map-support": "0.5.21",
|
|
48
48
|
"tinyglobby": "0.2.13",
|
|
49
|
-
"vite": "6.3.
|
|
49
|
+
"vite": "6.3.5",
|
|
50
50
|
"watchpack": "2.4.2"
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
53
|
-
"lmdb": "3.
|
|
53
|
+
"lmdb": "3.3.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@angular/core": "^20.0.0 || ^20.0.0-next.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@angular/platform-browser": "^20.0.0 || ^20.0.0-next.0",
|
|
61
61
|
"@angular/platform-server": "^20.0.0 || ^20.0.0-next.0",
|
|
62
62
|
"@angular/service-worker": "^20.0.0 || ^20.0.0-next.0",
|
|
63
|
-
"@angular/ssr": "^20.0.0-
|
|
63
|
+
"@angular/ssr": "^20.0.0-rc.0",
|
|
64
64
|
"karma": "^6.4.0",
|
|
65
65
|
"less": "^4.2.0",
|
|
66
66
|
"ng-packagr": "^20.0.0 || ^20.0.0-next.0",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
},
|
|
115
115
|
"packageManager": "pnpm@9.15.6",
|
|
116
116
|
"engines": {
|
|
117
|
-
"node": "^20.11.1 ||
|
|
117
|
+
"node": "^20.11.1 || ^22.11.0 || >=24.0.0",
|
|
118
118
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
|
119
119
|
"yarn": ">= 1.13.0"
|
|
120
120
|
},
|
|
@@ -229,8 +229,9 @@ async function executeBuild(options, context, rebuildState) {
|
|
|
229
229
|
const result = await (0, execute_post_bundle_1.executePostBundleSteps)(metafile, options, executionResult.outputFiles, executionResult.assetFiles, initialFiles,
|
|
230
230
|
// Set lang attribute to the defined source locale if present
|
|
231
231
|
i18nOptions.hasDefinedSourceLocale ? i18nOptions.sourceLocale : undefined);
|
|
232
|
-
|
|
233
|
-
executionResult.
|
|
232
|
+
// Deduplicate and add errors and warnings
|
|
233
|
+
executionResult.addErrors([...new Set(result.errors)]);
|
|
234
|
+
executionResult.addWarnings([...new Set(result.warnings)]);
|
|
234
235
|
executionResult.addPrerenderedRoutes(result.prerenderedRoutes);
|
|
235
236
|
executionResult.outputFiles.push(...result.additionalOutputFiles);
|
|
236
237
|
executionResult.assetFiles.push(...result.additionalAssets);
|
|
@@ -92,6 +92,21 @@ async function inlineI18n(metafile, options, executionResult, initialFiles) {
|
|
|
92
92
|
if (!i18nOptions.flatOutput) {
|
|
93
93
|
executionResult.assetFiles = updatedAssetFiles;
|
|
94
94
|
}
|
|
95
|
+
// Inline any template updates if present
|
|
96
|
+
if (executionResult.templateUpdates?.size) {
|
|
97
|
+
// The development server only allows a single locale but issue a warning if used programmatically (experimental)
|
|
98
|
+
// with multiple locales and template HMR.
|
|
99
|
+
if (i18nOptions.inlineLocales.size > 1) {
|
|
100
|
+
inlineResult.warnings.push(`Component HMR updates can only be inlined with a single locale. The first locale will be used.`);
|
|
101
|
+
}
|
|
102
|
+
const firstLocale = [...i18nOptions.inlineLocales][0];
|
|
103
|
+
for (const [id, content] of executionResult.templateUpdates) {
|
|
104
|
+
const templateUpdateResult = await inliner.inlineTemplateUpdate(firstLocale, i18nOptions.locales[firstLocale].translation, content, id);
|
|
105
|
+
executionResult.templateUpdates.set(id, templateUpdateResult.code);
|
|
106
|
+
inlineResult.errors.push(...templateUpdateResult.errors);
|
|
107
|
+
inlineResult.warnings.push(...templateUpdateResult.warnings);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
95
110
|
return inlineResult;
|
|
96
111
|
}
|
|
97
112
|
/**
|
|
@@ -23,6 +23,7 @@ const environment_options_1 = require("../../utils/environment-options");
|
|
|
23
23
|
const i18n_options_1 = require("../../utils/i18n-options");
|
|
24
24
|
const normalize_cache_1 = require("../../utils/normalize-cache");
|
|
25
25
|
const postcss_configuration_1 = require("../../utils/postcss-configuration");
|
|
26
|
+
const project_metadata_1 = require("../../utils/project-metadata");
|
|
26
27
|
const url_1 = require("../../utils/url");
|
|
27
28
|
const schema_1 = require("./schema");
|
|
28
29
|
/**
|
|
@@ -58,8 +59,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
|
|
|
58
59
|
// ref: https://github.com/nodejs/node/issues/7726
|
|
59
60
|
(0, node_fs_1.realpathSync)(context.workspaceRoot);
|
|
60
61
|
const projectMetadata = await context.getProjectMetadata(projectName);
|
|
61
|
-
const projectRoot =
|
|
62
|
-
const projectSourceRoot = normalizeDirectoryPath(node_path_1.default.join(workspaceRoot, projectMetadata.sourceRoot ?? 'src'));
|
|
62
|
+
const { projectRoot, projectSourceRoot } = (0, project_metadata_1.getProjectRootPaths)(workspaceRoot, projectMetadata);
|
|
63
63
|
// Gather persistent caching option and provide a project specific cache location
|
|
64
64
|
const cacheOptions = (0, normalize_cache_1.normalizeCacheOptions)(projectMetadata, workspaceRoot);
|
|
65
65
|
cacheOptions.path = node_path_1.default.join(cacheOptions.path, projectName);
|
|
@@ -69,7 +69,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
|
|
|
69
69
|
if (options.forceI18nFlatOutput) {
|
|
70
70
|
i18nOptions.flatOutput = true;
|
|
71
71
|
}
|
|
72
|
-
const entryPoints = normalizeEntryPoints(workspaceRoot, options.browser, options.entryPoints);
|
|
72
|
+
const entryPoints = normalizeEntryPoints(workspaceRoot, projectSourceRoot, options.browser, options.entryPoints);
|
|
73
73
|
const tsconfig = node_path_1.default.join(workspaceRoot, options.tsConfig);
|
|
74
74
|
const optimizationOptions = (0, utils_1.normalizeOptimization)(options.optimization);
|
|
75
75
|
const sourcemapOptions = (0, utils_1.normalizeSourceMaps)(options.sourceMap ?? false);
|
|
@@ -169,7 +169,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
|
|
|
169
169
|
server: 'server',
|
|
170
170
|
media: 'media',
|
|
171
171
|
...(typeof outputPath === 'string' ? undefined : outputPath),
|
|
172
|
-
base: normalizeDirectoryPath(node_path_1.default.resolve(workspaceRoot, typeof outputPath === 'string' ? outputPath : outputPath.base)),
|
|
172
|
+
base: (0, project_metadata_1.normalizeDirectoryPath)(node_path_1.default.resolve(workspaceRoot, typeof outputPath === 'string' ? outputPath : outputPath.base)),
|
|
173
173
|
clean: options.deleteOutputPath ?? true,
|
|
174
174
|
// For app-shell and SSG server files are not required by users.
|
|
175
175
|
// Omit these when SSR is not enabled.
|
|
@@ -355,23 +355,22 @@ async function getTailwindConfig(searchDirectories, workspaceRoot, context) {
|
|
|
355
355
|
* @param entryPoints Set of entry points to use if provided.
|
|
356
356
|
* @returns An object mapping entry point names to their file paths.
|
|
357
357
|
*/
|
|
358
|
-
function normalizeEntryPoints(workspaceRoot, browser, entryPoints
|
|
358
|
+
function normalizeEntryPoints(workspaceRoot, projectSourceRoot, browser, entryPoints) {
|
|
359
359
|
if (browser === '') {
|
|
360
360
|
throw new Error('`browser` option cannot be an empty string.');
|
|
361
361
|
}
|
|
362
362
|
// `browser` and `entryPoints` are mutually exclusive.
|
|
363
|
-
if (browser && entryPoints
|
|
363
|
+
if (browser && entryPoints) {
|
|
364
364
|
throw new Error('Only one of `browser` or `entryPoints` may be provided.');
|
|
365
365
|
}
|
|
366
|
-
if (!browser && entryPoints.size === 0) {
|
|
367
|
-
// Schema should normally reject this case, but programmatic usages of the builder might make this mistake.
|
|
368
|
-
throw new Error('Either `browser` or at least one `entryPoints` value must be provided.');
|
|
369
|
-
}
|
|
370
|
-
// Schema types force `browser` to always be provided, but it may be omitted when the builder is invoked programmatically.
|
|
371
366
|
if (browser) {
|
|
372
367
|
// Use `browser` alone.
|
|
373
368
|
return { 'main': node_path_1.default.join(workspaceRoot, browser) };
|
|
374
369
|
}
|
|
370
|
+
else if (!entryPoints) {
|
|
371
|
+
// Default browser entry if no explicit entry points
|
|
372
|
+
return { 'main': node_path_1.default.join(projectSourceRoot, 'main.ts') };
|
|
373
|
+
}
|
|
375
374
|
else if (entryPoints instanceof Map) {
|
|
376
375
|
return Object.fromEntries(Array.from(entryPoints.entries(), ([name, entryPoint]) => {
|
|
377
376
|
// Get the full file path to a relative entry point input. Leave bare specifiers alone so they are resolved as modules.
|
|
@@ -406,19 +405,6 @@ function normalizeEntryPoints(workspaceRoot, browser, entryPoints = new Set()) {
|
|
|
406
405
|
return entryPointPaths;
|
|
407
406
|
}
|
|
408
407
|
}
|
|
409
|
-
/**
|
|
410
|
-
* Normalize a directory path string.
|
|
411
|
-
* Currently only removes a trailing slash if present.
|
|
412
|
-
* @param path A path string.
|
|
413
|
-
* @returns A normalized path string.
|
|
414
|
-
*/
|
|
415
|
-
function normalizeDirectoryPath(path) {
|
|
416
|
-
const last = path[path.length - 1];
|
|
417
|
-
if (last === '/' || last === '\\') {
|
|
418
|
-
return path.slice(0, -1);
|
|
419
|
-
}
|
|
420
|
-
return path;
|
|
421
|
-
}
|
|
422
408
|
function normalizeGlobalEntries(rawEntries, defaultName) {
|
|
423
409
|
if (!rawEntries?.length) {
|
|
424
410
|
return [];
|
|
@@ -27,7 +27,7 @@ export type Schema = {
|
|
|
27
27
|
* The full path for the browser entry point to the application, relative to the current
|
|
28
28
|
* workspace.
|
|
29
29
|
*/
|
|
30
|
-
browser
|
|
30
|
+
browser?: string;
|
|
31
31
|
/**
|
|
32
32
|
* Budget thresholds to ensure parts of your application stay within boundaries which you
|
|
33
33
|
* set.
|
|
@@ -50,6 +50,7 @@ const tinyglobby_1 = require("tinyglobby");
|
|
|
50
50
|
const bundler_context_1 = require("../../tools/esbuild/bundler-context");
|
|
51
51
|
const utils_1 = require("../../tools/esbuild/utils");
|
|
52
52
|
const virtual_module_plugin_1 = require("../../tools/esbuild/virtual-module-plugin");
|
|
53
|
+
const project_metadata_1 = require("../../utils/project-metadata");
|
|
53
54
|
const index_1 = require("../application/index");
|
|
54
55
|
const results_1 = require("../application/results");
|
|
55
56
|
const schema_1 = require("../application/schema");
|
|
@@ -87,7 +88,7 @@ class AngularAssetsMiddleware {
|
|
|
87
88
|
}
|
|
88
89
|
switch (file.origin) {
|
|
89
90
|
case 'disk':
|
|
90
|
-
this.serveFile(file.inputPath, undefined, res);
|
|
91
|
+
this.serveFile(file.inputPath, undefined, res, undefined, undefined, /* doNotCache */ true);
|
|
91
92
|
break;
|
|
92
93
|
case 'memory':
|
|
93
94
|
// Include pathname to help with Content-Type headers.
|
|
@@ -266,8 +267,8 @@ async function getProjectSourceRoot(context) {
|
|
|
266
267
|
return context.workspaceRoot;
|
|
267
268
|
}
|
|
268
269
|
const projectMetadata = await context.getProjectMetadata(projectName);
|
|
269
|
-
const
|
|
270
|
-
return
|
|
270
|
+
const { projectSourceRoot } = (0, project_metadata_1.getProjectRootPaths)(context.workspaceRoot, projectMetadata);
|
|
271
|
+
return projectSourceRoot;
|
|
271
272
|
}
|
|
272
273
|
function normalizePolyfills(polyfills) {
|
|
273
274
|
if (typeof polyfills === 'string') {
|
|
@@ -13,6 +13,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.execute = execute;
|
|
14
14
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
15
15
|
const node_crypto_1 = require("node:crypto");
|
|
16
|
+
const node_module_1 = require("node:module");
|
|
16
17
|
const node_path_1 = __importDefault(require("node:path"));
|
|
17
18
|
const virtual_module_plugin_1 = require("../../tools/esbuild/virtual-module-plugin");
|
|
18
19
|
const load_esm_1 = require("../../utils/load-esm");
|
|
@@ -21,6 +22,7 @@ const results_1 = require("../application/results");
|
|
|
21
22
|
const schema_1 = require("../application/schema");
|
|
22
23
|
const application_builder_1 = require("../karma/application_builder");
|
|
23
24
|
const find_tests_1 = require("../karma/find-tests");
|
|
25
|
+
const karma_bridge_1 = require("./karma-bridge");
|
|
24
26
|
const options_1 = require("./options");
|
|
25
27
|
/**
|
|
26
28
|
* @experimental Direct usage of this function is considered experimental.
|
|
@@ -35,6 +37,12 @@ async function* execute(options, context, extensions = {}) {
|
|
|
35
37
|
context.logger.warn(`NOTE: The "${context.builder.builderName}" builder is currently EXPERIMENTAL and not ready for production use.`);
|
|
36
38
|
const normalizedOptions = await (0, options_1.normalizeOptions)(context, projectName, options);
|
|
37
39
|
const { projectSourceRoot, workspaceRoot, runnerName } = normalizedOptions;
|
|
40
|
+
// Translate options and use karma builder directly if specified
|
|
41
|
+
if (runnerName === 'karma') {
|
|
42
|
+
const karmaBridge = await (0, karma_bridge_1.useKarmaBuilder)(context, normalizedOptions);
|
|
43
|
+
yield* karmaBridge;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
38
46
|
if (runnerName !== 'vitest') {
|
|
39
47
|
context.logger.error('Unknown test runner: ' + runnerName);
|
|
40
48
|
return;
|
|
@@ -81,8 +89,14 @@ async function* execute(options, context, extensions = {}) {
|
|
|
81
89
|
loadContent: async () => {
|
|
82
90
|
const contents = [
|
|
83
91
|
// Initialize the Angular testing environment
|
|
84
|
-
`import { getTestBed } from '@angular/core/testing';`,
|
|
92
|
+
`import { getTestBed, ɵgetCleanupHook as getCleanupHook } from '@angular/core/testing';`,
|
|
85
93
|
`import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';`,
|
|
94
|
+
`import { beforeEach, afterEach } from 'vitest';`,
|
|
95
|
+
'',
|
|
96
|
+
// Same as https://github.com/angular/angular/blob/05a03d3f975771bb59c7eefd37c01fa127ee2229/packages/core/testing/src/test_hooks.ts#L21-L29
|
|
97
|
+
`beforeEach(getCleanupHook(false));`,
|
|
98
|
+
`afterEach(getCleanupHook(true));`,
|
|
99
|
+
'',
|
|
86
100
|
`getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting(), {`,
|
|
87
101
|
` errorOnUnknownElements: true,`,
|
|
88
102
|
` errorOnUnknownProperties: true,`,
|
|
@@ -97,6 +111,23 @@ async function* execute(options, context, extensions = {}) {
|
|
|
97
111
|
});
|
|
98
112
|
extensions.codePlugins.unshift(virtualTestBedInit);
|
|
99
113
|
let instance;
|
|
114
|
+
// Setup vitest browser options if configured
|
|
115
|
+
let browser;
|
|
116
|
+
if (normalizedOptions.browsers) {
|
|
117
|
+
const provider = findBrowserProvider(projectSourceRoot);
|
|
118
|
+
if (!provider) {
|
|
119
|
+
context.logger.error('The "browsers" option requires either "playwright" or "webdriverio" to be installed within the project.' +
|
|
120
|
+
' Please install one of these packages and rerun the test command.');
|
|
121
|
+
return { success: false };
|
|
122
|
+
}
|
|
123
|
+
browser = {
|
|
124
|
+
enabled: true,
|
|
125
|
+
provider,
|
|
126
|
+
instances: normalizedOptions.browsers.map((browserName) => ({
|
|
127
|
+
browser: browserName,
|
|
128
|
+
})),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
100
131
|
for await (const result of (0, application_1.buildApplicationInternal)(buildOptions, context, extensions)) {
|
|
101
132
|
if (result.kind === results_1.ResultKind.Failure) {
|
|
102
133
|
continue;
|
|
@@ -114,8 +145,12 @@ async function* execute(options, context, extensions = {}) {
|
|
|
114
145
|
test: {
|
|
115
146
|
root: outputPath,
|
|
116
147
|
setupFiles,
|
|
117
|
-
|
|
148
|
+
// Use `jsdom` if no browsers are explicitly configured.
|
|
149
|
+
// `node` is effectively no "environment" and the default.
|
|
150
|
+
environment: browser ? 'node' : 'jsdom',
|
|
118
151
|
watch: normalizedOptions.watch,
|
|
152
|
+
browser,
|
|
153
|
+
reporters: normalizedOptions.reporters ?? ['default'],
|
|
119
154
|
coverage: {
|
|
120
155
|
enabled: normalizedOptions.codeCoverage,
|
|
121
156
|
exclude: normalizedOptions.codeCoverageExclude,
|
|
@@ -128,3 +163,15 @@ async function* execute(options, context, extensions = {}) {
|
|
|
128
163
|
yield { success: testModules.every((testModule) => testModule.ok()) };
|
|
129
164
|
}
|
|
130
165
|
}
|
|
166
|
+
function findBrowserProvider(projectSourceRoot) {
|
|
167
|
+
const projectResolver = (0, node_module_1.createRequire)(projectSourceRoot + '/').resolve;
|
|
168
|
+
// These must be installed in the project to be used
|
|
169
|
+
const vitestBuiltinProviders = ['playwright', 'webdriverio'];
|
|
170
|
+
for (const providerName of vitestBuiltinProviders) {
|
|
171
|
+
try {
|
|
172
|
+
projectResolver(providerName);
|
|
173
|
+
return providerName;
|
|
174
|
+
}
|
|
175
|
+
catch { }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
|
+
*/
|
|
8
|
+
import type { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
|
|
9
|
+
import type { NormalizedUnitTestOptions } from './options';
|
|
10
|
+
export declare function useKarmaBuilder(context: BuilderContext, unitTestOptions: NormalizedUnitTestOptions): Promise<AsyncIterable<BuilderOutput>>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
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 () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.useKarmaBuilder = useKarmaBuilder;
|
|
44
|
+
async function useKarmaBuilder(context, unitTestOptions) {
|
|
45
|
+
const buildTargetOptions = (await context.validateOptions(await context.getTargetOptions(unitTestOptions.buildTarget), await context.getBuilderNameForTarget(unitTestOptions.buildTarget)));
|
|
46
|
+
const options = {
|
|
47
|
+
tsConfig: unitTestOptions.tsConfig,
|
|
48
|
+
polyfills: buildTargetOptions.polyfills,
|
|
49
|
+
assets: buildTargetOptions.assets,
|
|
50
|
+
scripts: buildTargetOptions.scripts,
|
|
51
|
+
styles: buildTargetOptions.styles,
|
|
52
|
+
inlineStyleLanguage: buildTargetOptions.inlineStyleLanguage,
|
|
53
|
+
stylePreprocessorOptions: buildTargetOptions.stylePreprocessorOptions,
|
|
54
|
+
externalDependencies: buildTargetOptions.externalDependencies,
|
|
55
|
+
loader: buildTargetOptions.loader,
|
|
56
|
+
define: buildTargetOptions.define,
|
|
57
|
+
include: unitTestOptions.include,
|
|
58
|
+
exclude: unitTestOptions.exclude,
|
|
59
|
+
sourceMap: buildTargetOptions.sourceMap,
|
|
60
|
+
progress: buildTargetOptions.progress,
|
|
61
|
+
watch: unitTestOptions.watch,
|
|
62
|
+
poll: buildTargetOptions.poll,
|
|
63
|
+
preserveSymlinks: buildTargetOptions.preserveSymlinks,
|
|
64
|
+
browsers: unitTestOptions.browsers?.join(','),
|
|
65
|
+
codeCoverage: unitTestOptions.codeCoverage,
|
|
66
|
+
codeCoverageExclude: unitTestOptions.codeCoverageExclude,
|
|
67
|
+
fileReplacements: buildTargetOptions.fileReplacements,
|
|
68
|
+
reporters: unitTestOptions.reporters,
|
|
69
|
+
webWorkerTsConfig: buildTargetOptions.webWorkerTsConfig,
|
|
70
|
+
aot: buildTargetOptions.aot,
|
|
71
|
+
};
|
|
72
|
+
const { execute } = await Promise.resolve().then(() => __importStar(require('../karma')));
|
|
73
|
+
return execute(options, context);
|
|
74
|
+
}
|
|
@@ -14,19 +14,19 @@ exports.normalizeOptions = normalizeOptions;
|
|
|
14
14
|
const architect_1 = require("@angular-devkit/architect");
|
|
15
15
|
const node_path_1 = __importDefault(require("node:path"));
|
|
16
16
|
const normalize_cache_1 = require("../../utils/normalize-cache");
|
|
17
|
+
const project_metadata_1 = require("../../utils/project-metadata");
|
|
17
18
|
async function normalizeOptions(context, projectName, options) {
|
|
18
19
|
// Setup base paths based on workspace root and project information
|
|
19
20
|
const workspaceRoot = context.workspaceRoot;
|
|
20
21
|
const projectMetadata = await context.getProjectMetadata(projectName);
|
|
21
|
-
const projectRoot =
|
|
22
|
-
const projectSourceRoot = normalizeDirectoryPath(node_path_1.default.join(workspaceRoot, projectMetadata.sourceRoot ?? 'src'));
|
|
22
|
+
const { projectRoot, projectSourceRoot } = (0, project_metadata_1.getProjectRootPaths)(workspaceRoot, projectMetadata);
|
|
23
23
|
// Gather persistent caching option and provide a project specific cache location
|
|
24
24
|
const cacheOptions = (0, normalize_cache_1.normalizeCacheOptions)(projectMetadata, workspaceRoot);
|
|
25
25
|
cacheOptions.path = node_path_1.default.join(cacheOptions.path, projectName);
|
|
26
26
|
// Target specifier defaults to the current project's build target using a development configuration
|
|
27
27
|
const buildTargetSpecifier = options.buildTarget ?? `::development`;
|
|
28
28
|
const buildTarget = (0, architect_1.targetFromTargetString)(buildTargetSpecifier, projectName, 'build');
|
|
29
|
-
const { codeCoverage, codeCoverageExclude, tsConfig, runner, reporters } = options;
|
|
29
|
+
const { codeCoverage, codeCoverageExclude, tsConfig, runner, reporters, browsers } = options;
|
|
30
30
|
return {
|
|
31
31
|
// Project/workspace information
|
|
32
32
|
workspaceRoot,
|
|
@@ -42,6 +42,7 @@ async function normalizeOptions(context, projectName, options) {
|
|
|
42
42
|
codeCoverageExclude,
|
|
43
43
|
tsConfig,
|
|
44
44
|
reporters,
|
|
45
|
+
browsers,
|
|
45
46
|
// TODO: Implement watch support
|
|
46
47
|
watch: false,
|
|
47
48
|
};
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* Unit testing options for Angular applications.
|
|
3
3
|
*/
|
|
4
4
|
export type Schema = {
|
|
5
|
+
/**
|
|
6
|
+
* A list of browsers to use for test execution. If undefined, jsdom on Node.js will be used
|
|
7
|
+
* instead of a browser.
|
|
8
|
+
*/
|
|
9
|
+
browsers?: string[];
|
|
5
10
|
/**
|
|
6
11
|
* A build builder target to serve in the format of `project:target[:configuration]`. You
|
|
7
12
|
* can also pass in more than one configuration name as a comma-separated list. Example:
|
|
@@ -50,5 +55,6 @@ export type Schema = {
|
|
|
50
55
|
* The name of the test runner to use for test execution.
|
|
51
56
|
*/
|
|
52
57
|
export declare enum Runner {
|
|
58
|
+
Karma = "karma",
|
|
53
59
|
Vitest = "vitest"
|
|
54
60
|
}
|
|
@@ -16,7 +16,15 @@
|
|
|
16
16
|
"runner": {
|
|
17
17
|
"type": "string",
|
|
18
18
|
"description": "The name of the test runner to use for test execution.",
|
|
19
|
-
"enum": ["vitest"]
|
|
19
|
+
"enum": ["karma", "vitest"]
|
|
20
|
+
},
|
|
21
|
+
"browsers": {
|
|
22
|
+
"description": "A list of browsers to use for test execution. If undefined, jsdom on Node.js will be used instead of a browser.",
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": {
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
"minItems": 1
|
|
20
28
|
},
|
|
21
29
|
"include": {
|
|
22
30
|
"type": "array",
|
|
@@ -121,10 +121,7 @@ class AotCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
121
121
|
const updateId = encodeURIComponent(`${host.getCanonicalFileName(relativePath)}@${node.name?.text}`);
|
|
122
122
|
const updateText = angularCompiler.emitHmrUpdateModule(node);
|
|
123
123
|
// If compiler cannot generate an update for the component, prevent template updates.
|
|
124
|
-
|
|
125
|
-
// prevents a template update at runtime.
|
|
126
|
-
// TODO: Support localized template update modules and remove this check.
|
|
127
|
-
if (updateText === null || updateText.includes('$localize')) {
|
|
124
|
+
if (updateText === null) {
|
|
128
125
|
// Build is needed if a template cannot be updated
|
|
129
126
|
templateUpdates = undefined;
|
|
130
127
|
break;
|
|
@@ -37,8 +37,8 @@ const wasm_plugin_1 = require("./wasm-plugin");
|
|
|
37
37
|
function createBrowserCodeBundleOptions(options, target, sourceFileCache, stylesheetBundler, angularCompilation, templateUpdates) {
|
|
38
38
|
return (loadCache) => {
|
|
39
39
|
const { entryPoints, outputNames, polyfills } = options;
|
|
40
|
-
const pluginOptions = (0, compiler_plugin_options_1.createCompilerPluginOptions)(options, sourceFileCache, loadCache, templateUpdates);
|
|
41
40
|
const zoneless = (0, utils_1.isZonelessApp)(polyfills);
|
|
41
|
+
const pluginOptions = (0, compiler_plugin_options_1.createCompilerPluginOptions)(options, sourceFileCache, loadCache, templateUpdates);
|
|
42
42
|
const buildOptions = {
|
|
43
43
|
...getEsBuildCommonOptions(options),
|
|
44
44
|
platform: 'browser',
|
|
@@ -51,22 +51,16 @@ function createBrowserCodeBundleOptions(options, target, sourceFileCache, styles
|
|
|
51
51
|
entryPoints,
|
|
52
52
|
target,
|
|
53
53
|
supported: (0, utils_1.getFeatureSupport)(target, zoneless),
|
|
54
|
-
plugins: [
|
|
55
|
-
(0, loader_import_attribute_plugin_1.createLoaderImportAttributePlugin)(),
|
|
56
|
-
(0, wasm_plugin_1.createWasmPlugin)({ allowAsync: zoneless, cache: loadCache }),
|
|
57
|
-
(0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)(),
|
|
58
|
-
(0, angular_localize_init_warning_plugin_1.createAngularLocalizeInitWarningPlugin)(),
|
|
59
|
-
(0, compiler_plugin_1.createCompilerPlugin)(
|
|
60
|
-
// JS/TS options
|
|
61
|
-
pluginOptions, angularCompilation,
|
|
62
|
-
// Component stylesheet bundler
|
|
63
|
-
stylesheetBundler),
|
|
64
|
-
],
|
|
65
54
|
};
|
|
55
|
+
buildOptions.plugins ??= [];
|
|
56
|
+
buildOptions.plugins.push((0, wasm_plugin_1.createWasmPlugin)({ allowAsync: zoneless, cache: loadCache }), (0, angular_localize_init_warning_plugin_1.createAngularLocalizeInitWarningPlugin)(), (0, compiler_plugin_1.createCompilerPlugin)(
|
|
57
|
+
// JS/TS options
|
|
58
|
+
pluginOptions, angularCompilation,
|
|
59
|
+
// Component stylesheet bundler
|
|
60
|
+
stylesheetBundler));
|
|
66
61
|
if (options.plugins) {
|
|
67
|
-
buildOptions.plugins
|
|
62
|
+
buildOptions.plugins.push(...options.plugins);
|
|
68
63
|
}
|
|
69
|
-
appendOptionsForExternalPackages(options, buildOptions);
|
|
70
64
|
return buildOptions;
|
|
71
65
|
};
|
|
72
66
|
}
|
|
@@ -186,20 +180,15 @@ function createServerMainCodeBundleOptions(options, target, sourceFileCache, sty
|
|
|
186
180
|
},
|
|
187
181
|
entryPoints,
|
|
188
182
|
supported: (0, utils_1.getFeatureSupport)(target, zoneless),
|
|
189
|
-
plugins: [
|
|
190
|
-
(0, wasm_plugin_1.createWasmPlugin)({ allowAsync: zoneless, cache: loadResultCache }),
|
|
191
|
-
(0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)(),
|
|
192
|
-
(0, angular_localize_init_warning_plugin_1.createAngularLocalizeInitWarningPlugin)(),
|
|
193
|
-
(0, compiler_plugin_1.createCompilerPlugin)(
|
|
194
|
-
// JS/TS options
|
|
195
|
-
pluginOptions,
|
|
196
|
-
// Browser compilation handles the actual Angular code compilation
|
|
197
|
-
new compilation_1.NoopCompilation(),
|
|
198
|
-
// Component stylesheet bundler
|
|
199
|
-
stylesheetBundler),
|
|
200
|
-
],
|
|
201
183
|
};
|
|
202
184
|
buildOptions.plugins ??= [];
|
|
185
|
+
buildOptions.plugins.push((0, wasm_plugin_1.createWasmPlugin)({ allowAsync: zoneless, cache: loadResultCache }), (0, angular_localize_init_warning_plugin_1.createAngularLocalizeInitWarningPlugin)(), (0, compiler_plugin_1.createCompilerPlugin)(
|
|
186
|
+
// JS/TS options
|
|
187
|
+
pluginOptions,
|
|
188
|
+
// Browser compilation handles the actual Angular code compilation
|
|
189
|
+
new compilation_1.NoopCompilation(),
|
|
190
|
+
// Component stylesheet bundler
|
|
191
|
+
stylesheetBundler));
|
|
203
192
|
if (!externalPackages) {
|
|
204
193
|
buildOptions.plugins.push((0, rxjs_esm_resolution_plugin_1.createRxjsEsmResolutionPlugin)());
|
|
205
194
|
}
|
|
@@ -259,7 +248,6 @@ function createServerMainCodeBundleOptions(options, target, sourceFileCache, sty
|
|
|
259
248
|
if (options.plugins) {
|
|
260
249
|
buildOptions.plugins.push(...options.plugins);
|
|
261
250
|
}
|
|
262
|
-
appendOptionsForExternalPackages(options, buildOptions);
|
|
263
251
|
return buildOptions;
|
|
264
252
|
};
|
|
265
253
|
}
|
|
@@ -291,19 +279,15 @@ function createSsrEntryCodeBundleOptions(options, target, sourceFileCache, style
|
|
|
291
279
|
'server': ssrEntryNamespace,
|
|
292
280
|
},
|
|
293
281
|
supported: (0, utils_1.getFeatureSupport)(target, true),
|
|
294
|
-
plugins: [
|
|
295
|
-
(0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)(),
|
|
296
|
-
(0, angular_localize_init_warning_plugin_1.createAngularLocalizeInitWarningPlugin)(),
|
|
297
|
-
(0, compiler_plugin_1.createCompilerPlugin)(
|
|
298
|
-
// JS/TS options
|
|
299
|
-
pluginOptions,
|
|
300
|
-
// Browser compilation handles the actual Angular code compilation
|
|
301
|
-
new compilation_1.NoopCompilation(),
|
|
302
|
-
// Component stylesheet bundler
|
|
303
|
-
stylesheetBundler),
|
|
304
|
-
],
|
|
305
282
|
};
|
|
306
283
|
buildOptions.plugins ??= [];
|
|
284
|
+
buildOptions.plugins.push((0, angular_localize_init_warning_plugin_1.createAngularLocalizeInitWarningPlugin)(), (0, compiler_plugin_1.createCompilerPlugin)(
|
|
285
|
+
// JS/TS options
|
|
286
|
+
pluginOptions,
|
|
287
|
+
// Browser compilation handles the actual Angular code compilation
|
|
288
|
+
new compilation_1.NoopCompilation(),
|
|
289
|
+
// Component stylesheet bundler
|
|
290
|
+
stylesheetBundler));
|
|
307
291
|
if (!externalPackages) {
|
|
308
292
|
buildOptions.plugins.push((0, rxjs_esm_resolution_plugin_1.createRxjsEsmResolutionPlugin)());
|
|
309
293
|
}
|
|
@@ -360,17 +344,16 @@ function createSsrEntryCodeBundleOptions(options, target, sourceFileCache, style
|
|
|
360
344
|
if (options.plugins) {
|
|
361
345
|
buildOptions.plugins.push(...options.plugins);
|
|
362
346
|
}
|
|
363
|
-
appendOptionsForExternalPackages(options, buildOptions);
|
|
364
347
|
return buildOptions;
|
|
365
348
|
};
|
|
366
349
|
}
|
|
367
350
|
function getEsBuildServerCommonOptions(options) {
|
|
368
351
|
const isNodePlatform = options.ssrOptions?.platform !== schema_1.ExperimentalPlatform.Neutral;
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
352
|
+
const commonOptions = getEsBuildCommonOptions(options);
|
|
353
|
+
commonOptions.define ??= {};
|
|
354
|
+
commonOptions.define['ngServerMode'] = 'true';
|
|
372
355
|
return {
|
|
373
|
-
...
|
|
356
|
+
...commonOptions,
|
|
374
357
|
platform: isNodePlatform ? 'node' : 'neutral',
|
|
375
358
|
outExtension: { '.js': '.mjs' },
|
|
376
359
|
// Note: `es2015` is needed for RxJS v6. If not specified, `module` would
|
|
@@ -413,14 +396,32 @@ function getEsBuildCommonOptions(options) {
|
|
|
413
396
|
}
|
|
414
397
|
else {
|
|
415
398
|
// Include default conditions
|
|
416
|
-
conditions.push('module');
|
|
417
|
-
|
|
399
|
+
conditions.push('module', optimizationOptions.scripts ? 'production' : 'development');
|
|
400
|
+
}
|
|
401
|
+
const plugins = [
|
|
402
|
+
(0, loader_import_attribute_plugin_1.createLoaderImportAttributePlugin)(),
|
|
403
|
+
(0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)(),
|
|
404
|
+
];
|
|
405
|
+
let packages = 'bundle';
|
|
406
|
+
if (options.externalPackages) {
|
|
407
|
+
// Package files affected by a customized loader should not be implicitly marked as external
|
|
408
|
+
if (options.loaderExtensions ||
|
|
409
|
+
options.plugins ||
|
|
410
|
+
typeof options.externalPackages === 'object') {
|
|
411
|
+
// Plugin must be added after custom plugins to ensure any added loader options are considered
|
|
412
|
+
plugins.push((0, external_packages_plugin_1.createExternalPackagesPlugin)(options.externalPackages !== true ? options.externalPackages : undefined));
|
|
413
|
+
packages = 'bundle';
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
// Safe to use the packages external option directly
|
|
417
|
+
packages = 'external';
|
|
418
|
+
}
|
|
418
419
|
}
|
|
419
420
|
return {
|
|
420
421
|
absWorkingDir: workspaceRoot,
|
|
421
422
|
format: 'esm',
|
|
422
423
|
bundle: true,
|
|
423
|
-
packages
|
|
424
|
+
packages,
|
|
424
425
|
assetNames: outputNames.media,
|
|
425
426
|
conditions,
|
|
426
427
|
resolveExtensions: ['.ts', '.tsx', '.mjs', '.js', '.cjs'],
|
|
@@ -453,15 +454,14 @@ function getEsBuildCommonOptions(options) {
|
|
|
453
454
|
},
|
|
454
455
|
loader: loaderExtensions,
|
|
455
456
|
footer,
|
|
457
|
+
plugins,
|
|
456
458
|
};
|
|
457
459
|
}
|
|
458
460
|
function getEsBuildCommonPolyfillsOptions(options, namespace, tryToResolvePolyfillsAsRelative, loadResultCache) {
|
|
459
461
|
const { jit, workspaceRoot, i18nOptions } = options;
|
|
460
|
-
const buildOptions =
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
plugins: [(0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)()],
|
|
464
|
-
};
|
|
462
|
+
const buildOptions = getEsBuildCommonOptions(options);
|
|
463
|
+
buildOptions.splitting = false;
|
|
464
|
+
buildOptions.plugins ??= [];
|
|
465
465
|
let polyfills = options.polyfills ? [...options.polyfills] : [];
|
|
466
466
|
// Angular JIT mode requires the runtime compiler
|
|
467
467
|
if (jit) {
|
|
@@ -486,12 +486,12 @@ function getEsBuildCommonPolyfillsOptions(options, namespace, tryToResolvePolyfi
|
|
|
486
486
|
needLocaleDataPlugin = true;
|
|
487
487
|
}
|
|
488
488
|
if (needLocaleDataPlugin) {
|
|
489
|
-
buildOptions.plugins
|
|
489
|
+
buildOptions.plugins.push((0, i18n_locale_plugin_1.createAngularLocaleDataPlugin)());
|
|
490
490
|
}
|
|
491
491
|
if (polyfills.length === 0) {
|
|
492
492
|
return;
|
|
493
493
|
}
|
|
494
|
-
buildOptions.plugins
|
|
494
|
+
buildOptions.plugins.push((0, virtual_module_plugin_1.createVirtualModulePlugin)({
|
|
495
495
|
namespace,
|
|
496
496
|
cache: loadResultCache,
|
|
497
497
|
loadContent: async (_, build) => {
|
|
@@ -539,19 +539,3 @@ function entryFileToWorkspaceRelative(workspaceRoot, entryFile) {
|
|
|
539
539
|
.replace(/.[mc]?ts$/, '')
|
|
540
540
|
.replace(/\\/g, '/'));
|
|
541
541
|
}
|
|
542
|
-
function appendOptionsForExternalPackages(options, buildOptions) {
|
|
543
|
-
if (!options.externalPackages) {
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
buildOptions.plugins ??= [];
|
|
547
|
-
// Package files affected by a customized loader should not be implicitly marked as external
|
|
548
|
-
if (options.loaderExtensions || options.plugins || typeof options.externalPackages === 'object') {
|
|
549
|
-
// Plugin must be added after custom plugins to ensure any added loader options are considered
|
|
550
|
-
buildOptions.plugins.push((0, external_packages_plugin_1.createExternalPackagesPlugin)(options.externalPackages !== true ? options.externalPackages : undefined));
|
|
551
|
-
buildOptions.packages = undefined;
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
// Safe to use the packages external option directly
|
|
555
|
-
buildOptions.packages = 'external';
|
|
556
|
-
}
|
|
557
|
-
}
|
|
@@ -13,6 +13,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.BundlerContext = exports.BuildOutputFileType = void 0;
|
|
14
14
|
const esbuild_1 = require("esbuild");
|
|
15
15
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
16
|
+
const node_module_1 = require("node:module");
|
|
16
17
|
const node_path_1 = require("node:path");
|
|
17
18
|
const load_result_cache_1 = require("./load-result-cache");
|
|
18
19
|
const utils_1 = require("./utils");
|
|
@@ -198,10 +199,11 @@ class BundlerContext {
|
|
|
198
199
|
if (this.incremental) {
|
|
199
200
|
// Add input files except virtual angular files which do not exist on disk
|
|
200
201
|
for (const input of Object.keys(result.metafile.inputs)) {
|
|
201
|
-
if (
|
|
202
|
-
|
|
203
|
-
this.watchFiles.add((0, node_path_1.join)(this.workspaceRoot, input));
|
|
202
|
+
if (isInternalAngularFile(input) || isInternalBundlerFile(input)) {
|
|
203
|
+
continue;
|
|
204
204
|
}
|
|
205
|
+
// Input file paths are always relative to the workspace root
|
|
206
|
+
this.watchFiles.add((0, node_path_1.join)(this.workspaceRoot, input));
|
|
205
207
|
}
|
|
206
208
|
}
|
|
207
209
|
// Return if the build encountered any errors
|
|
@@ -380,3 +382,16 @@ exports.BundlerContext = BundlerContext;
|
|
|
380
382
|
function isInternalAngularFile(file) {
|
|
381
383
|
return file.startsWith('angular:');
|
|
382
384
|
}
|
|
385
|
+
function isInternalBundlerFile(file) {
|
|
386
|
+
// Bundler virtual files such as "<define:???>" or "<runtime>"
|
|
387
|
+
if (file[0] === '<' && file.at(-1) === '>') {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
const DISABLED_BUILTIN = '(disabled):';
|
|
391
|
+
// Disabled node builtins such as "/some/path/(disabled):fs"
|
|
392
|
+
const disabledIndex = file.indexOf(DISABLED_BUILTIN);
|
|
393
|
+
if (disabledIndex >= 0) {
|
|
394
|
+
return node_module_1.builtinModules.includes(file.slice(disabledIndex + DISABLED_BUILTIN.length));
|
|
395
|
+
}
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
/**
|
|
9
9
|
* The options passed to the inliner for each file request
|
|
10
10
|
*/
|
|
11
|
-
interface
|
|
11
|
+
interface InlineFileRequest {
|
|
12
12
|
/**
|
|
13
13
|
* The filename that should be processed. The data for the file is provided to the Worker
|
|
14
14
|
* during Worker initialization.
|
|
@@ -23,14 +23,35 @@ interface InlineRequest {
|
|
|
23
23
|
*/
|
|
24
24
|
translation?: Record<string, unknown>;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* The options passed to the inliner for each code request
|
|
28
|
+
*/
|
|
29
|
+
interface InlineCodeRequest {
|
|
30
|
+
/**
|
|
31
|
+
* The code that should be processed.
|
|
32
|
+
*/
|
|
33
|
+
code: string;
|
|
34
|
+
/**
|
|
35
|
+
* The filename to use in error and warning messages for the provided code.
|
|
36
|
+
*/
|
|
37
|
+
filename: string;
|
|
38
|
+
/**
|
|
39
|
+
* The locale specifier that should be used during the inlining process of the file.
|
|
40
|
+
*/
|
|
41
|
+
locale: string;
|
|
42
|
+
/**
|
|
43
|
+
* The translation messages for the locale that should be used during the inlining process of the file.
|
|
44
|
+
*/
|
|
45
|
+
translation?: Record<string, unknown>;
|
|
46
|
+
}
|
|
26
47
|
/**
|
|
27
48
|
* Inlines the provided locale and translation into a JavaScript file that contains `$localize` usage.
|
|
28
49
|
* This function is the main entry for the Worker's action that is called by the worker pool.
|
|
29
50
|
*
|
|
30
51
|
* @param request An InlineRequest object representing the options for inlining
|
|
31
|
-
* @returns An
|
|
52
|
+
* @returns An object containing the inlined file and optional map content.
|
|
32
53
|
*/
|
|
33
|
-
export default function
|
|
54
|
+
export default function inlineFile(request: InlineFileRequest): Promise<{
|
|
34
55
|
file: string;
|
|
35
56
|
code: string;
|
|
36
57
|
map: string | undefined;
|
|
@@ -39,4 +60,18 @@ export default function inlineLocale(request: InlineRequest): Promise<{
|
|
|
39
60
|
message: string;
|
|
40
61
|
}[];
|
|
41
62
|
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Inlines the provided locale and translation into JavaScript code that contains `$localize` usage.
|
|
65
|
+
* This function is a secondary entry primarily for use with component HMR update modules.
|
|
66
|
+
*
|
|
67
|
+
* @param request An InlineRequest object representing the options for inlining
|
|
68
|
+
* @returns An object containing the inlined code.
|
|
69
|
+
*/
|
|
70
|
+
export declare function inlineCode(request: InlineCodeRequest): Promise<{
|
|
71
|
+
output: string;
|
|
72
|
+
messages: {
|
|
73
|
+
type: "warning" | "error";
|
|
74
|
+
message: string;
|
|
75
|
+
}[];
|
|
76
|
+
}>;
|
|
42
77
|
export {};
|
|
@@ -10,7 +10,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.default =
|
|
13
|
+
exports.default = inlineFile;
|
|
14
|
+
exports.inlineCode = inlineCode;
|
|
14
15
|
const remapping_1 = __importDefault(require("@ampproject/remapping"));
|
|
15
16
|
const core_1 = require("@babel/core");
|
|
16
17
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
@@ -25,9 +26,9 @@ const { files, missingTranslation, shouldOptimize } = (node_worker_threads_1.wor
|
|
|
25
26
|
* This function is the main entry for the Worker's action that is called by the worker pool.
|
|
26
27
|
*
|
|
27
28
|
* @param request An InlineRequest object representing the options for inlining
|
|
28
|
-
* @returns An
|
|
29
|
+
* @returns An object containing the inlined file and optional map content.
|
|
29
30
|
*/
|
|
30
|
-
async function
|
|
31
|
+
async function inlineFile(request) {
|
|
31
32
|
const data = files.get(request.filename);
|
|
32
33
|
(0, node_assert_1.default)(data !== undefined, `Invalid inline request for file '${request.filename}'.`);
|
|
33
34
|
const code = await data.text();
|
|
@@ -40,6 +41,20 @@ async function inlineLocale(request) {
|
|
|
40
41
|
messages: result.diagnostics.messages,
|
|
41
42
|
};
|
|
42
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Inlines the provided locale and translation into JavaScript code that contains `$localize` usage.
|
|
46
|
+
* This function is a secondary entry primarily for use with component HMR update modules.
|
|
47
|
+
*
|
|
48
|
+
* @param request An InlineRequest object representing the options for inlining
|
|
49
|
+
* @returns An object containing the inlined code.
|
|
50
|
+
*/
|
|
51
|
+
async function inlineCode(request) {
|
|
52
|
+
const result = await transformWithBabel(request.code, undefined, request);
|
|
53
|
+
return {
|
|
54
|
+
output: result.code,
|
|
55
|
+
messages: result.diagnostics.messages,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
43
58
|
/**
|
|
44
59
|
* Cached instance of the `@angular/localize/tools` module.
|
|
45
60
|
* This is used to remove the need to repeatedly import the module per file translation.
|
|
@@ -38,6 +38,11 @@ export declare class I18nInliner {
|
|
|
38
38
|
errors: string[];
|
|
39
39
|
warnings: string[];
|
|
40
40
|
}>;
|
|
41
|
+
inlineTemplateUpdate(locale: string, translation: Record<string, unknown> | undefined, templateCode: string, templateId: string): Promise<{
|
|
42
|
+
code: string;
|
|
43
|
+
errors: string[];
|
|
44
|
+
warnings: string[];
|
|
45
|
+
}>;
|
|
41
46
|
/**
|
|
42
47
|
* Stops all active transformation tasks and shuts down all workers.
|
|
43
48
|
* @returns A void promise that resolves when closing is complete.
|
|
@@ -201,6 +201,32 @@ class I18nInliner {
|
|
|
201
201
|
warnings,
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
|
+
async inlineTemplateUpdate(locale, translation, templateCode, templateId) {
|
|
205
|
+
const hasLocalize = templateCode.includes(LOCALIZE_KEYWORD);
|
|
206
|
+
if (!hasLocalize) {
|
|
207
|
+
return {
|
|
208
|
+
code: templateCode,
|
|
209
|
+
errors: [],
|
|
210
|
+
warnings: [],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const { output, messages } = await this.#workerPool.run({ code: templateCode, filename: templateId, locale, translation }, { name: 'inlineCode' });
|
|
214
|
+
const errors = [];
|
|
215
|
+
const warnings = [];
|
|
216
|
+
for (const message of messages) {
|
|
217
|
+
if (message.type === 'error') {
|
|
218
|
+
errors.push(message.message);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
warnings.push(message.message);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
code: output,
|
|
226
|
+
errors,
|
|
227
|
+
warnings,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
204
230
|
/**
|
|
205
231
|
* Stops all active transformation tasks and shuts down all workers.
|
|
206
232
|
* @returns A void promise that resolves when closing is complete.
|
|
@@ -315,7 +315,7 @@ function transformSupportedBrowsersToTargets(supportedBrowsers) {
|
|
|
315
315
|
}
|
|
316
316
|
return transformed;
|
|
317
317
|
}
|
|
318
|
-
const SUPPORTED_NODE_VERSIONS = '^20.11.1 ||
|
|
318
|
+
const SUPPORTED_NODE_VERSIONS = '^20.11.1 || ^22.11.0 || >=24.0.0';
|
|
319
319
|
/**
|
|
320
320
|
* Transform supported Node.js versions to esbuild target.
|
|
321
321
|
* @see https://esbuild.github.io/api/#target
|
|
@@ -27,7 +27,7 @@ function createChromeDevtoolsMiddleware(cacheDir, projectRoot) {
|
|
|
27
27
|
if (!devtoolsConfig) {
|
|
28
28
|
// We store the UUID and re-use it to ensure Chrome does not repeatedly ask for permissions when restarting the dev server.
|
|
29
29
|
try {
|
|
30
|
-
|
|
30
|
+
devtoolsConfig = (0, node_fs_1.readFileSync)(devtoolsConfigPath, 'utf-8');
|
|
31
31
|
const devtoolsConfigJson = JSON.parse(devtoolsConfig);
|
|
32
32
|
node_assert_1.default.equal(projectRoot, devtoolsConfigJson?.workspace.root);
|
|
33
33
|
}
|
|
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.normalizeCacheOptions = normalizeCacheOptions;
|
|
11
11
|
const node_path_1 = require("node:path");
|
|
12
12
|
/** Version placeholder is replaced during the build process with actual package version */
|
|
13
|
-
const VERSION = '20.0.0-
|
|
13
|
+
const VERSION = '20.0.0-rc.0';
|
|
14
14
|
function hasCacheMetadata(value) {
|
|
15
15
|
return (!!value &&
|
|
16
16
|
typeof value === 'object' &&
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a directory path string.
|
|
10
|
+
* Currently only removes a trailing slash if present.
|
|
11
|
+
* @param path A path string.
|
|
12
|
+
* @returns A normalized path string.
|
|
13
|
+
*/
|
|
14
|
+
export declare function normalizeDirectoryPath(path: string): string;
|
|
15
|
+
export declare function getProjectRootPaths(workspaceRoot: string, projectMetadata: {
|
|
16
|
+
root?: string;
|
|
17
|
+
sourceRoot?: string;
|
|
18
|
+
}): {
|
|
19
|
+
projectRoot: string;
|
|
20
|
+
projectSourceRoot: string;
|
|
21
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.normalizeDirectoryPath = normalizeDirectoryPath;
|
|
11
|
+
exports.getProjectRootPaths = getProjectRootPaths;
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
13
|
+
/**
|
|
14
|
+
* Normalize a directory path string.
|
|
15
|
+
* Currently only removes a trailing slash if present.
|
|
16
|
+
* @param path A path string.
|
|
17
|
+
* @returns A normalized path string.
|
|
18
|
+
*/
|
|
19
|
+
function normalizeDirectoryPath(path) {
|
|
20
|
+
const last = path[path.length - 1];
|
|
21
|
+
if (last === '/' || last === '\\') {
|
|
22
|
+
return path.slice(0, -1);
|
|
23
|
+
}
|
|
24
|
+
return path;
|
|
25
|
+
}
|
|
26
|
+
function getProjectRootPaths(workspaceRoot, projectMetadata) {
|
|
27
|
+
const projectRoot = normalizeDirectoryPath((0, node_path_1.join)(workspaceRoot, projectMetadata.root ?? ''));
|
|
28
|
+
const rawSourceRoot = projectMetadata.sourceRoot;
|
|
29
|
+
const projectSourceRoot = normalizeDirectoryPath(rawSourceRoot === undefined ? (0, node_path_1.join)(projectRoot, 'src') : (0, node_path_1.join)(workspaceRoot, rawSourceRoot));
|
|
30
|
+
return { projectRoot, projectSourceRoot };
|
|
31
|
+
}
|
package/src/utils/worker-pool.js
CHANGED
|
@@ -17,7 +17,7 @@ class WorkerPool extends piscina_1.Piscina {
|
|
|
17
17
|
idleTimeout: 1000,
|
|
18
18
|
// Web containers do not support transferable objects with receiveOnMessagePort which
|
|
19
19
|
// is used when the Atomics based wait loop is enable.
|
|
20
|
-
|
|
20
|
+
atomics: process.versions.webcontainer ? 'disabled' : 'sync',
|
|
21
21
|
recordTiming: false,
|
|
22
22
|
...options,
|
|
23
23
|
};
|