@dongmingzhixiu/vuexir-compiler 0.3.9 → 0.3.10

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.
@@ -41,6 +41,8 @@ function addJsExprState(expr) {
41
41
  jsExprStates.push({ name, init: '', computed: js });
42
42
  return name;
43
43
  }
44
+ /** R104 脚本里声明过的名字(const/let/function)——未登记为 state 时走运行时求值 */
45
+ let scriptDeclaredNames = new Set();
44
46
  /** R86 store 别名表:`ui` → StoreDef(每次 vueToIr 重建;与 vforConsts 同风格) */
45
47
  let storeAliases = new Map();
46
48
  /** R86 把 `alias.field.path` 拍平为 `alias_field.path`;getter/action 返回哨兵串 */
@@ -890,6 +892,12 @@ const TAG_MAP = {
890
892
  small: { type: 'text', defaultProps: { fontSize: 12 } },
891
893
  u: { type: 'text', defaultProps: { fontSize: 14 } },
892
894
  code: { type: 'text', defaultProps: { fontSize: 13 } },
895
+ // R104:真实页面高频标签(原生端无对应语义,按最接近的容器/文本降级)
896
+ select: { type: 'group', defaultProps: { flexDirection: 'column' } },
897
+ option: { type: 'text', defaultProps: { fontSize: 14 } },
898
+ aside: { type: 'group', defaultProps: { flexDirection: 'column' } },
899
+ figure: { type: 'group', defaultProps: { flexDirection: 'column' } },
900
+ figcaption: { type: 'text', defaultProps: { fontSize: 12 } },
893
901
  h1: { type: 'text', defaultProps: { fontSize: 24, fontWeight: 'bold' } },
894
902
  h2: { type: 'text', defaultProps: { fontSize: 20, fontWeight: 'bold' } },
895
903
  h3: { type: 'text', defaultProps: { fontSize: 17, fontWeight: 'medium' } },
@@ -908,6 +916,7 @@ export function vueToIr(source, opts = {}) {
908
916
  compPosition = new Map();
909
917
  vforConsts = {};
910
918
  storeAliases = new Map();
919
+ scriptDeclaredNames = new Set();
911
920
  storeConstArrays = {};
912
921
  derivedNames = new Map();
913
922
  storeGetterWarnings = [];
@@ -1002,6 +1011,17 @@ export function vueToIr(source, opts = {}) {
1002
1011
  if (script && script.content.trim()) {
1003
1012
  const src = script.content;
1004
1013
  stateInfo = extractScreenState(src);
1014
+ // R104:记录脚本里所有顶层声明(插值命中但非 state 时下沉求值)
1015
+ for (const dm of src.matchAll(/(?:const|let|var|function)\s+([A-Za-z_$][\w$]*)/g)) {
1016
+ scriptDeclaredNames.add(dm[1]);
1017
+ }
1018
+ for (const dm of src.matchAll(/const\s*\{([^}]+)\}\s*=/g)) {
1019
+ for (const part of dm[1].split(',')) {
1020
+ const nm = part.split(':').pop()?.trim().replace(/=.*$/, '').trim();
1021
+ if (nm && /^[A-Za-z_$][\w$]*$/.test(nm))
1022
+ scriptDeclaredNames.add(nm);
1023
+ }
1024
+ }
1005
1025
  const fns = extractFunctions(src);
1006
1026
  // M1 已落地的 setter 函数(x.value = <expr>)升级为完整 handler:body 原样进运行时(含 localStorage/状态读写)
1007
1027
  handlers = fns.map((f) => ({
@@ -1334,6 +1354,10 @@ function walk(node, classProps, comboRules, scopedRules, pseudoRules, warnings,
1334
1354
  if (state && state.states.some((s) => s.name === name)) {
1335
1355
  return [{ type: 'text', bind: { key: name } }];
1336
1356
  }
1357
+ // R104:脚本里声明过但不是 ref 状态(computed/普通 const/解构来的值)→ 交给 JS 运行时求值
1358
+ if (scriptDeclaredNames.has(name)) {
1359
+ return [{ type: 'text', props: { bind: addJsExprState(nameRaw) } }];
1360
+ }
1337
1361
  throw new VueCompileError(`插值 {{ ${name} }}:未在 <script setup> 中声明为 ref 状态(M1 白名单:仅支持已声明状态)`);
1338
1362
  }
1339
1363
  // R92 点路径兜底:{{ a.b ?? '默认' }} → fieldOf + fallback
@@ -1359,7 +1383,8 @@ function walk(node, classProps, comboRules, scopedRules, pseudoRules, warnings,
1359
1383
  return [{ type: 'text', props: { text: ' ' } }];
1360
1384
  }
1361
1385
  }
1362
- throw new VueCompileError(`插值表达式 {{ ${name} }} 暂不支持(M1 仅支持纯状态名;三元/比较表达式 M2 白名单)`);
1386
+ // R104:静态翻译不了 → 交给 JS 运行时求值(与元素文本分支保持一致)
1387
+ return [{ type: 'text', props: { bind: addJsExprState(nameRaw) } }];
1363
1388
  }
1364
1389
  case NodeTypes.ELEMENT:
1365
1390
  return [walkElement(node, classProps, comboRules, scopedRules, pseudoRules, warnings, state, compTable, propRefs, loopScope, ancestorClasses)];
@@ -1562,6 +1587,10 @@ function buildComponentTree(comp, warnings) {
1562
1587
  }
1563
1588
  function walkElement(node, classProps, comboRules, scopedRules, pseudoRules, warnings, state, compTable = [], propRefs, loopScope = [], ancestorClasses = []) {
1564
1589
  let elementClasses = []; // M5 后代选择器:本元素 class 清单(子节点递归传祖先链)
1590
+ // R104:<br> 换行 → 占位间距(原生端没有行内换行语义)
1591
+ if (node.tag === 'br') {
1592
+ return { type: 'spacer', props: { height: 8 } };
1593
+ }
1565
1594
  // M2 v-for:任意元素(含 <template>)带 v-for → 包一层 for 节点,自身去指令后作项模板
1566
1595
  const forDir = node.props.find((p) => p.type === NodeTypes.DIRECTIVE && p.name === 'for');
1567
1596
  if (forDir) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongmingzhixiu/vuexir-compiler",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Vue SFC → IR → 原生源码(SwiftUI / Compose)编译器核心:纯函数、零 IO",
5
5
  "type": "module",
6
6
  "license": "MIT",