← 输入框|github.com|刷新
Skip to content

fix(vite): keep source-only dependencies out of the HMR vendor bundle regardless of spec - #11560

Merged
NathanWalker merged 3 commits into
NativeScript:mainfrom
aleclarson:fix/vite-vendor-skip-source-only-deps
Oct 21, 2026
Merged

NathanWalker merged 3 commits into
NativeScript:mainfrom
aleclarson:fix/vite-vendor-skip-source-only-deps

Conversation

@aleclarson

@aleclarson aleclarson commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

PR Checklist

  • The PR title follows our guidelines.
  • There is an issue for the bug/feature this PR is for.
  • You have signed the CLA.
  • All existing tests are passing.
  • Tests for the changes are included.

What is the current behavior?

isUnvendorableLocalSource keeps a dependency out of the vendor bundle only in two cases:

  • its spec is link: or workspace:;
  • its spec is a file: directory and the package has no compiled JS entry.

Every other spec is vendored, even when the installed package ships only TypeScript source. That includes semver, git, url and file: tarballs. Examples of affected packages:

  • a package published to npm as raw .ts ("exports": { ".": "./src/index.ts" });
  • a semver dependency that a pnpm readPackage hook replaced with a symlink to a source checkout.

The vendor bundle is built by esbuild without the app's Vite plugins, so source packages break in it:

  • Imports that use tsconfig path aliases can't be resolved, and the optional-dependency plugin replaces them with stubs. The build passes, but the stub throws on the device when it runs.
  • Files that need a Vite plugin (custom loaders, framework JSX transforms) fail the whole vendor build, for example: No loader is configured for ".tsrx" files.

What is the new behavior?

  • The compiled-entry check now runs for every spec except link:/workspace:, which are still always skipped. A package with a compiled .js/.mjs/.cjs entry is vendored. A package without one is served through the Vite pipeline, the same way file: source directories already are.
  • If the installed package.json can't be found or read, the result is the same as before. file: directories are skipped; every other spec is vendored.
  • Two fixes in the entry check, needed now that it runs for every spec:
    • Packages whose exports doesn't include ./package.json were treated as having no compiled entry. The check now falls back to looking for <dir>/<name>/package.json in require.resolve.paths(name).
    • main: "lib" now also checks lib/index.js (and the platform variants), the way Node resolves a directory.

Reproduction

The fixture app depends on "src-lib": "1.0.0", which exports ./src/index.ts. That file imports ~lib/x (a tsconfig path alias) and ./ui.tsrx (handled by an app Vite plugin). The repro runs collectVendorModules followed by the vendor esbuild build (vendorManifestPlugin.buildStart) on Vite 8.3.4.

Build src-lib vendored Vendor build
@nativescript/vite@8.0.19 yes fails: No loader is configured for ".tsrx" files (~lib/x was also replaced by a stub that throws)
this PR no succeeds

Tests

manifest.spec.ts has a new collectVendorModules — source-only dependencies suite. It checks that these are excluded:

  • a semver package that exports only TypeScript;
  • a semver dependency that is a symlink to a source checkout.

It also checks that these are still vendored:

  • a package whose exports doesn't include ./package.json;
  • a main that points at a directory;
  • a plugin with only index.ios.js / index.android.js;
  • a file: tarball;
  • file: directories, both compiled and source;
  • a dependency that is declared but not installed.

The two exclusion tests fail on main.

npx vitest run in packages/vite: 1845 passed, 1 skipped.

Notes

  • A package whose package.json resolves but which has no JS entry at all is now served through Vite instead of being vendored. Three existing collectVendorModules fixtures were like this (only a package.json). I added a stub index.js to each so they still test the flavor and policy exclusions they were written for.
  • This PR doesn't change how packages that export only compiled subpaths (no . entry) are handled.

… regardless of spec

isUnvendorableLocalSource only ran its compiled-JS check for file:
directory specs, so semver/git/url deps — and semver specs whose install
linked a source checkout (e.g. a pnpm readPackage hook) — were swept into
the standalone esbuild vendor bundle even when the installed package ships
only TypeScript/TSX source. That pipeline has none of the app's Vite
plugins (tsconfig-paths resolver, JSX transforms, custom loaders like
.tsrx), so vendoring such a package fails the whole bundle or silently
stubs its imports.

- The source check now applies to every non-link:/workspace: spec via a
  classifyPackageEntry() classifier ('compiled' | 'source' | 'unknown').
- Manifest lookup falls back to a direct package.json probe over the
  require resolution dirs when exports omit ./package.json, so those
  packages classify correctly instead of bailing out.
- Extensionless mains that name a directory (main: "lib") now also probe
  <dir>/index<ext>, matching Node's directory-as-entry behavior.

When the installed package.json can't be found or parsed ('unknown'), the
behavior is unchanged: file: directory specs still skip vendor and every
other spec still vendors.
@nx-cloud

nx-cloud Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 395ceb5

Command Status Duration Result
nx run-many --target=test --configuration=ci --... ✅ Succeeded <1s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-10-21 18:37:26 UTC

@pkg-pr-new

pkg-pr-new Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@nativescript/core@11560
npm i https://pkg.pr.new/@nativescript/vite@11560
npm i https://pkg.pr.new/@nativescript/webpack@11560

commit: 395ceb5

…g vendor deps

A package with no entry file on disk (stale `main`, subpath-only layout,
wildcard-only `exports`) was classified as source-only and dropped from the
vendor bundle for every spec. That unvendored NativeScript plugins whose
stale `main` esbuild still resolves through the `index.<platform>.js`
fallback, and stopped vendoring the imported subpaths of monorepo root deps
without a root entry. Such packages now fall back to the spec-based
decision, as before.
@NathanWalker
NathanWalker merged commit a0a90b8 into NativeScript:main Oct 21, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants