@angular/build 21.0.0-next.1 → 21.0.0-next.3
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 +12 -12
- package/src/builders/application/options.d.ts +4 -1
- package/src/builders/dev-server/vite/index.js +1 -0
- package/src/builders/karma/find-tests.d.ts +2 -1
- package/src/builders/karma/find-tests.js +6 -2
- package/src/builders/unit-test/options.d.ts +1 -1
- package/src/builders/unit-test/options.js +1 -1
- package/src/builders/unit-test/runners/vitest/build-options.js +17 -3
- package/src/builders/unit-test/runners/vitest/executor.d.ts +5 -3
- package/src/builders/unit-test/runners/vitest/executor.js +191 -99
- package/src/builders/unit-test/schema.json +1 -3
- package/src/tools/esbuild/application-code-bundle.js +7 -4
- package/src/tools/esbuild/stylesheets/bundle-options.d.ts +4 -1
- package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.d.ts +4 -1
- package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js +6 -3
- package/src/tools/vite/utils.js +1 -0
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/postcss-configuration.d.ts +4 -1
- package/src/utils/postcss-configuration.js +2 -2
- package/src/utils/server-rendering/load-esm-from-memory.d.ts +2 -1
- package/src/typings.d.ts +0 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/build",
|
|
3
|
-
"version": "21.0.0-next.
|
|
3
|
+
"version": "21.0.0-next.3",
|
|
4
4
|
"description": "Official build system for Angular",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Angular CLI",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"builders": "builders.json",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@ampproject/remapping": "2.3.0",
|
|
26
|
-
"@angular-devkit/architect": "0.2100.0-next.
|
|
27
|
-
"@babel/core": "7.28.
|
|
26
|
+
"@angular-devkit/architect": "0.2100.0-next.3",
|
|
27
|
+
"@babel/core": "7.28.4",
|
|
28
28
|
"@babel/helper-annotate-as-pure": "7.27.3",
|
|
29
29
|
"@babel/helper-split-export-declaration": "7.24.7",
|
|
30
30
|
"@inquirer/confirm": "5.1.16",
|
|
@@ -35,18 +35,18 @@
|
|
|
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": "9.0.
|
|
39
|
-
"magic-string": "0.30.
|
|
38
|
+
"listr2": "9.0.3",
|
|
39
|
+
"magic-string": "0.30.19",
|
|
40
40
|
"mrmime": "2.0.1",
|
|
41
41
|
"parse5-html-rewriting-stream": "8.0.0",
|
|
42
42
|
"picomatch": "4.0.3",
|
|
43
43
|
"piscina": "5.1.3",
|
|
44
|
-
"rolldown": "1.0.0-beta.
|
|
45
|
-
"sass": "1.
|
|
44
|
+
"rolldown": "1.0.0-beta.36",
|
|
45
|
+
"sass": "1.92.1",
|
|
46
46
|
"semver": "7.7.2",
|
|
47
47
|
"source-map-support": "0.5.21",
|
|
48
|
-
"tinyglobby": "0.2.
|
|
49
|
-
"vite": "7.1.
|
|
48
|
+
"tinyglobby": "0.2.15",
|
|
49
|
+
"vite": "7.1.5",
|
|
50
50
|
"watchpack": "2.4.4"
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
@@ -60,14 +60,14 @@
|
|
|
60
60
|
"@angular/platform-browser": "^21.0.0-next.0",
|
|
61
61
|
"@angular/platform-server": "^21.0.0-next.0",
|
|
62
62
|
"@angular/service-worker": "^21.0.0-next.0",
|
|
63
|
-
"@angular/ssr": "^21.0.0-next.
|
|
63
|
+
"@angular/ssr": "^21.0.0-next.3",
|
|
64
64
|
"karma": "^6.4.0",
|
|
65
65
|
"less": "^4.2.0",
|
|
66
66
|
"ng-packagr": "^21.0.0-next.0",
|
|
67
67
|
"postcss": "^8.4.0",
|
|
68
68
|
"tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
|
|
69
69
|
"tslib": "^2.3.0",
|
|
70
|
-
"typescript": ">=5.
|
|
70
|
+
"typescript": ">=5.9 <6.0",
|
|
71
71
|
"vitest": "^3.1.1"
|
|
72
72
|
},
|
|
73
73
|
"peerDependenciesMeta": {
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"type": "git",
|
|
113
113
|
"url": "https://github.com/angular/angular-cli.git"
|
|
114
114
|
},
|
|
115
|
-
"packageManager": "pnpm@10.15.
|
|
115
|
+
"packageManager": "pnpm@10.15.1",
|
|
116
116
|
"engines": {
|
|
117
117
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0",
|
|
118
118
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
|
@@ -177,7 +177,10 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
|
|
|
177
177
|
file: string;
|
|
178
178
|
package: string;
|
|
179
179
|
} | undefined;
|
|
180
|
-
postcssConfiguration:
|
|
180
|
+
postcssConfiguration: {
|
|
181
|
+
configPath: string;
|
|
182
|
+
config: import("../../utils/postcss-configuration").PostcssConfiguration;
|
|
183
|
+
} | undefined;
|
|
181
184
|
i18nOptions: I18nOptions & {
|
|
182
185
|
duplicateTranslationBehavior?: I18NTranslation;
|
|
183
186
|
missingTranslationBehavior?: I18NTranslation;
|
|
@@ -317,6 +317,7 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
|
|
|
317
317
|
// Setup server and start listening
|
|
318
318
|
const serverConfiguration = await (0, server_1.setupServer)(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, ssrMode, prebundleTransformer, target, (0, internal_1.isZonelessApp)(polyfills), componentStyles, templateUpdates, browserOptions.loader, {
|
|
319
319
|
...browserOptions.define,
|
|
320
|
+
'ngJitMode': browserOptions.aot ? 'false' : 'true',
|
|
320
321
|
'ngHmrMode': browserOptions.templateUpdates ? 'true' : 'false',
|
|
321
322
|
}, extensions?.middleware, transformers?.indexHtml, thirdPartySourcemaps);
|
|
322
323
|
server = await createServer(serverConfiguration);
|
|
@@ -9,7 +9,8 @@ export declare function findTests(include: string[], exclude: string[], workspac
|
|
|
9
9
|
interface TestEntrypointsOptions {
|
|
10
10
|
projectSourceRoot: string;
|
|
11
11
|
workspaceRoot: string;
|
|
12
|
+
removeTestExtension?: boolean;
|
|
12
13
|
}
|
|
13
14
|
/** Generate unique bundle names for a set of test files. */
|
|
14
|
-
export declare function getTestEntrypoints(testFiles: string[], { projectSourceRoot, workspaceRoot }: TestEntrypointsOptions): Map<string, string>;
|
|
15
|
+
export declare function getTestEntrypoints(testFiles: string[], { projectSourceRoot, workspaceRoot, removeTestExtension }: TestEntrypointsOptions): Map<string, string>;
|
|
15
16
|
export {};
|
|
@@ -21,7 +21,7 @@ async function findTests(include, exclude, workspaceRoot, projectSourceRoot) {
|
|
|
21
21
|
return [...new Set(files.flat())];
|
|
22
22
|
}
|
|
23
23
|
/** Generate unique bundle names for a set of test files. */
|
|
24
|
-
function getTestEntrypoints(testFiles, { projectSourceRoot, workspaceRoot }) {
|
|
24
|
+
function getTestEntrypoints(testFiles, { projectSourceRoot, workspaceRoot, removeTestExtension }) {
|
|
25
25
|
const seen = new Set();
|
|
26
26
|
return new Map(Array.from(testFiles, (testFile) => {
|
|
27
27
|
const relativePath = removeRoots(testFile, [projectSourceRoot, workspaceRoot])
|
|
@@ -29,7 +29,11 @@ function getTestEntrypoints(testFiles, { projectSourceRoot, workspaceRoot }) {
|
|
|
29
29
|
.replace(/^[./\\]+/, '')
|
|
30
30
|
// Replace any path separators with dashes.
|
|
31
31
|
.replace(/[/\\]/g, '-');
|
|
32
|
-
|
|
32
|
+
let fileName = (0, node_path_1.basename)(relativePath, (0, node_path_1.extname)(relativePath));
|
|
33
|
+
if (removeTestExtension) {
|
|
34
|
+
fileName = fileName.replace(/\.(spec|test)$/, '');
|
|
35
|
+
}
|
|
36
|
+
const baseName = `spec-${fileName}`;
|
|
33
37
|
let uniqueName = baseName;
|
|
34
38
|
let suffix = 2;
|
|
35
39
|
while (seen.has(uniqueName)) {
|
|
@@ -15,7 +15,7 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
|
|
|
15
15
|
cacheOptions: import("../../utils/normalize-cache").NormalizedCachedOptions;
|
|
16
16
|
buildTarget: import("@angular-devkit/architect").Target;
|
|
17
17
|
include: string[];
|
|
18
|
-
exclude: string[];
|
|
18
|
+
exclude: string[] | undefined;
|
|
19
19
|
runnerName: import("./schema").Runner;
|
|
20
20
|
codeCoverage: {
|
|
21
21
|
exclude: string[] | undefined;
|
|
@@ -38,7 +38,7 @@ async function normalizeOptions(context, projectName, options) {
|
|
|
38
38
|
// Target/configuration specified options
|
|
39
39
|
buildTarget,
|
|
40
40
|
include: options.include ?? ['**/*.spec.ts'],
|
|
41
|
-
exclude: options.exclude
|
|
41
|
+
exclude: options.exclude,
|
|
42
42
|
runnerName: runner,
|
|
43
43
|
codeCoverage: options.codeCoverage
|
|
44
44
|
? {
|
|
@@ -43,8 +43,18 @@ function createTestBedInitVirtualFile(providersFile, projectSourceRoot) {
|
|
|
43
43
|
});
|
|
44
44
|
`;
|
|
45
45
|
}
|
|
46
|
+
function adjustOutputHashing(hashing) {
|
|
47
|
+
switch (hashing) {
|
|
48
|
+
case schema_1.OutputHashing.All:
|
|
49
|
+
case schema_1.OutputHashing.Media:
|
|
50
|
+
// Ensure media is continued to be hashed to avoid overwriting of output media files
|
|
51
|
+
return schema_1.OutputHashing.Media;
|
|
52
|
+
default:
|
|
53
|
+
return schema_1.OutputHashing.None;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
46
56
|
async function getVitestBuildOptions(options, baseBuildOptions) {
|
|
47
|
-
const { workspaceRoot, projectSourceRoot, include, exclude, watch, tsConfig, providersFile } = options;
|
|
57
|
+
const { workspaceRoot, projectSourceRoot, include, exclude = [], watch, tsConfig, providersFile, } = options;
|
|
48
58
|
// Find test files
|
|
49
59
|
const testFiles = await (0, test_discovery_1.findTests)(include, exclude, workspaceRoot, projectSourceRoot);
|
|
50
60
|
if (testFiles.length === 0) {
|
|
@@ -53,7 +63,11 @@ async function getVitestBuildOptions(options, baseBuildOptions) {
|
|
|
53
63
|
(exclude.length ? `- Excluded: ${exclude.join(', ')}\n` : '') +
|
|
54
64
|
`\nPlease check the 'test' target configuration in your project's 'angular.json' file.`);
|
|
55
65
|
}
|
|
56
|
-
const entryPoints = (0, test_discovery_1.getTestEntrypoints)(testFiles, {
|
|
66
|
+
const entryPoints = (0, test_discovery_1.getTestEntrypoints)(testFiles, {
|
|
67
|
+
projectSourceRoot,
|
|
68
|
+
workspaceRoot,
|
|
69
|
+
removeTestExtension: true,
|
|
70
|
+
});
|
|
57
71
|
entryPoints.set('init-testbed', 'angular:test-bed-init');
|
|
58
72
|
const buildOptions = {
|
|
59
73
|
...baseBuildOptions,
|
|
@@ -70,7 +84,7 @@ async function getVitestBuildOptions(options, baseBuildOptions) {
|
|
|
70
84
|
ssr: false,
|
|
71
85
|
prerender: false,
|
|
72
86
|
sourceMap: { scripts: true, vendor: false, styles: false },
|
|
73
|
-
outputHashing:
|
|
87
|
+
outputHashing: adjustOutputHashing(baseBuildOptions.outputHashing),
|
|
74
88
|
optimization: false,
|
|
75
89
|
tsConfig,
|
|
76
90
|
entryPoints,
|
|
@@ -13,11 +13,13 @@ export declare class VitestExecutor implements TestExecutor {
|
|
|
13
13
|
private vitest;
|
|
14
14
|
private readonly projectName;
|
|
15
15
|
private readonly options;
|
|
16
|
-
private
|
|
17
|
-
private
|
|
18
|
-
private
|
|
16
|
+
private buildResultFiles;
|
|
17
|
+
private testFileToEntryPoint;
|
|
18
|
+
private entryPointToTestFile;
|
|
19
19
|
constructor(projectName: string, options: NormalizedUnitTestBuilderOptions);
|
|
20
20
|
execute(buildResult: FullResult | IncrementalResult): AsyncIterable<BuilderOutput>;
|
|
21
21
|
[Symbol.asyncDispose](): Promise<void>;
|
|
22
|
+
private prepareSetupFiles;
|
|
23
|
+
private createVitestPlugins;
|
|
22
24
|
private initializeVitest;
|
|
23
25
|
}
|
|
@@ -12,73 +12,219 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.VitestExecutor = void 0;
|
|
14
14
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
15
|
-
const node_crypto_1 = require("node:crypto");
|
|
16
|
-
const node_fs_1 = require("node:fs");
|
|
17
15
|
const promises_1 = require("node:fs/promises");
|
|
18
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
19
17
|
const error_1 = require("../../../../utils/error");
|
|
20
18
|
const load_esm_1 = require("../../../../utils/load-esm");
|
|
21
19
|
const path_1 = require("../../../../utils/path");
|
|
22
20
|
const results_1 = require("../../../application/results");
|
|
23
|
-
const
|
|
21
|
+
const test_discovery_1 = require("../../test-discovery");
|
|
24
22
|
const browser_provider_1 = require("./browser-provider");
|
|
25
23
|
class VitestExecutor {
|
|
26
24
|
vitest;
|
|
27
25
|
projectName;
|
|
28
26
|
options;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
buildResultFiles = new Map();
|
|
28
|
+
// This is a reverse map of the entry points created in `build-options.ts`.
|
|
29
|
+
// It is used by the in-memory provider plugin to map the requested test file
|
|
30
|
+
// path back to its bundled output path.
|
|
31
|
+
// Example: `Map<'/path/to/src/app.spec.ts', 'spec-src-app-spec'>`
|
|
32
|
+
testFileToEntryPoint = new Map();
|
|
33
|
+
entryPointToTestFile = new Map();
|
|
36
34
|
constructor(projectName, options) {
|
|
37
35
|
this.projectName = projectName;
|
|
38
36
|
this.options = options;
|
|
39
|
-
this.outputPath = (0, path_1.toPosixPath)(node_path_1.default.join(options.workspaceRoot, generateOutputPath()));
|
|
40
|
-
process.on('SIGINT', this.sigintListener);
|
|
41
37
|
}
|
|
42
38
|
async *execute(buildResult) {
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
if (buildResult.kind === results_1.ResultKind.Full) {
|
|
40
|
+
this.buildResultFiles.clear();
|
|
41
|
+
for (const [path, file] of Object.entries(buildResult.files)) {
|
|
42
|
+
this.buildResultFiles.set(path, file);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
for (const file of buildResult.removed) {
|
|
47
|
+
this.buildResultFiles.delete(file.path);
|
|
48
|
+
}
|
|
49
|
+
for (const [path, file] of Object.entries(buildResult.files)) {
|
|
50
|
+
this.buildResultFiles.set(path, file);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// The `getTestEntrypoints` function is used here to create the same mapping
|
|
54
|
+
// that was used in `build-options.ts` to generate the build entry points.
|
|
55
|
+
// This is a deliberate duplication to avoid a larger refactoring of the
|
|
56
|
+
// builder's core interfaces to pass the entry points from the build setup
|
|
57
|
+
// phase to the execution phase.
|
|
58
|
+
if (this.testFileToEntryPoint.size === 0) {
|
|
59
|
+
const { include, exclude = [], workspaceRoot, projectSourceRoot } = this.options;
|
|
60
|
+
const testFiles = await (0, test_discovery_1.findTests)(include, exclude, workspaceRoot, projectSourceRoot);
|
|
61
|
+
const entryPoints = (0, test_discovery_1.getTestEntrypoints)(testFiles, {
|
|
62
|
+
projectSourceRoot,
|
|
63
|
+
workspaceRoot,
|
|
64
|
+
removeTestExtension: true,
|
|
65
|
+
});
|
|
66
|
+
for (const [entryPoint, testFile] of entryPoints) {
|
|
67
|
+
this.testFileToEntryPoint.set(testFile, entryPoint);
|
|
68
|
+
this.entryPointToTestFile.set(entryPoint + '.js', testFile);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
45
71
|
// Initialize Vitest if not already present.
|
|
46
72
|
this.vitest ??= await this.initializeVitest();
|
|
47
73
|
const vitest = this.vitest;
|
|
48
74
|
let testResults;
|
|
49
75
|
if (buildResult.kind === results_1.ResultKind.Incremental) {
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
76
|
+
// To rerun tests, Vitest needs the original test file paths, not the output paths.
|
|
77
|
+
const modifiedSourceFiles = new Set();
|
|
78
|
+
for (const modifiedFile of buildResult.modified) {
|
|
79
|
+
// The `modified` files in the build result are the output paths.
|
|
80
|
+
// We need to find the original source file path to pass to Vitest.
|
|
81
|
+
const source = this.entryPointToTestFile.get(modifiedFile);
|
|
82
|
+
if (source) {
|
|
83
|
+
modifiedSourceFiles.add(source);
|
|
84
|
+
}
|
|
85
|
+
vitest.invalidateFile((0, path_1.toPosixPath)(node_path_1.default.join(this.options.workspaceRoot, modifiedFile)));
|
|
60
86
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
87
|
+
const specsToRerun = [];
|
|
88
|
+
for (const file of modifiedSourceFiles) {
|
|
89
|
+
vitest.invalidateFile(file);
|
|
90
|
+
const specs = vitest.getModuleSpecifications(file);
|
|
91
|
+
if (specs) {
|
|
92
|
+
specsToRerun.push(...specs);
|
|
67
93
|
}
|
|
68
94
|
}
|
|
95
|
+
if (specsToRerun.length > 0) {
|
|
96
|
+
testResults = await vitest.rerunTestSpecifications(specsToRerun);
|
|
97
|
+
}
|
|
69
98
|
}
|
|
70
99
|
// Check if all the tests pass to calculate the result
|
|
71
|
-
const testModules = testResults?.testModules;
|
|
72
|
-
yield { success: testModules
|
|
100
|
+
const testModules = testResults?.testModules ?? this.vitest.state.getTestModules();
|
|
101
|
+
yield { success: testModules.every((testModule) => testModule.ok()) };
|
|
73
102
|
}
|
|
74
103
|
async [Symbol.asyncDispose]() {
|
|
75
|
-
process.off('SIGINT', this.sigintListener);
|
|
76
104
|
await this.vitest?.close();
|
|
77
|
-
|
|
105
|
+
}
|
|
106
|
+
prepareSetupFiles() {
|
|
107
|
+
const { setupFiles } = this.options;
|
|
108
|
+
// Add setup file entries for TestBed initialization and project polyfills
|
|
109
|
+
const testSetupFiles = ['init-testbed.js', ...setupFiles];
|
|
110
|
+
// TODO: Provide additional result metadata to avoid needing to extract based on filename
|
|
111
|
+
if (this.buildResultFiles.has('polyfills.js')) {
|
|
112
|
+
testSetupFiles.unshift('polyfills.js');
|
|
113
|
+
}
|
|
114
|
+
return testSetupFiles;
|
|
115
|
+
}
|
|
116
|
+
createVitestPlugins(testSetupFiles, browserOptions) {
|
|
117
|
+
const { workspaceRoot } = this.options;
|
|
118
|
+
return [
|
|
119
|
+
{
|
|
120
|
+
name: 'angular:project-init',
|
|
121
|
+
// Type is incorrect. This allows a Promise<void>.
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
123
|
+
configureVitest: async (context) => {
|
|
124
|
+
// Create a subproject that can be configured with plugins for browser mode.
|
|
125
|
+
// Plugins defined directly in the vite overrides will not be present in the
|
|
126
|
+
// browser specific Vite instance.
|
|
127
|
+
await context.injectTestProjects({
|
|
128
|
+
test: {
|
|
129
|
+
name: this.projectName,
|
|
130
|
+
root: workspaceRoot,
|
|
131
|
+
globals: true,
|
|
132
|
+
setupFiles: testSetupFiles,
|
|
133
|
+
// Use `jsdom` if no browsers are explicitly configured.
|
|
134
|
+
// `node` is effectively no "environment" and the default.
|
|
135
|
+
environment: browserOptions.browser ? 'node' : 'jsdom',
|
|
136
|
+
browser: browserOptions.browser,
|
|
137
|
+
include: this.options.include,
|
|
138
|
+
...(this.options.exclude ? { exclude: this.options.exclude } : {}),
|
|
139
|
+
},
|
|
140
|
+
plugins: [
|
|
141
|
+
{
|
|
142
|
+
name: 'angular:test-in-memory-provider',
|
|
143
|
+
enforce: 'pre',
|
|
144
|
+
resolveId: (id, importer) => {
|
|
145
|
+
if (importer && (id[0] === '.' || id[0] === '/')) {
|
|
146
|
+
let fullPath;
|
|
147
|
+
if (this.testFileToEntryPoint.has(importer)) {
|
|
148
|
+
fullPath = (0, path_1.toPosixPath)(node_path_1.default.join(this.options.workspaceRoot, id));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
fullPath = (0, path_1.toPosixPath)(node_path_1.default.join(node_path_1.default.dirname(importer), id));
|
|
152
|
+
}
|
|
153
|
+
const relativePath = node_path_1.default.relative(this.options.workspaceRoot, fullPath);
|
|
154
|
+
if (this.buildResultFiles.has((0, path_1.toPosixPath)(relativePath))) {
|
|
155
|
+
return fullPath;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (this.testFileToEntryPoint.has(id)) {
|
|
159
|
+
return id;
|
|
160
|
+
}
|
|
161
|
+
(0, node_assert_1.default)(this.buildResultFiles.size > 0, 'buildResult must be available for resolving.');
|
|
162
|
+
const relativePath = node_path_1.default.relative(this.options.workspaceRoot, id);
|
|
163
|
+
if (this.buildResultFiles.has((0, path_1.toPosixPath)(relativePath))) {
|
|
164
|
+
return id;
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
load: async (id) => {
|
|
168
|
+
(0, node_assert_1.default)(this.buildResultFiles.size > 0, 'buildResult must be available for in-memory loading.');
|
|
169
|
+
// Attempt to load as a source test file.
|
|
170
|
+
const entryPoint = this.testFileToEntryPoint.get(id);
|
|
171
|
+
let outputPath;
|
|
172
|
+
if (entryPoint) {
|
|
173
|
+
outputPath = entryPoint + '.js';
|
|
174
|
+
// To support coverage exclusion of the actual test file, the virtual
|
|
175
|
+
// test entry point only references the built and bundled intermediate file.
|
|
176
|
+
return {
|
|
177
|
+
code: `import "./${outputPath}";`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Attempt to load as a built artifact.
|
|
182
|
+
const relativePath = node_path_1.default.relative(this.options.workspaceRoot, id);
|
|
183
|
+
outputPath = (0, path_1.toPosixPath)(relativePath);
|
|
184
|
+
}
|
|
185
|
+
const outputFile = this.buildResultFiles.get(outputPath);
|
|
186
|
+
if (outputFile) {
|
|
187
|
+
const sourceMapPath = outputPath + '.map';
|
|
188
|
+
const sourceMapFile = this.buildResultFiles.get(sourceMapPath);
|
|
189
|
+
const code = outputFile.origin === 'memory'
|
|
190
|
+
? Buffer.from(outputFile.contents).toString('utf-8')
|
|
191
|
+
: await (0, promises_1.readFile)(outputFile.inputPath, 'utf-8');
|
|
192
|
+
const map = sourceMapFile
|
|
193
|
+
? sourceMapFile.origin === 'memory'
|
|
194
|
+
? Buffer.from(sourceMapFile.contents).toString('utf-8')
|
|
195
|
+
: await (0, promises_1.readFile)(sourceMapFile.inputPath, 'utf-8')
|
|
196
|
+
: undefined;
|
|
197
|
+
return {
|
|
198
|
+
code,
|
|
199
|
+
map: map ? JSON.parse(map) : undefined,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: 'angular:html-index',
|
|
206
|
+
transformIndexHtml: () => {
|
|
207
|
+
// Add all global stylesheets
|
|
208
|
+
if (this.buildResultFiles.has('styles.css')) {
|
|
209
|
+
return [
|
|
210
|
+
{
|
|
211
|
+
tag: 'link',
|
|
212
|
+
attrs: { href: 'styles.css', rel: 'stylesheet' },
|
|
213
|
+
injectTo: 'head',
|
|
214
|
+
},
|
|
215
|
+
];
|
|
216
|
+
}
|
|
217
|
+
return [];
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
];
|
|
78
225
|
}
|
|
79
226
|
async initializeVitest() {
|
|
80
|
-
const { codeCoverage, reporters, workspaceRoot,
|
|
81
|
-
const { outputPath, projectName, latestBuildResult } = this;
|
|
227
|
+
const { codeCoverage, reporters, workspaceRoot, browsers, debug, watch } = this.options;
|
|
82
228
|
let vitestNodeModule;
|
|
83
229
|
try {
|
|
84
230
|
vitestNodeModule = await (0, load_esm_1.loadEsmModule)('vitest/node');
|
|
@@ -96,14 +242,9 @@ class VitestExecutor {
|
|
|
96
242
|
if (browserOptions.errors?.length) {
|
|
97
243
|
throw new Error(browserOptions.errors.join('\n'));
|
|
98
244
|
}
|
|
99
|
-
(0, node_assert_1.default)(
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
// TODO: Provide additional result metadata to avoid needing to extract based on filename
|
|
103
|
-
const polyfillsFile = Object.keys(latestBuildResult.files).find((f) => f === 'polyfills.js');
|
|
104
|
-
if (polyfillsFile) {
|
|
105
|
-
testSetupFiles.unshift(polyfillsFile);
|
|
106
|
-
}
|
|
245
|
+
(0, node_assert_1.default)(this.buildResultFiles.size > 0, 'buildResult must be available before initializing vitest');
|
|
246
|
+
const testSetupFiles = this.prepareSetupFiles();
|
|
247
|
+
const plugins = this.createVitestPlugins(testSetupFiles, browserOptions);
|
|
107
248
|
const debugOptions = debug
|
|
108
249
|
? {
|
|
109
250
|
inspectBrk: true,
|
|
@@ -115,12 +256,12 @@ class VitestExecutor {
|
|
|
115
256
|
// Disable configuration file resolution/loading
|
|
116
257
|
config: false,
|
|
117
258
|
root: workspaceRoot,
|
|
118
|
-
project: ['base', projectName],
|
|
259
|
+
project: ['base', this.projectName],
|
|
119
260
|
name: 'base',
|
|
120
261
|
include: [],
|
|
121
262
|
reporters: reporters ?? ['default'],
|
|
122
263
|
watch,
|
|
123
|
-
coverage: generateCoverageOption(codeCoverage
|
|
264
|
+
coverage: generateCoverageOption(codeCoverage),
|
|
124
265
|
...debugOptions,
|
|
125
266
|
}, {
|
|
126
267
|
server: {
|
|
@@ -128,61 +269,12 @@ class VitestExecutor {
|
|
|
128
269
|
// be enabled as it controls other internal behavior related to rerunning tests.
|
|
129
270
|
watch: null,
|
|
130
271
|
},
|
|
131
|
-
plugins
|
|
132
|
-
{
|
|
133
|
-
name: 'angular:project-init',
|
|
134
|
-
async configureVitest(context) {
|
|
135
|
-
// Create a subproject that can be configured with plugins for browser mode.
|
|
136
|
-
// Plugins defined directly in the vite overrides will not be present in the
|
|
137
|
-
// browser specific Vite instance.
|
|
138
|
-
const [project] = await context.injectTestProjects({
|
|
139
|
-
test: {
|
|
140
|
-
name: projectName,
|
|
141
|
-
root: outputPath,
|
|
142
|
-
globals: true,
|
|
143
|
-
setupFiles: testSetupFiles,
|
|
144
|
-
// Use `jsdom` if no browsers are explicitly configured.
|
|
145
|
-
// `node` is effectively no "environment" and the default.
|
|
146
|
-
environment: browserOptions.browser ? 'node' : 'jsdom',
|
|
147
|
-
browser: browserOptions.browser,
|
|
148
|
-
},
|
|
149
|
-
plugins: [
|
|
150
|
-
{
|
|
151
|
-
name: 'angular:html-index',
|
|
152
|
-
transformIndexHtml: () => {
|
|
153
|
-
(0, node_assert_1.default)(latestBuildResult, 'buildResult must be available for HTML index transformation.');
|
|
154
|
-
// Add all global stylesheets
|
|
155
|
-
const styleFiles = Object.entries(latestBuildResult.files).filter(([file]) => file === 'styles.css');
|
|
156
|
-
return styleFiles.map(([href]) => ({
|
|
157
|
-
tag: 'link',
|
|
158
|
-
attrs: { href, rel: 'stylesheet' },
|
|
159
|
-
injectTo: 'head',
|
|
160
|
-
}));
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
],
|
|
164
|
-
});
|
|
165
|
-
// Adjust coverage excludes to not include the otherwise automatically inserted included unit tests.
|
|
166
|
-
// Vite does this as a convenience but is problematic for the bundling strategy employed by the
|
|
167
|
-
// builder's test setup. To workaround this, the excludes are adjusted here to only automatically
|
|
168
|
-
// exclude the TypeScript source test files.
|
|
169
|
-
project.config.coverage.exclude = [
|
|
170
|
-
...(codeCoverage?.exclude ?? []),
|
|
171
|
-
'**/*.{test,spec}.?(c|m)ts',
|
|
172
|
-
];
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
],
|
|
272
|
+
plugins,
|
|
176
273
|
});
|
|
177
274
|
}
|
|
178
275
|
}
|
|
179
276
|
exports.VitestExecutor = VitestExecutor;
|
|
180
|
-
function
|
|
181
|
-
const datePrefix = new Date().toISOString().replaceAll(/[-:.]/g, '');
|
|
182
|
-
const uuidSuffix = (0, node_crypto_1.randomUUID)().slice(0, 8);
|
|
183
|
-
return node_path_1.default.join('dist', 'test-out', `${datePrefix}-${uuidSuffix}`);
|
|
184
|
-
}
|
|
185
|
-
function generateCoverageOption(codeCoverage, workspaceRoot, outputPath) {
|
|
277
|
+
function generateCoverageOption(codeCoverage) {
|
|
186
278
|
if (!codeCoverage) {
|
|
187
279
|
return {
|
|
188
280
|
enabled: false,
|
|
@@ -191,8 +283,8 @@ function generateCoverageOption(codeCoverage, workspaceRoot, outputPath) {
|
|
|
191
283
|
return {
|
|
192
284
|
enabled: true,
|
|
193
285
|
excludeAfterRemap: true,
|
|
194
|
-
|
|
195
|
-
|
|
286
|
+
// Special handling for `exclude`/`reporters` due to an undefined value causing upstream failures
|
|
287
|
+
...(codeCoverage.exclude ? { exclude: codeCoverage.exclude } : {}),
|
|
196
288
|
...(codeCoverage.reporters
|
|
197
289
|
? { reporter: codeCoverage.reporters }
|
|
198
290
|
: {}),
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"items": {
|
|
40
40
|
"type": "string"
|
|
41
41
|
},
|
|
42
|
-
"default": [],
|
|
43
42
|
"description": "Globs of files to exclude, relative to the project root."
|
|
44
43
|
},
|
|
45
44
|
"watch": {
|
|
@@ -61,8 +60,7 @@
|
|
|
61
60
|
"description": "Globs to exclude from code coverage.",
|
|
62
61
|
"items": {
|
|
63
62
|
"type": "string"
|
|
64
|
-
}
|
|
65
|
-
"default": []
|
|
63
|
+
}
|
|
66
64
|
},
|
|
67
65
|
"codeCoverageReporters": {
|
|
68
66
|
"type": "array",
|
|
@@ -418,6 +418,8 @@ function getEsBuildCommonOptions(options) {
|
|
|
418
418
|
packages = 'external';
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
|
+
const minifySyntax = optimizationOptions.scripts;
|
|
422
|
+
const minifyIdentifiers = minifySyntax && environment_options_1.allowMangle;
|
|
421
423
|
return {
|
|
422
424
|
absWorkingDir: workspaceRoot,
|
|
423
425
|
format: 'esm',
|
|
@@ -429,9 +431,10 @@ function getEsBuildCommonOptions(options) {
|
|
|
429
431
|
metafile: true,
|
|
430
432
|
legalComments: options.extractLicenses ? 'none' : 'eof',
|
|
431
433
|
logLevel: options.verbose && !jsonLogs ? 'debug' : 'silent',
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
434
|
+
keepNames: !minifyIdentifiers,
|
|
435
|
+
minifyIdentifiers,
|
|
436
|
+
minifySyntax,
|
|
437
|
+
minifyWhitespace: minifySyntax,
|
|
435
438
|
pure: ['forwardRef'],
|
|
436
439
|
outdir: workspaceRoot,
|
|
437
440
|
outExtension: outExtension ? { '.js': `.${outExtension}` } : undefined,
|
|
@@ -448,7 +451,7 @@ function getEsBuildCommonOptions(options) {
|
|
|
448
451
|
// Only set to false when script optimizations are enabled. It should not be set to true because
|
|
449
452
|
// Angular turns `ngDevMode` into an object for development debugging purposes when not defined
|
|
450
453
|
// which a constant true value would break.
|
|
451
|
-
...(
|
|
454
|
+
...(minifySyntax ? { 'ngDevMode': 'false' } : undefined),
|
|
452
455
|
'ngJitMode': jit ? 'true' : 'false',
|
|
453
456
|
'ngServerMode': 'false',
|
|
454
457
|
'ngHmrMode': options.templateUpdates ? 'true' : 'false',
|
|
@@ -29,7 +29,10 @@ export interface BundleStylesheetOptions {
|
|
|
29
29
|
file: string;
|
|
30
30
|
package: string;
|
|
31
31
|
};
|
|
32
|
-
postcssConfiguration?:
|
|
32
|
+
postcssConfiguration?: {
|
|
33
|
+
config: PostcssConfiguration;
|
|
34
|
+
configPath: string;
|
|
35
|
+
};
|
|
33
36
|
publicPath?: string;
|
|
34
37
|
cacheOptions: NormalizedCachedOptions;
|
|
35
38
|
}
|
|
@@ -46,7 +46,10 @@ export interface StylesheetPluginOptions {
|
|
|
46
46
|
* initialized and used for every stylesheet. This overrides the tailwind integration
|
|
47
47
|
* and any tailwind usage must be manually configured in the custom postcss usage.
|
|
48
48
|
*/
|
|
49
|
-
postcssConfiguration?:
|
|
49
|
+
postcssConfiguration?: {
|
|
50
|
+
config: PostcssConfiguration;
|
|
51
|
+
configPath: string;
|
|
52
|
+
};
|
|
50
53
|
/**
|
|
51
54
|
* Optional Options for configuring Sass behavior.
|
|
52
55
|
*/
|
|
@@ -46,6 +46,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
46
46
|
exports.StylesheetPluginFactory = void 0;
|
|
47
47
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
48
48
|
const promises_1 = require("node:fs/promises");
|
|
49
|
+
const node_module_1 = require("node:module");
|
|
49
50
|
const node_path_1 = require("node:path");
|
|
50
51
|
const tinyglobby_1 = require("tinyglobby");
|
|
51
52
|
const error_1 = require("../../../utils/error");
|
|
@@ -143,13 +144,15 @@ class StylesheetPluginFactory {
|
|
|
143
144
|
node_assert_1.default.equal(++this.initPostcssCallCount, 1, '`initPostcss` was called more than once.');
|
|
144
145
|
const { options } = this;
|
|
145
146
|
if (options.postcssConfiguration) {
|
|
146
|
-
const
|
|
147
|
+
const { config, configPath } = options.postcssConfiguration;
|
|
148
|
+
const postCssInstanceKey = JSON.stringify(config);
|
|
147
149
|
let postcssProcessor = postcssProcessors.get(postCssInstanceKey)?.deref();
|
|
148
150
|
if (!postcssProcessor) {
|
|
149
151
|
postcss ??= (await Promise.resolve().then(() => __importStar(require('postcss')))).default;
|
|
150
152
|
postcssProcessor = postcss();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
const postCssPluginRequire = (0, node_module_1.createRequire)((0, node_path_1.dirname)(configPath) + '/');
|
|
154
|
+
for (const [pluginName, pluginOptions] of config.plugins) {
|
|
155
|
+
const plugin = postCssPluginRequire(pluginName);
|
|
153
156
|
if (typeof plugin !== 'function' || plugin.postcss !== true) {
|
|
154
157
|
throw new Error(`Attempted to load invalid Postcss plugin: "${pluginName}"`);
|
|
155
158
|
}
|
package/src/tools/vite/utils.js
CHANGED
|
@@ -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 = '21.0.0-next.
|
|
13
|
+
const VERSION = '21.0.0-next.3';
|
|
14
14
|
function hasCacheMetadata(value) {
|
|
15
15
|
return (!!value &&
|
|
16
16
|
typeof value === 'object' &&
|
|
@@ -14,4 +14,7 @@ export interface SearchDirectory {
|
|
|
14
14
|
}
|
|
15
15
|
export declare function generateSearchDirectories(roots: string[]): Promise<SearchDirectory[]>;
|
|
16
16
|
export declare function findTailwindConfiguration(searchDirectories: SearchDirectory[]): string | undefined;
|
|
17
|
-
export declare function loadPostcssConfiguration(searchDirectories: SearchDirectory[]): Promise<
|
|
17
|
+
export declare function loadPostcssConfiguration(searchDirectories: SearchDirectory[]): Promise<{
|
|
18
|
+
configPath: string;
|
|
19
|
+
config: PostcssConfiguration;
|
|
20
|
+
} | undefined>;
|
|
@@ -67,7 +67,7 @@ async function loadPostcssConfiguration(searchDirectories) {
|
|
|
67
67
|
config.plugins.push(element);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
return config;
|
|
70
|
+
return { config, configPath };
|
|
71
71
|
}
|
|
72
72
|
// Normalize plugin object map form
|
|
73
73
|
const entries = Object.entries(raw.plugins);
|
|
@@ -81,5 +81,5 @@ async function loadPostcssConfiguration(searchDirectories) {
|
|
|
81
81
|
}
|
|
82
82
|
config.plugins.push([name, options]);
|
|
83
83
|
}
|
|
84
|
-
return config;
|
|
84
|
+
return { config, configPath };
|
|
85
85
|
}
|
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.dev/license
|
|
7
7
|
*/
|
|
8
8
|
import type { ApplicationRef, Type } from '@angular/core';
|
|
9
|
+
import type { BootstrapContext } from '@angular/platform-browser';
|
|
9
10
|
import type { ɵextractRoutesAndCreateRouteTree, ɵgetOrCreateAngularServerApp } from '@angular/ssr';
|
|
10
11
|
/**
|
|
11
12
|
* Represents the exports available from the main server bundle.
|
|
12
13
|
*/
|
|
13
14
|
interface MainServerBundleExports {
|
|
14
|
-
default: (() => Promise<ApplicationRef>) | Type<unknown>;
|
|
15
|
+
default: ((context: BootstrapContext) => Promise<ApplicationRef>) | Type<unknown>;
|
|
15
16
|
ɵextractRoutesAndCreateRouteTree: typeof ɵextractRoutesAndCreateRouteTree;
|
|
16
17
|
ɵgetOrCreateAngularServerApp: typeof ɵgetOrCreateAngularServerApp;
|
|
17
18
|
}
|
package/src/typings.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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
|
-
// The `bundled_beasties` causes issues with module mappings in Bazel,
|
|
10
|
-
// leading to unexpected behavior with esbuild. Specifically, the problem occurs
|
|
11
|
-
// when esbuild resolves to a different module or version than expected, due to
|
|
12
|
-
// how Bazel handles module mappings.
|
|
13
|
-
//
|
|
14
|
-
// This change aims to resolve esbuild types correctly and maintain consistency
|
|
15
|
-
// in the Bazel build process.
|
|
16
|
-
|
|
17
|
-
declare module 'esbuild' {
|
|
18
|
-
export * from 'esbuild-wasm';
|
|
19
|
-
}
|