@angular/build 20.2.2 → 21.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.browserslistrc +5 -5
  2. package/package.json +18 -18
  3. package/src/builders/application/chunk-optimizer.js +2 -1
  4. package/src/builders/dev-server/builder.js +2 -2
  5. package/src/builders/dev-server/vite/hmr.d.ts +25 -0
  6. package/src/builders/dev-server/vite/hmr.js +113 -0
  7. package/src/builders/dev-server/vite/index.d.ts +21 -0
  8. package/src/builders/dev-server/{vite-server.js → vite/index.js} +19 -360
  9. package/src/builders/dev-server/vite/server.d.ts +15 -0
  10. package/src/builders/dev-server/vite/server.js +229 -0
  11. package/src/builders/dev-server/vite/utils.d.ts +36 -0
  12. package/src/builders/dev-server/vite/utils.js +76 -0
  13. package/src/builders/unit-test/builder.d.ts +1 -1
  14. package/src/builders/unit-test/builder.js +187 -299
  15. package/src/builders/unit-test/options.d.ts +1 -0
  16. package/src/builders/unit-test/options.js +2 -1
  17. package/src/builders/unit-test/runners/api.d.ts +47 -0
  18. package/src/builders/unit-test/runners/api.js +9 -0
  19. package/src/builders/unit-test/runners/karma/executor.d.ts +17 -0
  20. package/src/builders/unit-test/runners/karma/executor.js +93 -0
  21. package/src/builders/unit-test/runners/karma/index.d.ts +13 -0
  22. package/src/builders/unit-test/runners/karma/index.js +26 -0
  23. package/src/builders/unit-test/runners/vitest/browser-provider.d.ts +11 -0
  24. package/src/builders/unit-test/runners/vitest/browser-provider.js +69 -0
  25. package/src/builders/unit-test/runners/vitest/build-options.d.ts +11 -0
  26. package/src/builders/unit-test/runners/vitest/build-options.js +87 -0
  27. package/src/builders/unit-test/runners/vitest/executor.d.ts +23 -0
  28. package/src/builders/unit-test/runners/vitest/executor.js +200 -0
  29. package/src/builders/unit-test/runners/vitest/index.d.ts +13 -0
  30. package/src/builders/unit-test/runners/vitest/index.js +30 -0
  31. package/src/builders/unit-test/schema.d.ts +4 -0
  32. package/src/builders/unit-test/schema.json +4 -0
  33. package/src/builders/unit-test/test-discovery.d.ts +8 -0
  34. package/src/builders/unit-test/test-discovery.js +14 -0
  35. package/src/private.d.ts +1 -1
  36. package/src/private.js +2 -2
  37. package/src/utils/environment-options.d.ts +43 -0
  38. package/src/utils/environment-options.js +84 -30
  39. package/src/utils/normalize-cache.js +1 -1
  40. package/src/utils/version.js +1 -1
  41. package/src/builders/dev-server/vite-server.d.ts +0 -42
  42. package/src/builders/unit-test/karma-bridge.d.ts +0 -10
  43. package/src/builders/unit-test/karma-bridge.js +0 -82
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.KarmaExecutor = void 0;
44
+ class KarmaExecutor {
45
+ context;
46
+ options;
47
+ constructor(context, options) {
48
+ this.context = context;
49
+ this.options = options;
50
+ }
51
+ async *execute() {
52
+ const { context, options: unitTestOptions } = this;
53
+ if (unitTestOptions.debug) {
54
+ context.logger.warn('The "karma" test runner does not support the "debug" option. The option will be ignored.');
55
+ }
56
+ if (unitTestOptions.setupFiles.length) {
57
+ context.logger.warn('The "karma" test runner does not support the "setupFiles" option. The option will be ignored.');
58
+ }
59
+ const buildTargetOptions = (await context.validateOptions(await context.getTargetOptions(unitTestOptions.buildTarget), await context.getBuilderNameForTarget(unitTestOptions.buildTarget)));
60
+ const karmaOptions = {
61
+ tsConfig: unitTestOptions.tsConfig,
62
+ polyfills: buildTargetOptions.polyfills,
63
+ assets: buildTargetOptions.assets,
64
+ scripts: buildTargetOptions.scripts,
65
+ styles: buildTargetOptions.styles,
66
+ inlineStyleLanguage: buildTargetOptions.inlineStyleLanguage,
67
+ stylePreprocessorOptions: buildTargetOptions.stylePreprocessorOptions,
68
+ externalDependencies: buildTargetOptions.externalDependencies,
69
+ loader: buildTargetOptions.loader,
70
+ define: buildTargetOptions.define,
71
+ include: unitTestOptions.include,
72
+ exclude: unitTestOptions.exclude,
73
+ sourceMap: buildTargetOptions.sourceMap,
74
+ progress: unitTestOptions.buildProgress ?? buildTargetOptions.progress,
75
+ watch: unitTestOptions.watch,
76
+ poll: buildTargetOptions.poll,
77
+ preserveSymlinks: buildTargetOptions.preserveSymlinks,
78
+ browsers: unitTestOptions.browsers?.join(','),
79
+ codeCoverage: !!unitTestOptions.codeCoverage,
80
+ codeCoverageExclude: unitTestOptions.codeCoverage?.exclude,
81
+ fileReplacements: buildTargetOptions.fileReplacements,
82
+ reporters: unitTestOptions.reporters,
83
+ webWorkerTsConfig: buildTargetOptions.webWorkerTsConfig,
84
+ aot: buildTargetOptions.aot,
85
+ };
86
+ const { execute } = await Promise.resolve().then(() => __importStar(require('../../../karma')));
87
+ yield* execute(karmaOptions, context);
88
+ }
89
+ async [Symbol.asyncDispose]() {
90
+ // The Karma builder handles its own teardown
91
+ }
92
+ }
93
+ exports.KarmaExecutor = KarmaExecutor;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { TestRunner } from '../api';
9
+ /**
10
+ * A declarative definition of the Karma test runner.
11
+ */
12
+ declare const KarmaTestRunner: TestRunner;
13
+ export default KarmaTestRunner;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ const executor_1 = require("./executor");
11
+ /**
12
+ * A declarative definition of the Karma test runner.
13
+ */
14
+ const KarmaTestRunner = {
15
+ name: 'karma',
16
+ isStandalone: true,
17
+ getBuildOptions() {
18
+ return {
19
+ buildOptions: {},
20
+ };
21
+ },
22
+ async createExecutor(context, options) {
23
+ return new executor_1.KarmaExecutor(context, options);
24
+ },
25
+ };
26
+ exports.default = KarmaTestRunner;
@@ -0,0 +1,11 @@
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 setupBrowserConfiguration(browsers: string[] | undefined, debug: boolean, projectSourceRoot: string): {
9
+ browser?: import('vitest/node').BrowserConfigOptions;
10
+ errors?: string[];
11
+ };
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.setupBrowserConfiguration = setupBrowserConfiguration;
11
+ const node_module_1 = require("node:module");
12
+ function findBrowserProvider(projectResolver) {
13
+ // One of these must be installed in the project to use browser testing
14
+ const vitestBuiltinProviders = ['playwright', 'webdriverio'];
15
+ for (const providerName of vitestBuiltinProviders) {
16
+ try {
17
+ projectResolver(providerName);
18
+ return providerName;
19
+ }
20
+ catch { }
21
+ }
22
+ return undefined;
23
+ }
24
+ function normalizeBrowserName(browserName) {
25
+ // Normalize browser names to match Vitest's expectations for headless but also supports karma's names
26
+ // e.g., 'ChromeHeadless' -> 'chrome', 'FirefoxHeadless' -> 'firefox'
27
+ // and 'Chrome' -> 'chrome', 'Firefox' -> 'firefox'.
28
+ const normalized = browserName.toLowerCase();
29
+ return normalized.replace(/headless$/, '');
30
+ }
31
+ function setupBrowserConfiguration(browsers, debug, projectSourceRoot) {
32
+ if (browsers === undefined) {
33
+ return {};
34
+ }
35
+ const projectResolver = (0, node_module_1.createRequire)(projectSourceRoot + '/').resolve;
36
+ let errors;
37
+ try {
38
+ projectResolver('@vitest/browser');
39
+ }
40
+ catch {
41
+ errors ??= [];
42
+ errors.push('The "browsers" option requires the "@vitest/browser" package to be installed within the project.' +
43
+ ' Please install this package and rerun the test command.');
44
+ }
45
+ const provider = findBrowserProvider(projectResolver);
46
+ if (!provider) {
47
+ errors ??= [];
48
+ errors.push('The "browsers" option requires either "playwright" or "webdriverio" to be installed within the project.' +
49
+ ' Please install one of these packages and rerun the test command.');
50
+ }
51
+ // Vitest current requires the playwright browser provider to use the inspect-brk option used by "debug"
52
+ if (debug && provider !== 'playwright') {
53
+ errors ??= [];
54
+ errors.push('Debugging browser mode tests currently requires the use of "playwright".' +
55
+ ' Please install this package and rerun the test command.');
56
+ }
57
+ if (errors) {
58
+ return { errors };
59
+ }
60
+ const browser = {
61
+ enabled: true,
62
+ provider,
63
+ headless: browsers.some((name) => name.toLowerCase().includes('headless')),
64
+ instances: browsers.map((browserName) => ({
65
+ browser: normalizeBrowserName(browserName),
66
+ })),
67
+ };
68
+ return { browser };
69
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { ApplicationBuilderInternalOptions } from '../../../application/options';
9
+ import { NormalizedUnitTestBuilderOptions } from '../../options';
10
+ import { RunnerOptions } from '../api';
11
+ export declare function getVitestBuildOptions(options: NormalizedUnitTestBuilderOptions, baseBuildOptions: Partial<ApplicationBuilderInternalOptions>): Promise<RunnerOptions>;
@@ -0,0 +1,87 @@
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 __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.getVitestBuildOptions = getVitestBuildOptions;
14
+ const node_path_1 = __importDefault(require("node:path"));
15
+ const path_1 = require("../../../../utils/path");
16
+ const schema_1 = require("../../../application/schema");
17
+ const options_1 = require("../../options");
18
+ const test_discovery_1 = require("../../test-discovery");
19
+ function createTestBedInitVirtualFile(providersFile, projectSourceRoot) {
20
+ let providersImport = 'const providers = [];';
21
+ if (providersFile) {
22
+ const relativePath = node_path_1.default.relative(projectSourceRoot, providersFile);
23
+ const { dir, name } = node_path_1.default.parse(relativePath);
24
+ const importPath = (0, path_1.toPosixPath)(node_path_1.default.join(dir, name));
25
+ providersImport = `import providers from './${importPath}';`;
26
+ }
27
+ return `
28
+ // Initialize the Angular testing environment
29
+ import { NgModule } from '@angular/core';
30
+ import { getTestBed, ɵgetCleanupHook as getCleanupHook } from '@angular/core/testing';
31
+ import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
32
+ ${providersImport}
33
+ // Same as https://github.com/angular/angular/blob/05a03d3f975771bb59c7eefd37c01fa127ee2229/packages/core/testing/srcs/test_hooks.ts#L21-L29
34
+ beforeEach(getCleanupHook(false));
35
+ afterEach(getCleanupHook(true));
36
+ @NgModule({
37
+ providers,
38
+ })
39
+ export class TestModule {}
40
+ getTestBed().initTestEnvironment([BrowserTestingModule, TestModule], platformBrowserTesting(), {
41
+ errorOnUnknownElements: true,
42
+ errorOnUnknownProperties: true,
43
+ });
44
+ `;
45
+ }
46
+ async function getVitestBuildOptions(options, baseBuildOptions) {
47
+ const { workspaceRoot, projectSourceRoot, include, exclude, watch, tsConfig, providersFile } = options;
48
+ // Find test files
49
+ const testFiles = await (0, test_discovery_1.findTests)(include, exclude, workspaceRoot, projectSourceRoot);
50
+ if (testFiles.length === 0) {
51
+ throw new Error('No tests found matching the following patterns:\n' +
52
+ `- Included: ${include.join(', ')}\n` +
53
+ (exclude.length ? `- Excluded: ${exclude.join(', ')}\n` : '') +
54
+ `\nPlease check the 'test' target configuration in your project's 'angular.json' file.`);
55
+ }
56
+ const entryPoints = (0, test_discovery_1.getTestEntrypoints)(testFiles, { projectSourceRoot, workspaceRoot });
57
+ entryPoints.set('init-testbed', 'angular:test-bed-init');
58
+ const buildOptions = {
59
+ ...baseBuildOptions,
60
+ watch,
61
+ incrementalResults: watch,
62
+ index: false,
63
+ browser: undefined,
64
+ server: undefined,
65
+ outputMode: undefined,
66
+ localize: false,
67
+ budgets: [],
68
+ serviceWorker: false,
69
+ appShell: false,
70
+ ssr: false,
71
+ prerender: false,
72
+ sourceMap: { scripts: true, vendor: false, styles: false },
73
+ outputHashing: schema_1.OutputHashing.None,
74
+ optimization: false,
75
+ tsConfig,
76
+ entryPoints,
77
+ externalDependencies: ['vitest', '@vitest/browser/context'],
78
+ };
79
+ buildOptions.polyfills = (0, options_1.injectTestingPolyfills)(buildOptions.polyfills);
80
+ const testBedInitContents = createTestBedInitVirtualFile(providersFile, projectSourceRoot);
81
+ return {
82
+ buildOptions,
83
+ virtualFiles: {
84
+ 'angular:test-bed-init': testBedInitContents,
85
+ },
86
+ };
87
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { BuilderOutput } from '@angular-devkit/architect';
9
+ import { type FullResult, type IncrementalResult } from '../../../application/results';
10
+ import { NormalizedUnitTestBuilderOptions } from '../../options';
11
+ import type { TestExecutor } from '../api';
12
+ export declare class VitestExecutor implements TestExecutor {
13
+ private vitest;
14
+ private readonly projectName;
15
+ private readonly options;
16
+ private readonly outputPath;
17
+ private latestBuildResult;
18
+ private readonly sigintListener;
19
+ constructor(projectName: string, options: NormalizedUnitTestBuilderOptions);
20
+ execute(buildResult: FullResult | IncrementalResult): AsyncIterable<BuilderOutput>;
21
+ [Symbol.asyncDispose](): Promise<void>;
22
+ private initializeVitest;
23
+ }
@@ -0,0 +1,200 @@
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 __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.VitestExecutor = void 0;
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
+ const promises_1 = require("node:fs/promises");
18
+ const node_path_1 = __importDefault(require("node:path"));
19
+ const error_1 = require("../../../../utils/error");
20
+ const load_esm_1 = require("../../../../utils/load-esm");
21
+ const path_1 = require("../../../../utils/path");
22
+ const results_1 = require("../../../application/results");
23
+ const application_builder_1 = require("../../../karma/application_builder");
24
+ const browser_provider_1 = require("./browser-provider");
25
+ class VitestExecutor {
26
+ vitest;
27
+ projectName;
28
+ options;
29
+ outputPath;
30
+ latestBuildResult;
31
+ // Graceful shutdown signal handler
32
+ // This is needed to remove the temporary output directory on Ctrl+C
33
+ sigintListener = () => {
34
+ (0, node_fs_1.rmSync)(this.outputPath, { recursive: true, force: true });
35
+ };
36
+ constructor(projectName, options) {
37
+ this.projectName = projectName;
38
+ 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
+ }
42
+ async *execute(buildResult) {
43
+ await (0, application_builder_1.writeTestFiles)(buildResult.files, this.outputPath);
44
+ this.latestBuildResult = buildResult;
45
+ // Initialize Vitest if not already present.
46
+ this.vitest ??= await this.initializeVitest();
47
+ const vitest = this.vitest;
48
+ let testResults;
49
+ if (buildResult.kind === results_1.ResultKind.Incremental) {
50
+ const addedFiles = buildResult.added.map((file) => node_path_1.default.join(this.outputPath, file));
51
+ const modifiedFiles = buildResult.modified.map((file) => node_path_1.default.join(this.outputPath, file));
52
+ if (addedFiles.length === 0 && modifiedFiles.length === 0) {
53
+ yield { success: true };
54
+ return;
55
+ }
56
+ // If new files are added, use `start` to trigger test discovery.
57
+ // Also pass modified files to `start` to ensure they are re-run.
58
+ if (addedFiles.length > 0) {
59
+ await vitest.start([...addedFiles, ...modifiedFiles]);
60
+ }
61
+ else {
62
+ // For modified files only, use the more efficient `rerunTestSpecifications`
63
+ const specsToRerun = modifiedFiles.flatMap((file) => vitest.getModuleSpecifications(file));
64
+ if (specsToRerun.length > 0) {
65
+ modifiedFiles.forEach((file) => vitest.invalidateFile(file));
66
+ testResults = await vitest.rerunTestSpecifications(specsToRerun);
67
+ }
68
+ }
69
+ }
70
+ // Check if all the tests pass to calculate the result
71
+ const testModules = testResults?.testModules;
72
+ yield { success: testModules?.every((testModule) => testModule.ok()) ?? true };
73
+ }
74
+ async [Symbol.asyncDispose]() {
75
+ process.off('SIGINT', this.sigintListener);
76
+ await this.vitest?.close();
77
+ await (0, promises_1.rm)(this.outputPath, { recursive: true, force: true });
78
+ }
79
+ async initializeVitest() {
80
+ const { codeCoverage, reporters, workspaceRoot, setupFiles, browsers, debug, watch } = this.options;
81
+ const { outputPath, projectName, latestBuildResult } = this;
82
+ let vitestNodeModule;
83
+ try {
84
+ vitestNodeModule = await (0, load_esm_1.loadEsmModule)('vitest/node');
85
+ }
86
+ catch (error) {
87
+ (0, error_1.assertIsError)(error);
88
+ if (error.code !== 'ERR_MODULE_NOT_FOUND') {
89
+ throw error;
90
+ }
91
+ throw new Error('The `vitest` package was not found. Please install the package and rerun the test command.');
92
+ }
93
+ const { startVitest } = vitestNodeModule;
94
+ // Setup vitest browser options if configured
95
+ const browserOptions = (0, browser_provider_1.setupBrowserConfiguration)(browsers, debug, this.options.projectSourceRoot);
96
+ if (browserOptions.errors?.length) {
97
+ throw new Error(browserOptions.errors.join('\n'));
98
+ }
99
+ (0, node_assert_1.default)(latestBuildResult, 'buildResult must be available before initializing vitest');
100
+ // Add setup file entries for TestBed initialization and project polyfills
101
+ const testSetupFiles = ['init-testbed.js', ...setupFiles];
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
+ }
107
+ const debugOptions = debug
108
+ ? {
109
+ inspectBrk: true,
110
+ isolate: false,
111
+ fileParallelism: false,
112
+ }
113
+ : {};
114
+ return startVitest('test', undefined, {
115
+ // Disable configuration file resolution/loading
116
+ config: false,
117
+ root: workspaceRoot,
118
+ project: ['base', projectName],
119
+ name: 'base',
120
+ include: [],
121
+ reporters: reporters ?? ['default'],
122
+ watch,
123
+ coverage: generateCoverageOption(codeCoverage, workspaceRoot, this.outputPath),
124
+ ...debugOptions,
125
+ }, {
126
+ server: {
127
+ // Disable the actual file watcher. The boolean watch option above should still
128
+ // be enabled as it controls other internal behavior related to rerunning tests.
129
+ watch: null,
130
+ },
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
+ ],
176
+ });
177
+ }
178
+ }
179
+ exports.VitestExecutor = VitestExecutor;
180
+ function generateOutputPath() {
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) {
186
+ if (!codeCoverage) {
187
+ return {
188
+ enabled: false,
189
+ };
190
+ }
191
+ return {
192
+ enabled: true,
193
+ excludeAfterRemap: true,
194
+ include: [`${(0, path_1.toPosixPath)(node_path_1.default.relative(workspaceRoot, outputPath))}/**`],
195
+ // Special handling for `reporter` due to an undefined value causing upstream failures
196
+ ...(codeCoverage.reporters
197
+ ? { reporter: codeCoverage.reporters }
198
+ : {}),
199
+ };
200
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { TestRunner } from '../api';
9
+ /**
10
+ * A declarative definition of the Vitest test runner.
11
+ */
12
+ declare const VitestTestRunner: TestRunner;
13
+ export default VitestTestRunner;
@@ -0,0 +1,30 @@
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 __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const node_assert_1 = __importDefault(require("node:assert"));
14
+ const build_options_1 = require("./build-options");
15
+ const executor_1 = require("./executor");
16
+ /**
17
+ * A declarative definition of the Vitest test runner.
18
+ */
19
+ const VitestTestRunner = {
20
+ name: 'vitest',
21
+ getBuildOptions(options, baseBuildOptions) {
22
+ return (0, build_options_1.getVitestBuildOptions)(options, baseBuildOptions);
23
+ },
24
+ async createExecutor(context, options) {
25
+ const projectName = context.target?.project;
26
+ (0, node_assert_1.default)(projectName, 'The builder requires a target.');
27
+ return new executor_1.VitestExecutor(projectName, options);
28
+ },
29
+ };
30
+ exports.default = VitestTestRunner;
@@ -43,6 +43,10 @@ export type Schema = {
43
43
  * instead.
44
44
  */
45
45
  include?: string[];
46
+ /**
47
+ * Log progress to the console while building. Defaults to the build target's progress value.
48
+ */
49
+ progress?: boolean;
46
50
  /**
47
51
  * TypeScript file that exports an array of Angular providers to use during test execution.
48
52
  * The array must be a default export.
@@ -106,6 +106,10 @@
106
106
  "type": "string"
107
107
  },
108
108
  "description": "A list of global setup and configuration files that are included before the test files. The application's polyfills are always included before these files. The Angular Testbed is also initialized prior to the execution of these files."
109
+ },
110
+ "progress": {
111
+ "type": "boolean",
112
+ "description": "Log progress to the console while building. Defaults to the build target's progress value."
109
113
  }
110
114
  },
111
115
  "additionalProperties": false,
@@ -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 { findTests, getTestEntrypoints } from '../karma/find-tests';
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.getTestEntrypoints = exports.findTests = void 0;
11
+ // TODO: This should eventually contain the implementations for these
12
+ var find_tests_1 = require("../karma/find-tests");
13
+ Object.defineProperty(exports, "findTests", { enumerable: true, get: function () { return find_tests_1.findTests; } });
14
+ Object.defineProperty(exports, "getTestEntrypoints", { enumerable: true, get: function () { return find_tests_1.getTestEntrypoints; } });
package/src/private.d.ts CHANGED
@@ -17,7 +17,7 @@ import { BundleStylesheetOptions } from './tools/esbuild/stylesheets/bundle-opti
17
17
  export { buildApplicationInternal } from './builders/application';
18
18
  export type { ApplicationBuilderInternalOptions } from './builders/application/options';
19
19
  export { type Result, type ResultFile, ResultKind } from './builders/application/results';
20
- export { serveWithVite } from './builders/dev-server/vite-server';
20
+ export { serveWithVite } from './builders/dev-server/vite';
21
21
  export * from './tools/babel/plugins';
22
22
  export type { ExternalResultMetadata } from './tools/esbuild/bundler-execution-result';
23
23
  export { emitFilesToDisk } from './tools/esbuild/utils';
package/src/private.js CHANGED
@@ -38,8 +38,8 @@ var application_1 = require("./builders/application");
38
38
  Object.defineProperty(exports, "buildApplicationInternal", { enumerable: true, get: function () { return application_1.buildApplicationInternal; } });
39
39
  var results_1 = require("./builders/application/results");
40
40
  Object.defineProperty(exports, "ResultKind", { enumerable: true, get: function () { return results_1.ResultKind; } });
41
- var vite_server_1 = require("./builders/dev-server/vite-server");
42
- Object.defineProperty(exports, "serveWithVite", { enumerable: true, get: function () { return vite_server_1.serveWithVite; } });
41
+ var vite_1 = require("./builders/dev-server/vite");
42
+ Object.defineProperty(exports, "serveWithVite", { enumerable: true, get: function () { return vite_1.serveWithVite; } });
43
43
  // Tools
44
44
  __exportStar(require("./tools/babel/plugins"), exports);
45
45
  var utils_1 = require("./tools/esbuild/utils");