@dongmingzhixiu/vuexir-compiler 0.1.0 → 0.1.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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { IrNode } from '../ir/schema.js';
|
|
2
2
|
declare const MANIFEST: (label: string) => string;
|
|
3
3
|
declare const STYLES_XML = "<resources>\n <!-- R-SYSTEMBAR\uFF1A\u56FA\u5B9A\u6DF1\u8272\u4E3B\u9898 \u2192 \u7CFB\u7EDF\u680F\u56FE\u6807\u56FA\u5B9A\u6D45\u8272\uFF08windowLightStatusBar=false\uFF09\uFF0C\u4E0D\u8DDF\u968F\u7CFB\u7EDF\u660E\u6697 -->\n <style name=\"Theme.Vuexir\" parent=\"android:Theme.Material.NoActionBar\">\n <item name=\"android:windowBackground\">#0A0F1E</item>\n <item name=\"android:statusBarColor\">#0A0F1E</item>\n <item name=\"android:navigationBarColor\">#0A0F1E</item>\n <item name=\"android:windowLightStatusBar\">false</item>\n </style>\n</resources>\n";
|
|
4
|
-
declare const MAIN_ACTIVITY: (
|
|
4
|
+
declare const MAIN_ACTIVITY: (bundleId: string) => string;
|
|
5
5
|
declare const THEME_KT: (bundleId: string) => string;
|
|
6
6
|
declare const RUNTIME_KT: (bundleId: string) => string;
|
|
7
7
|
declare const NAV_TEST_KT: (bundleId: string) => string;
|
package/dist/codegen/compose.js
CHANGED
|
@@ -79,7 +79,7 @@ const STYLES_XML = `<resources>
|
|
|
79
79
|
</style>
|
|
80
80
|
</resources>
|
|
81
81
|
`;
|
|
82
|
-
const MAIN_ACTIVITY = (
|
|
82
|
+
const MAIN_ACTIVITY = (bundleId) => `package ${bundleId}
|
|
83
83
|
|
|
84
84
|
import android.graphics.Color
|
|
85
85
|
import android.os.Bundle
|
|
@@ -91,6 +91,7 @@ import androidx.activity.enableEdgeToEdge
|
|
|
91
91
|
class MainActivity : ComponentActivity() {
|
|
92
92
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
93
93
|
super.onCreate(savedInstanceState)
|
|
94
|
+
Store.init(applicationContext)
|
|
94
95
|
// R-SYSTEMBAR:应用固定深色主题(InkBg/InkBar),与系统明暗设置无关。
|
|
95
96
|
// enableEdgeToEdge() 默认用 SystemBarStyle.auto(...),会跟随系统明暗切换图标明暗——
|
|
96
97
|
// 系统处于浅色模式时给出深色图标,落在深色顶栏/底栏上不可读(真机反馈:系统栏文字看不清)。
|
|
@@ -144,6 +145,17 @@ fun VuexirTheme(content: @Composable () -> Unit) {
|
|
|
144
145
|
const RUNTIME_KT = (bundleId) => `package ${bundleId}
|
|
145
146
|
|
|
146
147
|
// Vuexir Android 运行时桥(对位 iOS RUNTIME_SWIFT)
|
|
148
|
+
/** R73 持久化桥:对位 iOS runtime 的 localStorage(UserDefaults)—— SharedPreferences 同步读写 */
|
|
149
|
+
object Store {
|
|
150
|
+
private var prefs: android.content.SharedPreferences? = null
|
|
151
|
+
fun init(ctx: android.content.Context) {
|
|
152
|
+
if (prefs == null) prefs = ctx.getSharedPreferences("vrn", android.content.Context.MODE_PRIVATE)
|
|
153
|
+
}
|
|
154
|
+
fun get(key: String, def: String = ""): String = prefs?.getString(key, def) ?: def
|
|
155
|
+
fun set(key: String, value: String) { prefs?.edit()?.putString(key, value)?.apply() }
|
|
156
|
+
fun remove(key: String) { prefs?.edit()?.remove(key)?.apply() }
|
|
157
|
+
}
|
|
158
|
+
|
|
147
159
|
object Runtime {
|
|
148
160
|
var pushHandler: ((String) -> Unit)? = null
|
|
149
161
|
fun push(route: String) {
|
|
@@ -422,7 +434,8 @@ function loopField(v, loop) {
|
|
|
422
434
|
function inlineHandlerKotlin(fn) {
|
|
423
435
|
if (fn.params && fn.params.trim() !== '')
|
|
424
436
|
return null; // 带参 handler 不内联(A5 需数据桥)
|
|
425
|
-
|
|
437
|
+
// R73:localStorage / Number() 现已支持(见 jsExprToKotlin);其余(window/document/fetch/setTimeout/return/嵌套函数)仍走协程或降级
|
|
438
|
+
if (/window\.|document\.|fetch\(|setTimeout|alert\(|console\.|return\b|function/.test(fn.body))
|
|
426
439
|
return null;
|
|
427
440
|
const out = [];
|
|
428
441
|
for (const raw of fn.body.split(/[\n;]+/)) {
|
|
@@ -599,6 +612,25 @@ function asyncLineKotlin(line, ctx) {
|
|
|
599
612
|
return null;
|
|
600
613
|
return `if (${cond}) {`;
|
|
601
614
|
}
|
|
615
|
+
// R73 localStorage(语句形态):setItem / removeItem
|
|
616
|
+
const lsSet = line.match(/^localStorage\.setItem\((.+),\s*(.+)\);?$/);
|
|
617
|
+
if (lsSet) {
|
|
618
|
+
const k = jsExprToKotlin(lsSet[1].trim());
|
|
619
|
+
const v = jsExprToKotlin(lsSet[2].trim());
|
|
620
|
+
if (k === null || v === null)
|
|
621
|
+
return null;
|
|
622
|
+
return `Store.set(${k}, ${v})`;
|
|
623
|
+
}
|
|
624
|
+
if (/^localStorage\.removeItem\(/.test(line)) {
|
|
625
|
+
const k = jsExprToKotlin(line.replace(/^localStorage\.removeItem\(/, '').replace(/\);?$/, ''));
|
|
626
|
+
return k === null ? null : `Store.remove(${k})`;
|
|
627
|
+
}
|
|
628
|
+
// 通用 `const x = <expr>` → `val x = <expr>`(置于 localStorage/fetch/JSON 专项之后)
|
|
629
|
+
const constAssign = line.match(/^const\s+([A-Za-z_$][\w$]*)\s*=\s*(.+?);?$/);
|
|
630
|
+
if (constAssign) {
|
|
631
|
+
const v = jsExprToKotlin(constAssign[2].trim());
|
|
632
|
+
return v === null ? null : `val ${constAssign[1]} = ${v}`;
|
|
633
|
+
}
|
|
602
634
|
// console.log(x) → Log.d
|
|
603
635
|
const logLine = line.match(/^console\.log\((.+)\);?$/);
|
|
604
636
|
if (logLine) {
|
|
@@ -648,6 +680,15 @@ function jsExprToKotlin(expr) {
|
|
|
648
680
|
return s;
|
|
649
681
|
// .length → .length()(JSONArray)
|
|
650
682
|
s = s.replace(/\.length\b(?!\()/g, '.length()');
|
|
683
|
+
// R73 localStorage → Store(函数式写法:getItem 直接用;setItem/removeItem 走语句分支)
|
|
684
|
+
s = s.replace(/localStorage\.getItem\((.+)\)$/g, (_m, k) => `Store.get(${k})`);
|
|
685
|
+
// JS 单引号字符串 → Kotlin 双引号(Kotlin 的单引号是 Char 字面量,'x' 编译不过)
|
|
686
|
+
s = s.replace(/'([^']*)'/g, (_m, inner) => `"${inner.replace(/"/g, '\\"')}"`);
|
|
687
|
+
// JS 类型转换 → Kotlin
|
|
688
|
+
s = s.replace(/Number\((.+)\)$/g, (_m, inner) => `${inner}.toInt()`);
|
|
689
|
+
s = s.replace(/String\((.+)\)$/g, (_m, inner) => `${inner}.toString()`);
|
|
690
|
+
// JSON.stringify(x) → x.toString()(Kotlin 无 JSON 全局对象;与 JS 侧动态列表协议一致)
|
|
691
|
+
s = s.replace(/JSON\.stringify\(([^()]*)\)/g, (_m, inner) => `${inner}.toString()`);
|
|
651
692
|
// 成员访问 a.b(对象状态字段)
|
|
652
693
|
s = s.replace(/\bstateStore\./g, '');
|
|
653
694
|
// 严格等号 → Kotlin
|
|
@@ -711,8 +752,10 @@ function emitNodeInner(node, ctx, depth, loop, useParamModifier = false, parentS
|
|
|
711
752
|
if (typeof activeBg === 'string' && nd.active) {
|
|
712
753
|
const when = kotlinCondExpr(nd.active.when);
|
|
713
754
|
const active = colorExpr(activeBg);
|
|
714
|
-
|
|
715
|
-
|
|
755
|
+
// 无基础背景时回退透明(对位 iOS `.background(when ? color : Color.clear)`)——
|
|
756
|
+
// 否则选中态会变成「所有行都高亮」(此前 bug)
|
|
757
|
+
const base = typeof props.background === 'string' ? colorExpr(props.background) : 'Color.Transparent';
|
|
758
|
+
return [`Modifier.background(if (${when}) ${active} else ${base})`];
|
|
716
759
|
}
|
|
717
760
|
return visualModifiers(props);
|
|
718
761
|
};
|
|
@@ -1191,7 +1234,7 @@ function runTabs(appDir, opts = {}) {
|
|
|
1191
1234
|
mkdirSync(resDir, { recursive: true });
|
|
1192
1235
|
writeFileSync(join(resDir, 'styles.xml'), STYLES_XML);
|
|
1193
1236
|
writeFileSync(join(pkgDir, 'Theme.kt'), THEME_KT(bundleId));
|
|
1194
|
-
writeFileSync(join(pkgDir, 'MainActivity.kt'), MAIN_ACTIVITY(
|
|
1237
|
+
writeFileSync(join(pkgDir, 'MainActivity.kt'), MAIN_ACTIVITY(bundleId));
|
|
1195
1238
|
writeFileSync(join(pkgDir, 'Runtime.kt'), RUNTIME_KT(bundleId));
|
|
1196
1239
|
// NavTest 断言绑定 vuexir-demo 文案:仅对参考样例生成,其他样例跳过(防无意义失败用例)
|
|
1197
1240
|
if (projectName.toLowerCase() === 'vuexirdemo') {
|
package/dist/transform/vue2ir.js
CHANGED
|
@@ -706,6 +706,15 @@ export function vueToIr(source, opts = {}) {
|
|
|
706
706
|
warnings.push(`handler「${h.name}」函数体疑似含 TS 类型标注(如 const x: number = 1),runtime.js 将解析失败——请移除类型标注(R13 仅支持 JS 子集)`);
|
|
707
707
|
}
|
|
708
708
|
}
|
|
709
|
+
// R74:状态名撞原生保留成员(SwiftUI `body`、Kotlin 关键字等)会导致生成代码编译失败 → 必须提前警告
|
|
710
|
+
{
|
|
711
|
+
const RESERVED = ['body', 'self', 'init', 'class', 'object', 'fun', 'val', 'var', 'when', 'is', 'in'];
|
|
712
|
+
for (const st of stateInfo?.states ?? []) {
|
|
713
|
+
if (RESERVED.includes(st.name)) {
|
|
714
|
+
warnings.push(`状态名「${st.name}」与原生保留成员冲突(SwiftUI body / Kotlin 关键字)——请改名(如 ${st.name}Text / ${st.name}Value),否则生成的 iOS/Android 代码无法编译`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
709
718
|
navBarConfig = extractScreenNavBar(src, warnings);
|
|
710
719
|
vforConsts = extractScreenConstArrays(src);
|
|
711
720
|
onMountedBody = extractOnMounted(src);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dongmingzhixiu/vuexir-compiler",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Vue SFC → IR → 原生源码(SwiftUI / Compose)编译器核心:纯函数、零 IO",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,12 +45,12 @@
|
|
|
45
45
|
},
|
|
46
46
|
"repository": {
|
|
47
47
|
"type": "git",
|
|
48
|
-
"url": "git+https://github.com/dongmingzhixiu/
|
|
48
|
+
"url": "git+https://github.com/dongmingzhixiu/vuexir.git",
|
|
49
49
|
"directory": "packages/compiler"
|
|
50
50
|
},
|
|
51
|
-
"homepage": "https://github.com/dongmingzhixiu/
|
|
51
|
+
"homepage": "https://github.com/dongmingzhixiu/vuexir#readme",
|
|
52
52
|
"bugs": {
|
|
53
|
-
"url": "https://github.com/dongmingzhixiu/
|
|
53
|
+
"url": "https://github.com/dongmingzhixiu/vuexir/issues"
|
|
54
54
|
},
|
|
55
55
|
"author": "panvu"
|
|
56
56
|
}
|