@dongmingzhixiu/vuexir-compiler 0.3.18 → 0.3.19

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.
@@ -427,7 +427,35 @@ function kotlinStr(s) {
427
427
  return `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\$/g, '${\'$\'}').replace(/\n/g, '\\n').replace(/\r/g, '\\r')}"`;
428
428
  }
429
429
  /** 条件表达式 → Kotlin(对位 iOS swiftCondExpr:=== / !== 归一 + 单引号字符串转双引号) */
430
- function kotlinCondExpr(expr) {
430
+ /** R114:文本绑定里引用的标识符若既不是页面状态也不是循环变量 → 视为未支持,回退空串 */
431
+ function safeBind(name, ctx, loop) {
432
+ const base = name.split('.')[0];
433
+ const isState = (ctx.states ?? []).some((st) => st.name === base);
434
+ const isLoop = loop && (loop.itemKey === base || loop.indexKey === base);
435
+ const isGlobal = ['true', 'false', 'Math', 'String', 'Number'].includes(base);
436
+ if (isState || isLoop || isGlobal)
437
+ return loopField(name, loop);
438
+ ctx.warnings?.push?.(`模板引用「${name}」在原生端没有对应状态 → 已回退空串(R114)`);
439
+ return '""';
440
+ }
441
+ function kotlinCondExpr(expr, ctx, loop) {
442
+ // R114:条件里引用了原生端不存在的标识符 → 回退为 true(保留内容)并告警,避免整页编译失败
443
+ {
444
+ const ids = [...expr.matchAll(/(?<![.\w$])([A-Za-z_$][\w$]*)/g)].map((m) => m[1]);
445
+ const known = new Set((ctx?.states ?? []).map((st) => st.name));
446
+ if (loop) {
447
+ known.add(loop.itemKey);
448
+ if (loop.indexKey)
449
+ known.add(loop.indexKey);
450
+ }
451
+ for (const k of ['true', 'false', 'Math', 'String', 'Number', 'stateStore'])
452
+ known.add(k);
453
+ const unknown = ids.filter((id) => !known.has(id));
454
+ if (unknown.length > 0 && ctx) {
455
+ ctx.warnings?.push?.(`条件「${expr.slice(0, 40)}」引用了原生端不存在的标识符(${unknown.join(', ')})→ 按 true 处理(R114)`);
456
+ return 'true';
457
+ }
458
+ }
431
459
  // R78 续(G12):对象字段条件 → 非空判断
432
460
  const f = expr.match(/^__vrnField__:(\w+):(\w+)$/);
433
461
  if (f)
@@ -987,7 +1015,7 @@ function emitNode(node, ctx, depth, loop, useParamModifier = false, parentStretc
987
1015
  const pad = ' '.repeat(depth);
988
1016
  // v-if → Kotlin if 包裹(表达式为双端兼容子集 ==/!=/&&/||/!,=== / !== 由 kotlinCondExpr 归一)
989
1017
  if (node.condition && node.type !== 'root') {
990
- return (pad + 'if (' + kotlinCondExpr(node.condition) + ') {\n' +
1018
+ return (pad + 'if (' + kotlinCondExpr(node.condition, ctx, loop) + ') {\n' +
991
1019
  emitNodeInner(node, ctx, depth + 1, loop, useParamModifier, parentStretch) +
992
1020
  '\n' + pad + '}\n');
993
1021
  }
@@ -1138,7 +1166,7 @@ function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentS
1138
1166
  if ('text' in seg)
1139
1167
  return String(seg.text).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\$/g, '${\'$\'}').replace(/\n/g, '\\n');
1140
1168
  if ('bind' in seg)
1141
- return '${' + loopField(seg.bind, loop) + '}';
1169
+ return '${' + safeBind(seg.bind, ctx, loop) + '}';
1142
1170
  if ('fieldOf' in seg)
1143
1171
  return ('${vrnField(' +
1144
1172
  seg.fieldOf.obj +
@@ -1169,7 +1197,7 @@ function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentS
1169
1197
  textExpr = kotlinStringExpr(p.bindExpr);
1170
1198
  }
1171
1199
  else if (p.bind !== undefined) {
1172
- textExpr = '"${' + loopField(p.bind, loop) + '}"';
1200
+ textExpr = '"${' + safeBind(String(p.bind), ctx, loop) + '}"';
1173
1201
  }
1174
1202
  else if (p.ref !== undefined) {
1175
1203
  textExpr = '"${' + loopField(p.ref, loop) + '}"';
@@ -502,13 +502,21 @@ function emitNode(node, ctx) {
502
502
  if (node.condition) {
503
503
  const L = ctx.level;
504
504
  const inner = emitNodeInner(node, ctx);
505
- const swiftCond = swiftCondExpr(node.condition);
505
+ const known = ctx.stateNames ?? new Set();
506
+ const swiftCond = swiftCondExpr(node.condition, known);
506
507
  return `${indent(L)}if ${swiftCond} {\n${inner}\n${indent(L)}}`;
507
508
  }
508
509
  return emitNodeInner(node, ctx);
509
510
  }
510
511
  /** JS 条件表达式 → Swift 兼容:===/!== 为 JS 严格比较,Swift 用 ==/!=;字符串字面量单引号 → 双引号 */
511
- function swiftCondExpr(expr) {
512
+ function swiftCondExpr(expr, known) {
513
+ // R114:条件引用原生端不存在的标识符 → true(保留内容),避免整页编译失败
514
+ if (known) {
515
+ const ids = [...expr.matchAll(/(?<![.\w$])([A-Za-z_$][\w$]*)/g)].map((m) => m[1]);
516
+ const unknown = ids.filter((id) => !known.has(id) && !['true', 'false', 'Math', 'String', 'Number'].includes(id));
517
+ if (unknown.length > 0)
518
+ return 'true';
519
+ }
512
520
  // R78 续(G12):对象字段条件 → 非空判断
513
521
  const f = expr.match(/^__vrnField__:(\w+):(\w+)$/);
514
522
  if (f)
@@ -612,6 +620,7 @@ function emitNodeInner(node, ctx) {
612
620
  parentAxis: 'column',
613
621
  level: L + 1,
614
622
  definiteHeight: true, // root 撑满全屏 → 子项建议高度确定(R-H3 可分配)
623
+ stateNames: ctx.stateNames,
615
624
  warnings: ctx.warnings,
616
625
  path: `${ctx.path}[${i}]`,
617
626
  inheritGap: gapInheritFor(c, props.gap, node.children ?? []),
@@ -1115,6 +1124,7 @@ function navBarModifiers(screen, warnings) {
1115
1124
  return mods.join('\n');
1116
1125
  }
1117
1126
  export function generateScreenSource(screen, opts = {}) {
1127
+ const __stateNames = new Set((screen.state ?? []).map((st) => st.name));
1118
1128
  const rt = opts.runtime ?? 'Runtime.shared';
1119
1129
  // R42:色板存在时颜色/字体走 Theme token;R43:frame 走 View 扩展短调用
1120
1130
  currentColorNames = opts.theme?.colorNames;
@@ -1129,6 +1139,7 @@ export function generateScreenSource(screen, opts = {}) {
1129
1139
  // R-PAGESCROLL:根容器始终为可滚动视口(见 emitNode root 分支),无需内容高度启发式
1130
1140
  const dataProps = [];
1131
1141
  const body = emitNode(screen.ui, {
1142
+ stateNames: __stateNames,
1132
1143
  parentAxis: 'column',
1133
1144
  level: 1,
1134
1145
  definiteHeight: true, // root 撑满全屏(maxHeight:.infinity)→ 根容器高度确定
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongmingzhixiu/vuexir-compiler",
3
- "version": "0.3.18",
3
+ "version": "0.3.19",
4
4
  "description": "Vue SFC → IR → 原生源码(SwiftUI / Compose)编译器核心:纯函数、零 IO",
5
5
  "type": "module",
6
6
  "license": "MIT",