@dongmingzhixiu/vuexir-compiler 0.5.6 → 0.5.7

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.
@@ -392,12 +392,39 @@ class NavTest {
392
392
  }
393
393
  `;
394
394
  /* ================= IR → Compose 生成 ================= */
395
+ /** R-THEME-ANDROID:项目 tokens.css 的 var(--x) 表(默认皮肤 warm 优先,后块不覆盖) */
396
+ let currentCssVars;
397
+ function loadCssVarsAndroid(root) {
398
+ const map = new Map();
399
+ const roots = [join(root, 'src/styles/tokens.css'), join(root, 'src/tokens.css'), join(root, 'styles/tokens.css')];
400
+ for (const p of roots) {
401
+ if (!existsSync(p))
402
+ continue;
403
+ const css = readFileSync(p, 'utf-8');
404
+ for (const m of css.matchAll(/(--[\w-]+)\s*:\s*([^;{}]+);/g)) {
405
+ // 首个值优先(:root/.skin-warm 块在前 → 默认皮肤色)
406
+ if (!map.has(m[1]))
407
+ map.set(m[1], m[2].trim());
408
+ }
409
+ }
410
+ return map;
411
+ }
395
412
  /**
396
413
  * 颜色 → Compose Color 表达式。
397
414
  * 支持 #rgb/#rrggbb 与 rgb()/rgba()(rgba 必须带 alpha,否则会拼出 `0xFFRGBA(...)` 这种非法字面量)。
415
+ * R-THEME-ANDROID:var(--x) → 解析 tokens.css 值(默认皮肤)。
398
416
  */
399
417
  function colorExpr(c) {
400
418
  const raw = c.trim();
419
+ if (raw.startsWith('var(')) {
420
+ const m = raw.match(/^var\(\s*(--[\w-]+)/);
421
+ if (m && currentCssVars) {
422
+ const v = currentCssVars.get(m[1]);
423
+ if (v !== undefined)
424
+ return colorExpr(v);
425
+ }
426
+ return 'Color(0xFF000000)';
427
+ }
401
428
  const fn = raw.match(/^rgba?\(([^)]+)\)$/i);
402
429
  if (fn) {
403
430
  const parts = fn[1].split(',').map((t) => Number(t.trim()));
@@ -439,10 +466,17 @@ function safeBind(name, ctx, loop) {
439
466
  return '""';
440
467
  }
441
468
  function kotlinCondExpr(expr, ctx, loop) {
469
+ // R-ELSE-AND:v-else 下沉互补状态(_vrnE#)在组件内不可求值 → true(R15 恒显语义,对位 iOS;否则未定义标识符编译失败)
470
+ if (/^_vrnE\d+$/.test(expr.trim()))
471
+ return 'true';
442
472
  // R128:下沉的条件状态 → 判真
443
473
  const sunk = expr.match(/^__vrnCond:(\w+)$/);
444
- if (sunk)
474
+ if (sunk) {
475
+ // v-else 互补状态下沉为 _vrnE# → 组件内不可求值,恒显(R15,对位 iOS)
476
+ if (/^_vrnE\d+$/.test(sunk[1]))
477
+ return 'true';
445
478
  return `vrnTruthy(${sunk[1]}.toString())`;
479
+ }
446
480
  // R114:条件里引用了原生端不存在的标识符 → 回退为 true(保留内容)并告警,避免整页编译失败
447
481
  {
448
482
  // 先剥掉字符串字面量,避免把 "success" 之类当成标识符
@@ -1050,6 +1084,8 @@ function subtreeHasWeight(node) {
1050
1084
  function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentStretch = false) {
1051
1085
  const pad = ' '.repeat(depth);
1052
1086
  const p = (node.props ?? {});
1087
+ /** R-COMP-ROOT-ABS:组件根(useParamModifier && depth===1)去掉 Modifier.align(顶层无 BoxScope,编译非法);保留 offset 平移 */
1088
+ const absM = (pp) => (useParamModifier && depth <= 2 ? absModifiers(pp).filter((m) => !m.includes('.align(')) : absModifiers(pp));
1053
1089
  const alphaMod = alphaModifier(p);
1054
1090
  const alphaArr = alphaMod ? [alphaMod] : [];
1055
1091
  /** 容器类型:存在 absolute 子节点 → Box(ZStack 语义,对位 iOS);否则按 flexDirection */
@@ -1154,7 +1190,7 @@ function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentS
1154
1190
  const chain = modChain([
1155
1191
  ...stretchModifiers(parentStretch, node),
1156
1192
  ...sizeModifiers(p),
1157
- ...absModifiers(p),
1193
+ ...absM(p),
1158
1194
  ...clipModifiers(p),
1159
1195
  ...groupBackground(node, p),
1160
1196
  ...borderModifiers(p),
@@ -1251,7 +1287,7 @@ function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentS
1251
1287
  return out;
1252
1288
  }
1253
1289
  case 'button': {
1254
- const chain = modChain([...stretchModifiers(parentStretch, node), ...sizeModifiers(p), ...absModifiers(p), ...clipModifiers(p), ...visualModifiers(p), ...borderModifiers(p), ...paddingModifiers(p), ...alphaArr, ...transformModifiers(p)], pad + ' ');
1290
+ const chain = modChain([...stretchModifiers(parentStretch, node), ...sizeModifiers(p), ...absM(p), ...clipModifiers(p), ...visualModifiers(p), ...borderModifiers(p), ...paddingModifiers(p), ...alphaArr, ...transformModifiers(p)], pad + ' ');
1255
1291
  // R108:动态按钮文案(props.bind)优先;Kotlin 里用字符串模板插值
1256
1292
  const label = p.bind !== undefined ? String(p.bind) : (p.label ?? '');
1257
1293
  const click = kotlinClickAction(p, ctx, loop);
@@ -1281,7 +1317,7 @@ function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentS
1281
1317
  ctx.warnings.push('input: v-model「' + String(st) + '」非页面级 string 状态(Android 暂只支持该形态),已生成占位注释');
1282
1318
  return pad + '// input: v-model ' + kotlinStr(String(st)) + ' 未支持(Android 仅页面级 string state)';
1283
1319
  }
1284
- const chain = modChain([...stretchModifiers(parentStretch, node), ...sizeModifiers(p), ...absModifiers(p), ...paddingModifiers(p), ...alphaArr, ...transformModifiers(p)], pad + ' ');
1320
+ const chain = modChain([...stretchModifiers(parentStretch, node), ...sizeModifiers(p), ...absM(p), ...paddingModifiers(p), ...alphaArr, ...transformModifiers(p)], pad + ' ');
1285
1321
  const ts = [];
1286
1322
  if (p.fontSize !== undefined)
1287
1323
  ts.push('fontSize = ' + sp(p.fontSize));
@@ -1300,7 +1336,7 @@ function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentS
1300
1336
  return out;
1301
1337
  }
1302
1338
  case 'image': {
1303
- const chain = modChain([...stretchModifiers(parentStretch, node), ...sizeModifiers(p), ...absModifiers(p), ...clipModifiers(p), ...visualModifiers(p), ...paddingModifiers(p), ...alphaArr, ...transformModifiers(p)], pad + ' ');
1339
+ const chain = modChain([...stretchModifiers(parentStretch, node), ...sizeModifiers(p), ...absM(p), ...clipModifiers(p), ...visualModifiers(p), ...paddingModifiers(p), ...alphaArr, ...transformModifiers(p)], pad + ' ');
1304
1340
  if (p.src)
1305
1341
  ctx.warnings.push('image src="' + p.src + '" 资源桥未实现(A5 TODO),生成占位 Box');
1306
1342
  let out = pad + '// image src=' + (p.src ?? '') + '(A5 资源桥 TODO)\n';
@@ -1382,7 +1418,7 @@ function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentS
1382
1418
  ...stretchModifiers(parentStretch, node),
1383
1419
  ...sizeModifiers(p),
1384
1420
  flexModifier(p),
1385
- ...absModifiers(p),
1421
+ ...absM(p),
1386
1422
  ...alphaArr,
1387
1423
  ...transformModifiers(p),
1388
1424
  ].concat(click ? ['Modifier.clickable { ' + click + ' }'] : []);
@@ -1447,10 +1483,34 @@ function loadComponents(pageFile) {
1447
1483
  return table;
1448
1484
  }
1449
1485
  /** 收集组件 IR 中的 ref 参数名(text props.ref → 组件函数参数) */
1486
+ /** R-COMP-PARAMS:表达式 → 根标识符(dialog.title → dialog;!allOpen → allOpen;__vrnField__:dialog:title → dialog;下沉条件 → '')对位 iOS swiftui.ts rootIdent */
1487
+ function rootIdent(expr) {
1488
+ const e = expr.replace(/^!/, '');
1489
+ const f = e.match(/^__vrnField__:(\w+)/);
1490
+ if (f)
1491
+ return f[1];
1492
+ if (e.startsWith('__vrnCond:') || e.startsWith('__vrnField__:'))
1493
+ return '';
1494
+ const id = e.match(/^([A-Za-z_$][\w$]*)/);
1495
+ return id ? id[1] : '';
1496
+ }
1497
+ /** 收集组件 IR 树中 text ref / v-if 条件 / v-for 列表 → prop 名(组件 Composable 参数集合,对位 iOS) */
1450
1498
  function collectRefs(node, acc = new Set()) {
1451
- const p = node.props;
1452
- if (p.ref)
1453
- acc.add(p.ref);
1499
+ if (node.type === 'text' && node.props?.ref !== undefined) {
1500
+ const r = rootIdent(node.props.ref);
1501
+ if (r)
1502
+ acc.add(r);
1503
+ }
1504
+ if (node.condition) {
1505
+ const r = rootIdent(node.condition);
1506
+ if (r)
1507
+ acc.add(r);
1508
+ }
1509
+ if (node.type === 'for' && node.vfor?.listState && !/^_vrnE\d+$/.test(node.vfor.listState)) {
1510
+ const r = rootIdent(node.vfor.listState);
1511
+ if (r)
1512
+ acc.add(r);
1513
+ }
1454
1514
  for (const c of node.children ?? [])
1455
1515
  collectRefs(c, acc);
1456
1516
  return acc;
@@ -1659,6 +1719,8 @@ const ICON_MAP = {
1659
1719
  explore: 'Icons.Filled.Explore', work: 'Icons.Filled.Work', settings: 'Icons.Filled.Settings',
1660
1720
  };
1661
1721
  function runTabs(appDir, opts = {}) {
1722
+ // R-THEME-ANDROID:加载项目 tokens.css var 表(colorExpr var(--x) 解析用)
1723
+ currentCssVars = loadCssVarsAndroid(findProjectRoot(appDir));
1662
1724
  const routerSrc = readFileSync(opts.routerFile ?? join(appDir, 'router.ts'), 'utf-8');
1663
1725
  const { tabs, details, warnings: routerWarnings } = parseRouter(routerSrc, findProjectRoot(appDir), opts.routerFile ?? join(appDir, 'router.ts'));
1664
1726
  for (const w of routerWarnings)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongmingzhixiu/vuexir-compiler",
3
- "version": "0.5.6",
3
+ "version": "0.5.7",
4
4
  "description": "Vue SFC → IR → 原生源码(SwiftUI / Compose)编译器核心:纯函数、零 IO",
5
5
  "type": "module",
6
6
  "license": "MIT",