@angular/build 21.0.0-next.3 → 21.0.0-next.5
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 +11 -11
- package/src/builders/application/index.js +2 -1
- package/src/builders/karma/application_builder.d.ts +0 -2
- package/src/builders/karma/application_builder.js +44 -352
- package/src/builders/karma/assets-middleware.d.ts +26 -0
- package/src/builders/karma/assets-middleware.js +65 -0
- package/src/builders/karma/coverage.d.ts +9 -0
- package/src/builders/karma/coverage.js +31 -0
- package/src/builders/karma/find-tests.d.ts +1 -9
- package/src/builders/karma/find-tests.js +6 -106
- package/src/builders/karma/karma-config.d.ts +11 -0
- package/src/builders/karma/karma-config.js +79 -0
- package/src/builders/karma/polyfills-plugin.d.ts +13 -0
- package/src/builders/karma/polyfills-plugin.js +74 -0
- package/src/builders/karma/progress-reporter.d.ts +17 -0
- package/src/builders/karma/progress-reporter.js +73 -0
- package/src/builders/karma/utils.d.ts +17 -0
- package/src/builders/karma/utils.js +66 -0
- package/src/builders/unit-test/builder.js +150 -44
- package/src/builders/unit-test/options.d.ts +5 -1
- package/src/builders/unit-test/options.js +12 -5
- package/src/builders/unit-test/runners/api.d.ts +19 -1
- package/src/builders/unit-test/runners/dependency-checker.d.ts +43 -0
- package/src/builders/unit-test/runners/dependency-checker.js +82 -0
- package/src/builders/unit-test/runners/karma/executor.js +26 -2
- package/src/builders/unit-test/runners/karma/index.js +17 -0
- package/src/builders/unit-test/runners/vitest/browser-provider.d.ts +3 -2
- package/src/builders/unit-test/runners/vitest/build-options.js +6 -4
- package/src/builders/unit-test/runners/vitest/executor.d.ts +4 -5
- package/src/builders/unit-test/runners/vitest/executor.js +23 -135
- package/src/builders/unit-test/runners/vitest/index.js +19 -2
- package/src/builders/unit-test/runners/vitest/plugins.d.ts +23 -0
- package/src/builders/unit-test/runners/vitest/plugins.js +131 -0
- package/src/builders/unit-test/schema.d.ts +59 -30
- package/src/builders/unit-test/schema.js +1 -1
- package/src/builders/unit-test/schema.json +70 -16
- package/src/builders/unit-test/test-discovery.d.ts +25 -1
- package/src/builders/unit-test/test-discovery.js +194 -5
- package/src/tools/angular/transformers/jit-bootstrap-transformer.js +1 -1
- package/src/tools/angular/transformers/jit-resource-transformer.js +1 -1
- package/src/tools/esbuild/application-code-bundle.js +4 -7
- package/src/tools/esbuild/stylesheets/less-language.js +2 -26
- package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js +2 -1
- package/src/tools/vite/middlewares/assets-middleware.d.ts +2 -0
- package/src/tools/vite/middlewares/assets-middleware.js +31 -0
- package/src/tools/vite/utils.js +0 -1
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/supported-browsers.js +7 -3
- package/src/utils/test-files.d.ts +17 -0
- package/src/utils/test-files.js +82 -0
- package/.browserslistrc +0 -7
|
@@ -6,20 +6,20 @@
|
|
|
6
6
|
"properties": {
|
|
7
7
|
"buildTarget": {
|
|
8
8
|
"type": "string",
|
|
9
|
-
"description": "
|
|
9
|
+
"description": "Specifies the build target to use for the unit test build in the format `project:target[:configuration]`. You can also pass a comma-separated list of configurations. Example: `project:target:production,staging`.",
|
|
10
10
|
"pattern": "^[^:\\s]*:[^:\\s]*(:[^\\s]+)?$"
|
|
11
11
|
},
|
|
12
12
|
"tsConfig": {
|
|
13
13
|
"type": "string",
|
|
14
|
-
"description": "The
|
|
14
|
+
"description": "The path to the TypeScript configuration file, relative to the workspace root."
|
|
15
15
|
},
|
|
16
16
|
"runner": {
|
|
17
17
|
"type": "string",
|
|
18
|
-
"description": "
|
|
18
|
+
"description": "Specifies the test runner to use for test execution.",
|
|
19
19
|
"enum": ["karma", "vitest"]
|
|
20
20
|
},
|
|
21
21
|
"browsers": {
|
|
22
|
-
"description": "
|
|
22
|
+
"description": "Specifies the browsers to use for test execution. When not specified, tests are run in a Node.js environment using jsdom. For both Vitest and Karma, browser names ending with 'Headless' (e.g., 'ChromeHeadless') will enable headless mode.",
|
|
23
23
|
"type": "array",
|
|
24
24
|
"items": {
|
|
25
25
|
"type": "string"
|
|
@@ -32,39 +32,43 @@
|
|
|
32
32
|
"type": "string"
|
|
33
33
|
},
|
|
34
34
|
"default": ["**/*.spec.ts"],
|
|
35
|
-
"description": "
|
|
35
|
+
"description": "Specifies glob patterns of files to include for testing, relative to the project root. This option also has special handling for directory paths (includes all `.spec.ts` files within) and file paths (includes the corresponding `.spec` file if one exists)."
|
|
36
36
|
},
|
|
37
37
|
"exclude": {
|
|
38
38
|
"type": "array",
|
|
39
39
|
"items": {
|
|
40
40
|
"type": "string"
|
|
41
41
|
},
|
|
42
|
-
"description": "
|
|
42
|
+
"description": "Specifies glob patterns of files to exclude from testing, relative to the project root."
|
|
43
|
+
},
|
|
44
|
+
"filter": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "Specifies a regular expression pattern to match against test suite and test names. Only tests with a name matching the pattern will be executed. For example, `^App` will run only tests in suites beginning with 'App'."
|
|
43
47
|
},
|
|
44
48
|
"watch": {
|
|
45
49
|
"type": "boolean",
|
|
46
|
-
"description": "
|
|
50
|
+
"description": "Enables watch mode, which re-runs tests when source files change. Defaults to `true` in TTY environments and `false` otherwise."
|
|
47
51
|
},
|
|
48
52
|
"debug": {
|
|
49
53
|
"type": "boolean",
|
|
50
|
-
"description": "
|
|
54
|
+
"description": "Enables debugging mode for tests, allowing the use of the Node Inspector.",
|
|
51
55
|
"default": false
|
|
52
56
|
},
|
|
53
57
|
"codeCoverage": {
|
|
54
58
|
"type": "boolean",
|
|
55
|
-
"description": "
|
|
59
|
+
"description": "Enables code coverage reporting for tests.",
|
|
56
60
|
"default": false
|
|
57
61
|
},
|
|
58
62
|
"codeCoverageExclude": {
|
|
59
63
|
"type": "array",
|
|
60
|
-
"description": "
|
|
64
|
+
"description": "Specifies glob patterns of files to exclude from the code coverage report.",
|
|
61
65
|
"items": {
|
|
62
66
|
"type": "string"
|
|
63
67
|
}
|
|
64
68
|
},
|
|
65
69
|
"codeCoverageReporters": {
|
|
66
70
|
"type": "array",
|
|
67
|
-
"description": "
|
|
71
|
+
"description": "Specifies the reporters to use for code coverage results. Each reporter can be a string representing its name, or a tuple containing the name and an options object. Built-in reporters include 'html', 'lcov', 'lcovonly', 'text', 'text-summary', 'cobertura', 'json', and 'json-summary'.",
|
|
68
72
|
"items": {
|
|
69
73
|
"oneOf": [
|
|
70
74
|
{
|
|
@@ -88,14 +92,49 @@
|
|
|
88
92
|
},
|
|
89
93
|
"reporters": {
|
|
90
94
|
"type": "array",
|
|
91
|
-
"description": "
|
|
95
|
+
"description": "Specifies the reporters to use during test execution. Each reporter can be a string representing its name, or a tuple containing the name and an options object. Built-in reporters include 'default', 'verbose', 'dots', 'json', 'junit', 'tap', 'tap-flat', and 'html'. You can also provide a path to a custom reporter.",
|
|
92
96
|
"items": {
|
|
93
|
-
"
|
|
97
|
+
"oneOf": [
|
|
98
|
+
{
|
|
99
|
+
"anyOf": [
|
|
100
|
+
{
|
|
101
|
+
"$ref": "#/definitions/reporters-enum"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"type": "string"
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"type": "array",
|
|
110
|
+
"minItems": 1,
|
|
111
|
+
"maxItems": 2,
|
|
112
|
+
"items": [
|
|
113
|
+
{
|
|
114
|
+
"anyOf": [
|
|
115
|
+
{
|
|
116
|
+
"$ref": "#/definitions/reporters-enum"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"type": "string"
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"type": "object"
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
]
|
|
94
129
|
}
|
|
95
130
|
},
|
|
131
|
+
"outputFile": {
|
|
132
|
+
"type": "string",
|
|
133
|
+
"description": "Specifies a file path for the test report, applying only to the first reporter. To configure output files for multiple reporters, use the tuple format `['reporter-name', { outputFile: '...' }]` within the `reporters` option. When not provided, output is written to the console."
|
|
134
|
+
},
|
|
96
135
|
"providersFile": {
|
|
97
136
|
"type": "string",
|
|
98
|
-
"description": "TypeScript file that
|
|
137
|
+
"description": "Specifies the path to a TypeScript file that provides an array of Angular providers for the test environment. The file must contain a default export of the provider array.",
|
|
99
138
|
"minLength": 1
|
|
100
139
|
},
|
|
101
140
|
"setupFiles": {
|
|
@@ -103,11 +142,22 @@
|
|
|
103
142
|
"items": {
|
|
104
143
|
"type": "string"
|
|
105
144
|
},
|
|
106
|
-
"description": "A list of global setup
|
|
145
|
+
"description": "A list of paths to global setup files that are executed before the test files. The application's polyfills and the Angular TestBed are always initialized before these files."
|
|
107
146
|
},
|
|
108
147
|
"progress": {
|
|
109
148
|
"type": "boolean",
|
|
110
|
-
"description": "
|
|
149
|
+
"description": "Shows build progress information in the console. Defaults to the `progress` setting of the specified `buildTarget`."
|
|
150
|
+
},
|
|
151
|
+
"listTests": {
|
|
152
|
+
"type": "boolean",
|
|
153
|
+
"description": "Lists all discovered test files and exits the process without building or executing the tests.",
|
|
154
|
+
"default": false
|
|
155
|
+
},
|
|
156
|
+
"dumpVirtualFiles": {
|
|
157
|
+
"type": "boolean",
|
|
158
|
+
"description": "Dumps build output files to the `.angular/cache` directory for debugging purposes.",
|
|
159
|
+
"default": false,
|
|
160
|
+
"visible": false
|
|
111
161
|
}
|
|
112
162
|
},
|
|
113
163
|
"additionalProperties": false,
|
|
@@ -124,6 +174,10 @@
|
|
|
124
174
|
"json",
|
|
125
175
|
"json-summary"
|
|
126
176
|
]
|
|
177
|
+
},
|
|
178
|
+
"reporters-enum": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"enum": ["default", "verbose", "dots", "json", "junit", "tap", "tap-flat", "html"]
|
|
127
181
|
}
|
|
128
182
|
}
|
|
129
183
|
}
|
|
@@ -5,4 +5,28 @@
|
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
6
|
* found in the LICENSE file at https://angular.dev/license
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Finds all test files in the project.
|
|
10
|
+
*
|
|
11
|
+
* @param include Glob patterns of files to include.
|
|
12
|
+
* @param exclude Glob patterns of files to exclude.
|
|
13
|
+
* @param workspaceRoot The absolute path to the workspace root.
|
|
14
|
+
* @param projectSourceRoot The absolute path to the project's source root.
|
|
15
|
+
* @returns A unique set of absolute paths to all test files.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findTests(include: string[], exclude: string[], workspaceRoot: string, projectSourceRoot: string): Promise<string[]>;
|
|
18
|
+
interface TestEntrypointsOptions {
|
|
19
|
+
projectSourceRoot: string;
|
|
20
|
+
workspaceRoot: string;
|
|
21
|
+
removeTestExtension?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generates unique, dash-delimited bundle names for a set of test files.
|
|
25
|
+
* This is used to create distinct output files for each test.
|
|
26
|
+
*
|
|
27
|
+
* @param testFiles An array of absolute paths to test files.
|
|
28
|
+
* @param options Configuration options for generating entry points.
|
|
29
|
+
* @returns A map where keys are the generated unique bundle names and values are the original file paths.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getTestEntrypoints(testFiles: string[], { projectSourceRoot, workspaceRoot, removeTestExtension }: TestEntrypointsOptions): Map<string, string>;
|
|
32
|
+
export {};
|
|
@@ -7,8 +7,197 @@
|
|
|
7
7
|
* found in the LICENSE file at https://angular.dev/license
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
exports.findTests = findTests;
|
|
11
|
+
exports.getTestEntrypoints = getTestEntrypoints;
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const tinyglobby_1 = require("tinyglobby");
|
|
15
|
+
const path_1 = require("../../utils/path");
|
|
16
|
+
/**
|
|
17
|
+
* Finds all test files in the project.
|
|
18
|
+
*
|
|
19
|
+
* @param include Glob patterns of files to include.
|
|
20
|
+
* @param exclude Glob patterns of files to exclude.
|
|
21
|
+
* @param workspaceRoot The absolute path to the workspace root.
|
|
22
|
+
* @param projectSourceRoot The absolute path to the project's source root.
|
|
23
|
+
* @returns A unique set of absolute paths to all test files.
|
|
24
|
+
*/
|
|
25
|
+
async function findTests(include, exclude, workspaceRoot, projectSourceRoot) {
|
|
26
|
+
const staticMatches = new Set();
|
|
27
|
+
const dynamicPatterns = [];
|
|
28
|
+
const normalizedExcludes = exclude.map((p) => normalizePattern(p, workspaceRoot, projectSourceRoot));
|
|
29
|
+
// 1. Separate static and dynamic patterns
|
|
30
|
+
for (const pattern of include) {
|
|
31
|
+
const normalized = normalizePattern(pattern, workspaceRoot, projectSourceRoot);
|
|
32
|
+
if ((0, tinyglobby_1.isDynamicPattern)(normalized)) {
|
|
33
|
+
dynamicPatterns.push(normalized);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const result = await handleStaticPattern(normalized, projectSourceRoot);
|
|
37
|
+
if (Array.isArray(result)) {
|
|
38
|
+
result.forEach((file) => staticMatches.add(file));
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// It was a static path that didn't resolve to a spec, treat as dynamic
|
|
42
|
+
dynamicPatterns.push(result);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// 2. Execute a single glob for all dynamic patterns
|
|
47
|
+
if (dynamicPatterns.length > 0) {
|
|
48
|
+
const globMatches = await (0, tinyglobby_1.glob)(dynamicPatterns, {
|
|
49
|
+
cwd: projectSourceRoot,
|
|
50
|
+
absolute: true,
|
|
51
|
+
ignore: ['**/node_modules/**', ...normalizedExcludes],
|
|
52
|
+
});
|
|
53
|
+
for (const match of globMatches) {
|
|
54
|
+
staticMatches.add(match);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 3. Combine and de-duplicate results
|
|
58
|
+
return [...staticMatches];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generates unique, dash-delimited bundle names for a set of test files.
|
|
62
|
+
* This is used to create distinct output files for each test.
|
|
63
|
+
*
|
|
64
|
+
* @param testFiles An array of absolute paths to test files.
|
|
65
|
+
* @param options Configuration options for generating entry points.
|
|
66
|
+
* @returns A map where keys are the generated unique bundle names and values are the original file paths.
|
|
67
|
+
*/
|
|
68
|
+
function getTestEntrypoints(testFiles, { projectSourceRoot, workspaceRoot, removeTestExtension }) {
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
const roots = [projectSourceRoot, workspaceRoot];
|
|
71
|
+
return new Map(Array.from(testFiles, (testFile) => {
|
|
72
|
+
const fileName = generateNameFromPath(testFile, roots, !!removeTestExtension);
|
|
73
|
+
const baseName = `spec-${fileName}`;
|
|
74
|
+
let uniqueName = baseName;
|
|
75
|
+
let suffix = 2;
|
|
76
|
+
while (seen.has(uniqueName)) {
|
|
77
|
+
uniqueName = `${baseName}-${suffix}`.replace(/([^\w](?:spec|test))-([\d]+)$/, '-$2$1');
|
|
78
|
+
++suffix;
|
|
79
|
+
}
|
|
80
|
+
seen.add(uniqueName);
|
|
81
|
+
return [uniqueName, testFile];
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Generates a unique, dash-delimited name from a file path.
|
|
86
|
+
* This is used to create a consistent and readable bundle name for a given test file.
|
|
87
|
+
* @param testFile The absolute path to the test file.
|
|
88
|
+
* @param roots An array of root paths to remove from the beginning of the test file path.
|
|
89
|
+
* @param removeTestExtension Whether to remove the `.spec` or `.test` extension from the result.
|
|
90
|
+
* @returns A dash-cased name derived from the relative path of the test file.
|
|
91
|
+
*/
|
|
92
|
+
function generateNameFromPath(testFile, roots, removeTestExtension) {
|
|
93
|
+
const relativePath = removeRoots(testFile, roots);
|
|
94
|
+
let startIndex = 0;
|
|
95
|
+
// Skip leading dots and slashes
|
|
96
|
+
while (startIndex < relativePath.length && /^[./\\]$/.test(relativePath[startIndex])) {
|
|
97
|
+
startIndex++;
|
|
98
|
+
}
|
|
99
|
+
let endIndex = relativePath.length;
|
|
100
|
+
if (removeTestExtension) {
|
|
101
|
+
const match = relativePath.match(/\.(spec|test)\.[^.]+$/);
|
|
102
|
+
if (match?.index) {
|
|
103
|
+
endIndex = match.index;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const extIndex = relativePath.lastIndexOf('.');
|
|
108
|
+
if (extIndex > startIndex) {
|
|
109
|
+
endIndex = extIndex;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Build the final string in a single pass
|
|
113
|
+
let result = '';
|
|
114
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
115
|
+
const char = relativePath[i];
|
|
116
|
+
result += char === '/' || char === '\\' ? '-' : char;
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
const removeLeadingSlash = (pattern) => {
|
|
121
|
+
if (pattern.charAt(0) === '/') {
|
|
122
|
+
return pattern.substring(1);
|
|
123
|
+
}
|
|
124
|
+
return pattern;
|
|
125
|
+
};
|
|
126
|
+
const removeRelativeRoot = (path, root) => {
|
|
127
|
+
if (path.startsWith(root)) {
|
|
128
|
+
return path.substring(root.length);
|
|
129
|
+
}
|
|
130
|
+
return path;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Removes potential root paths from a file path, returning a relative path.
|
|
134
|
+
* If no root path matches, it returns the file's basename.
|
|
135
|
+
*/
|
|
136
|
+
function removeRoots(path, roots) {
|
|
137
|
+
for (const root of roots) {
|
|
138
|
+
if (path.startsWith(root)) {
|
|
139
|
+
return path.substring(root.length);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return (0, node_path_1.basename)(path);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Normalizes a glob pattern by converting it to a POSIX path, removing leading slashes,
|
|
146
|
+
* and making it relative to the project source root.
|
|
147
|
+
*
|
|
148
|
+
* @param pattern The glob pattern to normalize.
|
|
149
|
+
* @param workspaceRoot The absolute path to the workspace root.
|
|
150
|
+
* @param projectSourceRoot The absolute path to the project's source root.
|
|
151
|
+
* @returns A normalized glob pattern.
|
|
152
|
+
*/
|
|
153
|
+
function normalizePattern(pattern, workspaceRoot, projectSourceRoot) {
|
|
154
|
+
// normalize pattern, glob lib only accepts forward slashes
|
|
155
|
+
let normalizedPattern = (0, path_1.toPosixPath)(pattern);
|
|
156
|
+
normalizedPattern = removeLeadingSlash(normalizedPattern);
|
|
157
|
+
const relativeProjectRoot = (0, path_1.toPosixPath)((0, node_path_1.relative)(workspaceRoot, projectSourceRoot) + '/');
|
|
158
|
+
// remove relativeProjectRoot to support relative paths from root
|
|
159
|
+
// such paths are easy to get when running scripts via IDEs
|
|
160
|
+
return removeRelativeRoot(normalizedPattern, relativeProjectRoot);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Handles static (non-glob) patterns by attempting to resolve them to a directory
|
|
164
|
+
* of spec files or a corresponding `.spec` file.
|
|
165
|
+
*
|
|
166
|
+
* @param pattern The static path pattern.
|
|
167
|
+
* @param projectSourceRoot The absolute path to the project's source root.
|
|
168
|
+
* @returns A promise that resolves to either an array of found spec files, a new glob pattern,
|
|
169
|
+
* or the original pattern if no special handling was applied.
|
|
170
|
+
*/
|
|
171
|
+
async function handleStaticPattern(pattern, projectSourceRoot) {
|
|
172
|
+
const fullPath = (0, node_path_1.join)(projectSourceRoot, pattern);
|
|
173
|
+
if (await isDirectory(fullPath)) {
|
|
174
|
+
return `${pattern}/**/*.spec.@(ts|tsx)`;
|
|
175
|
+
}
|
|
176
|
+
const fileExt = (0, node_path_1.extname)(pattern);
|
|
177
|
+
// Replace extension to `.spec.ext`. Example: `src/app/app.component.ts`-> `src/app/app.component.spec.ts`
|
|
178
|
+
const potentialSpec = (0, node_path_1.join)(projectSourceRoot, (0, node_path_1.dirname)(pattern), `${(0, node_path_1.basename)(pattern, fileExt)}.spec${fileExt}`);
|
|
179
|
+
if (await exists(potentialSpec)) {
|
|
180
|
+
return [potentialSpec];
|
|
181
|
+
}
|
|
182
|
+
return pattern;
|
|
183
|
+
}
|
|
184
|
+
/** Checks if a path exists and is a directory. */
|
|
185
|
+
async function isDirectory(path) {
|
|
186
|
+
try {
|
|
187
|
+
const stats = await node_fs_1.promises.stat(path);
|
|
188
|
+
return stats.isDirectory();
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** Checks if a path exists on the file system. */
|
|
195
|
+
async function exists(path) {
|
|
196
|
+
try {
|
|
197
|
+
await node_fs_1.promises.access(path, node_fs_1.constants.F_OK);
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -30,7 +30,7 @@ function replaceBootstrap(getTypeChecker) {
|
|
|
30
30
|
if (target.text === PLATFORM_BROWSER_DYNAMIC_NAME) {
|
|
31
31
|
if (!bootstrapNamespace) {
|
|
32
32
|
bootstrapNamespace = nodeFactory.createUniqueName('__NgCli_bootstrap_');
|
|
33
|
-
bootstrapImport = nodeFactory.createImportDeclaration(undefined, nodeFactory.createImportClause(
|
|
33
|
+
bootstrapImport = nodeFactory.createImportDeclaration(undefined, nodeFactory.createImportClause(undefined, undefined, nodeFactory.createNamespaceImport(bootstrapNamespace)), nodeFactory.createStringLiteral('@angular/platform-browser'));
|
|
34
34
|
}
|
|
35
35
|
replacedNodes.push(target);
|
|
36
36
|
return nodeFactory.updateCallExpression(node, nodeFactory.createPropertyAccessExpression(bootstrapNamespace, 'platformBrowser'), node.typeArguments, node.arguments);
|
|
@@ -141,7 +141,7 @@ function visitComponentMetadata(nodeFactory, node, styleReplacements, resourceIm
|
|
|
141
141
|
function createResourceImport(nodeFactory, url, resourceImportDeclarations) {
|
|
142
142
|
const urlLiteral = nodeFactory.createStringLiteral(url);
|
|
143
143
|
const importName = nodeFactory.createIdentifier(`__NG_CLI_RESOURCE__${resourceImportDeclarations.length}`);
|
|
144
|
-
resourceImportDeclarations.push(nodeFactory.createImportDeclaration(undefined, nodeFactory.createImportClause(
|
|
144
|
+
resourceImportDeclarations.push(nodeFactory.createImportDeclaration(undefined, nodeFactory.createImportClause(undefined, importName, undefined), urlLiteral));
|
|
145
145
|
return importName;
|
|
146
146
|
}
|
|
147
147
|
function getDecoratorOrigin(decorator, typeChecker) {
|
|
@@ -418,8 +418,6 @@ function getEsBuildCommonOptions(options) {
|
|
|
418
418
|
packages = 'external';
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
|
-
const minifySyntax = optimizationOptions.scripts;
|
|
422
|
-
const minifyIdentifiers = minifySyntax && environment_options_1.allowMangle;
|
|
423
421
|
return {
|
|
424
422
|
absWorkingDir: workspaceRoot,
|
|
425
423
|
format: 'esm',
|
|
@@ -431,10 +429,9 @@ function getEsBuildCommonOptions(options) {
|
|
|
431
429
|
metafile: true,
|
|
432
430
|
legalComments: options.extractLicenses ? 'none' : 'eof',
|
|
433
431
|
logLevel: options.verbose && !jsonLogs ? 'debug' : 'silent',
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
minifyWhitespace: minifySyntax,
|
|
432
|
+
minifyIdentifiers: optimizationOptions.scripts && environment_options_1.allowMangle,
|
|
433
|
+
minifySyntax: optimizationOptions.scripts,
|
|
434
|
+
minifyWhitespace: optimizationOptions.scripts,
|
|
438
435
|
pure: ['forwardRef'],
|
|
439
436
|
outdir: workspaceRoot,
|
|
440
437
|
outExtension: outExtension ? { '.js': `.${outExtension}` } : undefined,
|
|
@@ -451,7 +448,7 @@ function getEsBuildCommonOptions(options) {
|
|
|
451
448
|
// Only set to false when script optimizations are enabled. It should not be set to true because
|
|
452
449
|
// Angular turns `ngDevMode` into an object for development debugging purposes when not defined
|
|
453
450
|
// which a constant true value would break.
|
|
454
|
-
...(
|
|
451
|
+
...(optimizationOptions.scripts ? { 'ngDevMode': 'false' } : undefined),
|
|
455
452
|
'ngJitMode': jit ? 'true' : 'false',
|
|
456
453
|
'ngServerMode': 'false',
|
|
457
454
|
'ngHmrMode': options.templateUpdates ? 'true' : 'false',
|
|
@@ -55,11 +55,10 @@ exports.LessStylesheetLanguage = Object.freeze({
|
|
|
55
55
|
componentFilter: /^less;/,
|
|
56
56
|
fileFilter: /\.less$/,
|
|
57
57
|
process(data, file, _, options, build) {
|
|
58
|
-
return compileString(data, file, options, build.resolve.bind(build)
|
|
59
|
-
/* unsafeInlineJavaScript */ false);
|
|
58
|
+
return compileString(data, file, options, build.resolve.bind(build));
|
|
60
59
|
},
|
|
61
60
|
});
|
|
62
|
-
async function compileString(data, filename, options, resolver
|
|
61
|
+
async function compileString(data, filename, options, resolver) {
|
|
63
62
|
try {
|
|
64
63
|
lessPreprocessor ??= (await Promise.resolve().then(() => __importStar(require('less')))).default;
|
|
65
64
|
}
|
|
@@ -120,7 +119,6 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava
|
|
|
120
119
|
paths: options.includePaths,
|
|
121
120
|
plugins: [resolverPlugin],
|
|
122
121
|
rewriteUrls: 'all',
|
|
123
|
-
javascriptEnabled: unsafeInlineJavaScript,
|
|
124
122
|
sourceMap: options.sourcemap
|
|
125
123
|
? {
|
|
126
124
|
sourceMapFileInline: true,
|
|
@@ -137,28 +135,6 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava
|
|
|
137
135
|
catch (error) {
|
|
138
136
|
if (isLessException(error)) {
|
|
139
137
|
const location = convertExceptionLocation(error);
|
|
140
|
-
// Retry with a warning for less files requiring the deprecated inline JavaScript option
|
|
141
|
-
if (error.message.includes('Inline JavaScript is not enabled.')) {
|
|
142
|
-
const withJsResult = await compileString(data, filename, options, resolver,
|
|
143
|
-
/* unsafeInlineJavaScript */ true);
|
|
144
|
-
withJsResult.warnings = [
|
|
145
|
-
{
|
|
146
|
-
text: 'Deprecated inline execution of JavaScript has been enabled ("javascriptEnabled")',
|
|
147
|
-
location,
|
|
148
|
-
notes: [
|
|
149
|
-
{
|
|
150
|
-
location: null,
|
|
151
|
-
text: 'JavaScript found within less stylesheets may be executed at build time. [https://lesscss.org/usage/#less-options]',
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
location: null,
|
|
155
|
-
text: 'Support for "javascriptEnabled" may be removed from the Angular CLI starting with Angular v19.',
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
},
|
|
159
|
-
];
|
|
160
|
-
return withJsResult;
|
|
161
|
-
}
|
|
162
138
|
return {
|
|
163
139
|
errors: [
|
|
164
140
|
{
|
|
@@ -152,7 +152,8 @@ class StylesheetPluginFactory {
|
|
|
152
152
|
postcssProcessor = postcss();
|
|
153
153
|
const postCssPluginRequire = (0, node_module_1.createRequire)((0, node_path_1.dirname)(configPath) + '/');
|
|
154
154
|
for (const [pluginName, pluginOptions] of config.plugins) {
|
|
155
|
-
const
|
|
155
|
+
const pluginMod = postCssPluginRequire(pluginName);
|
|
156
|
+
const plugin = pluginMod.__esModule ? pluginMod['default'] : pluginMod;
|
|
156
157
|
if (typeof plugin !== 'function' || plugin.postcss !== true) {
|
|
157
158
|
throw new Error(`Attempted to load invalid Postcss plugin: "${pluginName}"`);
|
|
158
159
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.dev/license
|
|
7
7
|
*/
|
|
8
8
|
import type { Connect, ViteDevServer } from 'vite';
|
|
9
|
+
import { ResultFile } from '../../../builders/application/results';
|
|
9
10
|
import { AngularMemoryOutputFiles, AngularOutputAssets } from '../utils';
|
|
10
11
|
export interface ComponentStyleRecord {
|
|
11
12
|
rawContent: Uint8Array;
|
|
@@ -13,3 +14,4 @@ export interface ComponentStyleRecord {
|
|
|
13
14
|
reload?: boolean;
|
|
14
15
|
}
|
|
15
16
|
export declare function createAngularAssetsMiddleware(server: ViteDevServer, assets: AngularOutputAssets, outputFiles: AngularMemoryOutputFiles, componentStyles: Map<string, ComponentStyleRecord>, encapsulateStyle: (style: Uint8Array, componentId: string) => string): Connect.NextHandleFunction;
|
|
17
|
+
export declare function createBuildAssetsMiddleware(basePath: string, buildResultFiles: ReadonlyMap<string, ResultFile>, readHandler?: (path: string) => Buffer): Connect.NextHandleFunction;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.createAngularAssetsMiddleware = createAngularAssetsMiddleware;
|
|
11
|
+
exports.createBuildAssetsMiddleware = createBuildAssetsMiddleware;
|
|
11
12
|
const mrmime_1 = require("mrmime");
|
|
12
13
|
const node_crypto_1 = require("node:crypto");
|
|
13
14
|
const node_fs_1 = require("node:fs");
|
|
@@ -171,3 +172,33 @@ function checkAndHandleEtag(req, res, etag) {
|
|
|
171
172
|
}
|
|
172
173
|
return false;
|
|
173
174
|
}
|
|
175
|
+
function createBuildAssetsMiddleware(basePath, buildResultFiles, readHandler = node_fs_1.readFileSync) {
|
|
176
|
+
return function buildAssetsMiddleware(req, res, next) {
|
|
177
|
+
if (req.url === undefined || res.writableEnded) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
// Parse the incoming request.
|
|
181
|
+
// The base of the URL is unused but required to parse the URL.
|
|
182
|
+
const pathname = (0, utils_1.pathnameWithoutBasePath)(req.url, basePath);
|
|
183
|
+
const extension = (0, node_path_1.extname)(pathname);
|
|
184
|
+
if (extension && !/\.[mc]?[jt]s(?:\.map)?$/.test(extension)) {
|
|
185
|
+
const outputFile = buildResultFiles.get(pathname.slice(1));
|
|
186
|
+
if (outputFile) {
|
|
187
|
+
const contents = outputFile.origin === 'memory' ? outputFile.contents : readHandler(outputFile.inputPath);
|
|
188
|
+
const etag = `W/${(0, node_crypto_1.createHash)('sha256').update(contents).digest('hex')}`;
|
|
189
|
+
if (checkAndHandleEtag(req, res, etag)) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const mimeType = (0, mrmime_1.lookup)(extension);
|
|
193
|
+
if (mimeType) {
|
|
194
|
+
res.setHeader('Content-Type', mimeType);
|
|
195
|
+
}
|
|
196
|
+
res.setHeader('ETag', etag);
|
|
197
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
198
|
+
res.end(contents);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
next();
|
|
203
|
+
};
|
|
204
|
+
}
|
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.5';
|
|
14
14
|
function hasCacheMetadata(value) {
|
|
15
15
|
return (!!value &&
|
|
16
16
|
typeof value === 'object' &&
|
|
@@ -12,11 +12,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.getSupportedBrowsers = getSupportedBrowsers;
|
|
14
14
|
const browserslist_1 = __importDefault(require("browserslist"));
|
|
15
|
+
// The below is replaced by bazel `npm_package`.
|
|
16
|
+
const BASELINE_DATE = '2025-08-20';
|
|
15
17
|
function getSupportedBrowsers(projectRoot, logger) {
|
|
16
18
|
// Read the browserslist configuration containing Angular's browser support policy.
|
|
17
|
-
const angularBrowserslist = (0, browserslist_1.default)(
|
|
18
|
-
path: require.resolve('../../.browserslistrc'),
|
|
19
|
-
});
|
|
19
|
+
const angularBrowserslist = (0, browserslist_1.default)(`baseline widely available on ${getBaselineDate()}`);
|
|
20
20
|
// Use Angular's configuration as the default.
|
|
21
21
|
browserslist_1.default.defaults = angularBrowserslist;
|
|
22
22
|
// Get the minimum set of browser versions supported by Angular.
|
|
@@ -50,3 +50,7 @@ function getSupportedBrowsers(projectRoot, logger) {
|
|
|
50
50
|
}
|
|
51
51
|
return Array.from(browsersFromConfigOrDefault);
|
|
52
52
|
}
|
|
53
|
+
function getBaselineDate() {
|
|
54
|
+
// Unlike `npm_package`, `ts_project` which is used to run unit tests does not support substitutions.
|
|
55
|
+
return BASELINE_DATE[0] === 'B' ? '2025-01-01' : BASELINE_DATE;
|
|
56
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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 { ResultFile } from '../builders/application/results';
|
|
9
|
+
/**
|
|
10
|
+
* Writes a collection of build result files to a specified directory.
|
|
11
|
+
* This function handles both in-memory and on-disk files, creating subdirectories
|
|
12
|
+
* as needed.
|
|
13
|
+
*
|
|
14
|
+
* @param files A map of file paths to `ResultFile` objects, representing the build output.
|
|
15
|
+
* @param testDir The absolute path to the directory where the files should be written.
|
|
16
|
+
*/
|
|
17
|
+
export declare function writeTestFiles(files: Record<string, ResultFile>, testDir: string): Promise<void>;
|