fix(testing): publish @nx/vitest, @nx/cypress, @nx/playwright, @nx/vite from local dist#35743
Merged
FrozenPandaz merged 15 commits intoMay 22, 2026
Merged
Conversation
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
View your CI Pipeline Execution ↗ for commit 249ae27
☁️ Nx Cloud last updated this comment at |
323df49 to
8bcb368
Compare
1dd6f2b to
4624c77
Compare
…ports Switches the @nx/vitest package to the local-dist build layout used by @nx/devkit, @nx/nx, @nx/js, @nx/eslint, and @nx/jest. The package builds to packages/vitest/dist/ with nodenext module resolution and is published straight from the package directory, not from the workspace-root dist/packages/vitest. The generator/executor/migration paths in generators.json, executors.json and migrations.json are repointed to ./dist/src/...; the prompt-migration .md files are copied into dist via assets.json so the new paths resolve in published artifacts.
… exports Switches the @nx/cypress package to the local-dist build layout used by @nx/devkit, @nx/nx, @nx/js, @nx/eslint, and @nx/jest. The package builds to packages/cypress/dist/ with nodenext module resolution and is published straight from the package directory, not from the workspace-root dist/packages/cypress. Drops the './src/*' wildcard from the exports map and routes internal consumers through a curated './internal' entry; a 23.0.0-beta.17 migration rewrites '@nx/cypress/src/*' imports to either the public '@nx/cypress' entry (for re-exported symbols) or '@nx/cypress/internal' (for everything else). Consumers in @nx/angular, @nx/react, @nx/next and @nx/web are codemodded to match.
…denext exports Switches the @nx/playwright package to the local-dist build layout used by @nx/devkit, @nx/nx, @nx/js, @nx/eslint, and @nx/jest. The package builds to packages/playwright/dist/ with nodenext module resolution and is published straight from the package directory, not from the workspace-root dist/packages/playwright. The previous exports already referenced explicit subpaths only (no './src/*' wildcard) and no first-party consumer imports from '@nx/playwright/src/*', so this migration ships without a codemod migration step.
Switches the @nx/vite package to the local-dist build layout used by @nx/devkit, @nx/nx, @nx/js, @nx/eslint, @nx/jest, @nx/vitest, @nx/cypress and @nx/playwright. The package builds to packages/vite/dist/ with nodenext module resolution and is published straight from the package directory, not from the workspace-root dist/packages/vite. The previous exports already enumerated explicit subpaths only (no './src/*' wildcard), and no first-party consumer imports from '@nx/vite/src/*', so this migration ships without a codemod migration step.
…ations
The @nx/js, @nx/jest, @nx/eslint, and @nx/cypress 23.0.0 codemods walk
`require`/dynamic `import`/`jest.mock` call expressions and rewrite
`@nx/<name>/src/*` arguments, but a `typeof import('@nx/<name>/src/x')`
type query parses as an `ImportTypeNode` — a separate AST branch — so
the previous handler left it untouched.
In practice that meant the common `const m = require('@nx/<name>/src/x')
as typeof import('@nx/<name>/src/x')` typed-runtime-require idiom would
have its runtime argument rewritten to `/internal` while the type
argument kept pointing at the now-removed `./src/*` wildcard, leaving
external consumers with a TS error after running the migration. Walk
`ImportTypeNode` explicitly and rewrite its literal-type-node argument
when the specifier starts with the package's `src/` prefix.
Adds:
- the `<typeof import()>require()` cast case in tandem to each spec.
- (for @nx/cypress) coverage for `componentConfigurationGenerator` as a
public-routed symbol, default and default-plus-named imports, and
every entry in `MOCK_HELPER_METHODS` via `it.each` — drift between
these hardcoded sets and the real public/mock surface was the most
plausible future silent-regression path.
Updates the dist-build-migration skill to document the `ImportTypeNode`
handling and the expanded spec checklist.
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
After the local-dist migration the compiled `dist/src/utils/versions.js`
sits two levels under `dist/`, so `require('../../package.json')`
resolves to `dist/package.json` — which doesn't exist; the real
package.json is at the package root. `@nx/vitest`'s own
`init` generator was failing at module-load time with
`Cannot find module '../../package.json'` when installed via the
local registry in e2e runs.
Switch to the self-reference pattern `require(join('@nx/vitest',
'package.json')).version` already used by the other migrated packages
(@nx/cypress, @nx/playwright, @nx/vite, @nx/jest, @nx/eslint, @nx/js)
and add the matching `@nx/vitest` ignoredDependencies entry so the
dependency-checks lint rule doesn't flag the self-import.
…pers `assertValidMigrationPaths` already strips `dist/` from the `implementation` path of a migration entry before `require`-ing the source `.ts`, but did the existence check on the `prompt` path verbatim. After the dist-build migration, prompt entries in `migrations.json` point at `./dist/src/migrations/<dir>/<file>.md` (the published copy that ships under the package's `dist/`); the spec runs against the source tree, where the `.md` lives at `./src/migrations/<dir>/<file>.md`. Result: every prompt-form migration in @nx/vitest (and other packages that adopt the same shape post-migration) failed the `should have valid path generator: ...` assertion. `assertGeneratorsEnforceVersionFloor` had the same blind spot for `factory` paths in `generators.json` — `./dist/src/generators/…` made `require(join(packageRoot, factoryRelative))` resolve to a not-yet-built dist file rather than the source. @nx/playwright's `all-generators-enforce-floor.spec.ts` was failing on all three generators for this reason. Both helpers now drop the leading `dist/` segment before resolving, matching the existing implementation-path behaviour.
… under nodenext
After the local-dist migration, `@nx/vite` and `@nx/vitest` compile
with `module: nodenext`. Under that setting TypeScript no longer
downlevels `await import('@nx/eslint/internal')` into a synchronous
`Promise.resolve(require(...))` — it stays as a true ESM dynamic
import. ESM resolution walks up `node_modules` from the importing
file's location and ignores `Module._initPaths`, which is exactly the
mechanism `ensurePackage` uses to register the on-demand temp install.
Result: every generator path that goes through `ignoreViteTempFiles` /
`ignoreVitestTempFiles` (or the `@nx/vite:configuration` generator's
`includeVitest` branch) crashed with `Cannot find package
'@nx/eslint'` (or `'@nx/vitest'`) when run against the published
artifact in an e2e workspace. Same bug fired the
`migrations.spec.ts`-shape regressions in the `@nx/vite:setup-paths`
generator's downstream consumers in `@nx/web` and `@nx/react` e2es.
Switch the four affected call sites to a synchronous CommonJS
`require()` call after `ensurePackage`. `require()` honors
`Module._initPaths`, so the temp install resolves correctly. Type
information is preserved via `typeof import(...)` on the destructuring.
Also fixes `e2e/nx-build/src/nx-build.test.ts` which still expected
`@nx/playwright` to publish from the workspace-root
`dist/packages/playwright/index.js`; updates the expected paths for
`@nx/playwright`, `@nx/cypress`, `@nx/vite`, and `@nx/vitest` to
`packages/<name>/dist/index.js` matching the new layout.
Updates the dist-build-migration skill with a new step 15b documenting
the `ensurePackage` + `await import()` audit so future migrations
don't ship the same regression.
The 'should build all packages and produce correct output' assertion in e2e/nx-build/src/nx-build.test.ts still expected @nx/playwright to ship from the workspace-root dist/packages/playwright/index.js. Update the expected paths for the four newly-migrated packages (@nx/playwright, @nx/cypress, @nx/vite, @nx/vitest) to packages/<name>/dist/index.js matching the new layout, and add the cypress/vite/vitest entries since they weren't covered before.
…ges [Self-Healing CI Rerun]
…imumCypressVersion These were marked @deprecated in v23 (commit 5feafd6) pointing at the @nx/cypress/internal replacements. Not exported in the package exports map and no in-repo callers remain.
3c27bff to
249ae27
Compare
….0.0-beta.19 [Self-Healing CI Rerun]
Contributor
There was a problem hiding this comment.
Nx Cloud has identified a flaky task in your failed CI:
🔂 Since the failure was identified as flaky, we triggered a CI rerun by adding an empty commit to this branch.
🎓 Learn more about Self-Healing CI on nx.dev
AgentEnder
approved these changes
May 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Current Behavior
@nx/vitest,@nx/cypress,@nx/playwright, and@nx/viteall build to the workspace-rootdist/packages/<name>/directory and publish from there. They use the legacymodule: commonjs/moduleResolution: nodeconfiguration, anexportsmap with a./src/*wildcard (vitest/cypress), and anx-release-publish.options.packageRootthat points at the workspace-root dist tree. This is the same shape@nx/devkit,@nx/nx,@nx/js,@nx/eslint, and@nx/jestalready moved off — but the M2 group of testing/build packages was still on the old layout, blocking downstream packages (like@nx/web) from migrating.Separately, the 23.0.0
rewrite-internal-subpath-importscodemods shipped with@nx/js,@nx/jest,@nx/eslint(and the new one in this PR for@nx/cypress) walkrequire(...)/ dynamicimport(...)/jest.mock(...)call expressions but leavetypeof import('@nx/<name>/src/x')type queries untouched. The common typed-runtime-require idiomwould have its runtime arg rewritten to
/internalwhile the type arg kept pointing at the now-removed./src/*wildcard — leaving external consumers with a TS error after running the migration.Expected Behavior
Each of the four packages now builds to
packages/<name>/dist/withmodule: nodenext+ composite TypeScript, ships afilesfield inpackage.jsonlisting what to publish, declaresrelease.preserveLocalDependencyProtocols: true+manifestRootsToUpdate: ["packages/{projectName}"]inproject.json, and publishes straight from the package directory vianx-release-publish.options.packageRoot: packages/{projectName}.Per-package highlights:
@nx/vitest— straight structural migration. No@nx/vitest/src/*consumers, so no codemod migration shipped.assets.jsonupdated to globsrc/migrations/**/*.mdso prompt-form migrations resolve from./dist/src/....@nx/cypress— drops the./src/*wildcard from the exports map, ships a curatedinternal.tsre-export entry, and ships a23.0.0-beta.17symbol-aware codemod (rewrite-internal-subpath-imports) that routes@nx/cypress/src/*imports to either the public@nx/cypressentry (forconfigurationGenerator,componentConfigurationGenerator,cypressInitGenerator,migrateCypressProject) or@nx/cypress/internal(for everything else). 10 first-party consumers in@nx/angular/@nx/react/@nx/next/@nx/webare codemodded to match.@nx/playwright— straight structural migration; existing exports already enumerated explicit subpaths, no./src/*wildcard to drop.@nx/vite— structural migration + three executor.impl.tsfiles switched fromconst schema = await import('./schema.json')to a top-levelimport schema from './schema.json'(required undernodenext). Kept./plugins/nx-tsconfig-paths.plugin/./plugins/nx-copy-assets.plugin/./plugins/rollup-replace-files.pluginas explicit public entries because storybook / react-native templates bake them into generated user vite configs.The fourth commit fixes the
typeof import()blind spot across all four of the 23.0.0 subpath-rewrite codemods (@nx/js,@nx/jest,@nx/eslint, the new@nx/cypressone) by walkingImportTypeNodeand rewriting the literal-type-node argument when it points into the package'ssrc/. The cypress spec also adds explicit coverage forcomponentConfigurationGeneratoras a public-routed symbol, default and default-plus-named imports,jest.mock(..., factory), andit.each(MOCK_HELPER_METHODS)for both the jest and vi mock families — drift between those hardcoded sets and the real public/mock surface was the most plausible future silent-regression path. The dist-build-migration skill is updated to document theImportTypeNodehandling and the expanded spec checklist.Validation
pnpm nx run-many -t build-base -p angular,nuxt,remix,vue,web,react,vitest,cypress,playwright,vite,jest,js,eslint✅pnpm nx affected -t build,lint --base=origin/master✅ (53 projects, 103 tasks).pnpm nx run-many -t test -p jest,js,eslint,cypress --testPathPatterns="rewrite-internal-subpath-imports"✅ — 99 passing (32 cypress, 27 eslint, 20 jest, 20 js).@nx/webis now unblocked on 4 of its 5 prior dist-build dependencies (@nx/vitest,@nx/cypress,@nx/playwright,@nx/vite); only@nx/webpackremains on the old layout.Related Issue(s)
Linear: NXC-3581 — M2 epic, migrate testing/build packages to local dist.
Pre-create review (run before PR open)
Critical
typeof import('@nx/<name>/src/...')type queries — backported the fix to@nx/js,@nx/jest,@nx/eslint, and the new@nx/cypressmigration.componentConfigurationGenerator(the 4th public symbol), default imports, orjest.mock/vi.mockproper.Important
@nx/vite/@nx/vitest/@nx/playwrightship no codemod despite dropping their./src/*wildcards. No first-party consumers exist; external plugin authors using deep subpaths will hit breakage. Noted as a known breaking change for the upgrade guide rather than fixed in this PR — codemods can land in a follow-up if the surface turns out to matter.try { ... } catch {}incypress-version.ts(pre-existing; diff only touched JSDoc). Tracked for a follow-up.Suggestions
vite/project.jsonandplaywright/project.jsonomitdependsOn: ["^build", "build-base"]while cypress/vitest include it (covered bynx.jsontargetDefaults, cosmetic).cypressdropped legacy aliases./generators/./executors/./migrations(no.jsonsuffix) from the exports map — undocumented surface, low risk.