@nx/vitest 23.2.0-rc.0 → 23.2.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/plugins/plugin.js +110 -5
- package/package.json +5 -5
|
@@ -157,14 +157,70 @@ async function buildVitestTargets(configFilePath, projectRoot, options, context,
|
|
|
157
157
|
// Resolve under `mode: 'test'` to match Vitest, which defaults the Vite
|
|
158
158
|
// mode to 'test'; a config that branches on `command`/`mode` would
|
|
159
159
|
// otherwise enumerate a different spec set here than at test time.
|
|
160
|
+
// Capture the raw root after user hooks: graph construction and the
|
|
161
|
+
// atom run resolve it against different cwds.
|
|
162
|
+
let configuredViteRoot;
|
|
160
163
|
const viteServeConfig = await resolveConfig({
|
|
161
164
|
configFile: absoluteConfigFilePath,
|
|
162
165
|
mode: 'test',
|
|
166
|
+
plugins: [
|
|
167
|
+
{
|
|
168
|
+
// Promotes test.root before user hooks as Vitest does, so a
|
|
169
|
+
// later hook can override it. No options.root: atoms pass no --root.
|
|
170
|
+
name: 'nx-promote-vitest-root',
|
|
171
|
+
enforce: 'pre',
|
|
172
|
+
config(config) {
|
|
173
|
+
if (config.test?.root) {
|
|
174
|
+
return { root: config.test.root };
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'nx-capture-vitest-root',
|
|
180
|
+
enforce: 'post',
|
|
181
|
+
config: {
|
|
182
|
+
order: 'post',
|
|
183
|
+
handler(config) {
|
|
184
|
+
configuredViteRoot = config.root;
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
],
|
|
163
189
|
}, 'serve');
|
|
164
190
|
const projectRootRelativeTestPaths = await getTestPathsRelativeToProjectRoot(projectRoot, context.workspaceRoot,
|
|
165
191
|
// Only the glob path reads this config; the opt-out path forces the
|
|
166
192
|
// runtime by receiving no config, and takes `test.dir` separately.
|
|
167
193
|
useGlobDiscovery ? viteServeConfig : undefined, viteServeConfig.test?.dir);
|
|
194
|
+
// Each atom writes coverage to its own directory or the reports would
|
|
195
|
+
// overwrite one another. The nested flag never enables coverage.
|
|
196
|
+
const coverageReportsDirectory = viteServeConfig.test?.coverage?.reportsDirectory || 'coverage';
|
|
197
|
+
// Atom directories mirror spec paths. A shared base outside the project
|
|
198
|
+
// gets a project-root prefix unless it already ends with it, as the
|
|
199
|
+
// generated `<offset>/coverage/<projectRoot>` configs do.
|
|
200
|
+
const fullProjectRoot = (0, node_path_1.resolve)(context.workspaceRoot, projectRoot);
|
|
201
|
+
// A relative reportsDirectory resolves against the Vitest root, mapped
|
|
202
|
+
// here as the atom run would (cwd is the project root). A root computed
|
|
203
|
+
// from runtime state such as `process.cwd()` cannot be mapped and is
|
|
204
|
+
// not supported.
|
|
205
|
+
const effectiveVitestRoot = !configuredViteRoot
|
|
206
|
+
? fullProjectRoot
|
|
207
|
+
: (0, node_path_1.isAbsolute)(configuredViteRoot)
|
|
208
|
+
? configuredViteRoot
|
|
209
|
+
: (0, node_path_1.resolve)(fullProjectRoot, configuredViteRoot);
|
|
210
|
+
const resolvedReportsDirectory = (0, node_path_1.resolve)(effectiveVitestRoot, coverageReportsDirectory);
|
|
211
|
+
// Vitest refuses a reports directory equal to its root or cwd, and redirecting
|
|
212
|
+
// under it would point each atom at its own spec path for coverage to delete.
|
|
213
|
+
const vitestRejectsReportsDirectory = (0, node_path_1.relative)(effectiveVitestRoot, resolvedReportsDirectory) === '' ||
|
|
214
|
+
(0, node_path_1.relative)(fullProjectRoot, resolvedReportsDirectory) === '';
|
|
215
|
+
const atomSubfolderPrefix = isPathOutside((0, node_path_1.relative)(fullProjectRoot, resolvedReportsDirectory)) &&
|
|
216
|
+
!endsWithProjectRoot(resolvedReportsDirectory, projectRoot)
|
|
217
|
+
? projectRoot
|
|
218
|
+
: '';
|
|
219
|
+
// The cache only accepts outputs inside the workspace. A base outside it
|
|
220
|
+
// cannot be declared, and a cache hit would then replay without writing
|
|
221
|
+
// coverage, so the atoms and their parent are not cached either.
|
|
222
|
+
const isCoverageCacheable = !isPathOutside((0, node_path_1.relative)(context.workspaceRoot, resolvedReportsDirectory));
|
|
223
|
+
const atomOutputs = [];
|
|
168
224
|
for (const relativePath of projectRootRelativeTestPaths) {
|
|
169
225
|
if (relativePath.includes('../')) {
|
|
170
226
|
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' +
|
|
@@ -175,14 +231,28 @@ async function buildVitestTargets(configFilePath, projectRoot, options, context,
|
|
|
175
231
|
context,
|
|
176
232
|
}, null, 2)}`);
|
|
177
233
|
}
|
|
234
|
+
const outputSubfolder = atomSubfolderPrefix
|
|
235
|
+
? (0, devkit_1.joinPathFragments)(atomSubfolderPrefix, relativePath)
|
|
236
|
+
: relativePath;
|
|
237
|
+
// joinPathFragments strips Windows drive letters, so an absolute
|
|
238
|
+
// reports directory must be joined with the OS path helper.
|
|
239
|
+
const atomCoverageDirectory = (0, node_path_1.isAbsolute)(coverageReportsDirectory)
|
|
240
|
+
? (0, node_path_1.join)(coverageReportsDirectory, outputSubfolder)
|
|
241
|
+
: (0, devkit_1.joinPathFragments)(coverageReportsDirectory, outputSubfolder);
|
|
178
242
|
const targetName = `${options.ciTargetName}--${relativePath}`;
|
|
179
243
|
dependsOn.push(targetName);
|
|
180
244
|
targets[targetName] = {
|
|
181
245
|
// It does not make sense to run atomized tests in watch mode as they are intended to be run in CI
|
|
182
|
-
command: `vitest run ${relativePath}
|
|
183
|
-
|
|
246
|
+
command: `vitest run ${(0, internal_1.quoteShellArg)(relativePath)}${vitestRejectsReportsDirectory
|
|
247
|
+
? ''
|
|
248
|
+
: ` --coverage.reportsDirectory=${(0, internal_1.quoteShellArg)(atomCoverageDirectory)}`}`,
|
|
249
|
+
cache: isCoverageCacheable && targets[options.testTargetName].cache,
|
|
184
250
|
inputs: targets[options.testTargetName].inputs,
|
|
185
|
-
outputs:
|
|
251
|
+
outputs: isCoverageCacheable && !vitestRejectsReportsDirectory
|
|
252
|
+
? [
|
|
253
|
+
normalizeAtomOutputPath((0, node_path_1.join)(resolvedReportsDirectory, outputSubfolder), fullProjectRoot, context.workspaceRoot),
|
|
254
|
+
]
|
|
255
|
+
: [],
|
|
186
256
|
options: {
|
|
187
257
|
cwd: projectRoot,
|
|
188
258
|
env: targets[options.testTargetName].options.env,
|
|
@@ -200,14 +270,17 @@ async function buildVitestTargets(configFilePath, projectRoot, options, context,
|
|
|
200
270
|
},
|
|
201
271
|
},
|
|
202
272
|
};
|
|
273
|
+
atomOutputs.push(...targets[targetName].outputs);
|
|
203
274
|
targetGroup.push(targetName);
|
|
204
275
|
}
|
|
205
276
|
if (targetGroup.length > 0) {
|
|
206
277
|
targets[options.ciTargetName] = {
|
|
207
278
|
executor: 'nx:noop',
|
|
208
|
-
cache:
|
|
279
|
+
cache: isCoverageCacheable,
|
|
209
280
|
inputs: targets[options.testTargetName].inputs,
|
|
210
|
-
|
|
281
|
+
// Exactly the atom directories: a cache restore replaces each declared
|
|
282
|
+
// directory, and any wider one can hold other projects' coverage.
|
|
283
|
+
outputs: atomOutputs,
|
|
211
284
|
dependsOn,
|
|
212
285
|
metadata: {
|
|
213
286
|
technologies: ['vitest'],
|
|
@@ -292,6 +365,38 @@ function getOutputs(viteBuildConfig, projectRoot, workspaceRoot) {
|
|
|
292
365
|
hasTest: !!test,
|
|
293
366
|
};
|
|
294
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Whether a `relative()` result leaves the base directory. A directory named
|
|
370
|
+
* `..coverage` is a child, not traversal.
|
|
371
|
+
*/
|
|
372
|
+
function isPathOutside(relativePath) {
|
|
373
|
+
return (relativePath === '..' ||
|
|
374
|
+
relativePath.startsWith(`..${node_path_1.sep}`) ||
|
|
375
|
+
(0, node_path_1.isAbsolute)(relativePath));
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Maps an atom's absolute coverage directory, which must be inside the
|
|
379
|
+
* workspace, to the narrowest Nx root token.
|
|
380
|
+
*/
|
|
381
|
+
function normalizeAtomOutputPath(absoluteOutputPath, fullProjectRoot, workspaceRoot) {
|
|
382
|
+
const relativeToProject = (0, node_path_1.relative)(fullProjectRoot, absoluteOutputPath);
|
|
383
|
+
if (!isPathOutside(relativeToProject)) {
|
|
384
|
+
return (0, devkit_1.joinPathFragments)('{projectRoot}', relativeToProject);
|
|
385
|
+
}
|
|
386
|
+
return (0, devkit_1.joinPathFragments)('{workspaceRoot}', (0, node_path_1.relative)(workspaceRoot, absoluteOutputPath));
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Whether the reports directory ends with the project root's segments. The
|
|
390
|
+
* root project has none, so it always holds.
|
|
391
|
+
*/
|
|
392
|
+
function endsWithProjectRoot(resolvedPath, projectRoot) {
|
|
393
|
+
const pathSegments = resolvedPath.split(/[\\/]/).filter(Boolean);
|
|
394
|
+
const rootSegments = projectRoot
|
|
395
|
+
.split('/')
|
|
396
|
+
.filter((segment) => segment && segment !== '.');
|
|
397
|
+
return (pathSegments.length >= rootSegments.length &&
|
|
398
|
+
rootSegments.every((segment, i) => pathSegments[pathSegments.length - rootSegments.length + i] === segment));
|
|
399
|
+
}
|
|
295
400
|
function normalizeOutputPath(outputPath, projectRoot, workspaceRoot, path) {
|
|
296
401
|
if (!outputPath) {
|
|
297
402
|
if (projectRoot === '.') {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/vitest",
|
|
3
3
|
"description": "The Nx Plugin for Vitest to enable fast unit testing with Vitest.",
|
|
4
|
-
"version": "23.2.0-rc.
|
|
4
|
+
"version": "23.2.0-rc.2",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
@@ -81,13 +81,13 @@
|
|
|
81
81
|
"tslib": "^2.3.0",
|
|
82
82
|
"semver": "^7.6.3",
|
|
83
83
|
"@phenomnomnominal/tsquery": "~6.2.0",
|
|
84
|
-
"@nx/devkit": "23.2.0-rc.
|
|
85
|
-
"@nx/js": "23.2.0-rc.
|
|
84
|
+
"@nx/devkit": "23.2.0-rc.2",
|
|
85
|
+
"@nx/js": "23.2.0-rc.2"
|
|
86
86
|
},
|
|
87
87
|
"peerDependencies": {
|
|
88
88
|
"vitest": "^3.0.0 || ^4.0.0",
|
|
89
89
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
|
90
|
-
"@nx/eslint": "23.2.0-rc.
|
|
90
|
+
"@nx/eslint": "23.2.0-rc.2"
|
|
91
91
|
},
|
|
92
92
|
"peerDependenciesMeta": {
|
|
93
93
|
"@nx/eslint": {
|
|
@@ -101,6 +101,6 @@
|
|
|
101
101
|
}
|
|
102
102
|
},
|
|
103
103
|
"devDependencies": {
|
|
104
|
-
"nx": "23.2.0-rc.
|
|
104
|
+
"nx": "23.2.0-rc.2"
|
|
105
105
|
}
|
|
106
106
|
}
|