@nx/vite 23.1.0 → 23.2.0-canary.20260717-dc22f7e

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.
@@ -117,9 +117,13 @@ async function getServerExtraArgs(options, configuration, buildOptionsFromBuildT
117
117
  }
118
118
  }
119
119
  if (configuration) {
120
+ const normalizeWatchOption = (watch) => {
121
+ return watch === false ? undefined : watch;
122
+ };
120
123
  serverOptions = {
121
124
  ...serverOptions,
122
- watch: buildOptionsFromBuildTarget?.watch ?? serverOptions?.watch,
125
+ watch: normalizeWatchOption(buildOptionsFromBuildTarget?.watch) ??
126
+ normalizeWatchOption(serverOptions?.watch),
123
127
  };
124
128
  otherOptions = {
125
129
  ...otherOptions,
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkDependenciesInstalled = checkDependenciesInstalled;
4
4
  exports.moveToDevDependencies = moveToDevDependencies;
5
5
  const devkit_1 = require("@nx/devkit");
6
- const internal_1 = require("@nx/js/internal");
6
+ const internal_1 = require("@nx/devkit/internal");
7
+ const internal_2 = require("@nx/js/internal");
7
8
  const semver_1 = require("semver");
8
9
  const versions_1 = require("../../../utils/versions");
9
10
  const version_utils_1 = require("../../../utils/version-utils");
@@ -13,7 +14,7 @@ function hasIncompatibleInstalledEsbuild(host) {
13
14
  return false;
14
15
  }
15
16
  try {
16
- return !(0, semver_1.intersects)(installedEsbuildVersion, internal_1.esbuildVersion, {
17
+ return !(0, semver_1.intersects)(installedEsbuildVersion, internal_2.esbuildVersion, {
17
18
  includePrerelease: true,
18
19
  });
19
20
  }
@@ -35,7 +36,7 @@ async function checkDependenciesInstalled(host, schema) {
35
36
  title: 'Installed esbuild is incompatible with Vite 8. Using Vite 7.',
36
37
  bodyLines: [
37
38
  `Found esbuild version "${installedEsbuildVersion}" in the workspace root package.json.`,
38
- `Update esbuild to a range compatible with ${internal_1.esbuildVersion} if you want newly generated Vite projects to use Vite 8 by default.`,
39
+ `Update esbuild to a range compatible with ${internal_2.esbuildVersion} if you want newly generated Vite projects to use Vite 8 by default.`,
39
40
  ],
40
41
  });
41
42
  }
@@ -52,6 +53,11 @@ async function checkDependenciesInstalled(host, schema) {
52
53
  : installedMajor === 5
53
54
  ? versions_1.viteV5Version
54
55
  : versions_1.viteVersion;
56
+ // vite pulls in esbuild, whose install script only validates the prebuilt
57
+ // binary shipped via optional dependencies, so skip it.
58
+ (0, internal_1.acknowledgeBuildScripts)(host, (0, devkit_1.detectPackageManager)(host.root), {
59
+ esbuild: false,
60
+ });
55
61
  return (0, devkit_1.addDependenciesToPackageJson)(host, {}, {
56
62
  '@nx/vite': versions_1.nxVersion,
57
63
  '@nx/web': versions_1.nxVersion,
@@ -201,7 +201,7 @@ export default defineConfig({
201
201
  - [ ] Inline any external `vitest.workspace.*` content into `test.projects` and delete the workspace file (external file references are no longer supported). The pre-pass already inlined workspace files containing a plain static array of glob strings; the ones you see in `<advisory_context>` were skipped because of dynamic content or because the sibling config could not be merged into mechanically (existing `test.projects`/`test.workspace`, non-object-literal config, or a directory with only a `vite.config.*`). For an existing `test.projects`, merge the workspace entries into it; for a `vite.config.*`-only directory, decide whether its `test` block (if any) and the project list belong in that file or a new `vitest.config.*`.
202
202
  - [ ] If projects need different pool/environment options, set them inside each project entry rather than via the (now-removed) `poolMatchGlobs` / `environmentMatchGlobs` — see section 1.5.
203
203
 
204
- When you inline a workspace file yourself, also apply these two checks (the pre-pass applies them for the files it inlines):
204
+ When you inline a workspace file yourself, also apply these checks (the pre-pass applies them for the files it inlines):
205
205
 
206
206
  - [ ] **Config-less packages**: a root config with `test.projects` breaks `vitest` invocations from package directories that have no `vite.config.*`/`vitest.config.*` of their own (common for packages with a package.json `"test": "vitest run"` script). Vitest 4 finds the root config by walking up from the package directory, but resolves the `test.projects` globs relative to the package directory, so it errors with "No projects were found". For each such package, create a minimal local config so vitest stops at it and `@nx/vitest` infers the test target:
207
207
 
@@ -217,6 +217,8 @@ When you inline a workspace file yourself, also apply these two checks (the pre-
217
217
 
218
218
  - [ ] **Duplicate project names**: when the inlined globs match both a `vite.config.*` and a `vitest.config.*` in the same directory and both resolve to the same project name (the default name is the directory's package.json name, so two name-less configs always collide), vitest errors at startup with "Project name ... is not unique". Append a negative glob excluding the `vite.config.*` file (e.g. `'!packages/foo/vite.config.ts'`), matching vitest's own vitest-over-vite preference. Leave directories whose configs have distinct explicit `test.name` values alone; those are intentionally separate projects.
219
219
 
220
+ - [ ] **Root config self-match**: when the inlined globs match the root config file itself (e.g. `**/vitest.config.*` matches the `vitest.config.*` you inlined into), vitest resolves that root config as an extra project. A pure aggregator has no `include` of its own, so this extra project re-runs every test via vitest's default glob without each project's `environment`/`setupFiles`, duplicating runs and failing tests that depend on their project config. Append a negative glob excluding the root config file (e.g. `'!vitest.config.ts'`, relative to its own directory) so vitest treats it only as the projects orchestrator. Skip this when the root config is itself a real project with its own `test.include`: it runs its own tests and needs no exclusion.
221
+
220
222
  <fail_if note="inlining is a semantic merge, not a copy">
221
223
  The `vitest.workspace.*` file imports modules / calls functions / uses spreads that you cannot evaluate at edit time (dynamic project arrays, conditional imports). Write status: failed listing the file path and the dynamic shape that needs human resolution.
222
224
  </fail_if>
@@ -135,7 +135,12 @@ function tryAddProjectsToConfig(tree, configPath, entries) {
135
135
  if (countPropertyAssignments(configObject, 'test') > 1)
136
136
  return false;
137
137
  const testProperty = findPropertyAssignment(configObject, 'test');
138
- const projectsText = `projects: ${formatEntriesArray(entries)}`;
138
+ // Same self-match guard as the create path: exclude this root config from its
139
+ // own `vitest.config.*` glob so vitest doesn't resolve it as an extra
140
+ // project. Skip it when the config has its own `test.include`, which vitest
141
+ // runs as that project's tests and so must not be excluded.
142
+ const ext = configPath.slice(configPath.lastIndexOf('.'));
143
+ const selfExclusion = `!vitest.config${ext}`;
139
144
  if (testProperty) {
140
145
  const testObject = testProperty.initializer;
141
146
  if (!ts.isObjectLiteralExpression(testObject))
@@ -152,18 +157,29 @@ function tryAddProjectsToConfig(tree, configPath, entries) {
152
157
  // lists is a semantic decision the agent must make.
153
158
  return false;
154
159
  }
160
+ const entriesWithGuard = findPropertyAssignment(testObject, 'include')
161
+ ? entries
162
+ : [...entries, selfExclusion];
155
163
  const insertPos = testObject.getStart() + 1;
156
164
  tree.write(configPath, (0, ast_edits_1.applyTextEdits)(contents, [
157
- { start: insertPos, end: insertPos, replacement: `${projectsText},` },
165
+ {
166
+ start: insertPos,
167
+ end: insertPos,
168
+ replacement: `projects: ${formatEntriesArray(entriesWithGuard)},`,
169
+ },
158
170
  ]));
159
171
  }
160
172
  else {
173
+ // No `test` block at all, so no own `include`: apply the guard.
161
174
  const insertPos = configObject.getStart() + 1;
162
175
  tree.write(configPath, (0, ast_edits_1.applyTextEdits)(contents, [
163
176
  {
164
177
  start: insertPos,
165
178
  end: insertPos,
166
- replacement: `test: {${projectsText},},`,
179
+ replacement: `test: {projects: ${formatEntriesArray([
180
+ ...entries,
181
+ selfExclusion,
182
+ ])},},`,
167
183
  },
168
184
  ]));
169
185
  }
@@ -176,11 +192,17 @@ function createVitestConfig(tree, dir, workspaceFilePath, entries) {
176
192
  const ext = ['.ts', '.mts', '.mjs'].includes(workspaceExt)
177
193
  ? workspaceExt
178
194
  : '.mjs';
195
+ // Guard the fresh root config against matching its own `vitest.config.*`
196
+ // glob: vitest would resolve it as an extra project that, having no
197
+ // `include`, re-runs every test via the default glob without its project
198
+ // config. The negation is root-anchored, so it only drops this file and is
199
+ // inert when the globs don't match it.
200
+ const projects = formatEntriesArray([...entries, `!vitest.config${ext}`]);
179
201
  tree.write((0, devkit_1.joinPathFragments)(dir, `vitest.config${ext}`), `import { defineConfig } from 'vitest/config';
180
202
 
181
203
  export default defineConfig({
182
204
  test: {
183
- projects: ${formatEntriesArray(entries)},
205
+ projects: ${projects},
184
206
  },
185
207
  });
186
208
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/vite",
3
- "version": "23.1.0",
3
+ "version": "23.2.0-canary.20260717-dc22f7e",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -101,13 +101,13 @@
101
101
  "semver": "^7.6.3",
102
102
  "tslib": "^2.3.0",
103
103
  "ajv": "^8.0.0",
104
- "@nx/devkit": "23.1.0",
105
- "@nx/js": "23.1.0",
106
- "@nx/vitest": "23.1.0"
104
+ "@nx/devkit": "23.2.0-canary.20260717-dc22f7e",
105
+ "@nx/js": "23.2.0-canary.20260717-dc22f7e",
106
+ "@nx/vitest": "23.2.0-canary.20260717-dc22f7e"
107
107
  },
108
108
  "devDependencies": {
109
- "@nx/eslint": "23.1.0",
110
- "nx": "23.1.0"
109
+ "@nx/eslint": "23.2.0-canary.20260717-dc22f7e",
110
+ "nx": "23.2.0-canary.20260717-dc22f7e"
111
111
  },
112
112
  "peerDependencies": {
113
113
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"