@dongmingzhixiu/vuexir-compiler 0.3.3 → 0.3.4

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.
@@ -48,6 +48,7 @@ export declare function generateRuntimeSource(states: {
48
48
  name: string;
49
49
  init: number | string | boolean;
50
50
  list?: boolean;
51
+ obj?: boolean;
51
52
  computed?: string;
52
53
  }[], handlers: {
53
54
  name: string;
@@ -27,6 +27,20 @@ let storeGetterWarnings = [];
27
27
  let derivedNames = new Map();
28
28
  /** R88 store 文件导出的常量数组(v-for 编译期内联用) */
29
29
  let storeConstArrays = {};
30
+ /** R98 P3 表达式下沉:无法静态翻译的模板表达式 → 运行时求值状态(复用 R96 computed 通道) */
31
+ let jsExprSeq = 0;
32
+ let jsExprStates = [];
33
+ let jsExprStateNames = [];
34
+ /** 把一段 JS 表达式注册为运行时求值状态,返回状态名(模板 bind 用) */
35
+ function addJsExprState(expr) {
36
+ const name = `_vrnE${jsExprSeq++}`;
37
+ // ① ref.value → stateStore.x;② 裸状态名 → stateStore.x(对象态/字符串态都要走 stateStore,
38
+ // 否则 JS 里没有这个变量);③ 形如 fn( 的调用不重写(那是 utils bundle 提供的全局函数)
39
+ let js = expr.replace(/\b([A-Za-z_$][\w$]*)\.value\b/g, (all, id) => jsExprStateNames.includes(id) ? `stateStore.${id}` : all);
40
+ js = js.replace(/(?<![.\w$])([A-Za-z_$][\w$]*)(?!\s*\()/g, (all, id) => jsExprStateNames.includes(id) ? `stateStore.${id}` : all);
41
+ jsExprStates.push({ name, init: '', computed: js });
42
+ return name;
43
+ }
30
44
  /** R86 store 别名表:`ui` → StoreDef(每次 vueToIr 重建;与 vforConsts 同风格) */
31
45
  let storeAliases = new Map();
32
46
  /** R86 把 `alias.field.path` 拍平为 `alias_field.path`;getter/action 返回哨兵串 */
@@ -293,26 +307,34 @@ export function generateRuntimeSource(states, handlers, onMounted, onMountedAsyn
293
307
  // R96 computed 求值:页面里的 computed(() => expr) 原样在 JS 里算,结果写回状态
294
308
  function __vrnRecompute() {
295
309
  ${computedStates
296
- .map((st) => ` try { stateStore.${st.name} = String(${st.computed}); } catch (e) { /* 求值失败保持初值 */ }`)
310
+ .map((st) =>
311
+ // 注意:求值状态在 IR 里以空串声明(原生 @State 是 String),必须字符串化后再推送,
312
+ // 否则 iOS 侧类型不匹配会丢掉值;代价是这条链上的算术会退化成字符串拼接(见 RULES R98 限制)
313
+ ` try { var __v = (${st.computed}); stateStore.${st.name} = (__v === null || __v === undefined) ? '' : String(__v); } catch (e) { /* 求值失败保持初值 */ }`)
297
314
  .join('\n')}
298
315
  }
299
316
  __vrnRecompute();
300
317
  `;
301
318
  const initPairs = states.map((s) => {
302
319
  // M4b 动态列表态:list → JS 真数组('[]' 或 '["a","b"]' JSON 字面量,Swift JSONSerialization 可解码)
303
- const v = s.list
304
- ? String(s.init)
305
- : s.init === '[]'
306
- ? '[]'
307
- : typeof s.init === 'number'
308
- ? String(s.init)
309
- : typeof s.init === 'boolean'
310
- ? (s.init ? 'true' : 'false')
311
- : `"${s.init.replace(/"/g, '\\"')}"`;
320
+ // R98:对象态在 JS 侧要是真对象(原生侧仍收 JSON 字符串),否则 {{ form.name }} 求值失败
321
+ const v = s.obj && typeof s.init === 'string'
322
+ ? `JSON.parse(${JSON.stringify(s.init)})`
323
+ : s.list
324
+ ? String(s.init)
325
+ : s.init === '[]'
326
+ ? '[]'
327
+ : typeof s.init === 'number'
328
+ ? String(s.init)
329
+ : typeof s.init === 'boolean'
330
+ ? (s.init ? 'true' : 'false')
331
+ : `"${s.init.replace(/"/g, '\\"')}"`;
312
332
  return ` ${s.name}: ${v}`;
313
333
  });
314
334
  const handlerSrc = handlers
315
- .map((h) => `${h.async ? 'async ' : ''}function ${h.name}(${h.params}) {\n${indentBody(h.body)}\n}`)
335
+ .map((h) => `${h.async ? 'async ' : ''}function ${h.name}(${h.params}) {\n${indentBody(h.body)}\n${
336
+ // R96/R98:改完状态后重算派生值(computed / 表达式下沉),否则界面不会跟着变
337
+ (states.some((st) => st.computed) ? ' if (typeof __vrnRecompute === "function") __vrnRecompute();\n' : '')}}`)
316
338
  .join('\n');
317
339
  return `// vue-render-native · M3 状态运行时(JavaScriptCore 宿主;语义与 QuickJS 对齐,R12)
318
340
  // stateStore:Swift @State 与 JS 侧状态注册表的双向同步桥(JS 改 → __setState 写回原生;Swift push 经 __fromSwift 防回环)
@@ -880,6 +902,8 @@ export function vueToIr(source, opts = {}) {
880
902
  storeConstArrays = {};
881
903
  derivedNames = new Map();
882
904
  storeGetterWarnings = [];
905
+ jsExprSeq = 0;
906
+ jsExprStates = [];
883
907
  const warnings = [];
884
908
  const { descriptor, errors } = parse(source, { filename: 'Screen.vue' });
885
909
  if (errors.length > 0) {
@@ -1060,6 +1084,8 @@ export function vueToIr(source, opts = {}) {
1060
1084
  if (value !== undefined)
1061
1085
  stateInfo.states.push({ name, init: value });
1062
1086
  }
1087
+ // R98:把当前状态名快照给表达式下沉用(walk 阶段据此把裸状态名重写成 stateStore.x)
1088
+ jsExprStateNames = stateInfo.states.map((st) => st.name);
1063
1089
  // R74:状态名撞原生保留成员(SwiftUI `body`、Kotlin 关键字)→ 自动改名 + 警告(不再只警告导致后续编译失败)
1064
1090
  {
1065
1091
  const RESERVED = ['body', 'self', 'init', 'class', 'object', 'fun', 'val', 'var', 'when', 'is', 'in'];
@@ -1223,6 +1249,15 @@ export function vueToIr(source, opts = {}) {
1223
1249
  }
1224
1250
  // 5) <script setup> 逻辑源码 → 运行时 JS(R12:事件逻辑由原生 JS 引擎执行)
1225
1251
  // 仅支持 JS 子集(无 TS 类型标注、无 import);超出即编译期警告(后续阶段扩展)
1252
+ // R98 P3:把模板表达式下沉产生的求值状态并入页面状态,并重建 runtime(含 recompute)
1253
+ if (jsExprStates.length > 0 && stateInfo) {
1254
+ for (const st of jsExprStates) {
1255
+ if (!stateInfo.states.some((x) => x.name === st.name))
1256
+ stateInfo.states.push({ ...st });
1257
+ }
1258
+ runtime = generateRuntimeSource(stateInfo.states, handlers ?? [], onMountedBody, onMountedAsync);
1259
+ warnings.push(`模板表达式下沉:${jsExprStates.length} 处动态表达式改由 JS 运行时求值(R98)`);
1260
+ }
1226
1261
  for (const w of storeGetterWarnings)
1227
1262
  if (!warnings.includes(w))
1228
1263
  warnings.push(w);
@@ -1829,7 +1864,9 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
1829
1864
  parts.push({ text: ' ' });
1830
1865
  continue;
1831
1866
  }
1832
- throw new VueCompileError(`插值表达式 {{ ${name} }} 暂不支持(M1 仅支持纯状态名;三元/比较表达式 M2 白名单;动态列表项属性 M4b)`);
1867
+ // R98:元素文本里的复杂表达式同样下沉给 JS 运行时
1868
+ parts.push({ bind: addJsExprState(textOf(child.content).trim()) });
1869
+ continue;
1833
1870
  }
1834
1871
  // 混合段:静态文本之后的插值
1835
1872
  if (dyn)
@@ -1845,7 +1882,8 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
1845
1882
  parts.push({ fieldOf: { obj: mp.obj, field: mp.field } });
1846
1883
  }
1847
1884
  else {
1848
- throw new VueCompileError(`混合文本内插值 {{ ${name} }} 暂不支持(仅支持已声明状态 / 循环变量 / 动态列表项属性 / 已声明对象的点路径 R85)`);
1885
+ // R98:混合文本里的复杂表达式同样下沉
1886
+ parts.push({ bind: addJsExprState(textOf(child.content).trim()) });
1849
1887
  }
1850
1888
  }
1851
1889
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongmingzhixiu/vuexir-compiler",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Vue SFC → IR → 原生源码(SwiftUI / Compose)编译器核心:纯函数、零 IO",
5
5
  "type": "module",
6
6
  "license": "MIT",