Project Init

This commit is contained in:
Muluhabt
2026-05-29 15:23:46 +03:00
commit 2fbc557aac
67387 changed files with 6063341 additions and 0 deletions

22
node_modules/@nx/vitest/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2017-2026 Narwhal Technologies Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

33
node_modules/@nx/vitest/PLUGIN.md generated vendored Normal file
View File

@@ -0,0 +1,33 @@
# Vitest
After making changes to a project, run the relevant test file to verify your changes work correctly.
## Mode Detection
Check in order (first match wins):
| Mode | Detection |
| --------- | ---------------------------------------------------------- |
| Inference | `@nx/vitest` or `@nx/vite/plugin` in nx.json plugins array |
| Executor | `@nx/vitest:test` executor in project.json targets |
## Run Specific Test File
### Inference
```bash
nx test <project> -- <path/to/file.spec.ts>
```
### Executor
```bash
nx run <project>:test --testFile=<path/to/file.spec.ts>
```
## Quick Reference
| Task | Inference | Executor |
| ----------- | ----------------------------------- | ----------------------------------------------- |
| Run file | `nx test proj -- path/file.spec.ts` | `nx run proj:test --testFile=path/file.spec.ts` |
| Run pattern | `nx test proj -- -t "pattern"` | `nx run proj:test --testNamePattern="pattern"` |

68
node_modules/@nx/vitest/README.md generated vendored Normal file
View File

@@ -0,0 +1,68 @@
<p style="text-align: center;">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
<img alt="Nx - Smart Monorepos · Fast Builds" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
</picture>
</p>
<div style="text-align: center;">
[![CircleCI](https://circleci.com/gh/nrwl/nx.svg?style=svg)](https://circleci.com/gh/nrwl/nx)
[![License](https://img.shields.io/npm/l/@nx/workspace.svg?style=flat-square)]()
[![NPM Version](https://badge.fury.io/js/nx.svg)](https://www.npmjs.com/package/nx)
[![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)]()
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![Join the chat at https://gitter.im/nrwl-nx/community](https://badges.gitter.im/nrwl-nx/community.svg)](https://gitter.im/nrwl-nx/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join us on the Official Nx Discord Server](https://img.shields.io/discord/1143497901675401286?label=discord)](https://go.nx.dev/community)
</div>
<hr>
# Nx: Smart Monorepos · Fast Builds
Get to green PRs in half the time. Nx optimizes your builds, scales your CI, and fixes failed PRs. Built for developers and AI agents.
This package is a [Vitest plugin for Nx](https://nx.dev/nx-api/vitest).
## Getting Started
### Creating an Nx Workspace
**Using `npx`**
```bash
npx create-nx-workspace
```
**Using `npm init`**
```bash
npm init nx-workspace
```
**Using `yarn create`**
```bash
yarn create nx-workspace
```
### Adding Nx to an Existing Repository
Run:
```bash
npx nx@latest init
```
## Documentation & Resources
- [Nx.Dev: Documentation, Guides, Tutorials](https://nx.dev)
- [Intro to Nx](https://nx.dev/getting-started/intro)
- [Official Nx YouTube Channel](https://www.youtube.com/@NxDevtools)
- [Blog Posts About Nx](https://nx.dev/blog)
<p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-courses-and-videos.svg"
width="100%" alt="Nx - Smart Monorepos · Fast Builds"></a></p>

3
node_modules/@nx/vitest/executors.d.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export { VitestExecutorOptions } from './src/executors/test/schema';
export { vitestExecutor } from './src/executors/test/vitest.impl';
//# sourceMappingURL=executors.d.ts.map

1
node_modules/@nx/vitest/executors.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"executors.d.ts","sourceRoot":"","sources":["../../../packages/vitest/executors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC"}

5
node_modules/@nx/vitest/executors.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.vitestExecutor = void 0;
var vitest_impl_1 = require("./src/executors/test/vitest.impl");
Object.defineProperty(exports, "vitestExecutor", { enumerable: true, get: function () { return vitest_impl_1.vitestExecutor; } });

10
node_modules/@nx/vitest/executors.json generated vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"$schema": "https://json-schema.org/schema",
"executors": {
"test": {
"implementation": "./src/executors/test/vitest.impl",
"schema": "./src/executors/test/schema.json",
"description": "Test using Vitest"
}
}
}

4
node_modules/@nx/vitest/generators.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export * from './src/generators/init/init';
export { configurationGenerator } from './src/generators/configuration/configuration';
export { VitestGeneratorSchema } from './src/generators/configuration/schema';
//# sourceMappingURL=generators.d.ts.map

1
node_modules/@nx/vitest/generators.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../packages/vitest/generators.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC"}

7
node_modules/@nx/vitest/generators.js generated vendored Normal file
View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.configurationGenerator = void 0;
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./src/generators/init/init"), exports);
var configuration_1 = require("./src/generators/configuration/configuration");
Object.defineProperty(exports, "configurationGenerator", { enumerable: true, get: function () { return configuration_1.configurationGenerator; } });

18
node_modules/@nx/vitest/generators.json generated vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "Nx Vitest",
"version": "0.1",
"generators": {
"init": {
"factory": "./src/generators/init/init",
"schema": "./src/generators/init/schema.json",
"description": "Initialize the `@nx/vitest` plugin.",
"aliases": ["ng-add"],
"hidden": true
},
"configuration": {
"factory": "./src/generators/configuration/configuration",
"schema": "./src/generators/configuration/schema.json",
"description": "Add Vitest configuration to a project."
}
}
}

2
node_modules/@nx/vitest/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export { createNodesV2, VitestPluginOptions } from './src/plugins/plugin';
//# sourceMappingURL=index.d.ts.map

1
node_modules/@nx/vitest/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/vitest/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}

5
node_modules/@nx/vitest/index.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNodesV2 = void 0;
var plugin_1 = require("./src/plugins/plugin");
Object.defineProperty(exports, "createNodesV2", { enumerable: true, get: function () { return plugin_1.createNodesV2; } });

108
node_modules/@nx/vitest/migrations.json generated vendored Normal file
View File

@@ -0,0 +1,108 @@
{
"generators": {
"update-20-3-0": {
"version": "20.3.0-beta.2",
"description": "Add gitignore entry for temporary vitest config files.",
"implementation": "./src/migrations/update-20-3-0/add-vitest-temp-files-to-git-ignore"
},
"update-22-1-0": {
"version": "22.1.0-beta.8",
"description": "Create AI Instructions to help migrate users workspaces past breaking changes for Vitest 4.",
"implementation": "./src/migrations/update-22-1-0/create-ai-instructions-for-vitest-4"
},
"update-22-3-2": {
"version": "22.3.2-beta.0",
"requires": {
"@angular/build": ">=21.0.0"
},
"description": "Create AI Instructions to help migrate users workspaces past breaking changes for Vitest 4.",
"implementation": "./src/migrations/update-22-1-0/create-ai-instructions-for-vitest-4"
},
"update-22-6-0-prefix-reports-directory": {
"version": "22.6.0-beta.11",
"description": "Prefix reportsDirectory with {projectRoot} to maintain correct resolution after workspace-root-relative behavior change.",
"implementation": "./src/migrations/update-22-6-0/prefix-reports-directory-with-project-root"
}
},
"packageJsonUpdates": {
"22.1.0": {
"version": "22.1.0-beta.8",
"incompatibleWith": {
"@angular/build": "< 21.0.0"
},
"packages": {
"vitest": {
"version": "^4.0.0",
"alwaysAddToPackageJson": false
},
"@vitest/coverage-v8": {
"version": "^4.0.0",
"alwaysAddToPackageJson": false
},
"@vitest/coverage-istanbul": {
"version": "^4.0.0",
"alwaysAddToPackageJson": false
},
"@vitest/ui": {
"version": "^4.0.0",
"alwaysAddToPackageJson": false
}
}
},
"22.2.0-analog": {
"version": "22.2.0-beta.3",
"packages": {
"@analogjs/vite-plugin-angular": {
"version": "~2.1.2",
"alwaysAddToPackageJson": false
},
"@analogjs/vitest-angular": {
"version": "~2.1.2",
"alwaysAddToPackageJson": false
}
}
},
"22.3.2": {
"version": "22.3.2-beta.0",
"requires": {
"@angular/build": ">=21.0.0",
"vitest": "<=4.0.8"
},
"packages": {
"vitest": {
"version": "^4.0.8",
"alwaysAddToPackageJson": false
},
"@vitest/coverage-v8": {
"version": "^4.0.8",
"alwaysAddToPackageJson": false
},
"@vitest/coverage-istanbul": {
"version": "^4.0.8",
"alwaysAddToPackageJson": false
},
"@vitest/ui": {
"version": "^4.0.8",
"alwaysAddToPackageJson": false
},
"jsdom": {
"version": "^27.1.0",
"alwaysAddToPackageJson": false
}
}
},
"22.3.2-analog": {
"version": "22.3.2-beta.0",
"packages": {
"@analogjs/vite-plugin-angular": {
"version": "~2.2.0",
"alwaysAddToPackageJson": false
},
"@analogjs/vitest-angular": {
"version": "~2.2.0",
"alwaysAddToPackageJson": false
}
}
}
}
}

80
node_modules/@nx/vitest/package.json generated vendored Normal file
View File

@@ -0,0 +1,80 @@
{
"name": "@nx/vitest",
"description": "The Nx Plugin for Vitest to enable fast unit testing with Vitest.",
"version": "22.7.5",
"type": "commonjs",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
"directory": "packages/vitest"
},
"bugs": {
"url": "https://github.com/nrwl/nx/issues"
},
"keywords": [
"Monorepo",
"Vitest",
"Testing",
"Unit Testing"
],
"author": "Victor Savkin",
"license": "MIT",
"homepage": "https://nx.dev",
"main": "index.js",
"exports": {
".": {
"types": "./index.d.ts",
"default": "./index.js"
},
"./executors/*/schema.json": "./src/executors/*/schema.json",
"./executors/*/schema": "./src/executors/*/schema.d.ts",
"./generators/*/schema.json": "./src/generators/*/schema.json",
"./generators/*/schema": "./src/generators/*/schema.d.ts",
"./generators": {
"types": "./generators.d.ts",
"default": "./generators.js"
},
"./executors": {
"types": "./executors.d.ts",
"default": "./executors.js"
},
"./package.json": "./package.json",
"./generators.json": "./generators.json",
"./executors.json": "./executors.json",
"./migrations.json": "./migrations.json"
},
"nx-migrations": {
"migrations": "./migrations.json"
},
"executors": "./executors.json",
"generators": "./generators.json",
"dependencies": {
"@nx/devkit": "22.7.5",
"@nx/js": "22.7.5",
"tslib": "^2.3.0",
"semver": "^7.6.3",
"@phenomnomnominal/tsquery": "~6.2.0"
},
"peerDependencies": {
"@nx/eslint": "22.7.5",
"vitest": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"@nx/eslint": {
"optional": true
},
"vitest": {
"optional": true
},
"vite": {
"optional": true
}
},
"devDependencies": {
"nx": "22.7.5"
}
}

View File

@@ -0,0 +1,3 @@
declare const _default: any;
export default _default;
//# sourceMappingURL=compat.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"compat.d.ts","sourceRoot":"","sources":["../../../../../../packages/vitest/src/executors/test/compat.ts"],"names":[],"mappings":";AAGA,wBAAiD"}

6
node_modules/@nx/vitest/src/executors/test/compat.js generated vendored Normal file
View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const devkit_1 = require("@nx/devkit");
const vitest_impl_1 = tslib_1.__importDefault(require("./vitest.impl"));
exports.default = (0, devkit_1.convertNxExecutor)(vitest_impl_1.default);

View File

@@ -0,0 +1,20 @@
import type { RunnerTestFile } from 'vitest/node';
import type { Reporter } from 'vitest/reporters';
export declare class NxReporter implements Reporter {
private watch;
deferred: {
promise: Promise<boolean>;
resolve: (val: boolean) => void;
};
constructor(watch: boolean);
[Symbol.asyncIterator](): AsyncGenerator<{
hasErrors: boolean;
}, void, unknown>;
private setupDeferred;
/** Vitest ≥ 0.29 */
onTestRunEnd(files: any[], errors?: any): void;
/** Vitest ≤ 0.28 */
onFinished(files: RunnerTestFile[], errors?: unknown[]): void;
private _handleFinished;
}
//# sourceMappingURL=nx-reporter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"nx-reporter.d.ts","sourceRoot":"","sources":["../../../../../../../packages/vitest/src/executors/test/lib/nx-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,qBAAa,UAAW,YAAW,QAAQ;IAM7B,OAAO,CAAC,KAAK;IALzB,QAAQ,EAAE;QACR,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;KACjC,CAAC;gBAEkB,KAAK,EAAE,OAAO;IAI3B,CAAC,MAAM,CAAC,aAAa,CAAC;;;IAQ7B,OAAO,CAAC,aAAa;IAUrB,oBAAoB;IACpB,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,GAAG;IAIvC,oBAAoB;IACpB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE;IAKtD,OAAO,CAAC,eAAe;CAKxB"}

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NxReporter = void 0;
class NxReporter {
constructor(watch) {
this.watch = watch;
this.setupDeferred();
}
async *[Symbol.asyncIterator]() {
do {
const hasErrors = await this.deferred.promise;
yield { hasErrors };
this.setupDeferred();
} while (this.watch);
}
setupDeferred() {
let resolve;
this.deferred = {
promise: new Promise((res) => {
resolve = res;
}),
resolve,
};
}
/** Vitest ≥ 0.29 */
onTestRunEnd(files, errors) {
this._handleFinished(files, errors);
}
/** Vitest ≤ 0.28 */
onFinished(files, errors) {
this._handleFinished(files, errors);
}
// --- private ----------------------------------------------------------
_handleFinished(files, errors) {
const hasErrors = files.some((f) => f.result?.state === 'fail') || errors?.length > 0;
this.deferred.resolve(hasErrors);
}
}
exports.NxReporter = NxReporter;

View File

@@ -0,0 +1,12 @@
import { ExecutorContext } from '@nx/devkit';
import { VitestExecutorOptions } from '../schema';
export declare function getOptions(options: VitestExecutorOptions, context: ExecutorContext, projectRoot: string): Promise<Record<string, any>>;
/**
* Nx's resolveNxTokensInOptions strips {workspaceRoot}/ from option values,
* leaving a workspace-root-relative path. However, vitest resolves
* reportsDirectory relative to the project root. This function converts
* the path to absolute so vitest resolves it correctly.
*/
export declare function resolveReportsDirectory(reportsDirectory: string): string;
export declare function getOptionsAsArgv(obj: Record<string, any>): string[];
//# sourceMappingURL=utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../../../packages/vitest/src/executors/test/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAKhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAQlD,wBAAsB,UAAU,CAC9B,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAoG9B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAKxE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAcnE"}

102
node_modules/@nx/vitest/src/executors/test/lib/utils.js generated vendored Normal file
View File

@@ -0,0 +1,102 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOptions = getOptions;
exports.resolveReportsDirectory = resolveReportsDirectory;
exports.getOptionsAsArgv = getOptionsAsArgv;
const devkit_1 = require("@nx/devkit");
const options_utils_1 = require("../../../utils/options-utils");
const path_1 = require("path");
const executor_utils_1 = require("../../../utils/executor-utils");
async function getOptions(options, context, projectRoot) {
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
const { loadConfigFromFile } = await (0, executor_utils_1.loadViteDynamicImport)();
const viteConfigPath = (0, options_utils_1.normalizeViteConfigFilePath)(context.root, projectRoot, options.configFile);
if (!viteConfigPath) {
throw new Error((0, devkit_1.stripIndents) `
Unable to load test config from config file ${viteConfigPath}.
Please make sure that vitest is configured correctly,
or use the @nx/vitest:configuration generator to configure it for you.
You can read more here: https://nx.dev/nx-api/vitest/generators/configuration
`);
}
const resolved = await loadConfigFromFile({
mode: options?.mode ?? 'test',
command: 'serve',
}, viteConfigPath);
if (!resolved?.config?.['test']) {
devkit_1.logger.warn((0, devkit_1.stripIndents) `Unable to load test config from config file ${resolved?.path ?? viteConfigPath}
Some settings may not be applied as expected.
You can manually set the config in the project, ${context.projectName}, configuration.
`);
}
const root = projectRoot === '.'
? process.cwd()
: (0, path_1.relative)(context.cwd, (0, devkit_1.joinPathFragments)(context.root, projectRoot));
const { parseCLI } = await (0, executor_utils_1.loadVitestDynamicImport)();
// Use parseCLI for Vitest-specific normalization/validation
const { options: { watch, ...normalizedExtraArgs }, } = parseCLI(['vitest', ...getOptionsAsArgv(options)]);
// Filter out options that are handled specially or are parseCLI artifacts
const {
// Handled specially by executor
testFiles: _testFiles, configFile: _configFile, mode: _mode, runMode: _runMode, reportsDirectory, coverage, reporter, reporters,
// parseCLI artifacts
'--': _dashdash, color: _color, w: _w,
// Pass through any additional Vitest options
...passThroughOptions } = normalizedExtraArgs;
return {
// Explicitly set watch mode to false if not provided otherwise vitest
// will enable watch mode by default for non CI environments
watch: watch ?? false,
// Pass through any additional Vitest options
...passThroughOptions,
// This should not be needed as it's going to be set in vite.config.ts
// but leaving it here in case someone did not migrate correctly
root: resolved?.config?.root ?? root,
config: viteConfigPath,
// Vitest's resolveConfig processes reporters in two steps:
// 1. options.reporters (plural) sets resolved.reporters
// 2. resolved.reporter (singular, from config) overwrites resolved.reporters
// Setting reporter to [] prevents config's reporter from overriding NxReporter
// (which is pushed onto reporters in vitest.impl.ts).
reporter: [],
reporters: reporter ??
reporters ??
// reporter (singular) has higher priority in vitest but is not declared in InlineConfig
resolved?.config?.['test']?.reporter ??
resolved?.config?.['test']?.reporters,
coverage: {
...(coverage ?? {}),
...(reportsDirectory && {
reportsDirectory: resolveReportsDirectory(reportsDirectory),
}),
},
};
}
/**
* Nx's resolveNxTokensInOptions strips {workspaceRoot}/ from option values,
* leaving a workspace-root-relative path. However, vitest resolves
* reportsDirectory relative to the project root. This function converts
* the path to absolute so vitest resolves it correctly.
*/
function resolveReportsDirectory(reportsDirectory) {
if ((0, path_1.isAbsolute)(reportsDirectory)) {
return reportsDirectory;
}
return (0, path_1.resolve)(devkit_1.workspaceRoot, reportsDirectory);
}
function getOptionsAsArgv(obj) {
const argv = [];
for (const [key, value] of Object.entries(obj)) {
if (Array.isArray(value)) {
value.forEach((item) => argv.push(`--${key}=${item}`));
}
else if (typeof value === 'object' && value !== null) {
argv.push(`--${key}='${JSON.stringify(value)}'`);
}
else {
argv.push(`--${key}=${value}`);
}
}
return argv;
}

View File

@@ -0,0 +1,8 @@
export interface VitestExecutorOptions {
configFile?: string;
reportsDirectory?: string;
testFiles?: string[];
watch?: boolean;
mode?: string;
runMode?: 'test' | 'benchmark';
}

41
node_modules/@nx/vitest/src/executors/test/schema.json generated vendored Normal file
View File

@@ -0,0 +1,41 @@
{
"$schema": "https://json-schema.org/schema",
"version": 2,
"cli": "nx",
"title": "Vitest executor",
"description": "Test using Vitest.",
"type": "object",
"properties": {
"configFile": {
"type": "string",
"description": "The path to the local vitest config, relative to the workspace root.",
"x-completion-type": "file",
"x-completion-glob": "@(vitest|vite).config@(.js|.ts)",
"aliases": ["config"]
},
"reportsDirectory": {
"type": "string",
"description": "Directory to write coverage report to."
},
"mode": {
"type": "string",
"description": "Vite mode for loading configuration."
},
"runMode": {
"type": "string",
"description": "Vitest execution mode.",
"enum": ["test", "benchmark"],
"default": "test"
},
"testFiles": {
"aliases": ["testFile"],
"type": "array",
"items": { "type": "string" }
},
"watch": {
"description": "Watch files for changes and rerun tests related to changed files.",
"type": "boolean"
}
},
"required": []
}

View File

@@ -0,0 +1,7 @@
import { ExecutorContext } from '@nx/devkit';
import { VitestExecutorOptions } from './schema';
export declare function vitestExecutor(options: VitestExecutorOptions, context: ExecutorContext): AsyncGenerator<never, {
success: boolean;
}, unknown>;
export default vitestExecutor;
//# sourceMappingURL=vitest.impl.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"vitest.impl.d.ts","sourceRoot":"","sources":["../../../../../../packages/vitest/src/executors/test/vitest.impl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAiB,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAOjD,wBAAuB,cAAc,CACnC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,eAAe;;YAkEzB;AAED,eAAe,cAAc,CAAC"}

View File

@@ -0,0 +1,59 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.vitestExecutor = vitestExecutor;
const devkit_1 = require("@nx/devkit");
const path_1 = require("path");
const internal_1 = require("@nx/js/src/internal");
const nx_reporter_1 = require("./lib/nx-reporter");
const utils_1 = require("./lib/utils");
const executor_utils_1 = require("../../utils/executor-utils");
async function* vitestExecutor(options, context) {
const projectRoot = context.projectsConfigurations.projects[context.projectName].root;
(0, internal_1.registerTsConfigPaths)((0, path_1.resolve)(devkit_1.workspaceRoot, projectRoot, 'tsconfig.json'));
process.env.VITE_CJS_IGNORE_WARNING = 'true';
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
const { startVitest } = await (0, executor_utils_1.loadVitestDynamicImport)();
const resolvedOptions = (await (0, utils_1.getOptions)(options, context, projectRoot)) ?? {};
const watch = resolvedOptions['watch'] === true;
const nxReporter = new nx_reporter_1.NxReporter(watch);
if (resolvedOptions['reporters'] === undefined) {
resolvedOptions['reporters'] = [];
}
else if (typeof resolvedOptions['reporters'] === 'string') {
resolvedOptions['reporters'] = [resolvedOptions['reporters']];
}
resolvedOptions['reporters'].push(nxReporter);
const cliFilters = options.testFiles ?? [];
const ctx = await startVitest(options.runMode ?? 'test', cliFilters, resolvedOptions);
let hasErrors = false;
const processExit = () => {
ctx.exit();
if (hasErrors) {
process.exit(1);
}
else {
process.exit(0);
}
};
if (watch) {
process.on('SIGINT', processExit);
process.on('SIGTERM', processExit);
process.on('exit', processExit);
}
// vitest sets the exitCode in case of exception without notifying reporters
if (process.exitCode === undefined ||
(watch && ctx.state.getFiles().length > 0)) {
for await (const report of nxReporter) {
// vitest sets the exitCode = 1 when code coverage isn't met
hasErrors =
report.hasErrors || (process.exitCode && process.exitCode !== 0);
}
}
else {
hasErrors = process.exitCode !== 0;
}
return {
success: !hasErrors,
};
}
exports.default = vitestExecutor;

View File

@@ -0,0 +1,9 @@
import { GeneratorCallback, Tree } from '@nx/devkit';
import { VitestGeneratorSchema } from './schema';
/**
* @param hasPlugin some frameworks (e.g. Nuxt) provide their own plugin. Their generators handle the plugin detection.
*/
export declare function configurationGenerator(tree: Tree, schema: VitestGeneratorSchema, hasPlugin?: boolean): Promise<GeneratorCallback>;
export declare function configurationGeneratorInternal(tree: Tree, schema: VitestGeneratorSchema, hasPlugin?: boolean): Promise<GeneratorCallback>;
export default configurationGenerator;
//# sourceMappingURL=configuration.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../../../../../packages/vitest/src/generators/configuration/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,iBAAiB,EASjB,IAAI,EAGL,MAAM,YAAY,CAAC;AAcpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAmCjD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,qBAAqB,EAC7B,SAAS,UAAQ,8BAOlB;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,qBAAqB,EAC7B,SAAS,UAAQ,8BAmNlB;AAuOD,eAAe,sBAAsB,CAAC"}

View File

@@ -0,0 +1,357 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.configurationGenerator = configurationGenerator;
exports.configurationGeneratorInternal = configurationGeneratorInternal;
const tslib_1 = require("tslib");
const devkit_1 = require("@nx/devkit");
const js_1 = require("@nx/js");
const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
const versions_1 = require("@nx/js/src/utils/versions");
const path_1 = require("path");
const ensure_dependencies_1 = require("../../utils/ensure-dependencies");
const generator_utils_1 = require("../../utils/generator-utils");
const init_1 = tslib_1.__importDefault(require("../init/init"));
const detect_ui_framework_1 = require("../../utils/detect-ui-framework");
const version_utils_1 = require("../../utils/version-utils");
const semver_1 = require("semver");
/**
* Determines whether to use vitest.config.mts instead of vite.config.mts.
* Returns true for new non-framework projects that don't already have a vite.config.
*/
function shouldUseVitestConfig(tree, projectRoot, uiFramework) {
// Keep vite.config for framework projects (need vite plugins like react, angular, etc.)
if (uiFramework !== 'none') {
return false;
}
// Keep existing vite.config (backwards compatibility)
const extensions = ['ts', 'mts', 'js', 'mjs'];
const hasExistingViteConfig = extensions.some((ext) => tree.exists((0, devkit_1.joinPathFragments)(projectRoot, `vite.config.${ext}`)));
if (hasExistingViteConfig) {
return false;
}
// New non-framework project → use vitest.config.mts
return true;
}
/**
* @param hasPlugin some frameworks (e.g. Nuxt) provide their own plugin. Their generators handle the plugin detection.
*/
function configurationGenerator(tree, schema, hasPlugin = false) {
return configurationGeneratorInternal(tree, { addPlugin: false, ...schema }, hasPlugin);
}
async function configurationGeneratorInternal(tree, schema, hasPlugin = false) {
// Setting default to jsdom since it is the most common use case (React, Web).
// The @nx/js:lib generator specifically sets this to node to be more generic.
schema.testEnvironment ??= 'jsdom';
// Set the viteVersion to the installed version if it already exists in the workspace
const installedViteVersion = (0, version_utils_1.getInstalledViteMajorVersion)(tree);
schema.viteVersion ??= installedViteVersion;
const tasks = [];
const { root, projectType: _projectType } = (0, devkit_1.readProjectConfiguration)(tree, schema.project);
const projectType = schema.projectType ?? _projectType;
const uiFramework = schema.uiFramework ?? (await (0, detect_ui_framework_1.detectUiFramework)(schema.project));
const isRootProject = root === '.';
tasks.push(await (0, js_1.initGenerator)(tree, { ...schema, skipFormat: true }));
const initTask = await (0, init_1.default)(tree, {
skipFormat: true,
addPlugin: schema.addPlugin,
projectRoot: root,
viteVersion: schema.viteVersion,
skipPackageJson: schema.skipPackageJson,
keepExistingVersions: true,
});
tasks.push(initTask);
if (!schema.skipPackageJson) {
tasks.push(await (0, ensure_dependencies_1.ensureDependencies)(tree, { ...schema, uiFramework }));
}
(0, generator_utils_1.addOrChangeTestTarget)(tree, schema, hasPlugin);
if (!schema.skipViteConfig) {
if (uiFramework === 'angular') {
const relativeTestSetupPath = (0, devkit_1.joinPathFragments)('src', 'test-setup.ts');
const setupFile = (0, devkit_1.joinPathFragments)(root, relativeTestSetupPath);
if (!tree.exists(setupFile)) {
const angularMajorVersion = getAngularMajorVersion(tree);
const zoneless = schema.zoneless ?? isZonelessProject(tree, schema.project);
if (angularMajorVersion >= 21) {
tree.write(setupFile, `import '@angular/compiler';
import '@analogjs/vitest-angular/setup-snapshots';
import { setupTestBed } from '@analogjs/vitest-angular/setup-testbed';
setupTestBed(${zoneless ? '' : '{ zoneless: false }'});
`);
}
else if (angularMajorVersion === 20) {
tree.write(setupFile, `import '@angular/compiler';
import '@analogjs/vitest-angular/${zoneless ? 'setup-snapshots' : 'setup-zone'}';
import {
BrowserTestingModule,
platformBrowserTesting,
} from '@angular/platform-browser/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserTestingModule,
platformBrowserTesting(),
);
`);
}
else {
tree.write(setupFile, `import '@analogjs/vitest-angular/${zoneless ? 'setup-snapshots' : 'setup-zone'}';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
`);
}
}
(0, generator_utils_1.createOrEditViteConfig)(tree, {
project: schema.project,
includeLib: false,
includeVitest: true,
inSourceTests: false,
imports: [`import angular from '@analogjs/vite-plugin-angular'`],
plugins: ['angular()'],
setupFile: relativeTestSetupPath,
useEsmExtension: true,
}, true, { skipPackageJson: schema.skipPackageJson });
}
else if (uiFramework === 'react') {
(0, generator_utils_1.createOrEditViteConfig)(tree, {
project: schema.project,
includeLib: (0, ts_solution_setup_1.getProjectType)(tree, root, projectType) === 'library',
includeVitest: true,
inSourceTests: schema.inSourceTests,
rollupOptionsExternal: [
"'react'",
"'react-dom'",
"'react/jsx-runtime'",
],
imports: [
schema.compiler === 'swc'
? `import react from '@vitejs/plugin-react-swc'`
: `import react from '@vitejs/plugin-react'`,
],
plugins: ['react()'],
coverageProvider: schema.coverageProvider,
useEsmExtension: true,
}, true, { skipPackageJson: schema.skipPackageJson });
}
else {
const useVitestConfig = shouldUseVitestConfig(tree, root, uiFramework);
(0, generator_utils_1.createOrEditViteConfig)(tree, {
...schema,
includeVitest: true,
includeLib: (0, ts_solution_setup_1.getProjectType)(tree, root, projectType) === 'library',
useEsmExtension: true,
}, true, {
vitestFileName: useVitestConfig,
skipPackageJson: schema.skipPackageJson,
});
}
}
const isTsSolutionSetup = (0, ts_solution_setup_1.isUsingTsSolutionSetup)(tree);
createFiles(tree, schema, root, isTsSolutionSetup);
updateTsConfig(tree, schema, root, projectType);
if (isTsSolutionSetup) {
// in the TS solution setup, the test target depends on the build outputs
// so we need to setup the task pipeline accordingly
const nxJson = (0, devkit_1.readNxJson)(tree);
const testTarget = schema.testTarget ?? 'test';
nxJson.targetDefaults ??= {};
nxJson.targetDefaults[testTarget] ??= {};
nxJson.targetDefaults[testTarget].dependsOn ??= [];
nxJson.targetDefaults[testTarget].dependsOn = Array.from(new Set([...nxJson.targetDefaults[testTarget].dependsOn, '^build']));
(0, devkit_1.updateNxJson)(tree, nxJson);
}
const devDependencies = await getCoverageProviderDependency(tree, schema.coverageProvider);
devDependencies['@types/node'] = versions_1.typesNodeVersion;
if (!schema.skipPackageJson) {
const installDependenciesTask = (0, devkit_1.addDependenciesToPackageJson)(tree, {}, devDependencies, undefined, true);
tasks.push(installDependenciesTask);
}
// Setup workspace config file (https://vitest.dev/guide/workspace.html)
if (!isRootProject &&
!tree.exists(`vitest.workspace.ts`) &&
!tree.exists(`vitest.workspace.js`) &&
!tree.exists(`vitest.workspace.json`) &&
!tree.exists(`vitest.projects.ts`) &&
!tree.exists(`vitest.projects.js`) &&
!tree.exists(`vitest.projects.json`)) {
tree.write('vitest.workspace.ts', `export default ['**/vite.config.{mjs,js,ts,mts}', '**/vitest.config.{mjs,js,ts,mts}'];`);
}
if (!schema.skipFormat) {
await (0, devkit_1.formatFiles)(tree);
}
return (0, devkit_1.runTasksInSerial)(...tasks);
}
function updateTsConfig(tree, options, projectRoot, projectType) {
const setupFile = tryFindSetupFile(tree, projectRoot);
if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.spec.json'))) {
(0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.spec.json'), (json) => {
if (!json.compilerOptions?.types?.includes('vitest')) {
if (json.compilerOptions?.types) {
json.compilerOptions.types.push('vitest');
}
else {
json.compilerOptions ??= {};
json.compilerOptions.types = ['vitest'];
}
}
if (setupFile) {
json.files = [...(json.files ?? []), setupFile];
}
return json;
});
(0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.json'), (json) => {
if (json.references &&
!json.references.some((r) => r.path === './tsconfig.spec.json')) {
json.references.push({
path: './tsconfig.spec.json',
});
}
return json;
});
}
else {
(0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.json'), (json) => {
if (!json.compilerOptions?.types?.includes('vitest')) {
if (json.compilerOptions?.types) {
json.compilerOptions.types.push('vitest');
}
else {
json.compilerOptions ??= {};
json.compilerOptions.types = ['vitest'];
}
}
return json;
});
}
let runtimeTsconfigPath = (0, devkit_1.joinPathFragments)(projectRoot, (0, ts_solution_setup_1.getProjectType)(tree, projectRoot, projectType) === 'application'
? 'tsconfig.app.json'
: 'tsconfig.lib.json');
if (options.runtimeTsconfigFileName) {
runtimeTsconfigPath = (0, devkit_1.joinPathFragments)(projectRoot, options.runtimeTsconfigFileName);
if (!tree.exists(runtimeTsconfigPath)) {
throw new Error(`Cannot find the specified runtimeTsConfigFileName ("${options.runtimeTsconfigFileName}") at the project root "${projectRoot}".`);
}
}
if (tree.exists(runtimeTsconfigPath)) {
(0, devkit_1.updateJson)(tree, runtimeTsconfigPath, (json) => {
if (options.inSourceTests) {
(json.compilerOptions.types ??= []).push('vitest/importMeta');
}
else {
const uniqueExclude = new Set([
...(json.exclude || []),
'vite.config.ts',
'vite.config.mts',
'vitest.config.ts',
'vitest.config.mts',
'src/**/*.test.ts',
'src/**/*.spec.ts',
'src/**/*.test.tsx',
'src/**/*.spec.tsx',
'src/**/*.test.js',
'src/**/*.spec.js',
'src/**/*.test.jsx',
'src/**/*.spec.jsx',
]);
json.exclude = [...uniqueExclude];
}
if (setupFile) {
json.exclude = [...(json.exclude ?? []), setupFile];
}
return json;
});
}
else {
devkit_1.logger.warn(`Couldn't find a runtime tsconfig file at ${runtimeTsconfigPath} to exclude the test files from. ` +
`If you're using a different filename for your runtime tsconfig, please provide it with the '--runtimeTsconfigFileName' flag.`);
}
}
function createFiles(tree, options, projectRoot, isTsSolutionSetup) {
const rootOffset = (0, devkit_1.offsetFromRoot)(projectRoot);
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectRoot, {
tmpl: '',
...options,
projectRoot,
extendedConfig: isTsSolutionSetup
? `${rootOffset}tsconfig.base.json`
: './tsconfig.json',
outDir: isTsSolutionSetup
? `./out-tsc/vitest`
: `${rootOffset}dist/out-tsc`,
});
}
async function getCoverageProviderDependency(tree, coverageProvider) {
const { vitestCoverageV8, vitestCoverageIstanbul } = await (0, version_utils_1.getVitestDependenciesVersionsToInstall)(tree);
switch (coverageProvider) {
case 'v8':
return {
'@vitest/coverage-v8': vitestCoverageV8,
};
case 'istanbul':
return {
'@vitest/coverage-istanbul': vitestCoverageIstanbul,
};
default:
return {
'@vitest/coverage-v8': vitestCoverageV8,
};
}
}
function tryFindSetupFile(tree, projectRoot) {
const setupFile = (0, devkit_1.joinPathFragments)('src', 'test-setup.ts');
if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, setupFile))) {
return setupFile;
}
}
function getAngularMajorVersion(tree) {
const angularVersion = (0, devkit_1.getDependencyVersionFromPackageJson)(tree, '@angular/core');
if (!angularVersion) {
// assume the latest version will be installed
return 21;
}
const cleanedAngularVersion = (0, semver_1.clean)(angularVersion) ?? (0, semver_1.coerce)(angularVersion)?.version;
if (typeof cleanedAngularVersion !== 'string') {
// assume the latest version will be installed
return 21;
}
return (0, semver_1.major)(cleanedAngularVersion);
}
function isZonelessProject(tree, projectName) {
const project = (0, devkit_1.readProjectConfiguration)(tree, projectName);
if (project.projectType === 'application') {
const buildTarget = findBuildTarget(project);
if (!buildTarget?.options?.polyfills) {
return true;
}
const polyfills = buildTarget.options.polyfills;
const polyfillsList = Array.isArray(polyfills) ? polyfills : [polyfills];
return !polyfillsList.includes('zone.js');
}
// For libraries, check if zone.js is installed in the workspace
return (0, devkit_1.getDependencyVersionFromPackageJson)(tree, 'zone.js') === null;
}
function findBuildTarget(project) {
for (const target of Object.values(project.targets ?? {})) {
if ([
'@angular-devkit/build-angular:browser',
'@angular-devkit/build-angular:browser-esbuild',
'@angular-devkit/build-angular:application',
'@angular/build:application',
'@nx/angular:application',
'@nx/angular:browser-esbuild',
'@nx/angular:webpack-browser',
].includes(target.executor)) {
return target;
}
}
return project.targets?.build ?? null;
}
exports.default = configurationGenerator;

View File

@@ -0,0 +1,22 @@
{
"extends": "<%= extendedConfig %>",
"compilerOptions": {
"outDir": "<%= outDir %>",
"types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"]
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
]
}

View File

@@ -0,0 +1,18 @@
export interface VitestGeneratorSchema {
project: string;
uiFramework?: 'angular' | 'react' | 'vue' | 'none';
coverageProvider: 'v8' | 'istanbul' | 'custom';
inSourceTests?: boolean;
skipViteConfig?: boolean;
testTarget?: string;
skipFormat?: boolean;
skipPackageJson?: boolean;
testEnvironment?: 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' | string;
addPlugin?: boolean;
runtimeTsconfigFileName?: string;
compiler?: 'babel' | 'swc'; // default: babel
// internal options
projectType?: 'application' | 'library';
viteVersion?: 5 | 6 | 7 | 8;
zoneless?: boolean;
}

View File

@@ -0,0 +1,76 @@
{
"$schema": "https://json-schema.org/schema",
"cli": "nx",
"$id": "Vitest",
"title": "Vitest",
"type": "object",
"description": "Generate a Vitest setup for a project.",
"properties": {
"project": {
"type": "string",
"description": "The name of the project to test.",
"$default": {
"$source": "projectName"
}
},
"uiFramework": {
"type": "string",
"enum": ["angular", "react", "vue", "none"],
"description": "UI framework to use with vitest."
},
"inSourceTests": {
"type": "boolean",
"default": false,
"description": "Do not generate separate spec files and set up in-source testing."
},
"skipViteConfig": {
"type": "boolean",
"default": false,
"description": "Skip generating a vite config file."
},
"coverageProvider": {
"type": "string",
"enum": ["v8", "istanbul", "custom"],
"default": "v8",
"description": "Coverage provider to use."
},
"testTarget": {
"type": "string",
"description": "The test target of the project to be transformed to use the @nx/vitest:test executor.",
"hidden": true
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false,
"x-priority": "internal"
},
"testEnvironment": {
"description": "The vitest environment to use. See https://vitest.dev/config/#environment.",
"type": "string",
"enum": ["node", "jsdom", "happy-dom", "edge-runtime"]
},
"runtimeTsconfigFileName": {
"type": "string",
"description": "The name of the project's tsconfig file that includes the runtime source files. If not provided, it will default to `tsconfig.lib.json` for libraries and `tsconfig.app.json` for applications."
},
"compiler": {
"type": "string",
"enum": ["babel", "swc"],
"default": "babel",
"description": "The compiler to use"
},
"skipPackageJson": {
"type": "boolean",
"default": false,
"description": "Do not add dependencies to `package.json`.",
"x-priority": "internal"
},
"zoneless": {
"type": "boolean",
"description": "Whether the Angular project is zoneless. When not provided, it is auto-detected from the project configuration.",
"x-priority": "internal"
}
},
"required": ["project"]
}

View File

@@ -0,0 +1,7 @@
import { type Tree, type GeneratorCallback } from '@nx/devkit';
import { InitGeneratorSchema } from './schema';
export declare function updateDependencies(tree: Tree, schema: InitGeneratorSchema): GeneratorCallback;
export declare function updateNxJsonSettings(tree: Tree): void;
export declare function initGenerator(tree: Tree, schema: InitGeneratorSchema): Promise<GeneratorCallback>;
export default initGenerator;
//# sourceMappingURL=init.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../../packages/vitest/src/generators/init/init.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,IAAI,EACT,KAAK,iBAAiB,EAOvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAa/C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,mBAAmB,qBA2BzE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,QA4B9C;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,mBAAmB,8BAkC1E;AAED,eAAe,aAAa,CAAC"}

72
node_modules/@nx/vitest/src/generators/init/init.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateDependencies = updateDependencies;
exports.updateNxJsonSettings = updateNxJsonSettings;
exports.initGenerator = initGenerator;
const devkit_1 = require("@nx/devkit");
const add_plugin_1 = require("@nx/devkit/src/utils/add-plugin");
const versions_1 = require("../../utils/versions");
const plugin_1 = require("../../plugins/plugin");
const version_utils_1 = require("../../utils/version-utils");
const ignore_vitest_temp_files_1 = require("../../utils/ignore-vitest-temp-files");
function updateDependencies(tree, schema) {
// Determine which vite version to install:
// 1. Explicit viteVersion flag takes priority
// 2. If vite is already installed, keep the matching major version
// 3. Otherwise, use the latest default (^8.0.0)
const installedMajor = schema.viteVersion ?? (0, version_utils_1.getInstalledViteMajorVersion)(tree);
const viteVersionToUse = installedMajor === 5
? versions_1.viteV5Version
: installedMajor === 6
? versions_1.viteV6Version
: installedMajor === 7
? versions_1.viteV7Version
: versions_1.viteVersion;
return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
'@nx/vitest': versions_1.nxVersion,
vitest: versions_1.vitestVersion,
vite: viteVersionToUse,
}, undefined, schema.keepExistingVersions);
}
function updateNxJsonSettings(tree) {
const nxJson = (0, devkit_1.readNxJson)(tree);
const productionFileSet = nxJson.namedInputs?.production;
if (productionFileSet) {
productionFileSet.push('!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)', '!{projectRoot}/tsconfig.spec.json');
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
}
const hasPlugin = nxJson.plugins?.some((p) => typeof p === 'string' ? p === '@nx/vitest' : p.plugin === '@nx/vitest');
if (!hasPlugin) {
nxJson.targetDefaults ??= {};
nxJson.targetDefaults['@nx/vitest:test'] ??= {};
nxJson.targetDefaults['@nx/vitest:test'].cache ??= true;
nxJson.targetDefaults['@nx/vitest:test'].inputs ??= [
'default',
productionFileSet ? '^production' : '^default',
];
}
(0, devkit_1.updateNxJson)(tree, nxJson);
}
async function initGenerator(tree, schema) {
const nxJson = (0, devkit_1.readNxJson)(tree);
const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
nxJson.useInferencePlugins !== false;
schema.addPlugin ??= addPluginDefault;
if (schema.addPlugin) {
await (0, add_plugin_1.addPlugin)(tree, await (0, devkit_1.createProjectGraphAsync)(), '@nx/vitest', plugin_1.createNodesV2, {
testTargetName: ['test', 'vitest:test', 'vitest-test'],
ciTargetName: ['test-ci', 'vitest:test-ci', 'vitest-test-ci'],
}, schema.updatePackageScripts);
}
updateNxJsonSettings(tree);
await (0, ignore_vitest_temp_files_1.ignoreVitestTempFiles)(tree, schema.projectRoot);
const tasks = [];
if (!schema.skipPackageJson) {
tasks.push(updateDependencies(tree, schema));
}
if (!schema.skipFormat) {
await (0, devkit_1.formatFiles)(tree);
}
return (0, devkit_1.runTasksInSerial)(...tasks);
}
exports.default = initGenerator;

View File

@@ -0,0 +1,11 @@
export interface InitGeneratorSchema {
addPlugin?: boolean;
rootProject?: boolean;
keepExistingVersions?: boolean;
projectRoot?: string;
updatePackageScripts?: boolean;
skipFormat?: boolean;
skipPackageJson?: boolean;
// Internal only
viteVersion?: 5 | 6 | 7 | 8;
}

View File

@@ -0,0 +1,41 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "Init",
"title": "Nx Vitest Init Generator",
"type": "object",
"description": "Vitest init generator.",
"properties": {
"addPlugin": {
"type": "boolean",
"x-priority": "internal",
"description": "Add plugin to nx.json. Defaults to true for new workspaces."
},
"rootProject": {
"type": "boolean",
"x-priority": "internal"
},
"keepExistingVersions": {
"type": "boolean",
"x-priority": "internal",
"description": "Keep existing dependencies versions",
"default": false
},
"updatePackageScripts": {
"type": "boolean",
"x-priority": "internal",
"description": "Update package scripts",
"default": false
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false
},
"skipPackageJson": {
"description": "Do not add dependencies to `package.json`.",
"type": "boolean",
"default": false
}
},
"required": []
}

View File

@@ -0,0 +1,3 @@
import { Tree } from '@nx/devkit';
export default function addVitestTempFilesToGitIgnore(tree: Tree): void;
//# sourceMappingURL=add-vitest-temp-files-to-git-ignore.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"add-vitest-temp-files-to-git-ignore.d.ts","sourceRoot":"","sources":["../../../../../../packages/vitest/src/migrations/update-20-3-0/add-vitest-temp-files-to-git-ignore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,MAAM,CAAC,OAAO,UAAU,6BAA6B,CAAC,IAAI,EAAE,IAAI,QAsB/D"}

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = addVitestTempFilesToGitIgnore;
const ignore_vitest_temp_files_1 = require("../../utils/ignore-vitest-temp-files");
function addVitestTempFilesToGitIgnore(tree) {
// need to check if .gitignore exists before adding to it
// then need to check if it contains the following pattern
// **/vite.config.{js,ts,mjs,mts,cjs,cts}.timestamp*
// if it does, remove just this pattern
if (tree.exists('.gitignore')) {
const gitIgnoreContents = tree.read('.gitignore', 'utf-8');
if (gitIgnoreContents.includes('**/vitest.config.{js,ts,mjs,mts,cjs,cts}.timestamp*')) {
tree.write('.gitignore', gitIgnoreContents.replace('**/vitest.config.{js,ts,mjs,mts,cjs,cts}.timestamp*', ''));
}
}
(0, ignore_vitest_temp_files_1.addVitestTempFilesToGitIgnore)(tree);
}

View File

@@ -0,0 +1,3 @@
import { Tree } from '@nx/devkit';
export default function createAiInstructionsForVitest(tree: Tree): Promise<string[]>;
//# sourceMappingURL=create-ai-instructions-for-vitest-4.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"create-ai-instructions-for-vitest-4.d.ts","sourceRoot":"","sources":["../../../../../../packages/vitest/src/migrations/update-22-1-0/create-ai-instructions-for-vitest-4.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,wBAA8B,6BAA6B,CAAC,IAAI,EAAE,IAAI,qBAerE"}

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = createAiInstructionsForVitest;
const path_1 = require("path");
const fs_1 = require("fs");
async function createAiInstructionsForVitest(tree) {
const pathToAiInstructions = (0, path_1.join)(__dirname, 'files', 'ai-instructions-for-vitest-4.md');
if (!(0, fs_1.existsSync)(pathToAiInstructions)) {
return;
}
const contents = (0, fs_1.readFileSync)(pathToAiInstructions);
tree.write('tools/ai-migrations/MIGRATE_VITEST_4.md', contents);
return [
`We created 'tools/ai-migrations/MIGRATE_VITEST_4.md' with instructions for an AI Agent to help migrate your Vitest projects to Vitest 4.`,
];
}

View File

@@ -0,0 +1,725 @@
# Vitest 4.0 Migration Instructions for LLM
## Overview
These instructions guide you through migrating an Nx workspace containing multiple Vitest projects from Vitest 3.x to Vitest 4.0. Work systematically through each breaking change category.
## Pre-Migration Checklist
1. **Identify all Vitest projects**:
```bash
nx show projects --with-target test
```
2. **Locate all Vitest configuration files**:
- Search for `vitest.config.{ts,js,mjs}`
- Search for `vitest.workspace.{ts,js,mjs}`
- Check `project.json` files for inline Vitest configuration
3. **Identify affected code**:
- Test files: `**/*.{spec,test}.{ts,js,tsx,jsx}`
- Mock usage: Files using `vi.fn()`, `vi.spyOn()`, `vi.mock()`
- Coverage configuration references
## Migration Steps by Category
### 1. Configuration File Updates
#### 1.1 Coverage Configuration
**Search Pattern**: `coverage` in all `vitest.config.*` files and `project.json` test target options
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
export default defineConfig({
test: {
coverage: {
all: true,
extensions: ['.ts', '.tsx'],
ignoreEmptyLines: false,
experimentalAstAwareRemapping: true,
},
},
});
// ✅ AFTER (Vitest 4.0)
export default defineConfig({
test: {
coverage: {
// Explicitly define files to include in coverage
include: ['src/**/*.{ts,tsx}'],
// Remove: all, extensions, ignoreEmptyLines, experimentalAstAwareRemapping
},
},
});
```
**Action Items**:
- [ ] Remove `coverage.all` option
- [ ] Remove `coverage.extensions` option
- [ ] Remove `coverage.ignoreEmptyLines` option
- [ ] Remove `coverage.experimentalAstAwareRemapping` option
- [ ] Add explicit `coverage.include` patterns based on project structure
- [ ] Update any documentation referencing these options
#### 1.2 Pool Options Restructuring
**Search Pattern**: `poolOptions`, `maxThreads`, `maxForks`, `singleThread`, `singleFork` in all Vitest config files
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
export default defineConfig({
test: {
maxThreads: 4,
maxForks: 2,
singleThread: false,
poolOptions: {
threads: {
useAtomics: true,
},
vmThreads: {
memoryLimit: '512MB',
},
},
},
});
// ✅ AFTER (Vitest 4.0)
export default defineConfig({
test: {
maxWorkers: 4, // Consolidates maxThreads and maxForks
isolate: true, // Replaces singleThread: false
// Remove: poolOptions, threads.useAtomics
vmMemoryLimit: '512MB', // Moved to top-level
},
});
```
**Action Items**:
- [ ] Replace `maxThreads` and `maxForks` with single `maxWorkers` option
- [ ] Replace `singleThread: true` or `singleFork: true` with `maxWorkers: 1, isolate: false`
- [ ] Move all `poolOptions.*` nested options to top-level (e.g., `poolOptions.vmThreads.memoryLimit` → `vmMemoryLimit`)
- [ ] Remove `threads.useAtomics` option
- [ ] Update CI environment variables: `VITEST_MAX_THREADS` and `VITEST_MAX_FORKS` → `VITEST_MAX_WORKERS`
#### 1.3 Workspace to Projects Rename
**Search Pattern**: `workspace` property in Vitest config files
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
export default defineConfig({
test: {
workspace: ['apps/*', 'libs/*'],
},
});
// ✅ AFTER (Vitest 4.0)
export default defineConfig({
test: {
projects: ['apps/*', 'libs/*'],
},
});
```
**Action Items**:
- [ ] Rename `workspace` property to `projects` in all config files
- [ ] Remove external workspace file references (must be inline in config)
- [ ] Update `poolMatchGlobs` to use `projects` pattern matching instead
- [ ] Update `environmentMatchGlobs` to use `projects` pattern matching instead
#### 1.4 Browser Configuration
**Search Pattern**: `browser.provider`, `browser.testerScripts`, imports from `@vitest/browser`
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
export default defineConfig({
test: {
browser: {
enabled: true,
provider: 'playwright', // String value
testerScripts: ['./setup.js'],
},
},
});
// Import changes
import { page } from '@vitest/browser';
// ✅ AFTER (Vitest 4.0)
export default defineConfig({
test: {
browser: {
enabled: true,
provider: { name: 'playwright' }, // Object value
testerHtmlPath: './test-setup.html', // Renamed from testerScripts
},
},
});
// Import changes
import { page } from 'vitest/browser';
```
**Action Items**:
- [ ] Convert `browser.provider` string values to object format: `{ name: 'provider-name' }`
- [ ] Replace `browser.testerScripts` with `browser.testerHtmlPath`
- [ ] Update all imports from `@vitest/browser` to `vitest/browser`
- [ ] Remove `@vitest/browser` from dependencies if no longer needed
#### 1.5 Deprecated Configuration Options
**Search Pattern**: `deps.external`, `deps.inline`, `deps.fallbackCJS` in config files
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
export default defineConfig({
test: {
deps: {
external: ['some-package'],
inline: ['inline-package'],
fallbackCJS: true,
},
},
});
// ✅ AFTER (Vitest 4.0)
export default defineConfig({
test: {
server: {
deps: {
external: ['some-package'],
inline: ['inline-package'],
fallbackCJS: true,
},
},
},
});
```
**Action Items**:
- [ ] Move `deps.*` options under `server.deps` namespace
- [ ] Remove `poolMatchGlobs` (use `projects` with conditions instead)
- [ ] Remove `environmentMatchGlobs` (use `projects` with conditions instead)
### 2. Test Code Updates
#### 2.1 Mock Function Name Changes
**Search Pattern**: `.getMockName()` calls in test files
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
const mockFn = vi.fn();
expect(mockFn.getMockName()).toBe('spy'); // Old default
// ✅ AFTER (Vitest 4.0)
const mockFn = vi.fn();
expect(mockFn.getMockName()).toBe('vi.fn()'); // New default
// If you need custom names, set them explicitly
const namedMock = vi.fn().mockName('myCustomName');
expect(namedMock.getMockName()).toBe('myCustomName');
```
**Action Items**:
- [ ] Update test assertions checking default mock names from `'spy'` to `'vi.fn()'`
- [ ] Add explicit `.mockName()` calls where specific names are required
#### 2.2 Mock Invocation Call Order
**Search Pattern**: `.mock.invocationCallOrder` in test files
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
const mockFn = vi.fn();
mockFn();
expect(mockFn.mock.invocationCallOrder[0]).toBe(0); // Started at 0
// ✅ AFTER (Vitest 4.0)
const mockFn = vi.fn();
mockFn();
expect(mockFn.mock.invocationCallOrder[0]).toBe(1); // Now starts at 1 (Jest-compatible)
```
**Action Items**:
- [ ] Update assertions on `invocationCallOrder` to account for 1-based indexing
- [ ] Search for off-by-one errors in call order comparisons
#### 2.3 Constructor Spies and Mocks
**Search Pattern**: `vi.spyOn` on constructors, `vi.fn()` used as constructors
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x) - Arrow function constructors might have worked
const MockConstructor = vi.fn(() => ({ value: 42 }));
new MockConstructor(); // May have worked in v3
// ✅ AFTER (Vitest 4.0) - Must use function or class
const MockConstructor = vi.fn(function () {
return { value: 42 };
});
new MockConstructor(); // Correctly supports 'new'
// Or use class syntax
class MockClass {
value = 42;
}
const MockConstructor = vi.fn(MockClass);
```
**Action Items**:
- [ ] Convert arrow function mocks used as constructors to `function` keyword or `class` syntax
- [ ] Test all constructor spies to ensure `new` keyword works correctly
- [ ] Update any mocks that expect constructor behavior
#### 2.4 RestoreAllMocks Behavior
**Search Pattern**: `vi.restoreAllMocks()` in test files
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
vi.mock('./module', () => ({ fn: vi.fn() }));
vi.restoreAllMocks(); // Would restore automocks
// ✅ AFTER (Vitest 4.0)
vi.mock('./module', () => ({ fn: vi.fn() }));
vi.restoreAllMocks(); // Only restores manual spies, NOT automocks
// To reset automocks, use:
vi.unmock('./module');
// or
vi.resetModules();
```
**Action Items**:
- [ ] Review all `vi.restoreAllMocks()` usage
- [ ] Add explicit `vi.unmock()` or `vi.resetModules()` calls for automocked modules
- [ ] Ensure test isolation is maintained after this change
#### 2.5 SpyOn Return Value Changes
**Search Pattern**: `vi.spyOn()` on already mocked functions
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
const mock = vi.fn();
const spy = vi.spyOn({ method: mock }, 'method');
// spy !== mock (created new spy)
// ✅ AFTER (Vitest 4.0)
const mock = vi.fn();
const spy = vi.spyOn({ method: mock }, 'method');
// spy === mock (returns same instance)
```
**Action Items**:
- [ ] Review code that creates spies on existing mocks
- [ ] Remove redundant spy creation if same instance is returned
- [ ] Update assertions that check spy identity
#### 2.6 Automock Behavior Changes
**Search Pattern**: `vi.mock()` with factory functions, `.mockRestore()` on automocks
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
vi.mock('./utils', () => ({
get value() {
return 42;
}, // Would call getter
}));
import { value } from './utils';
console.log(value); // Would execute getter logic
// Restore might have worked
const spy = vi.spyOn(obj, 'method');
spy.mockRestore(); // Might work on automocks
// ✅ AFTER (Vitest 4.0)
vi.mock('./utils', () => ({
get value() {
return 42;
},
}));
import { value } from './utils';
console.log(value); // Returns undefined (doesn't call getter)
// Explicitly return value if needed
vi.mock('./utils', () => ({
value: 42, // Not a getter
}));
// mockRestore no longer works on automocks
const spy = vi.spyOn(obj, 'method');
spy.mockRestore(); // Throws error if method is automocked
// Use unmock instead
vi.unmock('./module');
```
**Action Items**:
- [ ] Convert automocked getters to plain property values where needed
- [ ] Remove `.mockRestore()` calls on automocked methods
- [ ] Use `vi.unmock()` to clear automocks instead
- [ ] Test instance method isolation (they now share state with prototype)
#### 2.7 Settled Results Immediate Population
**Search Pattern**: `.mock.settledResults` in test files
**Changes Required**:
```typescript
// ✅ AFTER (Vitest 4.0)
const asyncMock = vi.fn(async () => 'result');
const promise = asyncMock();
// settledResults is immediately populated with 'incomplete' status
expect(asyncMock.mock.settledResults[0]).toEqual({
type: 'incomplete',
value: undefined,
});
// After promise resolves
await promise;
expect(asyncMock.mock.settledResults[0]).toEqual({
type: 'fulfilled',
value: 'result',
});
```
**Action Items**:
- [ ] Update tests that check `settledResults` before promise resolution
- [ ] Handle `'incomplete'` status in assertions
- [ ] Ensure tests properly await promises before checking settled results
### 3. Reporter and CLI Changes
#### 3.1 Reporter API Changes
**Search Pattern**: Custom reporters, `onCollected`, `onTaskUpdate`, `onFinished`
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
export default {
onCollected(files) {
// Handle collected files
},
onTaskUpdate(task) {
// Handle task update
},
onFinished(files) {
// Handle completion
},
};
// ✅ AFTER (Vitest 4.0)
// Use new reporter API - consult Vitest 4 docs for replacement methods
```
**Action Items**:
- [ ] Review custom reporters for removed API usage
- [ ] Consult Vitest 4 documentation for new reporter API
- [ ] Update or rewrite custom reporters to use new APIs
#### 3.2 Built-in Reporter Changes
**Search Pattern**: `reporters: ['basic']`, `reporters: ['verbose']`
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
export default defineConfig({
test: {
reporters: ['basic'],
},
});
// ✅ AFTER (Vitest 4.0)
export default defineConfig({
test: {
reporters: [['default', { summary: false }]], // Equivalent to 'basic'
},
});
// For verbose (tree output)
reporters: ['tree']; // Use 'tree' for hierarchical output
```
**Action Items**:
- [ ] Replace `'basic'` reporter with `['default', { summary: false }]`
- [ ] Replace `'verbose'` reporter with `'tree'` for hierarchical output
- [ ] Update CI configuration if reporters are specified there
### 4. Snapshot Changes
#### 4.1 Custom Elements Shadow Root
**Search Pattern**: Snapshot tests involving custom elements or Web Components
**Changes Required**:
```typescript
// ✅ AFTER (Vitest 4.0)
// Shadow root contents now printed by default in snapshots
// If you want old behavior (don't print shadow root):
export default defineConfig({
test: {
printShadowRoot: false,
},
});
```
**Action Items**:
- [ ] Review snapshot tests for custom elements
- [ ] Update snapshots if shadow root contents are now included
- [ ] Add `printShadowRoot: false` if old behavior is required
### 5. Environment Variable Updates
**Search Pattern**: CI/CD configuration files, `.env` files, documentation
**Changes Required**:
```bash
# ❌ BEFORE (Vitest 3.x)
VITEST_MAX_THREADS=4
VITEST_MAX_FORKS=2
VITE_NODE_DEPS_MODULE_DIRECTORIES=/custom/path
# ✅ AFTER (Vitest 4.0)
VITEST_MAX_WORKERS=4
VITEST_MODULE_DIRECTORIES=/custom/path
```
**Action Items**:
- [ ] Update CI/CD pipeline environment variables
- [ ] Update `.env` files
- [ ] Update documentation referencing old environment variables
- [ ] Search for `VITEST_MAX_THREADS`, `VITEST_MAX_FORKS`, `VITE_NODE_DEPS_MODULE_DIRECTORIES`
### 6. Advanced: Module Runner Changes
**Search Pattern**: `vitest/execute`, `__vitest_executor`, `vite-node`
**Changes Required**:
```typescript
// ❌ BEFORE (Vitest 3.x)
import { execute } from 'vitest/execute';
// Access to __vitest_executor
// ✅ AFTER (Vitest 4.0)
// Use Vite's Module Runner API instead
// Consult Vite Module Runner documentation
```
**Action Items**:
- [ ] If using `vitest/execute`, migrate to Vite Module Runner
- [ ] Remove dependencies on `__vitest_executor`
- [ ] Update custom pool implementations (complete rewrite needed)
### 7. Type Definition Updates
**Search Pattern**: TypeScript imports from `vitest`, type errors after upgrade
**Changes Required**:
```typescript
// All deprecated type exports removed
// If you get TypeScript errors about missing types:
// - Check if you're using deprecated type names
// - Update to current type names from Vitest 4 API
// - Remove explicit @types/node if it was only needed due to Vitest bug
```
**Action Items**:
- [ ] Run TypeScript compilation on all test files
- [ ] Fix any type errors related to removed Vitest type definitions
- [ ] Review `@types/node` usage (may no longer be accidentally included)
## Post-Migration Validation
### 1. Run Tests Per Project
```bash
# Test each project individually
nx run-many -t test -p PROJECT_NAME
```
### 2. Run All Tests
```bash
# Run tests across all affected projects
nx affected -t test
```
### 3. Check Coverage
```bash
# Verify coverage generation works with new config
nx affected -t test --coverage
```
### 4. Validate CI Pipeline
```bash
# Run full CI validation
nx prepush
```
### 5. Review Migration Checklist
- [ ] All configuration files updated
- [ ] All test files pass
- [ ] Coverage reports generate correctly
- [ ] CI/CD pipeline runs successfully
- [ ] Environment variables updated
- [ ] Documentation updated
- [ ] No deprecated API warnings in console
## Common Issues and Solutions
### Issue: Coverage includes too many files
**Solution**: Add explicit `coverage.include` patterns to match your source files
### Issue: Tests fail with "arrow function constructors not supported"
**Solution**: Convert arrow functions used as constructors to `function` keyword or `class` syntax
### Issue: Automocks not resetting between tests
**Solution**: Use `vi.unmock()` or `vi.resetModules()` instead of `vi.restoreAllMocks()`
### Issue: Mock call order assertions failing
**Solution**: Update to 1-based indexing for `invocationCallOrder`
### Issue: Browser tests failing after upgrade
**Solution**: Check browser provider is object format and imports use `vitest/browser`
### Issue: TypeScript errors in test files
**Solution**: Update to new type definitions and remove usage of deprecated types
## Files to Review
Create a checklist of all files that need review:
```bash
# Configuration files
find . -name "vitest.config.*" -o -name "vitest.workspace.*"
find . -name "project.json" -exec grep -l "vitest" {} \;
# Test files
find . -name "*.spec.*" -o -name "*.test.*"
# Files with mock usage
rg "vi\.(fn|spyOn|mock|restoreAllMocks)" --type ts --type tsx --type js
# Files with coverage config
rg "coverage\.(all|extensions|ignoreEmptyLines)" --type ts --type js
# CI configuration
find . -name ".github/workflows/*.yml" -o -name ".gitlab-ci.yml" -o -name "azure-pipelines.yml"
```
## Migration Strategy for Large Workspaces
1. **Migrate in phases**: Start with a small project, validate, then expand
2. **Use feature branches**: Create separate branches for different migration aspects
3. **Run tests frequently**: After each configuration change, run affected tests
4. **Document issues**: Keep track of project-specific issues and solutions
5. **Automate where possible**: Create codemods for repetitive changes
## Useful Commands During Migration
```bash
# Find all vitest configurations
nx show projects --with-target test
# Test specific project after changes
nx test PROJECT_NAME
# Test all affected
nx affected -t test
# View project details
nx show project PROJECT_NAME --web
# Clear Nx cache if needed
nx reset
```
## Guard Rails
DO NOT
- Force tests to pass by removing test logic and replacing it with `expect(true).toBe(true)`
- Remove assertions
- Add additional mocks that force tests to pass
---
## Notes for LLM Execution
When executing this migration:
1. **Work systematically**: Complete one category before moving to the next
2. **Test after each change**: Don't batch all changes without validation
3. **Keep user informed**: Report progress through each section
4. **Handle errors promptly**: If tests fail, fix immediately before proceeding
5. **Update documentation**: Note any workspace-specific patterns or issues
6. **Create meaningful commits**: Group related changes together with clear messages
7. **Use TodoWrite tool**: Track migration progress for visibility

View File

@@ -0,0 +1,10 @@
import { type Tree } from '@nx/devkit';
/**
* Migrates reportsDirectory option for @nx/vitest:test and @nx/vite:test executors.
*
* Previously, reportsDirectory was resolved relative to the project root (cwd).
* Now it is resolved relative to the workspace root. This migration prepends
* {projectRoot}/ to existing naked paths so the resolved location stays the same.
*/
export default function prefixReportsDirectoryWithProjectRoot(tree: Tree): void;
//# sourceMappingURL=prefix-reports-directory-with-project-root.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"prefix-reports-directory-with-project-root.d.ts","sourceRoot":"","sources":["../../../../../../packages/vitest/src/migrations/update-22-6-0/prefix-reports-directory-with-project-root.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,IAAI,EAIV,MAAM,YAAY,CAAC;AAQpB;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,qCAAqC,CAAC,IAAI,EAAE,IAAI,QAGvE"}

View File

@@ -0,0 +1,105 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = prefixReportsDirectoryWithProjectRoot;
const devkit_1 = require("@nx/devkit");
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
const path_1 = require("path");
/**
* Migrates reportsDirectory option for @nx/vitest:test and @nx/vite:test executors.
*
* Previously, reportsDirectory was resolved relative to the project root (cwd).
* Now it is resolved relative to the workspace root. This migration prepends
* {projectRoot}/ to existing naked paths so the resolved location stays the same.
*/
function prefixReportsDirectoryWithProjectRoot(tree) {
migrateProjectConfigurations(tree);
migrateTargetDefaults(tree);
}
function migrateProjectConfigurations(tree) {
const projectsToUpdate = new Map();
for (const executorName of ['@nx/vitest:test', '@nx/vite:test']) {
(0, executor_options_utils_1.forEachExecutorOptions)(tree, executorName, (options, projectName, targetName, configuration) => {
if (needsMigration(options.reportsDirectory)) {
if (!projectsToUpdate.has(projectName)) {
projectsToUpdate.set(projectName, new Map());
}
const key = configuration
? `${targetName}::${configuration}`
: targetName;
projectsToUpdate
.get(projectName)
.set(key, { target: targetName, configuration });
}
});
}
for (const [projectName] of projectsToUpdate) {
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, projectName);
for (const [_targetName, target] of Object.entries(projectConfig.targets || {})) {
if (target.executor !== '@nx/vitest:test' &&
target.executor !== '@nx/vite:test') {
continue;
}
if (needsMigration(target.options?.reportsDirectory)) {
target.options.reportsDirectory = prependProjectRoot(target.options.reportsDirectory);
}
if (target.configurations) {
for (const config of Object.values(target.configurations)) {
if (needsMigration(config?.reportsDirectory)) {
config.reportsDirectory = prependProjectRoot(config.reportsDirectory);
}
}
}
}
(0, devkit_1.updateProjectConfiguration)(tree, projectName, projectConfig);
}
}
function migrateTargetDefaults(tree) {
const nxJson = (0, devkit_1.readNxJson)(tree);
if (!nxJson?.targetDefaults) {
return;
}
let hasChanges = false;
for (const [_key, targetConfig] of Object.entries(nxJson.targetDefaults)) {
if (targetConfig.executor !== '@nx/vitest:test' &&
targetConfig.executor !== '@nx/vite:test' &&
_key !== '@nx/vitest:test' &&
_key !== '@nx/vite:test') {
continue;
}
if (needsMigration(targetConfig.options?.reportsDirectory)) {
targetConfig.options.reportsDirectory = prependProjectRoot(targetConfig.options.reportsDirectory);
hasChanges = true;
}
if (targetConfig.configurations) {
for (const config of Object.values(targetConfig.configurations)) {
if (needsMigration(config?.reportsDirectory)) {
config.reportsDirectory = prependProjectRoot(config.reportsDirectory);
hasChanges = true;
}
}
}
}
if (hasChanges) {
(0, devkit_1.updateNxJson)(tree, nxJson);
}
}
function needsMigration(reportsDirectory) {
if (!reportsDirectory) {
return false;
}
if ((0, path_1.isAbsolute)(reportsDirectory)) {
return false;
}
// Already starts with {projectRoot} — already project-root-relative
if (reportsDirectory.startsWith('{projectRoot}')) {
return false;
}
// Already starts with {workspaceRoot} — user intended workspace-root-relative
if (reportsDirectory.startsWith('{workspaceRoot}')) {
return false;
}
return true;
}
function prependProjectRoot(reportsDirectory) {
return `{projectRoot}/${reportsDirectory}`;
}

25
node_modules/@nx/vitest/src/plugins/plugin.d.ts generated vendored Normal file
View File

@@ -0,0 +1,25 @@
import { CreateDependencies, CreateNodesV2 } from '@nx/devkit';
export interface VitestPluginOptions {
testTargetName?: string;
/**
* Atomizer for vitest
*/
ciTargetName?: string;
/**
* The name that should be used to group atomized tasks on CI
*/
ciGroupName?: string;
/**
* Default mode for running tests.
* - 'watch': Tests run in watch mode locally, auto-run in CI (default)
* - 'run': Tests run once and exit
*/
testMode?: 'watch' | 'run';
}
/**
* @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
*/
export declare const createDependencies: CreateDependencies;
export declare const createNodes: CreateNodesV2<VitestPluginOptions>;
export declare const createNodesV2: CreateNodesV2<VitestPluginOptions>;
//# sourceMappingURL=plugin.d.ts.map

1
node_modules/@nx/vitest/src/plugins/plugin.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/plugins/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAGlB,aAAa,EASd,MAAM,YAAY,CAAC;AAepB,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;CAC5B;AAoBD;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,kBAEhC,CAAC;AAIF,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,mBAAmB,CA2F1D,CAAC;AAEF,eAAO,MAAM,aAAa,oCAAc,CAAC"}

434
node_modules/@nx/vitest/src/plugins/plugin.js generated vendored Normal file
View File

@@ -0,0 +1,434 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNodesV2 = exports.createNodes = exports.createDependencies = void 0;
const devkit_1 = require("@nx/devkit");
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
const js_1 = require("@nx/js");
const internal_1 = require("@nx/js/src/internal");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const file_hasher_1 = require("nx/src/hasher/file-hasher");
const cache_directory_1 = require("nx/src/utils/cache-directory");
const plugins_1 = require("nx/src/utils/plugins");
const executor_utils_1 = require("../utils/executor-utils");
function readTargetsCache(cachePath) {
return process.env.NX_CACHE_PROJECT_GRAPH !== 'false' && (0, node_fs_1.existsSync)(cachePath)
? (0, devkit_1.readJsonFile)(cachePath)
: {};
}
function writeTargetsToCache(cachePath, results) {
(0, devkit_1.writeJsonFile)(cachePath, results);
}
/**
* @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
*/
const createDependencies = () => {
return [];
};
exports.createDependencies = createDependencies;
const vitestConfigGlob = '**/{vite,vitest}.config.{js,ts,mjs,mts,cjs,cts}';
exports.createNodes = [
vitestConfigGlob,
async (configFilePaths, options, context) => {
const pmc = (0, devkit_1.getPackageManagerCommand)((0, devkit_1.detectPackageManager)(context.workspaceRoot));
const optionsHash = (0, file_hasher_1.hashObject)(options);
const normalizedOptions = normalizeOptions(options);
const cachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `vitest-${optionsHash}.hash`);
const targetsCache = readTargetsCache(cachePath);
const { roots: projectRoots, configFiles: validConfigFiles } = configFilePaths.reduce((acc, configFile) => {
const potentialRoot = (0, node_path_1.dirname)(configFile);
if (checkIfConfigFileShouldBeProject(potentialRoot, context)) {
acc.roots.push(potentialRoot);
acc.configFiles.push(configFile);
}
return acc;
}, {
roots: [],
configFiles: [],
});
const lockfile = (0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot));
const tsconfigChainsByProjectRoot = collectTsconfigInputsByProjectRoot(projectRoots, context.workspaceRoot);
const hashes = await (0, calculate_hash_for_create_nodes_1.calculateHashesForCreateNodes)(projectRoots, normalizedOptions, context, projectRoots.map((root) => [
lockfile,
...(tsconfigChainsByProjectRoot.get(root) ?? []),
]));
try {
return await (0, devkit_1.createNodesFromFiles)(async (configFile, _, context, idx) => {
const projectRoot = (0, node_path_1.dirname)(configFile);
// results from vitest.config.js will be different from results of vite.config.js
// but the hash will be the same because it is based on the files under the project root.
// Adding the config file path to the hash ensures that the final hash value is different
// for different config files.
const hash = hashes[idx] + configFile;
const { projectType, metadata, targets } = (targetsCache[hash] ??=
await buildVitestTargets(configFile, projectRoot, normalizedOptions, context, pmc, tsconfigChainsByProjectRoot.get(projectRoot) ?? []));
const project = {
root: projectRoot,
targets,
metadata,
projectType,
};
return {
projects: {
[projectRoot]: project,
},
};
}, validConfigFiles, options, context);
}
finally {
writeTargetsToCache(cachePath, targetsCache);
}
},
];
exports.createNodesV2 = exports.createNodes;
async function buildVitestTargets(configFilePath, projectRoot, options, context, pmc, tsconfigInputs) {
const absoluteConfigFilePath = (0, devkit_1.joinPathFragments)(context.workspaceRoot, configFilePath);
// Workaround for the `build$3 is not a function` error that we sometimes see in agents.
// This should be removed later once we address the issue properly
try {
const importEsbuild = () => new Function('return import("esbuild")')();
await importEsbuild();
}
catch {
// do nothing
}
// Workaround for race condition with ESM-only Vite plugins (e.g. @vitejs/plugin-vue@6+)
// If vite.config.ts is compiled as CJS, then when both require('@vitejs/plugin-vue') and import('@vitejs/plugin-vue')
// are pending in the same process, Node will throw an error:
// Error [ERR_INTERNAL_ASSERTION]: Cannot require() ES Module @vitejs/plugin-vue/dist/index.js because it is not yet fully loaded.
// This may be caused by a race condition if the module is simultaneously dynamically import()-ed via Promise.all().
try {
const importVuePlugin = () => new Function('return import("@vitejs/plugin-vue")')();
await importVuePlugin();
}
catch {
// Plugin not installed or not needed, ignore
}
// Workaround for race condition with vitest/node on Node 24+
// When multiple vitest.config files are processed in parallel, Node can throw:
// Error [ERR_INTERNAL_ASSERTION]: Cannot require() ES Module vitest/dist/node.js
// because it is not yet fully loaded.
// See: https://github.com/nrwl/nx/issues/34028
try {
const importVitestNode = () => new Function('return import("vitest/node")')();
await importVitestNode();
}
catch {
// vitest/node not available or not needed, ignore
}
const { resolveConfig } = await (0, executor_utils_1.loadViteDynamicImport)();
const viteBuildConfig = await resolveConfig({
configFile: absoluteConfigFilePath,
mode: 'development',
}, 'build');
// If this is a root workspace config file with projects property, don't infer targets.
// The root config is just an orchestrator - the actual tests live in the individual project configs.
const isWorkspaceRoot = projectRoot === '.';
// TODO(jack): Remove this cast when @nx/vitest switches to moduleResolution:
// "nodenext". Vite 8's rolldown types break vitest's test augmentation.
const hasProjectsProperty = Array.isArray(viteBuildConfig?.test?.projects);
if (isWorkspaceRoot && hasProjectsProperty) {
return { targets: {}, metadata: {}, projectType: 'library' };
}
let metadata = {};
const { testOutputs, hasTest } = getOutputs(viteBuildConfig, projectRoot, context.workspaceRoot);
const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
const targets = {};
// if file is vitest.config or vite.config has definition for test, create targets for test and/or atomized tests
if (configFilePath.includes('vitest.config') || hasTest) {
const isTypecheckEnabled = !!viteBuildConfig?.test?.typecheck
?.enabled;
targets[options.testTargetName] = await testTarget(namedInputs, testOutputs, projectRoot, options.testMode, pmc, isTypecheckEnabled, tsconfigInputs);
if (options.ciTargetName) {
const groupName = options.ciGroupName ?? (0, plugins_1.deriveGroupNameFromTarget)(options.ciTargetName);
const targetGroup = [];
const dependsOn = [];
metadata = {
targetGroups: {
[groupName]: targetGroup,
},
};
const projectRootRelativeTestPaths = await getTestPathsRelativeToProjectRoot(projectRoot, context.workspaceRoot);
for (const relativePath of projectRootRelativeTestPaths) {
if (relativePath.includes('../')) {
throw new Error('@nx/vitest attempted to run tests outside of the project root. This is not supported and should not happen. Please open an issue at https://github.com/nrwl/nx/issues/new/choose with the following information:\n\n' +
`\n\n${JSON.stringify({
projectRoot,
relativePath,
projectRootRelativeTestPaths,
context,
}, null, 2)}`);
}
const targetName = `${options.ciTargetName}--${relativePath}`;
dependsOn.push(targetName);
targets[targetName] = {
// It does not make sense to run atomized tests in watch mode as they are intended to be run in CI
command: `vitest run ${relativePath}`,
cache: targets[options.testTargetName].cache,
inputs: targets[options.testTargetName].inputs,
outputs: targets[options.testTargetName].outputs,
options: {
cwd: projectRoot,
env: targets[options.testTargetName].options.env,
},
metadata: {
technologies: ['vitest'],
description: `Run Vitest Tests in ${relativePath}`,
help: {
command: `${pmc.exec} vitest --help`,
example: {
options: {
coverage: true,
},
},
},
},
};
targetGroup.push(targetName);
}
if (targetGroup.length > 0) {
targets[options.ciTargetName] = {
executor: 'nx:noop',
cache: true,
inputs: targets[options.testTargetName].inputs,
outputs: targets[options.testTargetName].outputs,
dependsOn,
metadata: {
technologies: ['vitest'],
description: 'Run Vitest Tests in CI',
nonAtomizedTarget: options.testTargetName,
help: {
command: `${pmc.exec} vitest --help`,
example: {
options: {
coverage: true,
},
},
},
},
};
targetGroup.unshift(options.ciTargetName);
}
}
}
return { targets, metadata, projectType: 'library' };
}
async function testTarget(namedInputs, outputs, projectRoot, testMode = 'watch', pmc, isTypecheckEnabled, tsconfigInputs) {
const command = testMode === 'run' ? 'vitest run' : 'vitest';
const depOutputsGlob = isTypecheckEnabled ? '**/*.{js,d.ts}' : '**/*.js';
return {
command,
options: { cwd: (0, devkit_1.joinPathFragments)(projectRoot) },
cache: true,
inputs: [
...('production' in namedInputs
? ['default', '^production']
: ['default', '^default']),
...tsconfigInputs.map((f) => ({
json: `{workspaceRoot}/${f}`,
fields: ['compilerOptions'],
})),
{
externalDependencies: ['vitest'],
},
{ env: 'CI' },
{ dependentTasksOutputFiles: depOutputsGlob, transitive: true },
],
outputs,
metadata: {
technologies: ['vitest'],
description: `Run Vitest tests`,
help: {
command: `${pmc.exec} vitest --help`,
example: {
options: {
bail: 1,
coverage: true,
},
},
},
},
};
}
function getOutputs(viteBuildConfig, projectRoot, workspaceRoot) {
const { test } = viteBuildConfig;
const reportsDirectoryPath = normalizeOutputPath(test?.coverage?.reportsDirectory, projectRoot, workspaceRoot, 'coverage');
return {
testOutputs: [reportsDirectoryPath],
hasTest: !!test,
};
}
function normalizeOutputPath(outputPath, projectRoot, workspaceRoot, path) {
if (!outputPath) {
if (projectRoot === '.') {
return `{projectRoot}/${path}`;
}
else {
return `{workspaceRoot}/${path}/{projectRoot}`;
}
}
else {
if ((0, node_path_1.isAbsolute)(outputPath)) {
return `{workspaceRoot}/${(0, node_path_1.relative)(workspaceRoot, outputPath)}`;
}
else {
if (outputPath.startsWith('..')) {
return (0, devkit_1.joinPathFragments)('{workspaceRoot}', projectRoot, outputPath);
}
else {
return (0, devkit_1.joinPathFragments)('{projectRoot}', outputPath);
}
}
}
}
function normalizeOptions(options) {
options ??= {};
options.testTargetName ??= 'test';
options.testMode ??= 'watch';
return options;
}
/**
* Collects tsconfig files that Vite's esbuild-based config bundler reads
* but are outside the project root (and thus not covered by `default`).
*
* Vite < 8 uses esbuild's Build API to bundle config files. esbuild walks
* UP from the entry point, reading and parsing every `tsconfig.json` in
* every ancestor directory plus their `extends` chains. Vite >= 8 uses
* rolldown with `tsconfig: false`, but pnpm can resolve different Vite
* versions per project, so we always collect — the walk is cheap (cached
* JSON reads) and over-declaring inputs for Vite 8 projects is harmless.
*
* Files already handled elsewhere are excluded:
* - Inside the project root → covered by `default` (`{projectRoot}/**\/*`)
* - The root tsconfig (tsconfig.base.json or tsconfig.json) → covered by
* the native TsConfiguration hash instruction
* - Inside node_modules → invalidated via lockfile
* - Outside the workspace → cannot be expressed as inputs
*/
function collectTsconfigInputsByProjectRoot(projectRoots, workspaceRoot) {
const jsonCache = new Map();
const result = new Map();
const rootTsConfigName = (0, js_1.getRootTsConfigFileName)();
for (const projectRoot of projectRoots) {
if (projectRoot === '.')
continue;
const outside = [];
const seen = new Set();
const projectPrefix = `${projectRoot}/`;
const collect = (absolutePath) => {
const wsRelative = (0, node_path_1.relative)(workspaceRoot, absolutePath)
.split(node_path_1.sep)
.join('/');
if (seen.has(wsRelative))
return;
seen.add(wsRelative);
if (wsRelative.startsWith('../') || wsRelative === '..')
return;
if (wsRelative.startsWith('node_modules/') ||
wsRelative.includes('/node_modules/'))
return;
if (wsRelative === projectRoot || wsRelative.startsWith(projectPrefix))
return;
if (wsRelative === rootTsConfigName)
return;
outside.push(wsRelative);
};
// 1. Walk the project tsconfig's extends chain
const projectTsconfig = (0, node_path_1.join)(workspaceRoot, projectRoot, 'tsconfig.json');
if ((0, node_fs_1.existsSync)(projectTsconfig)) {
(0, internal_1.walkTsconfigExtendsChain)(projectTsconfig, (absPath) => {
collect(absPath);
return 'continue';
}, { jsonCache });
}
// 2. Walk UP ancestor directories (esbuild reads every tsconfig.json
// between the entry point and the filesystem root)
let dir = (0, node_path_1.dirname)(projectRoot);
while (dir && dir !== '.') {
const ancestorTsconfig = (0, node_path_1.join)(workspaceRoot, dir, 'tsconfig.json');
if ((0, node_fs_1.existsSync)(ancestorTsconfig)) {
(0, internal_1.walkTsconfigExtendsChain)(ancestorTsconfig, (absPath) => {
collect(absPath);
return 'continue';
}, { jsonCache });
}
const parent = (0, node_path_1.dirname)(dir);
if (parent === dir)
break;
dir = parent;
}
// 3. Check the workspace root itself (dirname loop above stops at '.')
const rootTsconfig = (0, node_path_1.join)(workspaceRoot, 'tsconfig.json');
if ((0, node_fs_1.existsSync)(rootTsconfig)) {
(0, internal_1.walkTsconfigExtendsChain)(rootTsconfig, (absPath) => {
collect(absPath);
return 'continue';
}, { jsonCache });
}
if (outside.length > 0) {
result.set(projectRoot, outside);
}
}
return result;
}
function checkIfConfigFileShouldBeProject(projectRoot, context) {
// Do not create a project if package.json and project.json isn't there.
const siblingFiles = (0, node_fs_1.readdirSync)((0, node_path_1.join)(context.workspaceRoot, projectRoot));
if (!siblingFiles.includes('package.json') &&
!siblingFiles.includes('project.json')) {
return false;
}
return true;
}
async function getTestPathsRelativeToProjectRoot(projectRoot, workspaceRoot) {
const fullProjectRoot = (0, node_path_1.join)(workspaceRoot, projectRoot);
const { createVitest } = await Promise.resolve().then(() => __importStar(require('vitest/node')));
const vitest = await createVitest('test', {
root: fullProjectRoot,
dir: fullProjectRoot,
filesOnly: true,
watch: false,
});
const relevantTestSpecifications = await vitest.getRelevantTestSpecifications();
// Sort to keep atomized target name insertion order stable.
// vitest.getRelevantTestSpecifications uses tinyglobby internally,
// which does not sort its filesystem traversal output.
return relevantTestSpecifications
.filter((ts) => fullProjectRoot === '.' ? true : ts.moduleId.startsWith(fullProjectRoot))
.map((ts) => (0, devkit_1.normalizePath)((0, node_path_1.relative)(projectRoot, ts.moduleId)))
.sort();
}

View File

@@ -0,0 +1,2 @@
export declare function detectUiFramework(project: string): Promise<'angular' | 'react' | 'none'>;
//# sourceMappingURL=detect-ui-framework.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"detect-ui-framework.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/detect-ui-framework.ts"],"names":[],"mappings":"AAMA,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC,CAuBvC"}

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectUiFramework = detectUiFramework;
const devkit_1 = require("@nx/devkit");
const ANGULAR_NPM_SCOPE = 'angular';
const ANGULAR_DEPS = ['@nx/angular'];
const REACT_DEPS = ['react', '@nx/react'];
async function detectUiFramework(project) {
const graph = await (0, devkit_1.createProjectGraphAsync)();
for (const dep of graph.dependencies[project] ?? []) {
if (dep.source !== project || !dep.target.startsWith('npm:')) {
continue;
}
const npmDependency = dep.target.replace('npm:', '');
if (dep.target.startsWith(`npm:@${ANGULAR_NPM_SCOPE}/`) ||
ANGULAR_DEPS.includes(npmDependency)) {
return 'angular';
}
if (REACT_DEPS.includes(npmDependency)) {
return 'react';
}
}
return 'none';
}

View File

@@ -0,0 +1,9 @@
import { type GeneratorCallback, type Tree } from '@nx/devkit';
export type EnsureDependenciesOptions = {
uiFramework: 'angular' | 'react' | 'vue' | 'none';
compiler?: 'babel' | 'swc';
includeLib?: boolean;
testEnvironment?: 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' | string;
};
export declare function ensureDependencies(tree: Tree, schema: EnsureDependenciesOptions): Promise<GeneratorCallback>;
//# sourceMappingURL=ensure-dependencies.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ensure-dependencies.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/ensure-dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACV,MAAM,YAAY,CAAC;AAepB,MAAM,MAAM,yBAAyB,GAAG;IACtC,WAAW,EAAE,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IAClD,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,cAAc,GAAG,MAAM,CAAC;CAC5E,CAAC;AAEF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,iBAAiB,CAAC,CA2D5B"}

View File

@@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureDependencies = ensureDependencies;
const devkit_1 = require("@nx/devkit");
const semver_1 = require("semver");
const versions_1 = require("./versions");
const version_utils_1 = require("./version-utils");
async function ensureDependencies(tree, schema) {
const useVitestUi = schema.uiFramework === 'angular' ||
schema.uiFramework === 'react' ||
schema.uiFramework === 'vue';
const devDependencies = {};
if (schema.testEnvironment === 'jsdom') {
devDependencies['jsdom'] = versions_1.jsdomVersion;
}
else if (schema.testEnvironment === 'happy-dom') {
devDependencies['happy-dom'] = versions_1.happyDomVersion;
}
else if (schema.testEnvironment === 'edge-runtime') {
devDependencies['@edge-runtime/vm'] = versions_1.edgeRuntimeVmVersion;
}
else if (schema.testEnvironment !== 'node' && schema.testEnvironment) {
devkit_1.logger.info(`A custom environment was provided: ${schema.testEnvironment}. You need to install it manually.`);
}
if (schema.uiFramework === 'angular') {
devDependencies['@analogjs/vitest-angular'] = versions_1.analogVitestAngular;
devDependencies['@analogjs/vite-plugin-angular'] = versions_1.analogVitestAngular;
}
if (schema.uiFramework === 'react') {
if (schema.compiler === 'swc') {
devDependencies['@vitejs/plugin-react-swc'] = versions_1.vitePluginReactSwcVersion;
}
else {
// @vitejs/plugin-react v6 requires Vite 8+, use v4 for older versions.
// getDependencyVersionFromPackageJson resolves pnpm catalog: refs.
const viteRange = (0, devkit_1.getDependencyVersionFromPackageJson)(tree, 'vite');
const coerced = viteRange ? (0, semver_1.coerce)(viteRange) : null;
const viteMajor = coerced ? (0, semver_1.major)(coerced) : null;
devDependencies['@vitejs/plugin-react'] =
viteMajor !== null && viteMajor < 8
? versions_1.vitePluginReactV4Version
: versions_1.vitePluginReactVersion;
}
}
if (schema.includeLib) {
devDependencies['vite-plugin-dts'] = versions_1.vitePluginDtsVersion;
if ((0, devkit_1.detectPackageManager)() !== 'pnpm') {
devDependencies['ajv'] = versions_1.ajvVersion;
}
}
if (useVitestUi) {
const { vitestUi } = await (0, version_utils_1.getVitestDependenciesVersionsToInstall)(tree);
devDependencies['@vitest/ui'] = vitestUi;
}
return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, devDependencies, undefined, true);
}

View File

@@ -0,0 +1,3 @@
export declare function loadViteDynamicImport(): Promise<any>;
export declare function loadVitestDynamicImport(): Promise<typeof import("vitest/node")>;
//# sourceMappingURL=executor-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"executor-utils.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/executor-utils.ts"],"names":[],"mappings":"AAGA,wBAAgB,qBAAqB,IACW,OAAO,CAAC,GAAG,CAAC,CAC3D;AAED,wBAAgB,uBAAuB,IACgB,OAAO,CAC1D,cAAc,aAAa,CAAC,CAC7B,CACF"}

13
node_modules/@nx/vitest/src/utils/executor-utils.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadViteDynamicImport = loadViteDynamicImport;
exports.loadVitestDynamicImport = loadVitestDynamicImport;
// TODO(jack): Remove this cast when @nx/vitest switches to moduleResolution:
// "nodenext". Vite 8 ships ESM-only type declarations (.d.mts) not resolvable
// under moduleResolution: "node".
function loadViteDynamicImport() {
return Function('return import("vite")')();
}
function loadVitestDynamicImport() {
return Function('return import("vitest/node")')();
}

39
node_modules/@nx/vitest/src/utils/generator-utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,39 @@
import { Tree } from '@nx/devkit';
export type Target = 'build' | 'serve' | 'test' | 'preview';
export type TargetFlags = Partial<Record<Target, boolean>>;
export interface VitestGeneratorSchema {
project: string;
uiFramework?: 'angular' | 'react' | 'vue' | 'none';
coverageProvider: 'v8' | 'istanbul' | 'custom';
inSourceTests?: boolean;
skipViteConfig?: boolean;
testTarget?: string;
skipFormat?: boolean;
testEnvironment?: 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' | string;
addPlugin?: boolean;
runtimeTsconfigFileName?: string;
compiler?: 'babel' | 'swc';
projectType?: 'application' | 'library';
}
export declare function addOrChangeTestTarget(tree: Tree, options: VitestGeneratorSchema, hasPlugin: boolean): void;
export interface ViteConfigFileOptions {
project: string;
includeLib?: boolean;
includeVitest?: boolean;
inSourceTests?: boolean;
testEnvironment?: 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' | string;
rollupOptionsExternal?: string[];
imports?: string[];
plugins?: string[];
coverageProvider?: 'v8' | 'istanbul' | 'custom';
setupFile?: string;
useEsmExtension?: boolean;
port?: number;
previewPort?: number;
}
export declare function createOrEditViteConfig(tree: Tree, options: ViteConfigFileOptions, onlyVitest: boolean, extraOptions?: {
projectAlreadyHasViteTargets?: TargetFlags;
skipPackageJson?: boolean;
vitestFileName?: boolean;
}): void;
//# sourceMappingURL=generator-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"generator-utils.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/generator-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,IAAI,EAGL,MAAM,YAAY,CAAC;AAMpB,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAC5D,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACnD,gBAAgB,EAAE,IAAI,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC/C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,cAAc,GAAG,MAAM,CAAC;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,WAAW,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,OAAO,QAsCnB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,cAAc,GAAG,MAAM,CAAC;IAC3E,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,CAAC,EAAE,IAAI,GAAG,UAAU,GAAG,QAAQ,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,OAAO,EACnB,YAAY,GAAE;IACZ,4BAA4B,CAAC,EAAE,WAAW,CAAC;IAC3C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;CACrB,QA8MP"}

235
node_modules/@nx/vitest/src/utils/generator-utils.js generated vendored Normal file
View File

@@ -0,0 +1,235 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addOrChangeTestTarget = addOrChangeTestTarget;
exports.createOrEditViteConfig = createOrEditViteConfig;
const devkit_1 = require("@nx/devkit");
const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
const vite_config_edit_utils_1 = require("./vite-config-edit-utils");
const versions_1 = require("./versions");
function addOrChangeTestTarget(tree, options, hasPlugin) {
const nxJson = (0, devkit_1.readNxJson)(tree);
hasPlugin = nxJson.plugins?.some((p) => typeof p === 'string'
? p === '@nx/vitest'
: p.plugin === '@nx/vitest' || hasPlugin);
if (hasPlugin) {
return;
}
const project = (0, devkit_1.readProjectConfiguration)(tree, options.project);
const target = options.testTarget ?? 'test';
const reportsDirectory = (0, devkit_1.joinPathFragments)('coverage', project.root === '.' ? options.project : project.root);
const testOptions = {
reportsDirectory,
};
project.targets ??= {};
if (project.targets[target]) {
throw new Error(`Target "${target}" already exists in the project.`);
}
else {
project.targets[target] = {
executor: '@nx/vitest:test',
outputs: ['{options.reportsDirectory}'],
options: testOptions,
};
}
(0, devkit_1.updateProjectConfiguration)(tree, options.project, project);
}
function createOrEditViteConfig(tree, options, onlyVitest, extraOptions = {}) {
const { root: projectRoot } = (0, devkit_1.readProjectConfiguration)(tree, options.project);
const extension = options.useEsmExtension ? 'mts' : 'ts';
const viteConfigPath = extraOptions.vitestFileName
? `${projectRoot}/vitest.config.${extension}`
: `${projectRoot}/vite.config.${extension}`;
const isTsSolutionSetup = (0, ts_solution_setup_1.isUsingTsSolutionSetup)(tree);
const buildOutDir = isTsSolutionSetup
? './dist'
: projectRoot === '.'
? `./dist/${options.project}`
: `${(0, devkit_1.offsetFromRoot)(projectRoot)}dist/${projectRoot}`;
const buildOption = onlyVitest
? ''
: options.includeLib
? ` // Configuration for building your library.
// See: https://vite.dev/guide/build.html#library-mode
build: {
outDir: '${buildOutDir}',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
lib: {
// Could also be a dictionary or array of multiple entry points.
entry: 'src/index.ts',
name: '${options.project}',
fileName: 'index',
// Change this to the formats you want to support.
// Don't forget to update your package.json as well.
formats: ['es' as const]
},
rollupOptions: {
// External packages that should not be bundled into your library.
external: [${options.rollupOptionsExternal ?? ''}]
},
},`
: ` build: {
outDir: '${buildOutDir}',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
},`;
const imports = options.imports ? [...options.imports] : [];
const plugins = options.plugins ? [...options.plugins] : [];
if (!onlyVitest && options.includeLib && !isTsSolutionSetup) {
imports.push(`import dts from 'vite-plugin-dts'`, `import * as path from 'path'`);
}
if (!isTsSolutionSetup) {
imports.push(`import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'`, `import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'`);
plugins.push(`nxViteTsPaths()`, `nxCopyAssetsPlugin(['*.md'])`);
if (!extraOptions.skipPackageJson) {
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@nx/vite': versions_1.nxVersion });
}
}
if (!onlyVitest && options.includeLib) {
plugins.push(`dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json')${!isTsSolutionSetup ? ', pathsToAliases: false' : ''} })`);
}
const reportsDirectory = isTsSolutionSetup
? './test-output/vitest/coverage'
: projectRoot === '.'
? `./coverage/${options.project}`
: `${(0, devkit_1.offsetFromRoot)(projectRoot)}coverage/${projectRoot}`;
const testOption = options.includeVitest
? ` test: {
name: '${options.project}',
watch: false,
globals: true,
environment: '${options.testEnvironment ?? 'jsdom'}',
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
${options.setupFile ? ` setupFiles: ['${options.setupFile}'],\n` : ''}\
${options.inSourceTests
? ` includeSource: ['src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],\n`
: ''}\
reporters: ['default'],
coverage: {
reportsDirectory: '${reportsDirectory}',
provider: ${options.coverageProvider
? `'${options.coverageProvider}' as const`
: `'v8' as const`},
}
},`
: '';
const defineOption = options.inSourceTests
? ` define: {
'import.meta.vitest': undefined
},`
: '';
const devServerOption = onlyVitest
? ''
: options.includeLib
? ''
: ` server:{
port: ${options.port ?? 4200},
host: 'localhost',
},`;
const previewServerOption = onlyVitest
? ''
: options.includeLib
? ''
: ` preview:{
port: ${options.previewPort ?? 4300},
host: 'localhost',
},`;
const workerOption = isTsSolutionSetup
? ` // Uncomment this if you are using workers.
// worker: {
// plugins: [],
// },`
: ` // Uncomment this if you are using workers.
// worker: {
// plugins: () => [ nxViteTsPaths() ],
// },`;
const cacheDir = `cacheDir: '${normalizedJoinPaths((0, devkit_1.offsetFromRoot)(projectRoot), 'node_modules', '.vite', projectRoot === '.' ? options.project : projectRoot)}',`;
if (tree.exists(viteConfigPath)) {
handleViteConfigFileExists(tree, viteConfigPath, options, buildOption, buildOutDir, imports, plugins, testOption, reportsDirectory, cacheDir, projectRoot, (0, devkit_1.offsetFromRoot)(projectRoot), extraOptions.projectAlreadyHasViteTargets);
return;
}
// When using vitest.config, use vitest/config import and skip vite-specific options
const viteConfigContent = extraOptions.vitestFileName
? `import { defineConfig } from 'vitest/config';
${imports.join(';\n')}${imports.length ? ';' : ''}
export default defineConfig(() => ({
root: __dirname,
${printOptions(cacheDir, plugins.length ? ` plugins: [${plugins.join(', ')}],` : '', defineOption, testOption)}
}));
`.replace(/\s+(?=(\n|$))/gm, '\n')
: `/// <reference types='vitest' />
import { defineConfig } from 'vite';
${imports.join(';\n')}${imports.length ? ';' : ''}
export default defineConfig(() => ({
root: __dirname,
${printOptions(cacheDir, devServerOption, previewServerOption, ` plugins: [${plugins.join(', ')}],`, workerOption, buildOption, defineOption, testOption)}
}));
`.replace(/\s+(?=(\n|$))/gm, '\n');
tree.write(viteConfigPath, viteConfigContent);
}
function printOptions(...options) {
return options.filter(Boolean).join('\n');
}
function handleViteConfigFileExists(tree, viteConfigPath, options, buildOption, buildOutDir, imports, plugins, testOption, reportsDirectory, cacheDir, projectRoot, offsetFromRoot, projectAlreadyHasViteTargets) {
if (projectAlreadyHasViteTargets?.build &&
projectAlreadyHasViteTargets?.test) {
return;
}
if (process.env.NX_VERBOSE_LOGGING === 'true') {
devkit_1.logger.info(`vite.config.ts already exists for project ${options.project}.`);
}
const buildOptionObject = options.includeLib
? {
lib: {
entry: 'src/index.ts',
name: options.project,
fileName: 'index',
formats: ['es'],
},
rollupOptions: {
external: options.rollupOptionsExternal ?? [],
},
outDir: buildOutDir,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
}
: {
outDir: buildOutDir,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
};
const testOptionObject = {
globals: true,
environment: options.testEnvironment ?? 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: reportsDirectory,
provider: `'${options.coverageProvider ?? 'v8'}'`,
},
};
const changed = (0, vite_config_edit_utils_1.ensureViteConfigIsCorrect)(tree, viteConfigPath, buildOption, buildOptionObject, imports, plugins, testOption, testOptionObject, cacheDir, projectAlreadyHasViteTargets ?? {});
if (!changed) {
devkit_1.logger.warn(`Make sure the following setting exists in your Vite configuration file (${viteConfigPath}):
${buildOption}
`);
}
}
function normalizedJoinPaths(...paths) {
const path = (0, devkit_1.joinPathFragments)(...paths);
return path.startsWith('.') ? path : `./${path}`;
}

View File

@@ -0,0 +1,5 @@
import { type Tree } from '@nx/devkit';
export declare function ignoreVitestTempFiles(tree: Tree, projectRoot?: string | undefined): Promise<void>;
export declare function addVitestTempFilesToGitIgnore(tree: Tree): void;
export declare function isEslintInstalled(tree: Tree): boolean;
//# sourceMappingURL=ignore-vitest-temp-files.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ignore-vitest-temp-files.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/ignore-vitest-temp-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAG9E,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,IAAI,EACV,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,GAC/B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAU9D;AAiCD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAYrD"}

View File

@@ -0,0 +1,87 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ignoreVitestTempFiles = ignoreVitestTempFiles;
exports.addVitestTempFilesToGitIgnore = addVitestTempFilesToGitIgnore;
exports.isEslintInstalled = isEslintInstalled;
const devkit_1 = require("@nx/devkit");
const versions_1 = require("./versions");
async function ignoreVitestTempFiles(tree, projectRoot) {
addVitestTempFilesToGitIgnore(tree);
await ignoreVitestTempFilesInEslintConfig(tree, projectRoot);
}
function addVitestTempFilesToGitIgnore(tree) {
let gitIgnoreContents = tree.exists('.gitignore')
? tree.read('.gitignore', 'utf-8')
: '';
if (!/^vitest\.config\.\*\.timestamp\*$/m.test(gitIgnoreContents)) {
gitIgnoreContents = (0, devkit_1.stripIndents) `${gitIgnoreContents}
vitest.config.*.timestamp*`;
}
tree.write('.gitignore', gitIgnoreContents);
}
async function ignoreVitestTempFilesInEslintConfig(tree, projectRoot) {
if (!isEslintInstalled(tree)) {
return;
}
(0, devkit_1.ensurePackage)('@nx/eslint', versions_1.nxVersion);
const { addIgnoresToLintConfig, isEslintConfigSupported } = await Promise.resolve().then(() => __importStar(require('@nx/eslint/src/generators/utils/eslint-file')));
if (!isEslintConfigSupported(tree)) {
return;
}
const { useFlatConfig } = await Promise.resolve().then(() => __importStar(require('@nx/eslint/src/utils/flat-config')));
const isUsingFlatConfig = useFlatConfig(tree);
if (!projectRoot && !isUsingFlatConfig) {
// root eslintrc files ignore all files and the root eslintrc files add
// back all the project files, so we only add the ignores to the project
// eslintrc files
return;
}
// for flat config, we update the root config file
const directory = isUsingFlatConfig ? '' : (projectRoot ?? '');
addIgnoresToLintConfig(tree, directory, ['**/vitest.config.*.timestamp*']);
}
function isEslintInstalled(tree) {
try {
require('eslint');
return true;
}
catch { }
// it might not be installed yet, but it might be in the tree pending install
const { devDependencies, dependencies } = tree.exists('package.json')
? (0, devkit_1.readJson)(tree, 'package.json')
: {};
return !!devDependencies?.['eslint'] || !!dependencies?.['eslint'];
}

8
node_modules/@nx/vitest/src/utils/options-utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import { ExecutorContext } from '@nx/devkit';
/**
* Returns the path to the vite config file or undefined when not found.
*/
export declare function normalizeViteConfigFilePath(contextRoot: string, projectRoot: string, configFile?: string): string | undefined;
export declare function getProjectTsConfigPath(projectRoot: string): string | undefined;
export declare function getNxTargetOptions(target: string, context: ExecutorContext): any;
//# sourceMappingURL=options-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"options-utils.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/options-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAKhB,MAAM,YAAY,CAAC;AAGpB;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAgCpB;AAED,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAcpB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,OAG1E"}

41
node_modules/@nx/vitest/src/utils/options-utils.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeViteConfigFilePath = normalizeViteConfigFilePath;
exports.getProjectTsConfigPath = getProjectTsConfigPath;
exports.getNxTargetOptions = getNxTargetOptions;
const devkit_1 = require("@nx/devkit");
const fs_1 = require("fs");
/**
* Returns the path to the vite config file or undefined when not found.
*/
function normalizeViteConfigFilePath(contextRoot, projectRoot, configFile) {
if (configFile) {
const normalized = (0, devkit_1.joinPathFragments)(contextRoot, configFile);
if (!(0, fs_1.existsSync)(normalized)) {
throw new Error(`Could not find vite config at provided path "${normalized}".`);
}
return normalized;
}
const allowsExt = ['js', 'mjs', 'ts', 'cjs', 'mts', 'cts'];
for (const ext of allowsExt) {
if ((0, fs_1.existsSync)((0, devkit_1.joinPathFragments)(contextRoot, projectRoot, `vite.config.${ext}`))) {
return (0, devkit_1.joinPathFragments)(contextRoot, projectRoot, `vite.config.${ext}`);
}
else if ((0, fs_1.existsSync)((0, devkit_1.joinPathFragments)(contextRoot, projectRoot, `vitest.config.${ext}`))) {
return (0, devkit_1.joinPathFragments)(contextRoot, projectRoot, `vitest.config.${ext}`);
}
}
}
function getProjectTsConfigPath(projectRoot) {
return (0, fs_1.existsSync)((0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, projectRoot, 'tsconfig.app.json'))
? (0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.app.json')
: (0, fs_1.existsSync)((0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, projectRoot, 'tsconfig.lib.json'))
? (0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.lib.json')
: (0, fs_1.existsSync)((0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, projectRoot, 'tsconfig.json'))
? (0, devkit_1.joinPathFragments)(projectRoot, 'tsconfig.json')
: undefined;
}
function getNxTargetOptions(target, context) {
const targetObj = (0, devkit_1.parseTargetString)(target, context);
return (0, devkit_1.readTargetOptions)(targetObj, context);
}

16
node_modules/@nx/vitest/src/utils/version-utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
import type { Tree } from 'nx/src/generators/tree';
type VitestDependenciesVersions = {
vitest: string;
vitestUi: string;
vitestCoverageV8: string;
vitestCoverageIstanbul: string;
};
export declare function getVitestDependenciesVersionsToInstall(tree: Tree): Promise<VitestDependenciesVersions>;
export declare function isVitestV3(tree: Tree): Promise<boolean>;
export declare function isVitestV2(tree: Tree): Promise<boolean>;
export declare function getInstalledVitestVersion(tree: Tree): string;
export declare function getInstalledViteVersion(tree: Tree): string;
export declare function getInstalledViteMajorVersion(tree: Tree): 5 | 6 | 7 | 8 | undefined;
export declare function getInstalledVitestVersionFromGraph(): Promise<string>;
export {};
//# sourceMappingURL=version-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"version-utils.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/version-utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAcnD,KAAK,0BAA0B,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,wBAAsB,sCAAsC,CAC1D,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,0BAA0B,CAAC,CAwBrC;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,IAAI,oBAM1C;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,IAAI,oBAM1C;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAiB5D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAe1D;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GACT,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAW3B;AAED,wBAAsB,kCAAkC,oBASvD"}

90
node_modules/@nx/vitest/src/utils/version-utils.js generated vendored Normal file
View File

@@ -0,0 +1,90 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getVitestDependenciesVersionsToInstall = getVitestDependenciesVersionsToInstall;
exports.isVitestV3 = isVitestV3;
exports.isVitestV2 = isVitestV2;
exports.getInstalledVitestVersion = getInstalledVitestVersion;
exports.getInstalledViteVersion = getInstalledViteVersion;
exports.getInstalledViteMajorVersion = getInstalledViteMajorVersion;
exports.getInstalledVitestVersionFromGraph = getInstalledVitestVersionFromGraph;
const devkit_1 = require("@nx/devkit");
const semver_1 = require("semver");
const versions_1 = require("./versions");
async function getVitestDependenciesVersionsToInstall(tree) {
if (await isVitestV3(tree)) {
return {
vitest: versions_1.vitestV3Version,
vitestUi: versions_1.vitestV3Version,
vitestCoverageV8: versions_1.vitestV3CoverageV8Version,
vitestCoverageIstanbul: versions_1.vitestV3CoverageIstanbulVersion,
};
}
else if (await isVitestV2(tree)) {
return {
vitest: versions_1.vitestV2Version,
vitestUi: versions_1.vitestV2Version,
vitestCoverageV8: versions_1.vitestV2CoverageV8Version,
vitestCoverageIstanbul: versions_1.vitestV2CoverageIstanbulVersion,
};
}
else {
// Default to latest (v3)
return {
vitest: versions_1.vitestVersion,
vitestUi: versions_1.vitestVersion,
vitestCoverageV8: versions_1.vitestCoverageV8Version,
vitestCoverageIstanbul: versions_1.vitestCoverageIstanbulVersion,
};
}
}
async function isVitestV3(tree) {
let installedVitestVersion = await getInstalledVitestVersionFromGraph();
if (!installedVitestVersion) {
installedVitestVersion = getInstalledVitestVersion(tree);
}
return (0, semver_1.major)(installedVitestVersion) === 3;
}
async function isVitestV2(tree) {
let installedVitestVersion = await getInstalledVitestVersionFromGraph();
if (!installedVitestVersion) {
installedVitestVersion = getInstalledVitestVersion(tree);
}
return (0, semver_1.major)(installedVitestVersion) === 2;
}
function getInstalledVitestVersion(tree) {
const installedVitestVersion = (0, devkit_1.getDependencyVersionFromPackageJson)(tree, 'vitest');
if (!installedVitestVersion ||
installedVitestVersion === 'latest' ||
installedVitestVersion === 'beta') {
return (0, semver_1.clean)(versions_1.vitestVersion) ?? (0, semver_1.coerce)(versions_1.vitestVersion).version;
}
return ((0, semver_1.clean)(installedVitestVersion) ?? (0, semver_1.coerce)(installedVitestVersion).version);
}
function getInstalledViteVersion(tree) {
const installedViteVersion = (0, devkit_1.getDependencyVersionFromPackageJson)(tree, 'vite');
if (!installedViteVersion ||
installedViteVersion === 'latest' ||
installedViteVersion === 'beta') {
return (0, semver_1.clean)(versions_1.vitestVersion) ?? (0, semver_1.coerce)(versions_1.vitestVersion).version;
}
return (0, semver_1.clean)(installedViteVersion) ?? (0, semver_1.coerce)(installedViteVersion).version;
}
function getInstalledViteMajorVersion(tree) {
const installedViteVersion = getInstalledViteVersion(tree);
if (!installedViteVersion) {
return;
}
const installedMajor = (0, semver_1.major)(installedViteVersion);
if (installedMajor < 5 || installedMajor > 8) {
return undefined;
}
return installedMajor;
}
async function getInstalledVitestVersionFromGraph() {
const graph = await (0, devkit_1.createProjectGraphAsync)();
const vitestDep = graph.externalNodes?.['npm:vitest'];
if (!vitestDep) {
return undefined;
}
return ((0, semver_1.clean)(vitestDep.data.version) ?? (0, semver_1.coerce)(vitestDep.data.version).version);
}

28
node_modules/@nx/vitest/src/utils/versions.d.ts generated vendored Normal file
View File

@@ -0,0 +1,28 @@
export declare const nxVersion: any;
export declare const viteVersion = "^8.0.0";
export declare const viteV7Version = "^7.0.0";
export declare const viteV6Version = "^6.0.0";
export declare const viteV5Version = "^5.0.0";
export declare const vitestV4Version = "~4.1.0";
export declare const vitestV3Version = "^3.0.0";
export declare const vitestV2Version = "^2.1.8";
export declare const vitestVersion = "~4.1.0";
export declare const vitePluginReactVersion = "^6.0.0";
export declare const vitePluginReactV4Version = "^4.2.0";
export declare const vitePluginReactSwcVersion = "^4.3.0";
export declare const jsdomVersion = "^27.1.0";
export declare const vitePluginDtsVersion = "~4.5.0";
export declare const ajvVersion = "^8.0.0";
export declare const happyDomVersion = "~9.20.3";
export declare const edgeRuntimeVmVersion = "~3.0.2";
export declare const jitiVersion = "2.4.2";
export declare const analogVitestAngular = "~2.1.2";
export declare const vitestV4CoverageV8Version = "~4.1.0";
export declare const vitestV3CoverageV8Version = "^3.0.5";
export declare const vitestV2CoverageV8Version = "^2.1.8";
export declare const vitestCoverageV8Version = "~4.1.0";
export declare const vitestV4CoverageIstanbulVersion = "~4.1.0";
export declare const vitestV3CoverageIstanbulVersion = "^3.0.5";
export declare const vitestV2CoverageIstanbulVersion = "^2.1.8";
export declare const vitestCoverageIstanbulVersion = "~4.1.0";
//# sourceMappingURL=versions.d.ts.map

1
node_modules/@nx/vitest/src/utils/versions.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/versions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,KAAwC,CAAC;AAE/D,eAAO,MAAM,WAAW,WAAW,CAAC;AACpC,eAAO,MAAM,aAAa,WAAW,CAAC;AACtC,eAAO,MAAM,aAAa,WAAW,CAAC;AACtC,eAAO,MAAM,aAAa,WAAW,CAAC;AACtC,eAAO,MAAM,eAAe,WAAW,CAAC;AACxC,eAAO,MAAM,eAAe,WAAW,CAAC;AACxC,eAAO,MAAM,eAAe,WAAW,CAAC;AACxC,eAAO,MAAM,aAAa,WAAkB,CAAC;AAC7C,eAAO,MAAM,sBAAsB,WAAW,CAAC;AAC/C,eAAO,MAAM,wBAAwB,WAAW,CAAC;AACjD,eAAO,MAAM,yBAAyB,WAAW,CAAC;AAClD,eAAO,MAAM,YAAY,YAAY,CAAC;AACtC,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,UAAU,WAAW,CAAC;AACnC,eAAO,MAAM,eAAe,YAAY,CAAC;AACzC,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAG5C,eAAO,MAAM,yBAAyB,WAAW,CAAC;AAClD,eAAO,MAAM,yBAAyB,WAAW,CAAC;AAClD,eAAO,MAAM,yBAAyB,WAAW,CAAC;AAClD,eAAO,MAAM,uBAAuB,WAA4B,CAAC;AACjE,eAAO,MAAM,+BAA+B,WAAW,CAAC;AACxD,eAAO,MAAM,+BAA+B,WAAW,CAAC;AACxD,eAAO,MAAM,+BAA+B,WAAW,CAAC;AACxD,eAAO,MAAM,6BAA6B,WAAkC,CAAC"}

31
node_modules/@nx/vitest/src/utils/versions.js generated vendored Normal file
View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.vitestCoverageIstanbulVersion = exports.vitestV2CoverageIstanbulVersion = exports.vitestV3CoverageIstanbulVersion = exports.vitestV4CoverageIstanbulVersion = exports.vitestCoverageV8Version = exports.vitestV2CoverageV8Version = exports.vitestV3CoverageV8Version = exports.vitestV4CoverageV8Version = exports.analogVitestAngular = exports.jitiVersion = exports.edgeRuntimeVmVersion = exports.happyDomVersion = exports.ajvVersion = exports.vitePluginDtsVersion = exports.jsdomVersion = exports.vitePluginReactSwcVersion = exports.vitePluginReactV4Version = exports.vitePluginReactVersion = exports.vitestVersion = exports.vitestV2Version = exports.vitestV3Version = exports.vitestV4Version = exports.viteV5Version = exports.viteV6Version = exports.viteV7Version = exports.viteVersion = exports.nxVersion = void 0;
exports.nxVersion = require('../../package.json').version;
exports.viteVersion = '^8.0.0';
exports.viteV7Version = '^7.0.0';
exports.viteV6Version = '^6.0.0';
exports.viteV5Version = '^5.0.0';
exports.vitestV4Version = '~4.1.0';
exports.vitestV3Version = '^3.0.0';
exports.vitestV2Version = '^2.1.8';
exports.vitestVersion = exports.vitestV4Version;
exports.vitePluginReactVersion = '^6.0.0';
exports.vitePluginReactV4Version = '^4.2.0';
exports.vitePluginReactSwcVersion = '^4.3.0';
exports.jsdomVersion = '^27.1.0';
exports.vitePluginDtsVersion = '~4.5.0';
exports.ajvVersion = '^8.0.0';
exports.happyDomVersion = '~9.20.3';
exports.edgeRuntimeVmVersion = '~3.0.2';
exports.jitiVersion = '2.4.2';
exports.analogVitestAngular = '~2.1.2';
// Coverage providers
exports.vitestV4CoverageV8Version = '~4.1.0';
exports.vitestV3CoverageV8Version = '^3.0.5';
exports.vitestV2CoverageV8Version = '^2.1.8';
exports.vitestCoverageV8Version = exports.vitestV4CoverageV8Version;
exports.vitestV4CoverageIstanbulVersion = '~4.1.0';
exports.vitestV3CoverageIstanbulVersion = '^3.0.5';
exports.vitestV2CoverageIstanbulVersion = '^2.1.8';
exports.vitestCoverageIstanbulVersion = exports.vitestV4CoverageIstanbulVersion;

View File

@@ -0,0 +1,4 @@
import { Tree } from '@nx/devkit';
import { TargetFlags } from './generator-utils';
export declare function ensureViteConfigIsCorrect(tree: Tree, path: string, buildConfigString: string, buildConfigObject: {}, imports: string[], plugins: string[], testConfigString: string, testConfigObject: {}, cacheDir: string, projectAlreadyHasViteTargets?: TargetFlags): boolean;
//# sourceMappingURL=vite-config-edit-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"vite-config-edit-utils.d.ts","sourceRoot":"","sources":["../../../../../packages/vitest/src/utils/vite-config-edit-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,EAAE,EACrB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,MAAM,EAAE,EACjB,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,EAAE,EACpB,QAAQ,EAAE,MAAM,EAChB,4BAA4B,CAAC,EAAE,WAAW,GACzC,OAAO,CAwCT"}

View File

@@ -0,0 +1,359 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureViteConfigIsCorrect = ensureViteConfigIsCorrect;
const devkit_1 = require("@nx/devkit");
const js_1 = require("@nx/js");
function ensureViteConfigIsCorrect(tree, path, buildConfigString, buildConfigObject, imports, plugins, testConfigString, testConfigObject, cacheDir, projectAlreadyHasViteTargets) {
const fileContent = tree.read(path, 'utf-8');
let updatedContent = undefined;
if (!projectAlreadyHasViteTargets?.test && testConfigString?.length) {
updatedContent = handleBuildOrTestNode(fileContent, testConfigString, testConfigObject, 'test');
}
if (!projectAlreadyHasViteTargets?.build && buildConfigString?.length) {
updatedContent = handleBuildOrTestNode(updatedContent ?? fileContent, buildConfigString, buildConfigObject, 'build');
}
updatedContent =
handlePluginNode(updatedContent ?? fileContent, imports, plugins) ??
updatedContent;
if (cacheDir?.length) {
updatedContent = handleCacheDirNode(updatedContent ?? fileContent, cacheDir);
}
if (updatedContent) {
tree.write(path, updatedContent);
return true;
}
else {
return false;
}
}
function handleBuildOrTestNode(updatedFileContent, configContentString, configContentObject, name) {
const { tsquery } = require('@phenomnomnominal/tsquery');
const buildOrTestNode = tsquery.query(updatedFileContent, `PropertyAssignment:has(Identifier[name="${name}"])`);
if (buildOrTestNode.length) {
return tsquery.replace(updatedFileContent, `PropertyAssignment:has(Identifier[name="${name}"])`, (node) => {
const existingProperties = tsquery.query(node.initializer, 'PropertyAssignment');
let updatedPropsString = '';
for (const prop of existingProperties) {
const propName = prop.name.getText();
if (!configContentObject[propName] &&
propName !== 'dir' &&
propName !== 'reportsDirectory' &&
propName !== 'provider') {
// NOTE: Watch for formatting.
updatedPropsString += ` '${propName}': ${prop.initializer.getText()},\n`;
}
}
for (const [propName, propValue] of Object.entries(configContentObject)) {
// NOTE: Watch for formatting.
if (propName === 'coverage') {
let propString = ` '${propName}': {\n`;
for (const [pName, pValue] of Object.entries(propValue)) {
if (pName === 'provider') {
propString += ` '${pName}': ${pValue} as const,\n`;
}
else {
propString += ` '${pName}': '${pValue}',\n`;
}
}
propString += `}`;
updatedPropsString += `${propString}\n`;
}
else if (propName === 'lib') {
let propString = ` '${propName}': {\n`;
for (const [pName, pValue] of Object.entries(propValue)) {
if (pName === 'formats') {
propString += ` '${pName}': [${pValue
.map((format) => `'${format}' as const`)
.join(', ')}],\n`;
}
else {
propString += ` '${pName}': ${JSON.stringify(pValue)},\n`;
}
}
propString += ` },`;
updatedPropsString += `${propString}\n`;
}
else {
updatedPropsString += ` '${propName}': ${JSON.stringify(propValue)},\n`;
}
}
return `${name}: {
${updatedPropsString} }`;
});
}
else {
const foundDefineConfig = tsquery.query(updatedFileContent, 'CallExpression:has(Identifier[name="defineConfig"])');
if (foundDefineConfig.length) {
const conditionalConfig = tsquery.query(foundDefineConfig[0], 'ArrowFunction');
if (conditionalConfig.length) {
if (name === 'build') {
return transformConditionalConfig(conditionalConfig, updatedFileContent, configContentString);
}
else {
// no test config in conditional config
return updatedFileContent;
}
}
else {
const propertyAssignments = tsquery.query(foundDefineConfig[0], 'PropertyAssignment');
if (propertyAssignments.length) {
return (0, devkit_1.applyChangesToString)(updatedFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: propertyAssignments[0].getStart(),
text: configContentString,
},
]);
}
else {
return (0, devkit_1.applyChangesToString)(updatedFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: foundDefineConfig[0].getStart() + 14,
text: configContentString,
},
]);
}
}
}
else {
// build config does not exist and defineConfig is not used
// could also potentially be invalid syntax, so try-catch
try {
const defaultExport = tsquery.query(updatedFileContent, 'ExportAssignment');
const found = tsquery.query(defaultExport?.[0], 'ObjectLiteralExpression');
const startOfObject = found?.[0].getStart();
return (0, devkit_1.applyChangesToString)(updatedFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: startOfObject + 1,
text: configContentString,
},
]);
}
catch {
return updatedFileContent;
}
}
}
}
function transformCurrentBuildObject(index, returnStatements, appFileContent, buildConfigObject) {
if (!returnStatements?.[index]) {
return undefined;
}
const { tsquery } = require('@phenomnomnominal/tsquery');
const currentBuildObject = tsquery
.query(returnStatements[index], 'ObjectLiteralExpression')?.[0]
.getText();
const currentBuildObjectStart = returnStatements[index].getStart();
const currentBuildObjectEnd = returnStatements[index].getEnd();
const newReturnObject = tsquery.replace(returnStatements[index].getText(), 'ObjectLiteralExpression', (_node) => {
return `{
...${currentBuildObject},
...${JSON.stringify(buildConfigObject)}
}`;
});
const newContents = (0, devkit_1.applyChangesToString)(appFileContent, [
{
type: devkit_1.ChangeType.Delete,
start: currentBuildObjectStart,
length: currentBuildObjectEnd - currentBuildObjectStart,
},
{
type: devkit_1.ChangeType.Insert,
index: currentBuildObjectStart,
text: newReturnObject,
},
]);
return newContents;
}
function transformConditionalConfig(conditionalConfig, appFileContent, buildConfigObject) {
const { tsquery } = require('@phenomnomnominal/tsquery');
const { SyntaxKind } = require('typescript');
const functionBlock = tsquery.query(conditionalConfig[0], 'Block');
const ifStatement = tsquery.query(functionBlock?.[0], 'IfStatement');
const binaryExpressions = tsquery.query(ifStatement?.[0], 'BinaryExpression');
const buildExists = binaryExpressions?.find((binaryExpression) => binaryExpression.getText() === `command === 'build'`);
const buildExistsExpressionIndex = binaryExpressions?.findIndex((binaryExpression) => binaryExpression.getText() === `command === 'build'`);
const serveExists = binaryExpressions?.find((binaryExpression) => binaryExpression.getText() === `command === 'serve'`);
const elseKeywordExists = (0, js_1.findNodes)(ifStatement?.[0], SyntaxKind.ElseKeyword);
const returnStatements = tsquery.query(ifStatement[0], 'ReturnStatement');
if (!buildExists) {
if (serveExists && elseKeywordExists) {
// build options live inside the else block
return (transformCurrentBuildObject(returnStatements?.length - 1, returnStatements, appFileContent, buildConfigObject) ?? appFileContent);
}
else {
// no build options exist yet
const functionBlockStart = functionBlock?.[0].getStart();
const newContents = (0, devkit_1.applyChangesToString)(appFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: functionBlockStart + 1,
text: `
if (command === 'build') {
return ${JSON.stringify(buildConfigObject)}
}
`,
},
]);
return newContents;
}
}
else {
// build already exists
// it will be the return statement which lives
// at the buildExistsExpressionIndex
return (transformCurrentBuildObject(buildExistsExpressionIndex, returnStatements, appFileContent, buildConfigObject) ?? appFileContent);
}
}
function handlePluginNode(appFileContent, imports, plugins) {
const { tsquery } = require('@phenomnomnominal/tsquery');
const file = tsquery.ast(appFileContent);
const pluginsNode = tsquery.query(file, 'PropertyAssignment:has(Identifier[name="plugins"])');
let writeFile = false;
if (pluginsNode.length) {
appFileContent = tsquery.replace(file.getText(), 'PropertyAssignment:has(Identifier[name="plugins"])', (node) => {
const found = tsquery.query(node, 'ArrayLiteralExpression');
let updatedPluginsString = '';
const existingPluginNodes = found?.[0].elements ?? [];
for (const plugin of existingPluginNodes) {
updatedPluginsString += `${plugin.getText()}, `;
}
for (const plugin of plugins) {
if (!existingPluginNodes?.some((node) => node.getText().includes(plugin))) {
updatedPluginsString += `${plugin}, `;
}
}
return `plugins: [${updatedPluginsString}]`;
});
writeFile = true;
}
else {
// Plugins node does not exist yet
// So make one from scratch
const foundDefineConfig = tsquery.query(file, 'CallExpression:has(Identifier[name="defineConfig"])');
if (foundDefineConfig.length) {
const conditionalConfig = tsquery.query(foundDefineConfig[0], 'ArrowFunction');
if (conditionalConfig.length) {
// We are NOT transforming the conditional config
// with plugins
writeFile = false;
}
else {
const propertyAssignments = tsquery.query(foundDefineConfig[0], 'PropertyAssignment');
if (propertyAssignments.length) {
appFileContent = (0, devkit_1.applyChangesToString)(appFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: propertyAssignments[0].getStart(),
text: `plugins: [${plugins.join(', ')}],`,
},
]);
writeFile = true;
}
else {
appFileContent = (0, devkit_1.applyChangesToString)(appFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: foundDefineConfig[0].getStart() + 14,
text: `plugins: [${plugins.join(', ')}],`,
},
]);
writeFile = true;
}
}
}
else {
// Plugins option does not exist and defineConfig is not used
// could also potentially be invalid syntax, so try-catch
try {
const defaultExport = tsquery.query(file, 'ExportAssignment');
const found = tsquery?.query(defaultExport?.[0], 'ObjectLiteralExpression');
const startOfObject = found?.[0].getStart();
appFileContent = (0, devkit_1.applyChangesToString)(appFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: startOfObject + 1,
text: `plugins: [${plugins.join(', ')}],`,
},
]);
writeFile = true;
}
catch {
writeFile = false;
}
}
}
if (writeFile) {
const filteredImports = filterImport(appFileContent, imports);
return filteredImports.join(';\n') + '\n' + appFileContent;
}
}
function filterImport(appFileContent, imports) {
const { tsquery } = require('@phenomnomnominal/tsquery');
const file = tsquery.ast(appFileContent);
const importNodes = tsquery.query(file, ':matches(ImportDeclaration, VariableStatement)');
const importsArrayExisting = importNodes?.map((node) => {
return node.getText().slice(0, -1);
});
return imports.filter((importString) => {
return !importsArrayExisting?.includes(importString);
});
}
function handleCacheDirNode(appFileContent, cacheDir) {
const { tsquery } = require('@phenomnomnominal/tsquery');
const file = tsquery.ast(appFileContent);
const cacheDirNode = tsquery.query(file, 'PropertyAssignment:has(Identifier[name="cacheDir"])');
if (!cacheDirNode?.length || cacheDirNode?.length === 0) {
// cacheDir node does not exist yet
// So make one from scratch
const foundDefineConfig = tsquery.query(file, 'CallExpression:has(Identifier[name="defineConfig"])');
if (foundDefineConfig.length) {
const conditionalConfig = tsquery.query(foundDefineConfig[0], 'ArrowFunction');
if (conditionalConfig.length) {
// We are NOT transforming the conditional config
// with cacheDir
}
else {
const propertyAssignments = tsquery.query(foundDefineConfig[0], 'PropertyAssignment');
if (propertyAssignments.length) {
appFileContent = (0, devkit_1.applyChangesToString)(appFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: propertyAssignments[0].getStart(),
text: cacheDir,
},
]);
}
else {
appFileContent = (0, devkit_1.applyChangesToString)(appFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: foundDefineConfig[0].getStart() + 14,
text: cacheDir,
},
]);
}
}
}
else {
// cacheDir option does not exist and defineConfig is not used
// could also potentially be invalid syntax, so try-catch
try {
const defaultExport = tsquery.query(file, 'ExportAssignment');
const found = tsquery?.query(defaultExport?.[0], 'ObjectLiteralExpression');
const startOfObject = found?.[0].getStart();
appFileContent = (0, devkit_1.applyChangesToString)(appFileContent, [
{
type: devkit_1.ChangeType.Insert,
index: startOfObject + 1,
text: cacheDir,
},
]);
}
catch { }
}
}
return appFileContent;
}