@dongmingzhixiu/vuexir 0.4.0 → 0.4.1

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.
package/dist/bundle.d.ts CHANGED
@@ -11,3 +11,15 @@ export interface BundleResult {
11
11
  }
12
12
  /** 打包 src/utils 下可独立运行的 util(`@/types` 等纯类型导入会被 esbuild 擦除) */
13
13
  export declare function bundleProjectUtils(projectRoot: string): BundleResult;
14
+ /**
15
+ * R118:把页面 `<script setup>` 打成模块 —— computed / store 别名 / 局部函数
16
+ * 必须在 JS 侧真实存在,否则模板里的派生值算不出来(这一直是数据为空的根因)。
17
+ *
18
+ * 做法:脚本内容写入工程的临时文件(保证相对 import 可解析),末尾追加
19
+ * `export { <所有顶层绑定> }`,esbuild 打成 IIFE(globalName=__vrnPage),
20
+ * 运行时即可遍历这些导出并把 ref/computed 的值同步进 stateStore。
21
+ */
22
+ export declare function bundlePageScript(pageFile: string, projectRoot: string): {
23
+ code: string;
24
+ names: string[];
25
+ } | null;
package/dist/bundle.js CHANGED
@@ -11,9 +11,9 @@
11
11
  import { existsSync, readdirSync } from 'node:fs';
12
12
  import { join } from 'node:path';
13
13
  import { buildSync } from 'esbuild';
14
- import { mkdtempSync, writeFileSync } from 'node:fs';
14
+ import { mkdtempSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
15
15
  import { tmpdir } from 'node:os';
16
- import { DEXIE_SHIM_SOURCE, PINIA_SHIM_SOURCE, VUE_SHIM_SOURCE } from './shims.js';
16
+ import { DEXIE_SHIM_SOURCE, PINIA_SHIM_SOURCE, VUE_ROUTER_SHIM_SOURCE, VUE_SHIM_SOURCE } from './shims.js';
17
17
  /** 打包 src/utils 下可独立运行的 util(`@/types` 等纯类型导入会被 esbuild 擦除) */
18
18
  export function bundleProjectUtils(projectRoot) {
19
19
  const utilsDir = join(projectRoot, 'src', 'utils');
@@ -72,3 +72,68 @@ export function bundleProjectUtils(projectRoot) {
72
72
  }
73
73
  return { code: chunks.join('\n'), included, skipped };
74
74
  }
75
+ /**
76
+ * R118:把页面 `<script setup>` 打成模块 —— computed / store 别名 / 局部函数
77
+ * 必须在 JS 侧真实存在,否则模板里的派生值算不出来(这一直是数据为空的根因)。
78
+ *
79
+ * 做法:脚本内容写入工程的临时文件(保证相对 import 可解析),末尾追加
80
+ * `export { <所有顶层绑定> }`,esbuild 打成 IIFE(globalName=__vrnPage),
81
+ * 运行时即可遍历这些导出并把 ref/computed 的值同步进 stateStore。
82
+ */
83
+ export function bundlePageScript(pageFile, projectRoot) {
84
+ const src = readFileSync(pageFile, 'utf-8');
85
+ const m = src.match(/<script setup[^>]*>([\s\S]*?)<\/script>/);
86
+ if (!m)
87
+ return null;
88
+ const body = m[1];
89
+ // 顶层绑定名(const/let/function);导出的就是这些
90
+ const names = [...new Set([...body.matchAll(/(?:const|let|var|function)\s+([A-Za-z_$][\w$]*)/g)].map((x) => x[1]))];
91
+ if (names.length === 0)
92
+ return null;
93
+ const shimDir = mkdtempSync(join(tmpdir(), 'vrn-page-'));
94
+ const vueShim = join(shimDir, 'vue-shim.js');
95
+ writeFileSync(vueShim, VUE_SHIM_SOURCE);
96
+ const piniaShim = join(shimDir, 'pinia-shim.js');
97
+ writeFileSync(piniaShim, PINIA_SHIM_SOURCE);
98
+ const dexieShim = join(shimDir, 'dexie-shim.js');
99
+ writeFileSync(dexieShim, DEXIE_SHIM_SOURCE);
100
+ // 入口必须放在工程内,否则 vue/vue-router 等裸导入解析不到 node_modules
101
+ const entry = join(projectRoot, 'src', `.vrn-page-${Date.now()}.ts`);
102
+ writeFileSync(entry, `${body}\nexport { ${names.join(', ')} };\n`);
103
+ const routerShim = join(shimDir, 'vue-router-shim.js');
104
+ writeFileSync(routerShim, VUE_ROUTER_SHIM_SOURCE);
105
+ try {
106
+ const out = buildSync({
107
+ entryPoints: [entry],
108
+ bundle: true,
109
+ write: false,
110
+ format: 'iife',
111
+ globalName: '__vrnPage',
112
+ platform: 'neutral',
113
+ target: 'es2019',
114
+ logLevel: 'silent',
115
+ alias: {
116
+ '@': join(projectRoot, 'src'),
117
+ vue: vueShim,
118
+ 'vue-router': routerShim,
119
+ pinia: piniaShim,
120
+ dexie: dexieShim,
121
+ },
122
+ });
123
+ const code = out.outputFiles?.[0]?.text ?? '';
124
+ return code ? { code, names } : null;
125
+ }
126
+ catch (e) {
127
+ if (process.env.VRN_DEBUG_BUNDLE)
128
+ console.error('[bundle] 页面脚本打包失败:', e.message.split('\n')[0]);
129
+ return null;
130
+ }
131
+ finally {
132
+ try {
133
+ unlinkSync(entry);
134
+ }
135
+ catch {
136
+ /* 忽略 */
137
+ }
138
+ }
139
+ }
package/dist/gen-ios.js CHANGED
@@ -15,7 +15,7 @@
15
15
  import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync, copyFileSync, rmSync } from 'node:fs';
16
16
  import { basename, dirname, join } from 'node:path';
17
17
  import { fileURLToPath } from 'node:url';
18
- import { bundleProjectUtils } from './bundle.js';
18
+ import { bundlePageScript, bundleProjectUtils } from './bundle.js';
19
19
  import { TEMPLATE_VERSION, VRN_BRIDGE_SWIFT, projectYml as PROJECT_YML, templateFiles } from '@dongmingzhixiu/vuexir-templates';
20
20
  import { parseRouter as parseRouterV2, findProjectRoot } from '@dongmingzhixiu/vuexir-compiler';
21
21
  import { validateIr } from '@dongmingzhixiu/vuexir-compiler';
@@ -198,11 +198,24 @@ const RUNTIME_SWIFT = `final class Runtime {
198
198
  }
199
199
  ctx.setObject(routerBack, forKeyedSubscript: "__routerBack" as NSString)
200
200
 
201
- let resName = page == "main" ? "runtime" : "runtime_" + page
202
- if let path = Bundle.main.path(forResource: resName, ofType: "js") {
201
+ // R116:页面键既可能是 main(容器)也可能是页面 id → 两个候选都试,
202
+ // 否则 tab 页会去找 runtime.js 而实际文件是 runtime_<Page>.js(旧版直接不加载脚本)
203
+ var resName = page == "main" ? "runtime" : "runtime_" + page
204
+ var found = Bundle.main.path(forResource: resName, ofType: "js")
205
+ if found == nil {
206
+ let alt = page == "main" ? "runtime_Home" : "runtime"
207
+ if let p2 = Bundle.main.path(forResource: alt, ofType: "js") { found = p2; resName = alt }
208
+ }
209
+ if let path = found {
203
210
  NSLog("[vrn] loading %@", resName)
204
211
  if let src = try? String(contentsOfFile: path, encoding: .utf8) {
205
- ctx.evaluateScript(src)
212
+ // R117:JSC 会静默吞掉脚本异常 → 显式检查并打印,否则运行时错误完全不可见
213
+ var jsErr: JSValue?
214
+ ctx.evaluateScript(src, withSourceURL: URL(string: "vrn://" + resName + ".js"))
215
+ if let e = jsErr, !e.isUndefined {
216
+ let stack = e.objectForKeyedSubscript("stack")?.toString() ?? ""
217
+ NSLog("[vrn] JS 异常(%@): %@ | %@", resName, e.toString() ?? "?", stack)
218
+ }
206
219
  } else { NSLog("[vrn] read %@ failed", resName) }
207
220
  } else { NSLog("[vrn] %@ NOT in bundle", resName) }
208
221
  let om = ctx.objectForKeyedSubscript("__onMounted")
@@ -714,7 +727,11 @@ function runTabs(appDir, opts = {}) {
714
727
  for (const w of genWarnings)
715
728
  console.log(`[gen-ios] 生成提示(${tab.id},tab 页): ${w}`);
716
729
  writeFileSync(join(srcDir, `${pageId}View.swift`), swift);
717
- writeFileSync(join(srcDir, `runtime_${pageId}.js`), utilsBundle.code + '\n' + (r.runtime ?? ''));
730
+ // R118:页面脚本 bundle(computed/store 别名/局部函数)必须排在 utils 之后、runtime 之前
731
+ const pageBundle = bundlePageScript(tab.file, findProjectRoot(appDir));
732
+ if (pageBundle)
733
+ console.log(`[gen-ios] 页面脚本打包:${tab.id}(${pageBundle.names.length} 个绑定)`);
734
+ writeFileSync(join(srcDir, `runtime_${pageId}.js`), utilsBundle.code + '\n' + (pageBundle?.code ?? '') + '\n' + (r.runtime ?? ''));
718
735
  pageFiles.push(`${pageId}View.swift`);
719
736
  pageRuntimes.push({ id: pageId, src: r.runtime ?? '' });
720
737
  pageStates.push({
package/dist/shims.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * 由运行时 localStorage shim 提供),API 覆盖 app 实际用到的子集:
7
7
  * db.version(n).stores({...}) / table.get / put / bulkPut / toArray / delete / clear / count
8
8
  */
9
- export declare const DEXIE_SHIM_SOURCE = "\nfunction __vrnDbKey(db, table) { return \"vrn.db:\" + db + \":\" + table; }\n\nclass Table {\n constructor(dbName, name) { this.dbName = dbName; this.name = name; }\n _read() {\n try {\n var raw = localStorage.getItem(__vrnDbKey(this.dbName, this.name));\n return raw ? JSON.parse(raw) : [];\n } catch (e) { return []; }\n }\n _write(rows) {\n try { localStorage.setItem(__vrnDbKey(this.dbName, this.name), JSON.stringify(rows)); } catch (e) {}\n }\n async toArray() { return this._read(); }\n async count() { return this._read().length; }\n async get(key) {\n var keyName = this.keyPath || \"id\";\n return this._read().find(function (r) { return r && r[keyName] === key; });\n }\n async put(row) {\n var keyName = this.keyPath || \"id\";\n var rows = this._read();\n var i = rows.findIndex(function (r) { return r && r[keyName] === row[keyName]; });\n if (i >= 0) rows[i] = row; else rows.push(row);\n this._write(rows);\n return row[keyName];\n }\n async bulkPut(list) {\n var self = this;\n var rows = self._read();\n var keyName = self.keyPath || \"id\";\n (list || []).forEach(function (row) {\n var i = rows.findIndex(function (r) { return r && r[keyName] === row[keyName]; });\n if (i >= 0) rows[i] = row; else rows.push(row);\n });\n self._write(rows);\n return (list || []).length;\n }\n async delete(key) {\n var keyName = this.keyPath || \"id\";\n this._write(this._read().filter(function (r) { return !r || r[keyName] !== key; }));\n }\n async clear() { this._write([]); }\n where() { throw new Error(\"vrn dexie shim: where() \u6682\u4E0D\u652F\u6301\"); }\n orderBy() { throw new Error(\"vrn dexie shim: orderBy() \u6682\u4E0D\u652F\u6301\"); }\n}\n\nclass Version {\n constructor(db) { this._db = db; }\n stores(spec) {\n var db = this._db;\n Object.keys(spec || {}).forEach(function (tableName) {\n var keyPath = String(spec[tableName]).split(\",\")[0].trim();\n var t = new Table(db.name, tableName);\n t.keyPath = keyPath;\n db[tableName] = t;\n });\n return this;\n }\n}\n\nexport default class Dexie {\n constructor(name) { this.name = name; }\n version() { return new Version(this); }\n}\n";
9
+ export declare const DEXIE_SHIM_SOURCE = "\nfunction __vrnDbKey(db, table) { return \"vrn.db:\" + db + \":\" + table; }\n\nclass Table {\n constructor(dbName, name) { this.dbName = dbName; this.name = name; }\n _read() {\n try {\n var raw = localStorage.getItem(__vrnDbKey(this.dbName, this.name));\n return raw ? JSON.parse(raw) : [];\n } catch (e) { return []; }\n }\n _write(rows) {\n try { localStorage.setItem(__vrnDbKey(this.dbName, this.name), JSON.stringify(rows)); } catch (e) {}\n }\n async toArray() {\n var rows = this._read();\n try { (globalThis.__vrnErrs = globalThis.__vrnErrs || []).push(\"toArray:\" + this.name + \"=\" + rows.length); } catch (e) {}\n return rows;\n }\n async count() { return this._read().length; }\n async get(key) {\n var keyName = this.keyPath || \"id\";\n var hit = this._read().find(function (r) { return r && r[keyName] === key; });\n try { (globalThis.__vrnErrs = globalThis.__vrnErrs || []).push(\"get:\" + this.name + \".\" + key + \"=\" + (hit ? \"hit\" : \"miss\")); } catch (e) {}\n return hit;\n }\n async put(row) {\n var keyName = this.keyPath || \"id\";\n var rows = this._read();\n var i = rows.findIndex(function (r) { return r && r[keyName] === row[keyName]; });\n if (i >= 0) rows[i] = row; else rows.push(row);\n this._write(rows);\n return row[keyName];\n }\n async bulkPut(list) {\n var self = this;\n var rows = self._read();\n var keyName = self.keyPath || \"id\";\n (list || []).forEach(function (row) {\n var i = rows.findIndex(function (r) { return r && r[keyName] === row[keyName]; });\n if (i >= 0) rows[i] = row; else rows.push(row);\n });\n self._write(rows);\n return (list || []).length;\n }\n async delete(key) {\n var keyName = this.keyPath || \"id\";\n this._write(this._read().filter(function (r) { return !r || r[keyName] !== key; }));\n }\n async clear() { this._write([]); }\n where() { throw new Error(\"vrn dexie shim: where() \u6682\u4E0D\u652F\u6301\"); }\n orderBy() { throw new Error(\"vrn dexie shim: orderBy() \u6682\u4E0D\u652F\u6301\"); }\n}\n\nclass Version {\n constructor(db) { this._db = db; }\n stores(spec) {\n var db = this._db;\n Object.keys(spec || {}).forEach(function (tableName) {\n var keyPath = String(spec[tableName]).split(\",\")[0].trim();\n var t = new Table(db.name, tableName);\n t.keyPath = keyPath;\n db[tableName] = t;\n });\n return this;\n }\n}\n\nexport default class Dexie {\n constructor(name) { this.name = name; }\n version() { return new Version(this); }\n}\n";
10
10
  /**
11
11
  * R100 P4:`vue` 的最小 shim —— 只是为了让 stores/utils 里的 ref/computed 能在
12
12
  * 原生 JS 运行时(JSC/QuickJS)里跑起来,**不是完整响应式系统**:
@@ -16,10 +16,12 @@ export declare const DEXIE_SHIM_SOURCE = "\nfunction __vrnDbKey(db, table) { ret
16
16
  * · onMounted → 收集回调,由宿主在页面挂载后调用
17
17
  * · watch → 简单轮询式比较(同一个事件循环 tick 内触发)
18
18
  */
19
- export declare const VUE_SHIM_SOURCE = "\nvar __vrnMounted = [];\nexport function ref(v) {\n return { __vrnRef: true, value: v };\n}\nexport function shallowRef(v) { return ref(v); }\nexport function reactive(obj) { return obj; }\nexport function readonly(obj) { return obj; }\nexport function computed(fn) {\n var cache;\n var dirty = true;\n return {\n __vrnComputed: true,\n get value() {\n if (dirty) { cache = fn(); dirty = false; }\n return cache;\n },\n set value(_v) { dirty = true; },\n __invalidate: function () { dirty = true; },\n };\n}\nexport function onMounted(fn) { __vrnMounted.push(fn); }\nexport function onUnmounted() {}\nexport function onBeforeUnmount() {}\nexport function watch(source, cb) {\n var last = typeof source === \"function\" ? source() : source && source.value;\n return setInterval(function () {\n var cur = typeof source === \"function\" ? source() : source && source.value;\n if (cur !== last) { var prev = last; last = cur; cb(cur, prev); }\n }, 16);\n}\nexport function nextTick(fn) { return Promise.resolve().then(fn); }\nexport function toRefs(obj) {\n var out = {};\n Object.keys(obj || {}).forEach(function (k) { out[k] = ref(obj[k]); });\n return out;\n}\nexport function unref(v) { return v && v.__vrnRef ? v.value : v; }\nexport function isRef(v) { return !!(v && v.__vrnRef); }\nexport function __vrnRunMounted() {\n var list = __vrnMounted.slice();\n __vrnMounted.length = 0;\n list.forEach(function (fn) { try { fn(); } catch (e) {} });\n}\nexport default {\n ref: ref, shallowRef: shallowRef, reactive: reactive, readonly: readonly, computed: computed,\n onMounted: onMounted, onUnmounted: onUnmounted, onBeforeUnmount: onBeforeUnmount,\n watch: watch, nextTick: nextTick, toRefs: toRefs, unref: unref, isRef: isRef,\n};\n";
19
+ export declare const VUE_SHIM_SOURCE = "\nvar __vrnMounted = [];\nexport function ref(v) {\n return { __vrnRef: true, value: v };\n}\nexport function shallowRef(v) { return ref(v); }\nexport function reactive(obj) { return obj; }\nexport function readonly(obj) { return obj; }\nexport function computed(fn) {\n // R120\uFF1A\u4E0D\u505A\u7F13\u5B58\u2014\u2014\u539F\u751F\u7AEF\u6CA1\u6709\u4F9D\u8D56\u8FFD\u8E2A\uFF0Cstore.init() \u662F\u5F02\u6B65\u7684\uFF0C\n // \u9996\u6B21\u6C42\u503C\uFF08\u6570\u636E\u672A\u5230\uFF09\u4F1A\u88AB\u6C38\u4E45\u7F13\u5B58\u6210\u7A7A\u503C\u3002\u6BCF\u6B21\u8BFB\u90FD\u91CD\u7B97\uFF0C\u914D\u5408 tick \u540C\u6B65\u5373\u53EF\u3002\n return {\n __vrnComputed: true,\n get value() {\n try {\n return fn();\n } catch (e) {\n return undefined;\n }\n },\n set value(_v) {},\n };\n}\nexport function onMounted(fn) { __vrnMounted.push(fn); }\nexport function onUnmounted() {}\nexport function onBeforeUnmount() {}\nexport function watch(source, cb) {\n var last = typeof source === \"function\" ? source() : source && source.value;\n return setInterval(function () {\n var cur = typeof source === \"function\" ? source() : source && source.value;\n if (cur !== last) { var prev = last; last = cur; cb(cur, prev); }\n }, 16);\n}\nexport function nextTick(fn) { return Promise.resolve().then(fn); }\nexport function toRefs(obj) {\n var out = {};\n Object.keys(obj || {}).forEach(function (k) { out[k] = ref(obj[k]); });\n return out;\n}\nexport function unref(v) { return v && v.__vrnRef ? v.value : v; }\nexport function isRef(v) { return !!(v && v.__vrnRef); }\nexport function __vrnRunMounted() {\n var list = __vrnMounted.slice();\n __vrnMounted.length = 0;\n list.forEach(function (fn) { try { fn(); } catch (e) {} });\n}\nexport default {\n ref: ref, shallowRef: shallowRef, reactive: reactive, readonly: readonly, computed: computed,\n onMounted: onMounted, onUnmounted: onUnmounted, onBeforeUnmount: onBeforeUnmount,\n watch: watch, nextTick: nextTick, toRefs: toRefs, unref: unref, isRef: isRef,\n};\n";
20
20
  /**
21
21
  * R100 P4:`pinia` 的最小 shim —— 让 `stores/*.ts` 的 defineStore 能在运行时可加载。
22
22
  * 支持 options 写法(state/getters/actions)与 setup 写法(函数返回 state 与 action)。
23
23
  * 单例语义:同一 id 只创建一次(与 Pinia 一致),跨页面共享。
24
24
  */
25
- export declare const PINIA_SHIM_SOURCE = "\nvar __vrnStores = {};\nfunction __vrnBind(store, obj) {\n Object.keys(obj || {}).forEach(function (k) {\n var v = obj[k];\n if (typeof v === \"function\") store[k] = v.bind(store);\n else store[k] = v;\n });\n}\nexport function defineStore(id, def) {\n return function useStore() {\n if (__vrnStores[id]) return __vrnStores[id];\n var store = {};\n if (typeof def === \"function\") {\n var ret = def();\n __vrnBind(store, ret);\n Object.keys(ret || {}).forEach(function (k) {\n if (ret[k] && ret[k].__vrnRef) store[k] = ret[k].value;\n });\n } else {\n var state = typeof def.state === \"function\" ? def.state() : def.state || {};\n Object.keys(state).forEach(function (k) { store[k] = state[k]; });\n Object.keys(def.getters || {}).forEach(function (k) {\n var g = def.getters[k];\n Object.defineProperty(store, k, {\n enumerable: true,\n get: function () { try { return g.call(store, store); } catch (e) { return undefined; } },\n });\n });\n __vrnBind(store, def.actions);\n }\n __vrnStores[id] = store;\n return store;\n };\n}\nexport function createPinia() { return {}; }\nexport function storeToRefs(store) {\n var out = {};\n Object.keys(store || {}).forEach(function (k) {\n var v = store[k];\n if (typeof v !== \"function\") out[k] = { __vrnRef: true, get value() { return store[k]; }, set value(x) { store[k] = x; } };\n });\n return out;\n}\nexport default { defineStore: defineStore, createPinia: createPinia, storeToRefs: storeToRefs };\n";
25
+ export declare const PINIA_SHIM_SOURCE = "\nvar __vrnStores = (typeof globalThis !== \"undefined\" && globalThis.__vrnStores) || (globalThis.__vrnStores = {});\nfunction __vrnBind(store, obj) {\n Object.keys(obj || {}).forEach(function (k) {\n var v = obj[k];\n if (typeof v === \"function\") {\n // R119\uFF1Aaction \u5185\u90E8\u5F02\u5E38\u5728\u539F\u751F\u7AEF\u5B8C\u5168\u4E0D\u53EF\u89C1 \u2192 \u8BB0\u5F55\u5230\u5168\u5C40\u9519\u8BEF\u8868\u5E76 toast \u51FA\u6765\n store[k] = function () {\n var self = this;\n var rec = function (e) {\n try {\n var errs = (globalThis.__vrnErrs = globalThis.__vrnErrs || []);\n errs.push(k + \": \" + String((e && e.message) || e));\n if (typeof bridge !== \"undefined\" && bridge && bridge.toast) bridge.toast(\"E(\" + k + \"): \" + String((e && e.message) || e).slice(0, 40));\n } catch (_) {}\n };\n try {\n var r = v.apply(self, arguments);\n if (r && typeof r.then === \"function\") r.then(null, rec);\n return r;\n } catch (e) {\n rec(e);\n throw e;\n }\n };\n } else store[k] = v;\n });\n}\nexport function defineStore(id, def) {\n return function useStore() {\n if (__vrnStores[id]) return __vrnStores[id];\n var store = {};\n if (typeof def === \"function\") {\n var ret = def();\n __vrnBind(store, ret);\n Object.keys(ret || {}).forEach(function (k) {\n if (ret[k] && ret[k].__vrnRef) store[k] = ret[k].value;\n });\n } else {\n var state = typeof def.state === \"function\" ? def.state() : def.state || {};\n Object.keys(state).forEach(function (k) { store[k] = state[k]; });\n Object.keys(def.getters || {}).forEach(function (k) {\n var g = def.getters[k];\n Object.defineProperty(store, k, {\n enumerable: true,\n get: function () { try { return g.call(store, store); } catch (e) { return undefined; } },\n });\n });\n __vrnBind(store, def.actions);\n }\n __vrnStores[id] = store;\n return store;\n };\n}\nexport function createPinia() { return {}; }\nexport function storeToRefs(store) {\n var out = {};\n Object.keys(store || {}).forEach(function (k) {\n var v = store[k];\n if (typeof v !== \"function\") out[k] = { __vrnRef: true, get value() { return store[k]; }, set value(x) { store[k] = x; } };\n });\n return out;\n}\nexport default { defineStore: defineStore, createPinia: createPinia, storeToRefs: storeToRefs };\n";
26
+ /** R118:vue-router 最小 shim(页面里的 useRoute/useRouter 在原生端由编译器接管导航) */
27
+ export declare const VUE_ROUTER_SHIM_SOURCE = "\nexport function useRoute() {\n return {\n params: (typeof globalThis.__vrnRouteParams === \"object\" && globalThis.__vrnRouteParams) || {},\n query: {},\n path: (globalThis.__vrnRoutePath || \"/\"),\n name: \"\",\n };\n}\nexport function useRouter() {\n return {\n push: function (p) { try { if (typeof __routerPush === \"function\") __routerPush(typeof p === \"string\" ? p : (p && (p.name || p.path)) || \"\"); } catch (e) {} },\n replace: function (p) { return this.push(p); },\n back: function () { try { if (typeof __routerBack === \"function\") __routerBack(); } catch (e) {} },\n };\n}\nexport function createRouter(o) { return { options: o, install: function () {} }; }\nexport function createWebHashHistory() { return {}; }\nexport function createWebHistory() { return {}; }\nexport default { useRoute: useRoute, useRouter: useRouter, createRouter: createRouter };\n";
package/dist/shims.js CHANGED
@@ -20,11 +20,17 @@ class Table {
20
20
  _write(rows) {
21
21
  try { localStorage.setItem(__vrnDbKey(this.dbName, this.name), JSON.stringify(rows)); } catch (e) {}
22
22
  }
23
- async toArray() { return this._read(); }
23
+ async toArray() {
24
+ var rows = this._read();
25
+ try { (globalThis.__vrnErrs = globalThis.__vrnErrs || []).push("toArray:" + this.name + "=" + rows.length); } catch (e) {}
26
+ return rows;
27
+ }
24
28
  async count() { return this._read().length; }
25
29
  async get(key) {
26
30
  var keyName = this.keyPath || "id";
27
- return this._read().find(function (r) { return r && r[keyName] === key; });
31
+ var hit = this._read().find(function (r) { return r && r[keyName] === key; });
32
+ try { (globalThis.__vrnErrs = globalThis.__vrnErrs || []).push("get:" + this.name + "." + key + "=" + (hit ? "hit" : "miss")); } catch (e) {}
33
+ return hit;
28
34
  }
29
35
  async put(row) {
30
36
  var keyName = this.keyPath || "id";
@@ -91,16 +97,18 @@ export function shallowRef(v) { return ref(v); }
91
97
  export function reactive(obj) { return obj; }
92
98
  export function readonly(obj) { return obj; }
93
99
  export function computed(fn) {
94
- var cache;
95
- var dirty = true;
100
+ // R120:不做缓存——原生端没有依赖追踪,store.init() 是异步的,
101
+ // 首次求值(数据未到)会被永久缓存成空值。每次读都重算,配合 tick 同步即可。
96
102
  return {
97
103
  __vrnComputed: true,
98
104
  get value() {
99
- if (dirty) { cache = fn(); dirty = false; }
100
- return cache;
105
+ try {
106
+ return fn();
107
+ } catch (e) {
108
+ return undefined;
109
+ }
101
110
  },
102
- set value(_v) { dirty = true; },
103
- __invalidate: function () { dirty = true; },
111
+ set value(_v) {},
104
112
  };
105
113
  }
106
114
  export function onMounted(fn) { __vrnMounted.push(fn); }
@@ -138,12 +146,31 @@ export default {
138
146
  * 单例语义:同一 id 只创建一次(与 Pinia 一致),跨页面共享。
139
147
  */
140
148
  export const PINIA_SHIM_SOURCE = `
141
- var __vrnStores = {};
149
+ var __vrnStores = (typeof globalThis !== "undefined" && globalThis.__vrnStores) || (globalThis.__vrnStores = {});
142
150
  function __vrnBind(store, obj) {
143
151
  Object.keys(obj || {}).forEach(function (k) {
144
152
  var v = obj[k];
145
- if (typeof v === "function") store[k] = v.bind(store);
146
- else store[k] = v;
153
+ if (typeof v === "function") {
154
+ // R119:action 内部异常在原生端完全不可见 → 记录到全局错误表并 toast 出来
155
+ store[k] = function () {
156
+ var self = this;
157
+ var rec = function (e) {
158
+ try {
159
+ var errs = (globalThis.__vrnErrs = globalThis.__vrnErrs || []);
160
+ errs.push(k + ": " + String((e && e.message) || e));
161
+ if (typeof bridge !== "undefined" && bridge && bridge.toast) bridge.toast("E(" + k + "): " + String((e && e.message) || e).slice(0, 40));
162
+ } catch (_) {}
163
+ };
164
+ try {
165
+ var r = v.apply(self, arguments);
166
+ if (r && typeof r.then === "function") r.then(null, rec);
167
+ return r;
168
+ } catch (e) {
169
+ rec(e);
170
+ throw e;
171
+ }
172
+ };
173
+ } else store[k] = v;
147
174
  });
148
175
  }
149
176
  export function defineStore(id, def) {
@@ -183,3 +210,25 @@ export function storeToRefs(store) {
183
210
  }
184
211
  export default { defineStore: defineStore, createPinia: createPinia, storeToRefs: storeToRefs };
185
212
  `;
213
+ /** R118:vue-router 最小 shim(页面里的 useRoute/useRouter 在原生端由编译器接管导航) */
214
+ export const VUE_ROUTER_SHIM_SOURCE = `
215
+ export function useRoute() {
216
+ return {
217
+ params: (typeof globalThis.__vrnRouteParams === "object" && globalThis.__vrnRouteParams) || {},
218
+ query: {},
219
+ path: (globalThis.__vrnRoutePath || "/"),
220
+ name: "",
221
+ };
222
+ }
223
+ export function useRouter() {
224
+ return {
225
+ push: function (p) { try { if (typeof __routerPush === "function") __routerPush(typeof p === "string" ? p : (p && (p.name || p.path)) || ""); } catch (e) {} },
226
+ replace: function (p) { return this.push(p); },
227
+ back: function () { try { if (typeof __routerBack === "function") __routerBack(); } catch (e) {} },
228
+ };
229
+ }
230
+ export function createRouter(o) { return { options: o, install: function () {} }; }
231
+ export function createWebHashHistory() { return {}; }
232
+ export function createWebHistory() { return {}; }
233
+ export default { useRoute: useRoute, useRouter: useRouter, createRouter: createRouter };
234
+ `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongmingzhixiu/vuexir",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "vrn 命令行:build ios|android、preview、doctor",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,8 +21,8 @@
21
21
  "LICENSE"
22
22
  ],
23
23
  "dependencies": {
24
- "@dongmingzhixiu/vuexir-compiler": "^0.4.0",
25
- "@dongmingzhixiu/vuexir-templates": "^0.4.0"
24
+ "@dongmingzhixiu/vuexir-compiler": "^0.4.1",
25
+ "@dongmingzhixiu/vuexir-templates": "^0.4.1"
26
26
  },
27
27
  "engines": {
28
28
  "node": ">=18"