@nx/vitest 22.7.0-beta.12 → 22.7.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json 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": "22.7.0-beta.12",
4
+ "version": "22.7.0-beta.14",
5
5
  "type": "commonjs",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -52,8 +52,8 @@
52
52
  "executors": "./executors.json",
53
53
  "generators": "./generators.json",
54
54
  "dependencies": {
55
- "@nx/devkit": "22.7.0-beta.12",
56
- "@nx/js": "22.7.0-beta.12",
55
+ "@nx/devkit": "22.7.0-beta.14",
56
+ "@nx/js": "22.7.0-beta.14",
57
57
  "tslib": "^2.3.0",
58
58
  "semver": "^7.6.3",
59
59
  "@phenomnomnominal/tsquery": "~6.1.4"
@@ -71,6 +71,6 @@
71
71
  }
72
72
  },
73
73
  "devDependencies": {
74
- "nx": "22.7.0-beta.12"
74
+ "nx": "22.7.0-beta.14"
75
75
  }
76
76
  }
@@ -1 +1 @@
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;AAWpB,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,CAmF1D,CAAC;AAEF,eAAO,MAAM,aAAa,oCAAc,CAAC"}
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"}
@@ -38,6 +38,7 @@ const devkit_1 = require("@nx/devkit");
38
38
  const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
39
39
  const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
40
40
  const js_1 = require("@nx/js");
41
+ const internal_1 = require("@nx/js/src/internal");
41
42
  const node_fs_1 = require("node:fs");
42
43
  const node_path_1 = require("node:path");
43
44
  const file_hasher_1 = require("nx/src/hasher/file-hasher");
@@ -80,7 +81,11 @@ exports.createNodes = [
80
81
  configFiles: [],
81
82
  });
82
83
  const lockfile = (0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot));
83
- const hashes = await (0, calculate_hash_for_create_nodes_1.calculateHashesForCreateNodes)(projectRoots, normalizedOptions, context, projectRoots.map((r) => [lockfile]));
84
+ const tsconfigChainsByProjectRoot = collectTsconfigInputsByProjectRoot(projectRoots, context.workspaceRoot);
85
+ const hashes = await (0, calculate_hash_for_create_nodes_1.calculateHashesForCreateNodes)(projectRoots, normalizedOptions, context, projectRoots.map((root) => [
86
+ lockfile,
87
+ ...(tsconfigChainsByProjectRoot.get(root) ?? []),
88
+ ]));
84
89
  try {
85
90
  return await (0, devkit_1.createNodesFromFiles)(async (configFile, _, context, idx) => {
86
91
  const projectRoot = (0, node_path_1.dirname)(configFile);
@@ -90,7 +95,7 @@ exports.createNodes = [
90
95
  // for different config files.
91
96
  const hash = hashes[idx] + configFile;
92
97
  const { projectType, metadata, targets } = (targetsCache[hash] ??=
93
- await buildVitestTargets(configFile, projectRoot, normalizedOptions, context, pmc));
98
+ await buildVitestTargets(configFile, projectRoot, normalizedOptions, context, pmc, tsconfigChainsByProjectRoot.get(projectRoot) ?? []));
94
99
  const project = {
95
100
  root: projectRoot,
96
101
  targets,
@@ -110,7 +115,7 @@ exports.createNodes = [
110
115
  },
111
116
  ];
112
117
  exports.createNodesV2 = exports.createNodes;
113
- async function buildVitestTargets(configFilePath, projectRoot, options, context, pmc) {
118
+ async function buildVitestTargets(configFilePath, projectRoot, options, context, pmc, tsconfigInputs) {
114
119
  const absoluteConfigFilePath = (0, devkit_1.joinPathFragments)(context.workspaceRoot, configFilePath);
115
120
  // Workaround for the `build$3 is not a function` error that we sometimes see in agents.
116
121
  // This should be removed later once we address the issue properly
@@ -165,7 +170,9 @@ async function buildVitestTargets(configFilePath, projectRoot, options, context,
165
170
  const targets = {};
166
171
  // if file is vitest.config or vite.config has definition for test, create targets for test and/or atomized tests
167
172
  if (configFilePath.includes('vitest.config') || hasTest) {
168
- targets[options.testTargetName] = await testTarget(namedInputs, testOutputs, projectRoot, options.testMode, pmc);
173
+ const isTypecheckEnabled = !!viteBuildConfig?.test?.typecheck
174
+ ?.enabled;
175
+ targets[options.testTargetName] = await testTarget(namedInputs, testOutputs, projectRoot, options.testMode, pmc, isTypecheckEnabled, tsconfigInputs);
169
176
  if (options.ciTargetName) {
170
177
  const groupName = options.ciGroupName ?? (0, plugins_1.deriveGroupNameFromTarget)(options.ciTargetName);
171
178
  const targetGroup = [];
@@ -240,8 +247,9 @@ async function buildVitestTargets(configFilePath, projectRoot, options, context,
240
247
  }
241
248
  return { targets, metadata, projectType: 'library' };
242
249
  }
243
- async function testTarget(namedInputs, outputs, projectRoot, testMode = 'watch', pmc) {
250
+ async function testTarget(namedInputs, outputs, projectRoot, testMode = 'watch', pmc, isTypecheckEnabled, tsconfigInputs) {
244
251
  const command = testMode === 'run' ? 'vitest run' : 'vitest';
252
+ const depOutputsGlob = isTypecheckEnabled ? '**/*.{js,d.ts}' : '**/*.js';
245
253
  return {
246
254
  command,
247
255
  options: { cwd: (0, devkit_1.joinPathFragments)(projectRoot) },
@@ -250,10 +258,15 @@ async function testTarget(namedInputs, outputs, projectRoot, testMode = 'watch',
250
258
  ...('production' in namedInputs
251
259
  ? ['default', '^production']
252
260
  : ['default', '^default']),
261
+ ...tsconfigInputs.map((f) => ({
262
+ json: `{workspaceRoot}/${f}`,
263
+ fields: ['compilerOptions'],
264
+ })),
253
265
  {
254
266
  externalDependencies: ['vitest'],
255
267
  },
256
268
  { env: 'CI' },
269
+ { dependentTasksOutputFiles: depOutputsGlob, transitive: true },
257
270
  ],
258
271
  outputs,
259
272
  metadata: {
@@ -308,6 +321,90 @@ function normalizeOptions(options) {
308
321
  options.testMode ??= 'watch';
309
322
  return options;
310
323
  }
324
+ /**
325
+ * Collects tsconfig files that Vite's esbuild-based config bundler reads
326
+ * but are outside the project root (and thus not covered by `default`).
327
+ *
328
+ * Vite < 8 uses esbuild's Build API to bundle config files. esbuild walks
329
+ * UP from the entry point, reading and parsing every `tsconfig.json` in
330
+ * every ancestor directory plus their `extends` chains. Vite >= 8 uses
331
+ * rolldown with `tsconfig: false`, but pnpm can resolve different Vite
332
+ * versions per project, so we always collect — the walk is cheap (cached
333
+ * JSON reads) and over-declaring inputs for Vite 8 projects is harmless.
334
+ *
335
+ * Files already handled elsewhere are excluded:
336
+ * - Inside the project root → covered by `default` (`{projectRoot}/**\/*`)
337
+ * - The root tsconfig (tsconfig.base.json or tsconfig.json) → covered by
338
+ * the native TsConfiguration hash instruction
339
+ * - Inside node_modules → invalidated via lockfile
340
+ * - Outside the workspace → cannot be expressed as inputs
341
+ */
342
+ function collectTsconfigInputsByProjectRoot(projectRoots, workspaceRoot) {
343
+ const jsonCache = new Map();
344
+ const result = new Map();
345
+ const rootTsConfigName = (0, js_1.getRootTsConfigFileName)();
346
+ for (const projectRoot of projectRoots) {
347
+ if (projectRoot === '.')
348
+ continue;
349
+ const outside = [];
350
+ const seen = new Set();
351
+ const projectPrefix = `${projectRoot}/`;
352
+ const collect = (absolutePath) => {
353
+ const wsRelative = (0, node_path_1.relative)(workspaceRoot, absolutePath)
354
+ .split(node_path_1.sep)
355
+ .join('/');
356
+ if (seen.has(wsRelative))
357
+ return;
358
+ seen.add(wsRelative);
359
+ if (wsRelative.startsWith('../') || wsRelative === '..')
360
+ return;
361
+ if (wsRelative.startsWith('node_modules/') ||
362
+ wsRelative.includes('/node_modules/'))
363
+ return;
364
+ if (wsRelative === projectRoot || wsRelative.startsWith(projectPrefix))
365
+ return;
366
+ if (wsRelative === rootTsConfigName)
367
+ return;
368
+ outside.push(wsRelative);
369
+ };
370
+ // 1. Walk the project tsconfig's extends chain
371
+ const projectTsconfig = (0, node_path_1.join)(workspaceRoot, projectRoot, 'tsconfig.json');
372
+ if ((0, node_fs_1.existsSync)(projectTsconfig)) {
373
+ (0, internal_1.walkTsconfigExtendsChain)(projectTsconfig, (absPath) => {
374
+ collect(absPath);
375
+ return 'continue';
376
+ }, { jsonCache });
377
+ }
378
+ // 2. Walk UP ancestor directories (esbuild reads every tsconfig.json
379
+ // between the entry point and the filesystem root)
380
+ let dir = (0, node_path_1.dirname)(projectRoot);
381
+ while (dir && dir !== '.') {
382
+ const ancestorTsconfig = (0, node_path_1.join)(workspaceRoot, dir, 'tsconfig.json');
383
+ if ((0, node_fs_1.existsSync)(ancestorTsconfig)) {
384
+ (0, internal_1.walkTsconfigExtendsChain)(ancestorTsconfig, (absPath) => {
385
+ collect(absPath);
386
+ return 'continue';
387
+ }, { jsonCache });
388
+ }
389
+ const parent = (0, node_path_1.dirname)(dir);
390
+ if (parent === dir)
391
+ break;
392
+ dir = parent;
393
+ }
394
+ // 3. Check the workspace root itself (dirname loop above stops at '.')
395
+ const rootTsconfig = (0, node_path_1.join)(workspaceRoot, 'tsconfig.json');
396
+ if ((0, node_fs_1.existsSync)(rootTsconfig)) {
397
+ (0, internal_1.walkTsconfigExtendsChain)(rootTsconfig, (absPath) => {
398
+ collect(absPath);
399
+ return 'continue';
400
+ }, { jsonCache });
401
+ }
402
+ if (outside.length > 0) {
403
+ result.set(projectRoot, outside);
404
+ }
405
+ }
406
+ return result;
407
+ }
311
408
  function checkIfConfigFileShouldBeProject(projectRoot, context) {
312
409
  // Do not create a project if package.json and project.json isn't there.
313
410
  const siblingFiles = (0, node_fs_1.readdirSync)((0, node_path_1.join)(context.workspaceRoot, projectRoot));