@dongmingzhixiu/vuexir-compiler 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.
@@ -474,6 +474,13 @@ function kotlinCondExpr(expr, ctx, loop) {
474
474
  const cmpField = s.match(/^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)\s*(==|!=)\s*(.+)$/);
475
475
  if (cmpField)
476
476
  return `vrnField(${cmpField[1]}, "${cmpField[2]}") ${cmpField[3]} ${cmpField[4]}`;
477
+ // R121:复合条件逐段翻译
478
+ if (/&&|\|\|/.test(s) && !/[()]/.test(s)) {
479
+ const parts = s.split(/(&&|\|\|)/);
480
+ return parts
481
+ .map((seg) => (seg === '&&' || seg === '||' ? seg : kotlinCondExpr(seg.trim(), ctx, loop)))
482
+ .join(' ');
483
+ }
477
484
  const andExpr = s.match(/^([A-Za-z_$][\w$.]*)\s*&&\s*([A-Za-z_$][\w$.]*)$/);
478
485
  if (andExpr) {
479
486
  const truthy = (v) => v.includes('.') ? `vrnField(${v.split('.')[0]}, "${v.split('.').slice(1).join('.')}").isNotEmpty()` : `${v}.toString().isNotEmpty()`;
@@ -539,6 +539,15 @@ function swiftCondExpr(expr, known) {
539
539
  // R110:数值状态直接当条件(Swift 不接受 Int 当 Bool)
540
540
  if (/^[A-Za-z_$][\w$]*$/.test(s))
541
541
  return `vrnTruthy(String(${s}))`;
542
+ // R121:复合条件(a && b / a || b / !a)→ 逐段翻译后重组(每段按单条件规则判真)
543
+ if (/&&|\|\|/.test(s) && !/__vrnField__/.test(s) && !/[()]/.test(s)) {
544
+ const parts = s.split(/(&&|\|\|)/);
545
+ return parts
546
+ .map((seg) => (seg === '&&' || seg === '||' ? seg : swiftCondExpr(seg.trim(), known)))
547
+ .join(' ');
548
+ }
549
+ if (/^![A-Za-z_$][\w$]*$/.test(s))
550
+ return `!vrnTruthy(String(${s.slice(1)}))`;
542
551
  // R113:对象字段条件(form.avatarData)→ 走 vrnField 取 JSON 字段
543
552
  const negField = s.match(/^!([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/);
544
553
  if (negField)
@@ -49,6 +49,10 @@ let scriptDeclaredNames = new Set();
49
49
  let storeAliases = new Map();
50
50
  /** R86 把 `alias.field.path` 拍平为 `alias_field.path`;getter/action 返回哨兵串 */
51
51
  function flattenStorePath(name) {
52
+ // R123:页面脚本自己的响应式绑定(soon/nearest/…)由运行时同步填充,
53
+ // 绝不能再被 store 折叠改写成 store_xxx(否则 v-for 会去读静态状态,永远是空)
54
+ if (jsExprStateNames.includes(name))
55
+ return name;
52
56
  // R89:computed 折叠出的派生名(all → ui_upcoming;all.length → ui_upcoming.length)
53
57
  if (derivedNames.size > 0) {
54
58
  const m0 = name.match(/^([A-Za-z_$][\w$]*)(\..+)?$/);
@@ -323,6 +327,8 @@ __vrnRecompute();
323
327
  var __n = 0;
324
328
  function __tick() {
325
329
  __vrnRecompute();
330
+ // R122:页面脚本(__vrnPage)的真实值必须最后写入,否则会被静态 computed 覆盖成空
331
+ __vrnSyncPage();
326
332
  if (++__n < 20 && typeof setTimeout === "function") setTimeout(__tick, 100);
327
333
  }
328
334
  __tick();
@@ -332,7 +338,7 @@ __vrnRecompute();
332
338
  // M4b 动态列表态:list → JS 真数组('[]' 或 '["a","b"]' JSON 字面量,Swift JSONSerialization 可解码)
333
339
  // R98:对象态在 JS 侧要是真对象(原生侧仍收 JSON 字符串),否则 {{ form.name }} 求值失败
334
340
  const v = s.obj && typeof s.init === 'string'
335
- ? `JSON.parse(${JSON.stringify(s.init)})`
341
+ ? `(function(){try{return JSON.parse(${JSON.stringify(s.init)});}catch(e){return {};}})()`
336
342
  : s.list
337
343
  ? String(s.init)
338
344
  : s.init === '[]'
@@ -470,6 +476,53 @@ var router = {
470
476
  if (typeof __routerBack === "function") __routerBack();
471
477
  }
472
478
  };
479
+ // R118 页面状态同步:把页面脚本 bundle(__vrnPage)里的 ref/computed 值写进 stateStore,
480
+ // 模板里 {{ nearest }} / {{ soon }} 这类页面级派生值就有了真实来源
481
+ // 把数组里的嵌套对象拍平成「点键」,让 v-for 里的 row.item.name 能直接取到
482
+ function __vrnFlat(v) {
483
+ function flatObj(o, prefix, out) {
484
+ for (var k in o) {
485
+ var val = o[k];
486
+ var key = prefix ? prefix + "." + k : k;
487
+ if (val && typeof val === "object" && !Array.isArray(val)) flatObj(val, key, out);
488
+ else if (Array.isArray(val)) out[key] = "[" + val.length + "]";
489
+ else out[key] = String(val);
490
+ }
491
+ return out;
492
+ }
493
+ if (Array.isArray(v)) return v.map(function (it) { return (it && typeof it === "object") ? flatObj(it, "", {}) : it; });
494
+ return v;
495
+ }
496
+ function __vrnSyncPage() {
497
+ try {
498
+ var page = typeof __vrnPage !== "undefined" ? __vrnPage : (typeof globalThis !== "undefined" ? globalThis.__vrnPage : undefined);
499
+ if (!page) return;
500
+ for (var k in page) {
501
+ try {
502
+ var v = page[k];
503
+ var val;
504
+ if (v && typeof v === "object" && "value" in v) val = v.value; // ref / computed
505
+ else continue; // 函数/常量交给模板静态值
506
+ if (val === undefined || val === null) continue;
507
+ stateStore[k] = (typeof val === "object") ? JSON.stringify(__vrnFlat(val)) : val;
508
+ } catch (e) {}
509
+ }
510
+ } catch (e) {}
511
+ }
512
+ // R116:store 引导——原生端没有 main.ts 的启动序列,这里对所有 useXxxStore 单例触发 init()
513
+ (function () {
514
+ try {
515
+ var names = Object.getOwnPropertyNames(globalThis).filter(function (n) {
516
+ return /^use[A-Za-z0-9_$]*Store$/.test(n) && typeof globalThis[n] === "function";
517
+ });
518
+ names.forEach(function (n) {
519
+ try {
520
+ var st = globalThis[n]();
521
+ if (st && typeof st.init === "function") st.init();
522
+ } catch (e) {}
523
+ });
524
+ } catch (e) {}
525
+ })();
473
526
  // handlers(<script setup> 函数体:ref.value → stateStore 重写)
474
527
  ${handlerSrc}
475
528
  ${computedBlock}${"" /* R102 见 computedBlock 内:仅当存在派生状态时才调度重算循环,避免无谓的定时器 */}// onMounted(Swift onAppear → invoke("__onMounted"))
@@ -1117,6 +1170,23 @@ export function vueToIr(source, opts = {}) {
1117
1170
  if (value !== undefined)
1118
1171
  stateInfo.states.push({ name, init: value });
1119
1172
  }
1173
+ // R121:页面脚本里的 computed/普通绑定在运行时由 __vrnSyncPage 回填,
1174
+ // 这里先注册为状态(init 空串),模板的 v-for / v-if 就能引用它们
1175
+ {
1176
+ const RESERVED2 = new Set(['body', 'self', 'init', 'class', 'object', 'fun', 'val', 'var', 'when', 'is', 'in', 'store']);
1177
+ // 只注册 computed/ref/reactive 绑定(普通常量数组已由代码生成侧处理,重复注册会撞名)
1178
+ const reactiveBindings = new Set();
1179
+ for (const m2 of src.matchAll(/const\s+([A-Za-z_$][\w$]*)\s*=\s*(?:computed|ref|reactive|shallowRef|toRef)\s*\(/g)) {
1180
+ reactiveBindings.add(m2[1]);
1181
+ }
1182
+ for (const nm of reactiveBindings) {
1183
+ if (RESERVED2.has(nm))
1184
+ continue;
1185
+ if (stateInfo.states.some((st) => st.name === nm))
1186
+ continue;
1187
+ stateInfo.states.push({ name: nm, init: '' });
1188
+ }
1189
+ }
1120
1190
  // R98:把当前状态名快照给表达式下沉用(walk 阶段据此把裸状态名重写成 stateStore.x)
1121
1191
  jsExprStateNames = stateInfo.states.map((st) => st.name);
1122
1192
  // R74:状态名撞原生保留成员(SwiftUI `body`、Kotlin 关键字)→ 自动改名 + 警告(不再只警告导致后续编译失败)
@@ -1931,7 +2001,7 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
1931
2001
  const dot = name.split('.');
1932
2002
  const base = dot[0];
1933
2003
  const loopVar = loopScope.find((l) => l.itemKey === base || l.indexKey === base);
1934
- const dyn = loopVar && dot.length === 2 && dot[0] === loopVar.itemKey && /^\w+$/.test(dot[1]);
2004
+ const dyn = loopVar && dot.length >= 2 && dot[0] === loopVar.itemKey && dot.slice(1).every((seg) => /^\w+$/.test(seg));
1935
2005
  if (!text && !parts.some((p) => 'bind' in p || 'dynField' in p || 'fieldOf' in p)) {
1936
2006
  // M2 模板内三元(D1 小范围):cond ? 'a' : 'b' → Swift 三元字符串表达式(Text(expr))
1937
2007
  const tri = parseTernaryExpr(name);
@@ -1941,7 +2011,7 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
1941
2011
  }
1942
2012
  // M4b 单插值动态列表项属性 {{ item.field }} → 单段 bindParts
1943
2013
  if (dyn) {
1944
- parts.push({ dynField: { var: dot[0], field: dot[1] } });
2014
+ parts.push({ dynField: { var: dot[0], field: dot.slice(1).join('.') } });
1945
2015
  continue;
1946
2016
  }
1947
2017
  if (name.startsWith('\u0000store:')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongmingzhixiu/vuexir-compiler",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Vue SFC → IR → 原生源码(SwiftUI / Compose)编译器核心:纯函数、零 IO",
5
5
  "type": "module",
6
6
  "license": "MIT",