@angular/build 20.0.0-rc.0 → 20.0.0-rc.1
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 +7 -7
- package/src/builders/unit-test/builder.js +110 -52
- package/src/builders/unit-test/options.d.ts +2 -1
- package/src/builders/unit-test/options.js +3 -3
- package/src/builders/unit-test/schema.d.ts +5 -0
- package/src/builders/unit-test/schema.json +5 -0
- package/src/utils/load-proxy-config.js +1 -1
- package/src/utils/normalize-cache.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/build",
|
|
3
|
-
"version": "20.0.0-rc.
|
|
3
|
+
"version": "20.0.0-rc.1",
|
|
4
4
|
"description": "Official build system for Angular",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Angular CLI",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"builders": "builders.json",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@ampproject/remapping": "2.3.0",
|
|
26
|
-
"@angular-devkit/architect": "0.2000.0-rc.
|
|
26
|
+
"@angular-devkit/architect": "0.2000.0-rc.1",
|
|
27
27
|
"@babel/core": "7.27.1",
|
|
28
28
|
"@babel/helper-annotate-as-pure": "7.27.1",
|
|
29
29
|
"@babel/helper-split-export-declaration": "7.24.7",
|
|
30
|
-
"@inquirer/confirm": "5.1.
|
|
30
|
+
"@inquirer/confirm": "5.1.10",
|
|
31
31
|
"@vitejs/plugin-basic-ssl": "2.0.0",
|
|
32
32
|
"beasties": "0.3.3",
|
|
33
33
|
"browserslist": "^4.23.0",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"picomatch": "4.0.2",
|
|
43
43
|
"piscina": "5.0.0",
|
|
44
44
|
"rollup": "4.40.2",
|
|
45
|
-
"sass": "1.
|
|
46
|
-
"semver": "7.7.
|
|
45
|
+
"sass": "1.88.0",
|
|
46
|
+
"semver": "7.7.2",
|
|
47
47
|
"source-map-support": "0.5.21",
|
|
48
48
|
"tinyglobby": "0.2.13",
|
|
49
49
|
"vite": "6.3.5",
|
|
@@ -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-rc.
|
|
63
|
+
"@angular/ssr": "^20.0.0-rc.1",
|
|
64
64
|
"karma": "^6.4.0",
|
|
65
65
|
"less": "^4.2.0",
|
|
66
66
|
"ng-packagr": "^20.0.0 || ^20.0.0-next.0",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"type": "git",
|
|
113
113
|
"url": "https://github.com/angular/angular-cli.git"
|
|
114
114
|
},
|
|
115
|
-
"packageManager": "pnpm@9.15.
|
|
115
|
+
"packageManager": "pnpm@9.15.9",
|
|
116
116
|
"engines": {
|
|
117
117
|
"node": "^20.11.1 || ^22.11.0 || >=24.0.0",
|
|
118
118
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
|
@@ -16,6 +16,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
16
16
|
const node_module_1 = require("node:module");
|
|
17
17
|
const node_path_1 = __importDefault(require("node:path"));
|
|
18
18
|
const virtual_module_plugin_1 = require("../../tools/esbuild/virtual-module-plugin");
|
|
19
|
+
const error_1 = require("../../utils/error");
|
|
19
20
|
const load_esm_1 = require("../../utils/load-esm");
|
|
20
21
|
const application_1 = require("../application");
|
|
21
22
|
const results_1 = require("../application/results");
|
|
@@ -27,6 +28,7 @@ const options_1 = require("./options");
|
|
|
27
28
|
/**
|
|
28
29
|
* @experimental Direct usage of this function is considered experimental.
|
|
29
30
|
*/
|
|
31
|
+
// eslint-disable-next-line max-lines-per-function
|
|
30
32
|
async function* execute(options, context, extensions = {}) {
|
|
31
33
|
// Determine project name from builder context target
|
|
32
34
|
const projectName = context.target?.project;
|
|
@@ -55,7 +57,19 @@ async function* execute(options, context, extensions = {}) {
|
|
|
55
57
|
}
|
|
56
58
|
const entryPoints = (0, find_tests_1.getTestEntrypoints)(testFiles, { projectSourceRoot, workspaceRoot });
|
|
57
59
|
entryPoints.set('init-testbed', 'angular:test-bed-init');
|
|
58
|
-
|
|
60
|
+
let vitestNodeModule;
|
|
61
|
+
try {
|
|
62
|
+
vitestNodeModule = await (0, load_esm_1.loadEsmModule)('vitest/node');
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
(0, error_1.assertIsError)(error);
|
|
66
|
+
if (error.code !== 'ERR_MODULE_NOT_FOUND') {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
context.logger.error('The `vitest` package was not found. Please install the package and rerun the test command.');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const { startVitest } = vitestNodeModule;
|
|
59
73
|
// Setup test file build options based on application build target options
|
|
60
74
|
const buildTargetOptions = (await context.validateOptions(await context.getTargetOptions(normalizedOptions.buildTarget), await context.getBuilderNameForTarget(normalizedOptions.buildTarget)));
|
|
61
75
|
if (buildTargetOptions.polyfills?.includes('zone.js')) {
|
|
@@ -65,6 +79,7 @@ async function* execute(options, context, extensions = {}) {
|
|
|
65
79
|
const buildOptions = {
|
|
66
80
|
...buildTargetOptions,
|
|
67
81
|
watch: normalizedOptions.watch,
|
|
82
|
+
incrementalResults: normalizedOptions.watch,
|
|
68
83
|
outputPath,
|
|
69
84
|
index: false,
|
|
70
85
|
browser: undefined,
|
|
@@ -89,15 +104,28 @@ async function* execute(options, context, extensions = {}) {
|
|
|
89
104
|
loadContent: async () => {
|
|
90
105
|
const contents = [
|
|
91
106
|
// Initialize the Angular testing environment
|
|
107
|
+
`import { NgModule } from '@angular/core';`,
|
|
92
108
|
`import { getTestBed, ɵgetCleanupHook as getCleanupHook } from '@angular/core/testing';`,
|
|
93
109
|
`import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';`,
|
|
94
110
|
`import { beforeEach, afterEach } from 'vitest';`,
|
|
95
111
|
'',
|
|
112
|
+
normalizedOptions.providersFile
|
|
113
|
+
? `import providers from './${node_path_1.default
|
|
114
|
+
.relative(projectSourceRoot, normalizedOptions.providersFile)
|
|
115
|
+
.replace(/.[mc]?ts$/, '')
|
|
116
|
+
.replace(/\\/g, '/')}'`
|
|
117
|
+
: 'const providers = [];',
|
|
118
|
+
'',
|
|
96
119
|
// Same as https://github.com/angular/angular/blob/05a03d3f975771bb59c7eefd37c01fa127ee2229/packages/core/testing/src/test_hooks.ts#L21-L29
|
|
97
120
|
`beforeEach(getCleanupHook(false));`,
|
|
98
121
|
`afterEach(getCleanupHook(true));`,
|
|
99
122
|
'',
|
|
100
|
-
|
|
123
|
+
`@NgModule({`,
|
|
124
|
+
` providers,`,
|
|
125
|
+
`})`,
|
|
126
|
+
`export class TestModule {}`,
|
|
127
|
+
'',
|
|
128
|
+
`getTestBed().initTestEnvironment([BrowserTestingModule, TestModule], platformBrowserTesting(), {`,
|
|
101
129
|
` errorOnUnknownElements: true,`,
|
|
102
130
|
` errorOnUnknownProperties: true,`,
|
|
103
131
|
'});',
|
|
@@ -112,60 +140,58 @@ async function* execute(options, context, extensions = {}) {
|
|
|
112
140
|
extensions.codePlugins.unshift(virtualTestBedInit);
|
|
113
141
|
let instance;
|
|
114
142
|
// Setup vitest browser options if configured
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
const { browser, errors } = setupBrowserConfiguration(normalizedOptions.browsers, projectSourceRoot);
|
|
144
|
+
if (errors?.length) {
|
|
145
|
+
errors.forEach((error) => context.logger.error(error));
|
|
146
|
+
return { success: false };
|
|
147
|
+
}
|
|
148
|
+
// Add setup file entries for TestBed initialization and project polyfills
|
|
149
|
+
const setupFiles = ['init-testbed.js'];
|
|
150
|
+
if (buildTargetOptions?.polyfills?.length) {
|
|
151
|
+
setupFiles.unshift('polyfills.js');
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
for await (const result of (0, application_1.buildApplicationInternal)(buildOptions, context, extensions)) {
|
|
155
|
+
if (result.kind === results_1.ResultKind.Failure) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
else if (result.kind !== results_1.ResultKind.Full && result.kind !== results_1.ResultKind.Incremental) {
|
|
159
|
+
node_assert_1.default.fail('A full and/or incremental build result is required from the application builder.');
|
|
160
|
+
}
|
|
161
|
+
(0, node_assert_1.default)(result.files, 'Builder did not provide result files.');
|
|
162
|
+
await (0, application_builder_1.writeTestFiles)(result.files, outputPath);
|
|
163
|
+
instance ??= await startVitest('test', undefined /* cliFilters */, undefined /* options */, {
|
|
164
|
+
test: {
|
|
165
|
+
root: outputPath,
|
|
166
|
+
globals: true,
|
|
167
|
+
setupFiles,
|
|
168
|
+
// Use `jsdom` if no browsers are explicitly configured.
|
|
169
|
+
// `node` is effectively no "environment" and the default.
|
|
170
|
+
environment: browser ? 'node' : 'jsdom',
|
|
171
|
+
watch: normalizedOptions.watch,
|
|
172
|
+
browser,
|
|
173
|
+
reporters: normalizedOptions.reporters ?? ['default'],
|
|
174
|
+
coverage: {
|
|
175
|
+
enabled: normalizedOptions.codeCoverage,
|
|
176
|
+
exclude: normalizedOptions.codeCoverageExclude,
|
|
177
|
+
excludeAfterRemap: true,
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
// Check if all the tests pass to calculate the result
|
|
182
|
+
const testModules = instance.state.getTestModules();
|
|
183
|
+
yield { success: testModules.every((testModule) => testModule.ok()) };
|
|
137
184
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
if (normalizedOptions.watch) {
|
|
188
|
+
// Vitest will automatically close if not using watch mode
|
|
189
|
+
await instance?.close();
|
|
143
190
|
}
|
|
144
|
-
instance ??= await startVitest('test', undefined /* cliFilters */, undefined /* options */, {
|
|
145
|
-
test: {
|
|
146
|
-
root: outputPath,
|
|
147
|
-
setupFiles,
|
|
148
|
-
// Use `jsdom` if no browsers are explicitly configured.
|
|
149
|
-
// `node` is effectively no "environment" and the default.
|
|
150
|
-
environment: browser ? 'node' : 'jsdom',
|
|
151
|
-
watch: normalizedOptions.watch,
|
|
152
|
-
browser,
|
|
153
|
-
reporters: normalizedOptions.reporters ?? ['default'],
|
|
154
|
-
coverage: {
|
|
155
|
-
enabled: normalizedOptions.codeCoverage,
|
|
156
|
-
exclude: normalizedOptions.codeCoverageExclude,
|
|
157
|
-
excludeAfterRemap: true,
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
// Check if all the tests pass to calculate the result
|
|
162
|
-
const testModules = instance.state.getTestModules();
|
|
163
|
-
yield { success: testModules.every((testModule) => testModule.ok()) };
|
|
164
191
|
}
|
|
165
192
|
}
|
|
166
|
-
function findBrowserProvider(
|
|
167
|
-
|
|
168
|
-
// These must be installed in the project to be used
|
|
193
|
+
function findBrowserProvider(projectResolver) {
|
|
194
|
+
// One of these must be installed in the project to use browser testing
|
|
169
195
|
const vitestBuiltinProviders = ['playwright', 'webdriverio'];
|
|
170
196
|
for (const providerName of vitestBuiltinProviders) {
|
|
171
197
|
try {
|
|
@@ -175,3 +201,35 @@ function findBrowserProvider(projectSourceRoot) {
|
|
|
175
201
|
catch { }
|
|
176
202
|
}
|
|
177
203
|
}
|
|
204
|
+
function setupBrowserConfiguration(browsers, projectSourceRoot) {
|
|
205
|
+
if (browsers === undefined) {
|
|
206
|
+
return {};
|
|
207
|
+
}
|
|
208
|
+
const projectResolver = (0, node_module_1.createRequire)(projectSourceRoot + '/').resolve;
|
|
209
|
+
let errors;
|
|
210
|
+
try {
|
|
211
|
+
projectResolver('@vitest/browser');
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
errors ??= [];
|
|
215
|
+
errors.push('The "browsers" option requires the "@vitest/browser" package to be installed within the project.' +
|
|
216
|
+
' Please install this package and rerun the test command.');
|
|
217
|
+
}
|
|
218
|
+
const provider = findBrowserProvider(projectResolver);
|
|
219
|
+
if (!provider) {
|
|
220
|
+
errors ??= [];
|
|
221
|
+
errors.push('The "browsers" option requires either "playwright" or "webdriverio" to be installed within the project.' +
|
|
222
|
+
' Please install one of these packages and rerun the test command.');
|
|
223
|
+
}
|
|
224
|
+
if (errors) {
|
|
225
|
+
return { errors };
|
|
226
|
+
}
|
|
227
|
+
const browser = {
|
|
228
|
+
enabled: true,
|
|
229
|
+
provider,
|
|
230
|
+
instances: browsers.map((browserName) => ({
|
|
231
|
+
browser: browserName,
|
|
232
|
+
})),
|
|
233
|
+
};
|
|
234
|
+
return { browser };
|
|
235
|
+
}
|
|
@@ -22,5 +22,6 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
|
|
|
22
22
|
tsConfig: string;
|
|
23
23
|
reporters: string[] | undefined;
|
|
24
24
|
browsers: string[] | undefined;
|
|
25
|
-
watch: boolean;
|
|
25
|
+
watch: boolean | undefined;
|
|
26
|
+
providersFile: string | undefined;
|
|
26
27
|
}>;
|
|
@@ -26,7 +26,7 @@ async function normalizeOptions(context, projectName, options) {
|
|
|
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, browsers } = options;
|
|
29
|
+
const { codeCoverage, codeCoverageExclude, tsConfig, runner, reporters, browsers, watch } = options;
|
|
30
30
|
return {
|
|
31
31
|
// Project/workspace information
|
|
32
32
|
workspaceRoot,
|
|
@@ -43,8 +43,8 @@ async function normalizeOptions(context, projectName, options) {
|
|
|
43
43
|
tsConfig,
|
|
44
44
|
reporters,
|
|
45
45
|
browsers,
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
watch,
|
|
47
|
+
providersFile: options.providersFile && node_path_1.default.join(workspaceRoot, options.providersFile),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
@@ -34,6 +34,11 @@ export type Schema = {
|
|
|
34
34
|
* instead.
|
|
35
35
|
*/
|
|
36
36
|
include?: string[];
|
|
37
|
+
/**
|
|
38
|
+
* TypeScript file that exports an array of Angular providers to use during test execution.
|
|
39
|
+
* The array must be a default export.
|
|
40
|
+
*/
|
|
41
|
+
providersFile?: string;
|
|
37
42
|
/**
|
|
38
43
|
* Test runner reporters to use. Directly passed to the test runner.
|
|
39
44
|
*/
|
|
@@ -65,6 +65,11 @@
|
|
|
65
65
|
"items": {
|
|
66
66
|
"type": "string"
|
|
67
67
|
}
|
|
68
|
+
},
|
|
69
|
+
"providersFile": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"description": "TypeScript file that exports an array of Angular providers to use during test execution. The array must be a default export.",
|
|
72
|
+
"minLength": 1
|
|
68
73
|
}
|
|
69
74
|
},
|
|
70
75
|
"additionalProperties": false,
|
|
@@ -93,7 +93,7 @@ async function loadProxyConfiguration(root, proxyConfig) {
|
|
|
93
93
|
}
|
|
94
94
|
catch (e) {
|
|
95
95
|
(0, error_1.assertIsError)(e);
|
|
96
|
-
if (e.code === 'ERR_REQUIRE_ESM') {
|
|
96
|
+
if (e.code === 'ERR_REQUIRE_ESM' || e.code === 'ERR_REQUIRE_ASYNC_MODULE') {
|
|
97
97
|
// Load the ESM configuration file using the TypeScript dynamic import workaround.
|
|
98
98
|
// Once TypeScript provides support for keeping the dynamic import this workaround can be
|
|
99
99
|
// changed to a direct dynamic import.
|
|
@@ -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-rc.
|
|
13
|
+
const VERSION = '20.0.0-rc.1';
|
|
14
14
|
function hasCacheMetadata(value) {
|
|
15
15
|
return (!!value &&
|
|
16
16
|
typeof value === 'object' &&
|