@dongmingzhixiu/vuexir-compiler 0.3.1 → 0.3.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/ir/schema.d.ts
CHANGED
|
@@ -265,6 +265,8 @@ export interface IrScreen {
|
|
|
265
265
|
init: number | string | boolean;
|
|
266
266
|
list?: boolean;
|
|
267
267
|
obj?: boolean;
|
|
268
|
+
/** R96 computed 表达式(JS 运行时求值后写回该状态) */
|
|
269
|
+
computed?: string;
|
|
268
270
|
}[];
|
|
269
271
|
/** M3 <script setup> 提取的 handler 函数(@click="fn()" 引用;body 已做 .value → stateStore 重写)
|
|
270
272
|
* R72:async:true 表示 `async function`(JS 侧保留 async 关键字;Android 侧按协程形状生成) */
|
|
@@ -8,6 +8,8 @@ export interface ScreenStateInfo {
|
|
|
8
8
|
init: number | string | boolean;
|
|
9
9
|
list?: boolean;
|
|
10
10
|
obj?: boolean;
|
|
11
|
+
/** R96 computed 表达式(JS 运行时求值后写回该状态) */
|
|
12
|
+
computed?: string;
|
|
11
13
|
}[];
|
|
12
14
|
/** setter 函数名 → { target: 状态变量名, param: 形参名, expr: 无参函数体的赋值表达式 } */
|
|
13
15
|
setters: Record<string, {
|
|
@@ -46,6 +48,7 @@ export declare function generateRuntimeSource(states: {
|
|
|
46
48
|
name: string;
|
|
47
49
|
init: number | string | boolean;
|
|
48
50
|
list?: boolean;
|
|
51
|
+
computed?: string;
|
|
49
52
|
}[], handlers: {
|
|
50
53
|
name: string;
|
|
51
54
|
params: string;
|
package/dist/transform/vue2ir.js
CHANGED
|
@@ -285,6 +285,19 @@ export function rewriteStateRefs(body, names) {
|
|
|
285
285
|
}
|
|
286
286
|
/** 生成 runtime.js 完整内容(M3:stateStore Proxy + handlers + onMounted + localStorage shim) */
|
|
287
287
|
export function generateRuntimeSource(states, handlers, onMounted, onMountedAsync = false) {
|
|
288
|
+
// R96 computed:JS 运行时按表达式求值并写回 stateStore(写回经 Proxy → pushState 直达原生)
|
|
289
|
+
const computedStates = states.filter((st) => st.computed);
|
|
290
|
+
const computedBlock = computedStates.length === 0
|
|
291
|
+
? ''
|
|
292
|
+
: `
|
|
293
|
+
// R96 computed 求值:页面里的 computed(() => expr) 原样在 JS 里算,结果写回状态
|
|
294
|
+
function __vrnRecompute() {
|
|
295
|
+
${computedStates
|
|
296
|
+
.map((st) => ` try { stateStore.${st.name} = String(${st.computed}); } catch (e) { /* 求值失败保持初值 */ }`)
|
|
297
|
+
.join('\n')}
|
|
298
|
+
}
|
|
299
|
+
__vrnRecompute();
|
|
300
|
+
`;
|
|
288
301
|
const initPairs = states.map((s) => {
|
|
289
302
|
// M4b 动态列表态:list → JS 真数组('[]' 或 '["a","b"]' JSON 字面量,Swift JSONSerialization 可解码)
|
|
290
303
|
const v = s.list
|
|
@@ -424,7 +437,7 @@ var router = {
|
|
|
424
437
|
};
|
|
425
438
|
// handlers(<script setup> 函数体:ref.value → stateStore 重写)
|
|
426
439
|
${handlerSrc}
|
|
427
|
-
// onMounted(Swift onAppear → invoke("__onMounted"))
|
|
440
|
+
${computedBlock}// onMounted(Swift onAppear → invoke("__onMounted"))
|
|
428
441
|
${onMounted !== undefined ? `${onMountedAsync ? 'async ' : ''}function __onMounted() {\n${indentBody(onMounted)}\n}` : ''}`;
|
|
429
442
|
}
|
|
430
443
|
/** 简单缩进(handler 体统一 2 空格缩进) */
|
|
@@ -994,6 +1007,33 @@ export function vueToIr(source, opts = {}) {
|
|
|
994
1007
|
derivedNames.set(cm[1], flat);
|
|
995
1008
|
}
|
|
996
1009
|
}
|
|
1010
|
+
// R96 computed 求值:`const X = computed(() => expr)` 状态自足时注册为「运行时求值状态」
|
|
1011
|
+
// expr 里的 ref.value 会被重写成 stateStore.X;含未知标识符(外部函数/未声明变量)则跳过,
|
|
1012
|
+
// 留给后续 P2 打包阶段(需要 utils 进 bundle 才能算)
|
|
1013
|
+
{
|
|
1014
|
+
const stateNames = stateInfo.states.map((st) => st.name);
|
|
1015
|
+
const SAFE = new Set([
|
|
1016
|
+
'Math', 'String', 'Number', 'Boolean', 'Date', 'JSON', 'parseInt', 'parseFloat',
|
|
1017
|
+
'undefined', 'null', 'true', 'false', 'Array', 'Object', 'stateStore',
|
|
1018
|
+
]);
|
|
1019
|
+
for (const cm of src.matchAll(/const\s+([A-Za-z_$][\w$]*)\s*=\s*computed\s*\(\s*\(\s*\)\s*=>\s*([^\n]+?)\s*\)\s*$/gm)) {
|
|
1020
|
+
const name = cm[1];
|
|
1021
|
+
const rawExpr = cm[2].trim();
|
|
1022
|
+
if (stateInfo.states.some((st) => st.name === name))
|
|
1023
|
+
continue;
|
|
1024
|
+
const jsExpr = rawExpr.replace(/\b([A-Za-z_$][\w$]*)\.value\b/g, (all, id) => stateNames.includes(id) ? `stateStore.${id}` : all);
|
|
1025
|
+
const idents = [...jsExpr.matchAll(/(?<![.\w$])([A-Za-z_$][\w$]*)/g)].map((m2) => m2[1]);
|
|
1026
|
+
const unknown = idents.filter((id) => !SAFE.has(id) && !/^stateStore$/.test(id) && !stateNames.includes(id));
|
|
1027
|
+
// 未知标识符只允许出现在字符串字面量里(粗筛:去掉引号内容再判一次)
|
|
1028
|
+
const stripped = jsExpr.replace(/'[^']*'|"[^"]*"|`[^`]*`/g, '""');
|
|
1029
|
+
const unknown2 = [...stripped.matchAll(/(?<![.\w$])([A-Za-z_$][\w$]*)/g)]
|
|
1030
|
+
.map((m2) => m2[1])
|
|
1031
|
+
.filter((id) => !SAFE.has(id) && !stateNames.includes(id));
|
|
1032
|
+
if (unknown.length === 0 || unknown2.length === 0) {
|
|
1033
|
+
stateInfo.states.push({ name, init: '', computed: jsExpr });
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
997
1037
|
// R89 常量折叠:模块级纯常量(含可静态求值的模板字面量)注入为状态
|
|
998
1038
|
for (const km of src.matchAll(/const\s+([A-Za-z_$][\w$]*)\s*(?::[^=]+)?=\s*(`[^`]*`|'[^']*'|"[^"]*"|-?\d+(?:\.\d+)?)\s*$/gm)) {
|
|
999
1039
|
const raw = km[2];
|
|
@@ -1068,7 +1108,8 @@ export function vueToIr(source, opts = {}) {
|
|
|
1068
1108
|
if (onMountedBody !== undefined)
|
|
1069
1109
|
onMountedBody = renameSrc(onMountedBody);
|
|
1070
1110
|
}
|
|
1071
|
-
|
|
1111
|
+
const hasComputed = (stateInfo?.states ?? []).some((st) => st.computed);
|
|
1112
|
+
if (handlers.length > 0 || onMountedBody !== undefined || hasComputed) {
|
|
1072
1113
|
runtime = generateRuntimeSource(stateInfo?.states ?? [], handlers, onMountedBody, onMountedAsync);
|
|
1073
1114
|
}
|
|
1074
1115
|
}
|