@angular-devkit/build-angular 19.0.0-next.0 → 19.0.0-next.10
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/LICENSE +5 -5
- package/package.json +33 -33
- package/src/builders/browser-esbuild/index.js +12 -3
- package/src/builders/dev-server/options.js +1 -1
- package/src/builders/dev-server/schema.d.ts +1 -8
- package/src/builders/dev-server/schema.json +1 -7
- package/src/builders/extract-i18n/application-extraction.js +5 -0
- package/src/builders/extract-i18n/options.js +1 -1
- package/src/builders/extract-i18n/schema.d.ts +0 -7
- package/src/builders/extract-i18n/schema.json +0 -6
- package/src/builders/karma/application_builder.d.ts +19 -0
- package/src/builders/karma/application_builder.js +285 -0
- package/src/builders/karma/browser_builder.d.ts +21 -0
- package/src/builders/karma/browser_builder.js +137 -0
- package/src/builders/karma/find-tests-plugin.js +2 -96
- package/src/builders/karma/find-tests.d.ts +8 -0
- package/src/builders/karma/find-tests.js +105 -0
- package/src/builders/karma/index.js +72 -124
- package/src/builders/karma/init_test_bed.js +19 -0
- package/src/builders/karma/schema.d.ts +14 -0
- package/src/builders/karma/schema.js +11 -1
- package/src/builders/karma/schema.json +6 -0
- package/src/tools/webpack/configs/common.js +2 -1
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/process-bundle.js +0 -5
- package/src/utils/tailwind.js +1 -1
- package/src/builders/browser-esbuild/builder-status-warnings.d.ts +0 -10
- package/src/builders/browser-esbuild/builder-status-warnings.js +0 -39
|
@@ -0,0 +1,137 @@
|
|
|
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 (mod) {
|
|
26
|
+
if (mod && mod.__esModule) return mod;
|
|
27
|
+
var result = {};
|
|
28
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
29
|
+
__setModuleDefault(result, mod);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.execute = execute;
|
|
34
|
+
const private_1 = require("@angular/build/private");
|
|
35
|
+
const path = __importStar(require("path"));
|
|
36
|
+
const rxjs_1 = require("rxjs");
|
|
37
|
+
const configs_1 = require("../../tools/webpack/configs");
|
|
38
|
+
const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
|
|
39
|
+
const schema_1 = require("../browser/schema");
|
|
40
|
+
const find_tests_plugin_1 = require("./find-tests-plugin");
|
|
41
|
+
function execute(options, context, karmaOptions, transforms = {}) {
|
|
42
|
+
return (0, rxjs_1.from)(initializeBrowser(options, context)).pipe((0, rxjs_1.switchMap)(async ([karma, webpackConfig]) => {
|
|
43
|
+
const projectName = context.target?.project;
|
|
44
|
+
if (!projectName) {
|
|
45
|
+
throw new Error(`The 'karma' builder requires a target to be specified.`);
|
|
46
|
+
}
|
|
47
|
+
const projectMetadata = await context.getProjectMetadata(projectName);
|
|
48
|
+
const sourceRoot = (projectMetadata.sourceRoot ?? projectMetadata.root ?? '');
|
|
49
|
+
if (!options.main) {
|
|
50
|
+
webpackConfig.entry ??= {};
|
|
51
|
+
if (typeof webpackConfig.entry === 'object' && !Array.isArray(webpackConfig.entry)) {
|
|
52
|
+
if (Array.isArray(webpackConfig.entry['main'])) {
|
|
53
|
+
webpackConfig.entry['main'].push(getBuiltInMainFile());
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
webpackConfig.entry['main'] = [getBuiltInMainFile()];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
webpackConfig.plugins ??= [];
|
|
61
|
+
webpackConfig.plugins.push(new find_tests_plugin_1.FindTestsPlugin({
|
|
62
|
+
include: options.include,
|
|
63
|
+
exclude: options.exclude,
|
|
64
|
+
workspaceRoot: context.workspaceRoot,
|
|
65
|
+
projectSourceRoot: path.join(context.workspaceRoot, sourceRoot),
|
|
66
|
+
}));
|
|
67
|
+
karmaOptions.buildWebpack = {
|
|
68
|
+
options,
|
|
69
|
+
webpackConfig,
|
|
70
|
+
logger: context.logger,
|
|
71
|
+
};
|
|
72
|
+
const parsedKarmaConfig = await karma.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true });
|
|
73
|
+
return [karma, parsedKarmaConfig];
|
|
74
|
+
}), (0, rxjs_1.switchMap)(([karma, karmaConfig]) => new rxjs_1.Observable((subscriber) => {
|
|
75
|
+
// Pass onto Karma to emit BuildEvents.
|
|
76
|
+
karmaConfig.buildWebpack ??= {};
|
|
77
|
+
if (typeof karmaConfig.buildWebpack === 'object') {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
+
karmaConfig.buildWebpack.failureCb ??= () => subscriber.next({ success: false });
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
81
|
+
karmaConfig.buildWebpack.successCb ??= () => subscriber.next({ success: true });
|
|
82
|
+
}
|
|
83
|
+
// Complete the observable once the Karma server returns.
|
|
84
|
+
const karmaServer = new karma.Server(karmaConfig, (exitCode) => {
|
|
85
|
+
subscriber.next({ success: exitCode === 0 });
|
|
86
|
+
subscriber.complete();
|
|
87
|
+
});
|
|
88
|
+
const karmaStart = karmaServer.start();
|
|
89
|
+
// Cleanup, signal Karma to exit.
|
|
90
|
+
return () => {
|
|
91
|
+
void karmaStart.then(() => karmaServer.stop());
|
|
92
|
+
};
|
|
93
|
+
})), (0, rxjs_1.defaultIfEmpty)({ success: false }));
|
|
94
|
+
}
|
|
95
|
+
async function initializeBrowser(options, context, webpackConfigurationTransformer) {
|
|
96
|
+
// Purge old build disk cache.
|
|
97
|
+
await (0, private_1.purgeStaleBuildCache)(context);
|
|
98
|
+
const karma = await Promise.resolve().then(() => __importStar(require('karma')));
|
|
99
|
+
const { config } = await (0, webpack_browser_config_1.generateBrowserWebpackConfigFromContext)(
|
|
100
|
+
// only two properties are missing:
|
|
101
|
+
// * `outputPath` which is fixed for tests
|
|
102
|
+
// * `budgets` which might be incorrect due to extra dev libs
|
|
103
|
+
{
|
|
104
|
+
...options,
|
|
105
|
+
outputPath: '',
|
|
106
|
+
budgets: undefined,
|
|
107
|
+
optimization: false,
|
|
108
|
+
buildOptimizer: false,
|
|
109
|
+
aot: false,
|
|
110
|
+
vendorChunk: true,
|
|
111
|
+
namedChunks: true,
|
|
112
|
+
extractLicenses: false,
|
|
113
|
+
outputHashing: schema_1.OutputHashing.None,
|
|
114
|
+
// The webpack tier owns the watch behavior so we want to force it in the config.
|
|
115
|
+
// When not in watch mode, webpack-dev-middleware will call `compiler.watch` anyway.
|
|
116
|
+
// https://github.com/webpack/webpack-dev-middleware/blob/698c9ae5e9bb9a013985add6189ff21c1a1ec185/src/index.js#L65
|
|
117
|
+
// https://github.com/webpack/webpack/blob/cde1b73e12eb8a77eb9ba42e7920c9ec5d29c2c9/lib/Compiler.js#L379-L388
|
|
118
|
+
watch: true,
|
|
119
|
+
}, context, (wco) => [(0, configs_1.getCommonConfig)(wco), (0, configs_1.getStylesConfig)(wco)]);
|
|
120
|
+
return [karma, (await webpackConfigurationTransformer?.(config)) ?? config];
|
|
121
|
+
}
|
|
122
|
+
function getBuiltInMainFile() {
|
|
123
|
+
const content = Buffer.from(`
|
|
124
|
+
import { getTestBed } from '@angular/core/testing';
|
|
125
|
+
import {
|
|
126
|
+
BrowserDynamicTestingModule,
|
|
127
|
+
platformBrowserDynamicTesting,
|
|
128
|
+
} from '@angular/platform-browser-dynamic/testing';
|
|
129
|
+
|
|
130
|
+
// Initialize the Angular testing environment.
|
|
131
|
+
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
|
|
132
|
+
errorOnUnknownElements: true,
|
|
133
|
+
errorOnUnknownProperties: true
|
|
134
|
+
});
|
|
135
|
+
`).toString('base64');
|
|
136
|
+
return `ng-virtual-main.js!=!data:text/javascript;base64,${content}`;
|
|
137
|
+
}
|
|
@@ -6,39 +6,14 @@
|
|
|
6
6
|
* Use of this source code is governed by an MIT-style license that can be
|
|
7
7
|
* found in the LICENSE file at https://angular.dev/license
|
|
8
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 (mod) {
|
|
26
|
-
if (mod && mod.__esModule) return mod;
|
|
27
|
-
var result = {};
|
|
28
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
29
|
-
__setModuleDefault(result, mod);
|
|
30
|
-
return result;
|
|
31
|
-
};
|
|
32
9
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
10
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
11
|
};
|
|
35
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
13
|
exports.FindTestsPlugin = void 0;
|
|
37
14
|
const assert_1 = __importDefault(require("assert"));
|
|
38
|
-
const fast_glob_1 = __importStar(require("fast-glob"));
|
|
39
|
-
const fs_1 = require("fs");
|
|
40
15
|
const mini_css_extract_plugin_1 = require("mini-css-extract-plugin");
|
|
41
|
-
const
|
|
16
|
+
const find_tests_1 = require("./find-tests");
|
|
42
17
|
/**
|
|
43
18
|
* The name of the plugin provided to Webpack when tapping Webpack compiler hooks.
|
|
44
19
|
*/
|
|
@@ -56,7 +31,7 @@ class FindTestsPlugin {
|
|
|
56
31
|
let originalImport;
|
|
57
32
|
// Add tests files are part of the entry-point.
|
|
58
33
|
webpackOptions.entry = async () => {
|
|
59
|
-
const specFiles = await findTests(include, exclude, workspaceRoot, projectSourceRoot);
|
|
34
|
+
const specFiles = await (0, find_tests_1.findTests)(include, exclude, workspaceRoot, projectSourceRoot);
|
|
60
35
|
const entrypoints = await entry;
|
|
61
36
|
const entrypoint = entrypoints['main'];
|
|
62
37
|
if (!entrypoint.import) {
|
|
@@ -81,72 +56,3 @@ class FindTestsPlugin {
|
|
|
81
56
|
}
|
|
82
57
|
}
|
|
83
58
|
exports.FindTestsPlugin = FindTestsPlugin;
|
|
84
|
-
// go through all patterns and find unique list of files
|
|
85
|
-
async function findTests(include, exclude, workspaceRoot, projectSourceRoot) {
|
|
86
|
-
const matchingTestsPromises = include.map((pattern) => findMatchingTests(pattern, exclude, workspaceRoot, projectSourceRoot));
|
|
87
|
-
const files = await Promise.all(matchingTestsPromises);
|
|
88
|
-
// Unique file names
|
|
89
|
-
return [...new Set(files.flat())];
|
|
90
|
-
}
|
|
91
|
-
const normalizePath = (path) => path.replace(/\\/g, '/');
|
|
92
|
-
const removeLeadingSlash = (pattern) => {
|
|
93
|
-
if (pattern.charAt(0) === '/') {
|
|
94
|
-
return pattern.substring(1);
|
|
95
|
-
}
|
|
96
|
-
return pattern;
|
|
97
|
-
};
|
|
98
|
-
const removeRelativeRoot = (path, root) => {
|
|
99
|
-
if (path.startsWith(root)) {
|
|
100
|
-
return path.substring(root.length);
|
|
101
|
-
}
|
|
102
|
-
return path;
|
|
103
|
-
};
|
|
104
|
-
async function findMatchingTests(pattern, ignore, workspaceRoot, projectSourceRoot) {
|
|
105
|
-
// normalize pattern, glob lib only accepts forward slashes
|
|
106
|
-
let normalizedPattern = normalizePath(pattern);
|
|
107
|
-
normalizedPattern = removeLeadingSlash(normalizedPattern);
|
|
108
|
-
const relativeProjectRoot = normalizePath((0, path_1.relative)(workspaceRoot, projectSourceRoot) + '/');
|
|
109
|
-
// remove relativeProjectRoot to support relative paths from root
|
|
110
|
-
// such paths are easy to get when running scripts via IDEs
|
|
111
|
-
normalizedPattern = removeRelativeRoot(normalizedPattern, relativeProjectRoot);
|
|
112
|
-
// special logic when pattern does not look like a glob
|
|
113
|
-
if (!(0, fast_glob_1.isDynamicPattern)(normalizedPattern)) {
|
|
114
|
-
if (await isDirectory((0, path_1.join)(projectSourceRoot, normalizedPattern))) {
|
|
115
|
-
normalizedPattern = `${normalizedPattern}/**/*.spec.@(ts|tsx)`;
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
// see if matching spec file exists
|
|
119
|
-
const fileExt = (0, path_1.extname)(normalizedPattern);
|
|
120
|
-
// Replace extension to `.spec.ext`. Example: `src/app/app.component.ts`-> `src/app/app.component.spec.ts`
|
|
121
|
-
const potentialSpec = (0, path_1.join)(projectSourceRoot, (0, path_1.dirname)(normalizedPattern), `${(0, path_1.basename)(normalizedPattern, fileExt)}.spec${fileExt}`);
|
|
122
|
-
if (await exists(potentialSpec)) {
|
|
123
|
-
return [potentialSpec];
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// normalize the patterns in the ignore list
|
|
128
|
-
const normalizedIgnorePatternList = ignore.map((pattern) => removeRelativeRoot(removeLeadingSlash(normalizePath(pattern)), relativeProjectRoot));
|
|
129
|
-
return (0, fast_glob_1.default)(normalizedPattern, {
|
|
130
|
-
cwd: projectSourceRoot,
|
|
131
|
-
absolute: true,
|
|
132
|
-
ignore: ['**/node_modules/**', ...normalizedIgnorePatternList],
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
async function isDirectory(path) {
|
|
136
|
-
try {
|
|
137
|
-
const stats = await fs_1.promises.stat(path);
|
|
138
|
-
return stats.isDirectory();
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
async function exists(path) {
|
|
145
|
-
try {
|
|
146
|
-
await fs_1.promises.access(path, fs_1.constants.F_OK);
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
@@ -0,0 +1,8 @@
|
|
|
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
|
+
export declare function findTests(include: string[], exclude: string[], workspaceRoot: string, projectSourceRoot: string): Promise<string[]>;
|
|
@@ -0,0 +1,105 @@
|
|
|
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 (mod) {
|
|
26
|
+
if (mod && mod.__esModule) return mod;
|
|
27
|
+
var result = {};
|
|
28
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
29
|
+
__setModuleDefault(result, mod);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.findTests = findTests;
|
|
34
|
+
const fast_glob_1 = __importStar(require("fast-glob"));
|
|
35
|
+
const fs_1 = require("fs");
|
|
36
|
+
const path_1 = require("path");
|
|
37
|
+
/* Go through all patterns and find unique list of files */
|
|
38
|
+
async function findTests(include, exclude, workspaceRoot, projectSourceRoot) {
|
|
39
|
+
const matchingTestsPromises = include.map((pattern) => findMatchingTests(pattern, exclude, workspaceRoot, projectSourceRoot));
|
|
40
|
+
const files = await Promise.all(matchingTestsPromises);
|
|
41
|
+
// Unique file names
|
|
42
|
+
return [...new Set(files.flat())];
|
|
43
|
+
}
|
|
44
|
+
const normalizePath = (path) => path.replace(/\\/g, '/');
|
|
45
|
+
const removeLeadingSlash = (pattern) => {
|
|
46
|
+
if (pattern.charAt(0) === '/') {
|
|
47
|
+
return pattern.substring(1);
|
|
48
|
+
}
|
|
49
|
+
return pattern;
|
|
50
|
+
};
|
|
51
|
+
const removeRelativeRoot = (path, root) => {
|
|
52
|
+
if (path.startsWith(root)) {
|
|
53
|
+
return path.substring(root.length);
|
|
54
|
+
}
|
|
55
|
+
return path;
|
|
56
|
+
};
|
|
57
|
+
async function findMatchingTests(pattern, ignore, workspaceRoot, projectSourceRoot) {
|
|
58
|
+
// normalize pattern, glob lib only accepts forward slashes
|
|
59
|
+
let normalizedPattern = normalizePath(pattern);
|
|
60
|
+
normalizedPattern = removeLeadingSlash(normalizedPattern);
|
|
61
|
+
const relativeProjectRoot = normalizePath((0, path_1.relative)(workspaceRoot, projectSourceRoot) + '/');
|
|
62
|
+
// remove relativeProjectRoot to support relative paths from root
|
|
63
|
+
// such paths are easy to get when running scripts via IDEs
|
|
64
|
+
normalizedPattern = removeRelativeRoot(normalizedPattern, relativeProjectRoot);
|
|
65
|
+
// special logic when pattern does not look like a glob
|
|
66
|
+
if (!(0, fast_glob_1.isDynamicPattern)(normalizedPattern)) {
|
|
67
|
+
if (await isDirectory((0, path_1.join)(projectSourceRoot, normalizedPattern))) {
|
|
68
|
+
normalizedPattern = `${normalizedPattern}/**/*.spec.@(ts|tsx)`;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// see if matching spec file exists
|
|
72
|
+
const fileExt = (0, path_1.extname)(normalizedPattern);
|
|
73
|
+
// Replace extension to `.spec.ext`. Example: `src/app/app.component.ts`-> `src/app/app.component.spec.ts`
|
|
74
|
+
const potentialSpec = (0, path_1.join)(projectSourceRoot, (0, path_1.dirname)(normalizedPattern), `${(0, path_1.basename)(normalizedPattern, fileExt)}.spec${fileExt}`);
|
|
75
|
+
if (await exists(potentialSpec)) {
|
|
76
|
+
return [potentialSpec];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// normalize the patterns in the ignore list
|
|
81
|
+
const normalizedIgnorePatternList = ignore.map((pattern) => removeRelativeRoot(removeLeadingSlash(normalizePath(pattern)), relativeProjectRoot));
|
|
82
|
+
return (0, fast_glob_1.default)(normalizedPattern, {
|
|
83
|
+
cwd: projectSourceRoot,
|
|
84
|
+
absolute: true,
|
|
85
|
+
ignore: ['**/node_modules/**', ...normalizedIgnorePatternList],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async function isDirectory(path) {
|
|
89
|
+
try {
|
|
90
|
+
const stats = await fs_1.promises.stat(path);
|
|
91
|
+
return stats.isDirectory();
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function exists(path) {
|
|
98
|
+
try {
|
|
99
|
+
await fs_1.promises.access(path, fs_1.constants.F_OK);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -37,122 +37,56 @@ const core_1 = require("@angular-devkit/core");
|
|
|
37
37
|
const module_1 = require("module");
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const rxjs_1 = require("rxjs");
|
|
40
|
-
const
|
|
41
|
-
const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
|
|
42
|
-
const schema_1 = require("../browser/schema");
|
|
43
|
-
const find_tests_plugin_1 = require("./find-tests-plugin");
|
|
44
|
-
async function initialize(options, context, webpackConfigurationTransformer) {
|
|
45
|
-
// Purge old build disk cache.
|
|
46
|
-
await (0, private_1.purgeStaleBuildCache)(context);
|
|
47
|
-
const { config } = await (0, webpack_browser_config_1.generateBrowserWebpackConfigFromContext)(
|
|
48
|
-
// only two properties are missing:
|
|
49
|
-
// * `outputPath` which is fixed for tests
|
|
50
|
-
// * `budgets` which might be incorrect due to extra dev libs
|
|
51
|
-
{
|
|
52
|
-
...options,
|
|
53
|
-
outputPath: '',
|
|
54
|
-
budgets: undefined,
|
|
55
|
-
optimization: false,
|
|
56
|
-
buildOptimizer: false,
|
|
57
|
-
aot: false,
|
|
58
|
-
vendorChunk: true,
|
|
59
|
-
namedChunks: true,
|
|
60
|
-
extractLicenses: false,
|
|
61
|
-
outputHashing: schema_1.OutputHashing.None,
|
|
62
|
-
// The webpack tier owns the watch behavior so we want to force it in the config.
|
|
63
|
-
// When not in watch mode, webpack-dev-middleware will call `compiler.watch` anyway.
|
|
64
|
-
// https://github.com/webpack/webpack-dev-middleware/blob/698c9ae5e9bb9a013985add6189ff21c1a1ec185/src/index.js#L65
|
|
65
|
-
// https://github.com/webpack/webpack/blob/cde1b73e12eb8a77eb9ba42e7920c9ec5d29c2c9/lib/Compiler.js#L379-L388
|
|
66
|
-
watch: true,
|
|
67
|
-
}, context, (wco) => [(0, configs_1.getCommonConfig)(wco), (0, configs_1.getStylesConfig)(wco)]);
|
|
68
|
-
const karma = await Promise.resolve().then(() => __importStar(require('karma')));
|
|
69
|
-
return [karma, (await webpackConfigurationTransformer?.(config)) ?? config];
|
|
70
|
-
}
|
|
40
|
+
const schema_1 = require("./schema");
|
|
71
41
|
/**
|
|
72
42
|
* @experimental Direct usage of this function is considered experimental.
|
|
73
43
|
*/
|
|
74
44
|
function execute(options, context, transforms = {}) {
|
|
75
45
|
// Check Angular version.
|
|
76
46
|
(0, private_1.assertCompatibleAngularVersion)(context.workspaceRoot);
|
|
47
|
+
return (0, rxjs_1.from)(getExecuteWithBuilder(options, context)).pipe((0, rxjs_1.mergeMap)(([useEsbuild, executeWithBuilder]) => {
|
|
48
|
+
const karmaOptions = getBaseKarmaOptions(options, context, useEsbuild);
|
|
49
|
+
return executeWithBuilder.execute(options, context, karmaOptions, transforms);
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
function getBaseKarmaOptions(options, context, useEsbuild) {
|
|
77
53
|
let singleRun;
|
|
78
54
|
if (options.watch !== undefined) {
|
|
79
55
|
singleRun = !options.watch;
|
|
80
56
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
if (typeof webpackConfig.entry === 'object' && !Array.isArray(webpackConfig.entry)) {
|
|
110
|
-
if (Array.isArray(webpackConfig.entry['main'])) {
|
|
111
|
-
webpackConfig.entry['main'].push(getBuiltInMainFile());
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
webpackConfig.entry['main'] = [getBuiltInMainFile()];
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const projectMetadata = await context.getProjectMetadata(projectName);
|
|
119
|
-
const sourceRoot = (projectMetadata.sourceRoot ?? projectMetadata.root ?? '');
|
|
120
|
-
webpackConfig.plugins ??= [];
|
|
121
|
-
webpackConfig.plugins.push(new find_tests_plugin_1.FindTestsPlugin({
|
|
122
|
-
include: options.include,
|
|
123
|
-
exclude: options.exclude,
|
|
124
|
-
workspaceRoot: context.workspaceRoot,
|
|
125
|
-
projectSourceRoot: path.join(context.workspaceRoot, sourceRoot),
|
|
126
|
-
}));
|
|
127
|
-
karmaOptions.buildWebpack = {
|
|
128
|
-
options,
|
|
129
|
-
webpackConfig,
|
|
130
|
-
logger: context.logger,
|
|
131
|
-
};
|
|
132
|
-
const parsedKarmaConfig = await karma.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true });
|
|
133
|
-
return [karma, parsedKarmaConfig];
|
|
134
|
-
}), (0, rxjs_1.switchMap)(([karma, karmaConfig]) => new rxjs_1.Observable((subscriber) => {
|
|
135
|
-
// Pass onto Karma to emit BuildEvents.
|
|
136
|
-
karmaConfig.buildWebpack ??= {};
|
|
137
|
-
if (typeof karmaConfig.buildWebpack === 'object') {
|
|
138
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
139
|
-
karmaConfig.buildWebpack.failureCb ??= () => subscriber.next({ success: false });
|
|
140
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
141
|
-
karmaConfig.buildWebpack.successCb ??= () => subscriber.next({ success: true });
|
|
57
|
+
// Determine project name from builder context target
|
|
58
|
+
const projectName = context.target?.project;
|
|
59
|
+
if (!projectName) {
|
|
60
|
+
throw new Error(`The 'karma' builder requires a target to be specified.`);
|
|
61
|
+
}
|
|
62
|
+
const karmaOptions = options.karmaConfig
|
|
63
|
+
? {}
|
|
64
|
+
: getBuiltInKarmaConfig(context.workspaceRoot, projectName, useEsbuild);
|
|
65
|
+
karmaOptions.singleRun = singleRun;
|
|
66
|
+
// Workaround https://github.com/angular/angular-cli/issues/28271, by clearing context by default
|
|
67
|
+
// for single run executions. Not clearing context for multi-run (watched) builds allows the
|
|
68
|
+
// Jasmine Spec Runner to be visible in the browser after test execution.
|
|
69
|
+
karmaOptions.client ??= {};
|
|
70
|
+
karmaOptions.client.clearContext ??= singleRun ?? false; // `singleRun` defaults to `false` per Karma docs.
|
|
71
|
+
// Convert browsers from a string to an array
|
|
72
|
+
if (typeof options.browsers === 'string' && options.browsers) {
|
|
73
|
+
karmaOptions.browsers = options.browsers.split(',');
|
|
74
|
+
}
|
|
75
|
+
else if (options.browsers === false) {
|
|
76
|
+
karmaOptions.browsers = [];
|
|
77
|
+
}
|
|
78
|
+
if (options.reporters) {
|
|
79
|
+
// Split along commas to make it more natural, and remove empty strings.
|
|
80
|
+
const reporters = options.reporters
|
|
81
|
+
.reduce((acc, curr) => acc.concat(curr.split(',')), [])
|
|
82
|
+
.filter((x) => !!x);
|
|
83
|
+
if (reporters.length > 0) {
|
|
84
|
+
karmaOptions.reporters = reporters;
|
|
142
85
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
subscriber.next({ success: exitCode === 0 });
|
|
146
|
-
subscriber.complete();
|
|
147
|
-
});
|
|
148
|
-
const karmaStart = karmaServer.start();
|
|
149
|
-
// Cleanup, signal Karma to exit.
|
|
150
|
-
return () => {
|
|
151
|
-
void karmaStart.then(() => karmaServer.stop());
|
|
152
|
-
};
|
|
153
|
-
})), (0, rxjs_1.defaultIfEmpty)({ success: false }));
|
|
86
|
+
}
|
|
87
|
+
return karmaOptions;
|
|
154
88
|
}
|
|
155
|
-
function getBuiltInKarmaConfig(workspaceRoot, projectName) {
|
|
89
|
+
function getBuiltInKarmaConfig(workspaceRoot, projectName, useEsbuild) {
|
|
156
90
|
let coverageFolderName = projectName.charAt(0) === '@' ? projectName.slice(1) : projectName;
|
|
157
91
|
if (/[A-Z]/.test(coverageFolderName)) {
|
|
158
92
|
coverageFolderName = core_1.strings.dasherize(coverageFolderName);
|
|
@@ -161,17 +95,14 @@ function getBuiltInKarmaConfig(workspaceRoot, projectName) {
|
|
|
161
95
|
// Any changes to the config here need to be synced to: packages/schematics/angular/config/files/karma.conf.js.template
|
|
162
96
|
return {
|
|
163
97
|
basePath: '',
|
|
164
|
-
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
|
98
|
+
frameworks: ['jasmine', ...(useEsbuild ? [] : ['@angular-devkit/build-angular'])],
|
|
165
99
|
plugins: [
|
|
166
100
|
'karma-jasmine',
|
|
167
101
|
'karma-chrome-launcher',
|
|
168
102
|
'karma-jasmine-html-reporter',
|
|
169
103
|
'karma-coverage',
|
|
170
|
-
'@angular-devkit/build-angular/plugins/karma',
|
|
104
|
+
...(useEsbuild ? [] : ['@angular-devkit/build-angular/plugins/karma']),
|
|
171
105
|
].map((p) => workspaceRootRequire(p)),
|
|
172
|
-
client: {
|
|
173
|
-
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
|
174
|
-
},
|
|
175
106
|
jasmineHtmlReporter: {
|
|
176
107
|
suppressAll: true, // removes the duplicated traces
|
|
177
108
|
},
|
|
@@ -198,19 +129,36 @@ function getBuiltInKarmaConfig(workspaceRoot, projectName) {
|
|
|
198
129
|
};
|
|
199
130
|
}
|
|
200
131
|
exports.default = (0, architect_1.createBuilder)(execute);
|
|
201
|
-
function
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
132
|
+
async function getExecuteWithBuilder(options, context) {
|
|
133
|
+
const useEsbuild = await checkForEsbuild(options, context);
|
|
134
|
+
const executeWithBuilderModule = useEsbuild
|
|
135
|
+
? Promise.resolve().then(() => __importStar(require('./application_builder'))) : Promise.resolve().then(() => __importStar(require('./browser_builder')));
|
|
136
|
+
return [useEsbuild, await executeWithBuilderModule];
|
|
137
|
+
}
|
|
138
|
+
async function checkForEsbuild(options, context) {
|
|
139
|
+
if (options.builderMode !== schema_1.BuilderMode.Detect) {
|
|
140
|
+
return options.builderMode === schema_1.BuilderMode.Application;
|
|
141
|
+
}
|
|
142
|
+
// Look up the current project's build target using a development configuration.
|
|
143
|
+
const buildTargetSpecifier = `::development`;
|
|
144
|
+
const buildTarget = (0, architect_1.targetFromTargetString)(buildTargetSpecifier, context.target?.project, 'build');
|
|
145
|
+
try {
|
|
146
|
+
const developmentBuilderName = await context.getBuilderNameForTarget(buildTarget);
|
|
147
|
+
return isEsbuildBased(developmentBuilderName);
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
if (!(e instanceof Error) || e.message !== 'Project target does not exist.') {
|
|
151
|
+
throw e;
|
|
152
|
+
}
|
|
153
|
+
// If we can't find a development builder, we can't use 'detect'.
|
|
154
|
+
throw new Error('Failed to detect the builder used by the application. Please set builderMode explicitly.');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function isEsbuildBased(builderName) {
|
|
158
|
+
if (builderName === '@angular/build:application' ||
|
|
159
|
+
builderName === '@angular-devkit/build-angular:application' ||
|
|
160
|
+
builderName === '@angular-devkit/build-angular:browser-esbuild') {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
216
164
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
import { getTestBed } from '@angular/core/testing';
|
|
10
|
+
import {
|
|
11
|
+
BrowserDynamicTestingModule,
|
|
12
|
+
platformBrowserDynamicTesting,
|
|
13
|
+
} from '@angular/platform-browser-dynamic/testing';
|
|
14
|
+
|
|
15
|
+
// Initialize the Angular testing environment.
|
|
16
|
+
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
|
|
17
|
+
errorOnUnknownElements: true,
|
|
18
|
+
errorOnUnknownProperties: true,
|
|
19
|
+
});
|
|
@@ -10,6 +10,11 @@ export interface Schema {
|
|
|
10
10
|
* Override which browsers tests are run against. Set to `false` to not use any browser.
|
|
11
11
|
*/
|
|
12
12
|
browsers?: Browsers;
|
|
13
|
+
/**
|
|
14
|
+
* Determines how to build the code under test. If set to 'detect', attempts to follow the
|
|
15
|
+
* development builder.
|
|
16
|
+
*/
|
|
17
|
+
builderMode?: BuilderMode;
|
|
13
18
|
/**
|
|
14
19
|
* Output a code coverage report.
|
|
15
20
|
*/
|
|
@@ -121,6 +126,15 @@ export interface AssetPatternClass {
|
|
|
121
126
|
* Override which browsers tests are run against. Set to `false` to not use any browser.
|
|
122
127
|
*/
|
|
123
128
|
export type Browsers = boolean | string;
|
|
129
|
+
/**
|
|
130
|
+
* Determines how to build the code under test. If set to 'detect', attempts to follow the
|
|
131
|
+
* development builder.
|
|
132
|
+
*/
|
|
133
|
+
export declare enum BuilderMode {
|
|
134
|
+
Application = "application",
|
|
135
|
+
Browser = "browser",
|
|
136
|
+
Detect = "detect"
|
|
137
|
+
}
|
|
124
138
|
export interface FileReplacement {
|
|
125
139
|
replace?: string;
|
|
126
140
|
replaceWith?: string;
|