@dongmingzhixiu/vuexir 0.4.0 → 0.4.2
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 +12 -0
- package/dist/bundle.js +67 -2
- package/dist/gen-ios.js +22 -5
- package/dist/shims.d.ts +5 -3
- package/dist/shims.js +79 -12
- package/package.json +3 -3
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
|
-
|
|
202
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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() {
|
|
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
|
|
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.
|
|
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 () {\n try {\n return g.call(store, store);\n } catch (e) {\n // R124\uFF1Agetter \u5F02\u5E38\u5728\u539F\u751F\u7AEF\u4E0D\u53EF\u89C1 \u2192 \u8BB0\u5F55\u5E76 toast \u4E00\u6B21\n try {\n if (!globalThis.__vrnGetErr) {\n globalThis.__vrnGetErr = {};\n }\n if (!globalThis.__vrnGetErr[k]) {\n globalThis.__vrnGetErr[k] = 1;\n if (typeof bridge !== \"undefined\" && bridge && bridge.toast) {\n bridge.toast(\"G(\" + k + \"): \" + String((e && e.message) || e).slice(0, 36));\n }\n }\n } catch (_) {}\n return undefined;\n }\n },\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() {
|
|
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
|
-
|
|
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
|
-
|
|
95
|
-
|
|
100
|
+
// R120:不做缓存——原生端没有依赖追踪,store.init() 是异步的,
|
|
101
|
+
// 首次求值(数据未到)会被永久缓存成空值。每次读都重算,配合 tick 同步即可。
|
|
96
102
|
return {
|
|
97
103
|
__vrnComputed: true,
|
|
98
104
|
get value() {
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
try {
|
|
106
|
+
return fn();
|
|
107
|
+
} catch (e) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
101
110
|
},
|
|
102
|
-
set value(_v) {
|
|
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")
|
|
146
|
-
|
|
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) {
|
|
@@ -163,7 +190,25 @@ export function defineStore(id, def) {
|
|
|
163
190
|
var g = def.getters[k];
|
|
164
191
|
Object.defineProperty(store, k, {
|
|
165
192
|
enumerable: true,
|
|
166
|
-
get: function () {
|
|
193
|
+
get: function () {
|
|
194
|
+
try {
|
|
195
|
+
return g.call(store, store);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
// R124:getter 异常在原生端不可见 → 记录并 toast 一次
|
|
198
|
+
try {
|
|
199
|
+
if (!globalThis.__vrnGetErr) {
|
|
200
|
+
globalThis.__vrnGetErr = {};
|
|
201
|
+
}
|
|
202
|
+
if (!globalThis.__vrnGetErr[k]) {
|
|
203
|
+
globalThis.__vrnGetErr[k] = 1;
|
|
204
|
+
if (typeof bridge !== "undefined" && bridge && bridge.toast) {
|
|
205
|
+
bridge.toast("G(" + k + "): " + String((e && e.message) || e).slice(0, 36));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} catch (_) {}
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
},
|
|
167
212
|
});
|
|
168
213
|
});
|
|
169
214
|
__vrnBind(store, def.actions);
|
|
@@ -183,3 +228,25 @@ export function storeToRefs(store) {
|
|
|
183
228
|
}
|
|
184
229
|
export default { defineStore: defineStore, createPinia: createPinia, storeToRefs: storeToRefs };
|
|
185
230
|
`;
|
|
231
|
+
/** R118:vue-router 最小 shim(页面里的 useRoute/useRouter 在原生端由编译器接管导航) */
|
|
232
|
+
export const VUE_ROUTER_SHIM_SOURCE = `
|
|
233
|
+
export function useRoute() {
|
|
234
|
+
return {
|
|
235
|
+
params: (typeof globalThis.__vrnRouteParams === "object" && globalThis.__vrnRouteParams) || {},
|
|
236
|
+
query: {},
|
|
237
|
+
path: (globalThis.__vrnRoutePath || "/"),
|
|
238
|
+
name: "",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
export function useRouter() {
|
|
242
|
+
return {
|
|
243
|
+
push: function (p) { try { if (typeof __routerPush === "function") __routerPush(typeof p === "string" ? p : (p && (p.name || p.path)) || ""); } catch (e) {} },
|
|
244
|
+
replace: function (p) { return this.push(p); },
|
|
245
|
+
back: function () { try { if (typeof __routerBack === "function") __routerBack(); } catch (e) {} },
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
export function createRouter(o) { return { options: o, install: function () {} }; }
|
|
249
|
+
export function createWebHashHistory() { return {}; }
|
|
250
|
+
export function createWebHistory() { return {}; }
|
|
251
|
+
export default { useRoute: useRoute, useRouter: useRouter, createRouter: createRouter };
|
|
252
|
+
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dongmingzhixiu/vuexir",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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.
|
|
25
|
-
"@dongmingzhixiu/vuexir-templates": "^0.4.
|
|
24
|
+
"@dongmingzhixiu/vuexir-compiler": "^0.4.2",
|
|
25
|
+
"@dongmingzhixiu/vuexir-templates": "^0.4.2"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": ">=18"
|