Skip to content

fix(vite): unvendored plugin-patterned deps rewrite to __nsVendorRequire stubs instead of the /ns/m HTTP fallback #11458

Closed

Description

@aleclarson

Issue Description

In dev sessions (ns run HMR over /ns/m), an npm package that depends on a NativeScript runtime plugin via a regular dependencies entry fails when the app does not also declare the plugin. The wrapper package's import { Img } from '@nativescript-community/ui-image' is rewritten into a synchronous __nsVendorRequire("@nativescript-community/ui-image"); the vendor registry has no entry (the plugin isn't vendored), so the device gets the __nsMissing proxy stub and downstream calls fail with things like X is not a constructor.

This hits hard under pnpm's isolated linker (pnpm workspaces, nodeLinker: isolated), where transitive dependencies aren't resolvable from the project root — but the same failure exists anywhere a plugin-patterned package is absent from the vendor manifest (NS_VENDOR_EXCLUDE, flavor vendor.exclude, local file:/workspace: sources that ship a plugin dep).

ns prepare handles this fine — NodeModulesDependenciesBuilder BFS-traverses each package's dependencies via require.resolve from the resolved parent dir, so the plugin's platforms/ios/Podfile and platforms/android/include.gradle merge correctly without the app declaring the plugin. Only the Vite dev server loses the JS side.

Root cause

collectVendorModules intentionally does not collect transitive runtime deps (see the comment in hmr/shared/vendor/manifest-collect.ts), expecting "non-vendor packages" to "route through HTTP to the Vite dev server." That fallback exists — GET /ns/m/node_modules/@nativescript-community/ui-image (bare, unvendored, transitive) does return real transformed code. But two call sites assume plugin-patterned specifiers are always vendored and never let them reach the fallback:

  1. resolveVendorRouting (hmr/server/websocket-module-specifiers.ts): the isLikelyNativeScriptRuntimePluginSpecifier fast paths return { route: 'vendor' } before the manifest-membership check below them.
  2. ensureNativeScriptModuleBindings (hmr/server/websocket-module-bindings.ts): canonical is force-assigned from isLikelyNativeScriptPluginSpecifier(specifier) — pattern match only, no manifest check — so the import becomes a __nsVendorRequire binding instead of staying an HTTP import.

Repro

app/package.json:  "@scope/gif": "workspace:*"          # or published dep
packages/gif/package.json:  "dependencies": { "@nativescript-community/ui-image": "^4.6.20" }
packages/gif/src/index.native.ts:  import { Img } from '@nativescript-community/ui-image'

pnpm, nodeLinker: isolated. ns run ios → fetch GET /ns/m/node_modules/@scope/gif/src/index.native.ts → served code contains __nsVendorRequire("@nativescript-community/ui-image") → registry miss → Img is a soft-stub Proxy.

Proposed fix

Gate the plugin-patterned vendor routes on vendor-manifest membership:

  • In resolveVendorRouting, hoist getVendorManifest() before the fast paths and require manifest.modules[pkgName]; unvendored plugin deps return null → callers emit the per-module /ns/m/node_modules/... HTTP form, which resolves them via Vite's importer-aware resolution (works from .pnpm realpaths too).
  • In ensureNativeScriptModuleBindings, drop canonical when it isn't in the manifest, and only apply the isLikelyNativeScriptPluginSpecifier force-canonical branch when resolveVendorSpecifier(specifier) hits.

Verified locally in a pnpm-isolated workspace: with the gate applied, the wrapper leaf's served code emits import { Img } from "http://…/ns/m/node_modules/.pnpm/@[email protected]/node_modules/@nativescript-community/ui-image/index.ios.js", which returns 200 with transformed code.

Happy to send the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions