@dongmingzhixiu/vuexir-compiler 0.4.1 → 0.4.3

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.
@@ -439,6 +439,10 @@ function safeBind(name, ctx, loop) {
439
439
  return '""';
440
440
  }
441
441
  function kotlinCondExpr(expr, ctx, loop) {
442
+ // R128:下沉的条件状态 → 判真
443
+ const sunk = expr.match(/^__vrnCond:(\w+)$/);
444
+ if (sunk)
445
+ return `vrnTruthy(${sunk[1]}.toString())`;
442
446
  // R114:条件里引用了原生端不存在的标识符 → 回退为 true(保留内容)并告警,避免整页编译失败
443
447
  {
444
448
  // 先剥掉字符串字面量,避免把 "success" 之类当成标识符
@@ -511,6 +511,10 @@ function emitNode(node, ctx) {
511
511
  }
512
512
  /** JS 条件表达式 → Swift 兼容:===/!== 为 JS 严格比较,Swift 用 ==/!=;字符串字面量单引号 → 双引号 */
513
513
  function swiftCondExpr(expr, known) {
514
+ // R128:下沉的条件状态 → 判真
515
+ const sunk = expr.match(/^__vrnCond:(\w+)$/);
516
+ if (sunk)
517
+ return `vrnTruthy(${sunk[1]})`;
514
518
  // R114:条件引用原生端不存在的标识符 → true(保留内容),避免整页编译失败
515
519
  const knownSet = known && known.size > 0 ? known : currentStateNames;
516
520
  if (knownSet && knownSet.size > 0) {
@@ -810,7 +814,7 @@ function emitNodeInner(node, ctx) {
810
814
  let line = isRef
811
815
  ? pad(`Text(${props.ref})`)
812
816
  : props.bindExpr !== undefined
813
- ? pad(`Text(${props.bindExpr})`)
817
+ ? pad(`Text(${String(props.bindExpr).includes('.') ? '""' : props.bindExpr})`)
814
818
  : props.fieldOf !== undefined
815
819
  ? pad(`Text(vrnField(${props.fieldOf.obj}, "${props.fieldOf.field}"${props.fieldOf.fallback !== undefined ? `, "${swiftStringEscape(props.fieldOf.fallback)}"` : ''}))`)
816
820
  : props.dynField !== undefined
@@ -41,6 +41,8 @@ function addJsExprState(expr) {
41
41
  jsExprStates.push({ name, init: '', computed: js });
42
42
  return name;
43
43
  }
44
+ /** R125 当前生效的 v-for 作用域栈(绕开 loopScope 透传不一致的问题) */
45
+ let activeLoops = [];
44
46
  /** R115 是否允许未注册组件降级(由 vueToIr 选项设置) */
45
47
  let allowUnknownComponentsFlag = false;
46
48
  /** R104 脚本里声明过的名字(const/let/function)——未登记为 state 时走运行时求值 */
@@ -979,6 +981,7 @@ export function vueToIr(source, opts = {}) {
979
981
  jsExprSeq = 0;
980
982
  jsExprStates = [];
981
983
  allowUnknownComponentsFlag = opts.allowUnknownComponents === true;
984
+ activeLoops = [];
982
985
  const warnings = [];
983
986
  const { descriptor, errors } = parse(source, { filename: 'Screen.vue' });
984
987
  if (errors.length > 0) {
@@ -1417,12 +1420,18 @@ function walk(node, classProps, comboRules, scopedRules, pseudoRules, warnings,
1417
1420
  if (tri)
1418
1421
  return [{ type: 'text', props: { bindExpr: tri } }];
1419
1422
  // M2:v-for 循环变量(item/index)优先于状态表
1420
- const loopVar = loopScope.find((l) => l.itemKey === name || l.indexKey === name);
1423
+ const loopVar = loopScope.find((l) => l.itemKey === name || l.indexKey === name) ??
1424
+ [...activeLoops].reverse().find((l) => l.itemKey === name.split('.')[0] || l.indexKey === name.split('.')[0]);
1421
1425
  if (loopVar) {
1422
- // M4b 动态列表项属性访问 {{ item.field }} → dynField(Swift 侧 [String: Any] 字段取值)
1426
+ // M4b 动态列表项属性访问 {{ item.field }} / item.a.b → dynField(点路径拍平键)
1423
1427
  const dot = name.split('.');
1424
- if (dot.length === 2 && dot[0] === loopVar.itemKey && /^\w+$/.test(dot[1])) {
1425
- return [{ type: 'text', props: { dynField: { var: dot[0], field: dot[1] } } }];
1428
+ if (dot.length >= 2 && dot[0] === loopVar.itemKey && dot.slice(1).every((seg) => /^\w+$/.test(seg))) {
1429
+ return [{ type: 'text', props: { dynField: { var: dot[0], field: dot.slice(1).join('.') } } }];
1430
+ }
1431
+ // R126:循环体内出现复杂表达式(三元等)→ 原生端无法静态求值,置空避免非法代码
1432
+ if (!/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(name)) {
1433
+ warnings.push(`v-for 项内复杂表达式「${name.slice(0, 30)}」暂不支持 → 已置空(R126)`);
1434
+ return [{ type: 'text', props: { text: ' ' } }];
1426
1435
  }
1427
1436
  return [{ type: 'text', bind: { key: name } }];
1428
1437
  }
@@ -1707,10 +1716,12 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
1707
1716
  ...node,
1708
1717
  props: node.props.filter((p) => !(p.type === NodeTypes.DIRECTIVE && p.name === 'for')),
1709
1718
  };
1719
+ activeLoops.push({ itemKey: vfSunk.itemKey, indexKey: vfSunk.indexKey });
1710
1720
  const itemSunk = walkElement(innerNodeSunk, classProps, comboRules, scopedRules, pseudoRules, warnings, state, compTable, propRefs, [
1711
1721
  ...loopScope,
1712
1722
  { itemKey: vfSunk.itemKey, indexKey: vfSunk.indexKey },
1713
1723
  ], ancestorClasses);
1724
+ activeLoops.pop();
1714
1725
  return { type: 'for', vfor: vfSunk, children: [itemSunk] };
1715
1726
  }
1716
1727
  }
@@ -1746,10 +1757,12 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
1746
1757
  throw new VueCompileError(`v-for 动态数据源「${vf.listState}」未在 <script setup> 中声明为 ref 数组(M4b)`);
1747
1758
  }
1748
1759
  const innerNode = { ...node, props: node.props.filter((p) => !(p.type === NodeTypes.DIRECTIVE && p.name === 'for')) };
1760
+ activeLoops.push({ itemKey: vf.itemKey, indexKey: vf.indexKey });
1749
1761
  const item = walkElement(innerNode, classProps, comboRules, scopedRules, pseudoRules, warnings, state, compTable, propRefs, [
1750
1762
  ...loopScope,
1751
1763
  { itemKey: vf.itemKey, indexKey: vf.indexKey },
1752
1764
  ], ancestorClasses);
1765
+ activeLoops.pop();
1753
1766
  return { type: 'for', vfor: vf, children: [item] };
1754
1767
  }
1755
1768
  // R76 Vue 内置容器组件(Transition/KeepAlive/Teleport/Suspense/Fragment)→ 透明展开:
@@ -1984,6 +1997,7 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
1984
1997
  // 组件 View 内 {{ prop }}(propRefs)→ 单插值时可整体作为 text ref(Text(prop))
1985
1998
  // - 其余(group/image):递归 walk
1986
1999
  const children = [];
2000
+ let prevCondExpr;
1987
2001
  if (mapping.type === 'text') {
1988
2002
  let text = '';
1989
2003
  let refOnly;
@@ -2000,7 +2014,8 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
2000
2014
  // 动态列表项属性 {{ item.field }}:基名(dot[0])命中循环变量才成立
2001
2015
  const dot = name.split('.');
2002
2016
  const base = dot[0];
2003
- const loopVar = loopScope.find((l) => l.itemKey === base || l.indexKey === base);
2017
+ const loopVar = loopScope.find((l) => l.itemKey === base || l.indexKey === base) ??
2018
+ [...activeLoops].reverse().find((l) => l.itemKey === base || l.indexKey === base);
2004
2019
  const dyn = loopVar && dot.length >= 2 && dot[0] === loopVar.itemKey && dot.slice(1).every((seg) => /^\w+$/.test(seg));
2005
2020
  if (!text && !parts.some((p) => 'bind' in p || 'dynField' in p || 'fieldOf' in p)) {
2006
2021
  // M2 模板内三元(D1 小范围):cond ? 'a' : 'b' → Swift 三元字符串表达式(Text(expr))
@@ -2069,7 +2084,7 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
2069
2084
  }
2070
2085
  // 混合段:静态文本之后的插值
2071
2086
  if (dyn)
2072
- parts.push({ dynField: { var: dot[0], field: dot[1] } });
2087
+ parts.push({ dynField: { var: dot[0], field: dot.slice(1).join('.') } });
2073
2088
  else if (/^\w+$/.test(name) && (loopVar || (state && state.states.some((s) => s.name === name))))
2074
2089
  parts.push({ bind: name });
2075
2090
  else if (propRefs)
@@ -2163,7 +2178,30 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
2163
2178
  }
2164
2179
  continue;
2165
2180
  }
2166
- children.push(...walk(child, classProps, comboRules, scopedRules, pseudoRules, warnings, state, compTable, propRefs, loopScope, [...ancestorClasses, ...elementClasses]));
2181
+ // R128:v-else / v-else-if 与前一兄弟互补(此前无条件渲染,空状态块常显)
2182
+ const __kids = walk(child, classProps, comboRules, scopedRules, pseudoRules, warnings, state, compTable, propRefs, loopScope, [
2183
+ ...ancestorClasses,
2184
+ ...elementClasses,
2185
+ ]);
2186
+ if (child.type === NodeTypes.ELEMENT) {
2187
+ const dirs = child.props.filter((p2) => p2.type === NodeTypes.DIRECTIVE);
2188
+ const isElse = dirs.some((d) => d.name === 'else');
2189
+ const elseIf = dirs.find((d) => d.name === 'else-if');
2190
+ if (isElse || elseIf) {
2191
+ const prev = prevCondExpr;
2192
+ const own = elseIf?.exp?.content?.trim();
2193
+ if (prev) {
2194
+ const condExpr = own ? `(${prev}) && (${own})` : `!(${prev})`;
2195
+ const stName = addJsExprState(`String(${condExpr})`);
2196
+ for (const k of __kids)
2197
+ k.condition = `__vrnCond:${stName}`;
2198
+ }
2199
+ }
2200
+ const ifDir = dirs.find((d) => d.name === 'if');
2201
+ if (ifDir?.exp?.content)
2202
+ prevCondExpr = ifDir.exp.content.trim();
2203
+ }
2204
+ children.push(...__kids);
2167
2205
  }
2168
2206
  }
2169
2207
  // R108:文本节点丢弃布局类属性(原生端 width/height/flexDirection 只对容器有意义)
@@ -2225,7 +2263,19 @@ function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, war
2225
2263
  const mCond = nc.match(/^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/);
2226
2264
  const ownerKnown = mCond !== null &&
2227
2265
  ((state?.states ?? []).some((st) => st.name === mCond[1]) || (propRefs?.has(mCond[1]) ?? false));
2228
- irNode.condition = ownerKnown && mCond ? `__vrnField__:${mCond[1]}:${mCond[2]}` : nc;
2266
+ // R128:条件里的复杂表达式(含 || && 比较、对象字段组合)统一下沉给 JS 运行时求值,
2267
+ // 生成 `__vrnCond:<state>`,两端只判真——不再往条件翻译器里堆规则
2268
+ const simpleCond = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)?$/.test(nc) ||
2269
+ /^![A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)?$/.test(nc) ||
2270
+ /^[A-Za-z_$][\w$]*(\.length)?\s*(==|!=|>|<|>=|<=)\s*('.*'|".*"|-?\d+)$/.test(nc);
2271
+ const usesLoopVar = activeLoops.some((l) => new RegExp(`\\b${l.itemKey}\\b`).test(nc));
2272
+ if (!simpleCond && !usesLoopVar) {
2273
+ const stName = addJsExprState(`String(${nc})`);
2274
+ irNode.condition = `__vrnCond:${stName}`;
2275
+ }
2276
+ else {
2277
+ irNode.condition = ownerKnown && mCond ? `__vrnField__:${mCond[1]}:${mCond[2]}` : nc;
2278
+ }
2229
2279
  }
2230
2280
  if (activeWhen && activeProps) {
2231
2281
  irNode.active = { when: activeWhen, props: activeProps };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongmingzhixiu/vuexir-compiler",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Vue SFC → IR → 原生源码(SwiftUI / Compose)编译器核心:纯函数、零 IO",
5
5
  "type": "module",
6
6
  "license": "MIT",