[Vite, Vue, HMR] per-module-served plugin subpath that imports `nativescript-vue` bare evaluates the real package root and double-runs `init()` (Element for AbsoluteLayout already registered)

未关闭
#11,421 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
45/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
活跃
技术栈
ios, typescript, vite

调研方向

首先使用 HMR 重现 collectionview 导入,然后阅读 hmr/shared/vendor/manifest-collect.ts 和 hmr/server/deps-bundle.ts,尤其关注 issue 中提到的 Solid 处理和 shim 逻辑。当按模块提供的 Vue 插件导入能够复用已打包的 nativescript-vue 实例、避免重复注册,并且无需 NS_VENDOR_INCLUDE 即可保留 HMR 时,即视为完成。

由索引模型根据 Issue 内容生成。

描述

Issue Description

Runtime error (dev server / HMR only). @nativescript/vite 8.0.5 (same on 8.0.4) · nativescript-vue 3.0.3 · @nativescript/core 9.1.1 · vite 8.2.2 · @nativescript/ios 9.1.0 · iOS simulator (iPhone 17 Pro Max).

Scope: iOS simulator only; not tested on Android or a physical device.

A blank Vue app boots fine under HMR. It stops booting the moment I import one plugin subpath whose file is served per-module (not shimmed into the deps bundle) and contains a bare import ... from 'nativescript-vue'. I only tested that one plugin, but the routing below suggests any file with that shape would do the same.

Bare nativescript-vue is mapped in the device import map to the real package root (/ns/m/node_modules/nativescript-vue), unlike @nativescript/core, which is mapped to the /ns/core bridge. (Every other package root maps the way nativescript-vue does — @nativescript/core is the special case.) The real root cannot be shimmed (its export * from '@vue/runtime-core' is a bare star), so importing it evaluates it — and its module-scope init() runs a second time, on top of the copy the deps bundle already evaluated:

Error: Element for AbsoluteLayout already registered.
If this is intentional set 'overwriteExisting: true' in 'meta'
    at registerElement       (/ns/deps-bundle.mjs)
    at registerCoreElements  (/ns/deps-bundle.mjs)
    at init                  (/ns/deps-bundle.mjs)
    at                       (/ns/m/node_modules/nativescript-vue:41:1)

Concrete trigger: @nativescript-community/ui-collectionview/vue3. There is a config-only workaround (NS_VENDOR_INCLUDE, below), so this is not a blocker — but there may be a similar case to cover for Vue to what manifest-collect already does for Solid.

Expected behavior: importing a plugin's Vue entry should share the one nativescript-vue instance the deps bundle already evaluated, the way app-code imports (rewritten to /ns/rt) do.

A second, separate bridge problem surfaces once this one is worked around: #11422 ($navigateTo "app navigator missing" race).

Line numbers below refer to the published @nativescript/vite 8.0.5 package (compiled .js in node_modules) unless marked .ts, which refers to packages/vite on main.

Reproduction

Minimal repro (blank app, two steps):

  1. ns create a blank Vue TS app (template-blank-vue 9.1.0 — it ships webpack), then npx nativescript-vite init (adds @valor/nativescript-websockets plus the two config keys). Boots under HMR, and live HMR works — a Home.vue edit applies on device.
  2. npm i @nativescript-community/ui-collectionview@6.0.25, and in app/app.ts:
import { createApp } from 'nativescript-vue';
import CollectionView from '@nativescript-community/ui-collectionview/vue3';
import Home from './components/Home.vue';

const app = createApp(Home);
app.use(CollectionView);
app.start();
  1. ns debug ioscrashes with the error above.

Controls, same tree:

Variant Result
plugin installed but not imported boots
pinia installed and app.use(createPinia()) boots
plugin imported (as above) crashes
ns debug ios --no-hmr boots

So the trigger is exactly that one import.

Mechanism — steps 1-3 from my run's manifest, the served file and the import map; steps 4-5 from reading the shipped code
  1. The vue3/ subpath isn't in the bundle. In my run's deps-bundle manifest (node_modules/.ns-vite/deps-bundle-ios-*.json), the only keys under @nativescript-community/ui-collectionview were index.ios.js and index-common.js — its root entry closure. vue3/index.js and vue3/component.js are not bundle keys, so no shim is emitted and they are served as real per-module ESM.

  2. Their nativescript-vue import is not rewritten. Served vue3/component.js line 2 is a bare import { NSVRoot, defineComponent, getCurrentInstance, h, ref, render, toRaw, watch } from "nativescript-vue"; — while line 1 of the same served file was rewritten to import __ns_core_ns1 from ".../ns/core". App-code imports of nativescript-vue are rewritten to /ns/rt; this node_modules file's are not.

  3. The import map sends it to the real root. /ns/import-map.jsonimportMap.imports (1987 entries) maps "nativescript-vue"http://<host>:5173/ns/m/node_modules/nativescript-vue, but "@nativescript/core"http://<host>:5173/ns/core. That asymmetry is what routes the bare import at the real package root.

  4. The real root can't be shimmed. nativescript-vue/dist/index.js:16 is export * from '@vue/runtime-core' — a bare star. collectDepsModuleExportInfo returns names: null for a non-relative export * (hmr/server/deps-bundle.js:781-785; .ts ~:798-813), and the shim site (hmr/server/deps-bundle.js:920-938) emits a shim only when names !== null or opaqueCjs is set. Neither holds → per-module serving → the URL evaluates the file, and dist/index.js:10 init() runs.

  5. The bundle already ran init(). Every vendor file gets a static namespace import in the synthetic entry (hmr/server/deps-bundle.js:310-322), tree-shaking is off (hmr/server/deps-bundle.js:631-633; .ts:658, comment: "dep packages rely on registration side effects"), and the deps bundle is evaluated as part of the dev-client graph, imported before the app entry (hmr/shared/runtime/session-bootstrap.js:276 vs :304). init()registerCoreElements() (nativescript-vue/dist/nativescript/index.js:3-4) → registerElement('AbsoluteLayout') (dist/nativescript/elements.js:6) into a module-local elementMap (dist/registry/index.js:4) that throws on a repeat (dist/registry/index.js:39-45).

The served root's init() resolves registerCoreElements through dist/nativescript/index.js, which is served as a shim into the bundle — so the second call writes into the already-populated map and throws.

What it is not (all tested)
  • Not dependencies vs devDependenciesnativescript-vue is in dependencies, and the blank-template layout matches the failing app.
  • Not whether vue is in the vendor set — a variant with vue in vendorSpecToKey and 17 @vue/* keys boots.
  • Not pinia — installed and used, boots.
  • Not duplicate Vue copies — @vue/* resolves to a single hoisted copy, no nested duplicates.
  • Not Vite's dep optimizer — optimizeDeps.noDiscovery/exclude changed nothing.
  • Not stale caches — ns clean + reinstall reproduces identically.
Workaround (config only)
NS_VENDOR_INCLUDE=@nativescript-community/ui-collectionview/vue3 ns debug ios

Exactly that bare subpath. addCandidate accepts subpaths (hmr/shared/vendor/manifest-collect.js:465-471 — adds to vendor, and skips only the dependency traversal for non-root specifiers; the env hook is at :629, .ts:616). resolveSpecToFile (hmr/server/deps-bundle.js:184-200) falls through to the /index<ext> candidate fan-out (:109-114) and lands on vue3/index.js; esbuild pulls component.js into the closure. What I observed directly: with that one env var the manifest gains vue3/index.js and vue3/component.js as keys, the served component.js becomes a __NS_DEPS_MODULES__ shim, and the app boots.

Verified: the repro boots, live HMR works, and on a real app (larger, same stack) the already registered error is gone.

Suggested direction (there's precedent in the plugin's own code)

hmr/shared/vendor/manifest-collect.js:614-628 (.ts:601-611) vendors @tanstack/solid-router, @tanstack/router-core and @tanstack/history when @nativescript/tanstack-router is a dependency, for three reasons — the third of which is this class of problem: "Import solid-js internally — loading via HTTP creates a separate solid-js instance". The Vue flavor has no equivalent, and given the import map above, any per-module-served node_modules file that imports nativescript-vue bare would resolve to the real root the same way (only the collectionview subpath was actually exercised).

Options, in rough order of how targeted they seem:

  • Route bare nativescript-vue in the import map to the bridge/bundle, the way @nativescript/core/ns/core already is.
  • Auto-vendor node_modules files whose source imports nativescript-vue.
  • Make the root shim-able despite the bare export * (resolve the star target instead of returning names: null).

Happy to test a patch on the repro.

Relevant log output (if applicable)
Error: Element for AbsoluteLayout already registered.
If this is intentional set 'overwriteExisting: true' in 'meta'
    at registerElement       (/ns/deps-bundle.mjs)
    at registerCoreElements  (/ns/deps-bundle.mjs)
    at init                  (/ns/deps-bundle.mjs)
    at                       (/ns/m/node_modules/nativescript-vue:41:1)
Environment
OS: macOS 26.6.2
CPU: (16) arm64 Apple M3 Max
Shell: /bin/zsh
node: 24.21.0
npm: 11.19.0
nativescript: 9.1.0 (project-local)

# ios
xcode: 26.6/17F113
cocoapods: 1.14.2
platforms:
  - iOS 26.5
"dependencies": {
  "@nativescript/core": "~9.1.0",
  "nativescript-vue": "~3.0.3",
  "@valor/nativescript-websockets": "^2.0.2",
  "@nativescript-community/ui-collectionview": "6.0.25"
},
"devDependencies": {
  "@nativescript/ios": "~9.1.0",
  "@nativescript/tailwind": "^4.0.0",
  "@nativescript/types": "~9.1.0",
  "@nativescript/vite": "~8.0.4",
  "@types/node": "^20.0.0",
  "@vitejs/plugin-vue": "^6.0.8",
  "@vitejs/plugin-vue-jsx": "^5.1.6",
  "nativescript": "~9.1.0",
  "tailwindcss": "^4.0.0",
  "typescript": "~6.0.0",
  "vite": "^8.2.2",
  "vue-tsc": "^3.3.9"
}
Please accept these terms
主要语言
TypeScript
星标
25.7k
派生
1.7k
平均合并
1 天 5 小时
30 天内合并 PR
35

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

NativeScript/NativeScript 的其他 Issue

查看 NativeScript/NativeScript 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。