@zerodep-dev/vue-tsx-pro-mini 0.0.0-stage → 0.4.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.
- package/LICENSE +21 -0
- package/README.md +69 -2
- package/dist/build.d.ts +104 -0
- package/dist/build.js +430 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +123 -0
- package/dist/elements-BDIG7-jU.js +63 -0
- package/dist/globals.d.ts +1 -0
- package/dist/globals.js +16 -0
- package/dist/index.d.ts +188 -0
- package/dist/index.js +15 -0
- package/dist/page-upnBSowU.js +1219 -0
- package/dist/testing.d.ts +50 -0
- package/dist/testing.js +109 -0
- package/package.json +71 -3
- package/template/gitignore +2 -0
- package/template/index.html +17 -0
- package/template/mini.config.ts +12 -0
- package/template/package.json +22 -0
- package/template/src/app.ts +5 -0
- package/template/src/main.ts +5 -0
- package/template/src/pages/HomePage.tsx +25 -0
- package/template/tsconfig.json +16 -0
- package/template/vite.config.ts +5 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Component } from "vue";
|
|
2
|
+
//#region src/testing.d.ts
|
|
3
|
+
/** 页面数据里的一个节点(模板看到的字段) */
|
|
4
|
+
export interface PageNode {
|
|
5
|
+
/** 编号 */
|
|
6
|
+
s: string;
|
|
7
|
+
/** 模板类型 */
|
|
8
|
+
t: string;
|
|
9
|
+
/** 文字(文本节点) */
|
|
10
|
+
v?: string;
|
|
11
|
+
/** 类名 */
|
|
12
|
+
c?: string;
|
|
13
|
+
/** 行内样式(页面数据里是样式字典 ss 的编号,测试工具换回样式串) */
|
|
14
|
+
st?: string;
|
|
15
|
+
/** 子节点 */
|
|
16
|
+
cn?: PageNode[];
|
|
17
|
+
[field: string]: unknown;
|
|
18
|
+
}
|
|
19
|
+
type Data = Record<string, unknown>;
|
|
20
|
+
/** 按小程序的规则应用 setData:键是 a.b[0].c 这样的路径,中间缺的对象、数组自动补上 */
|
|
21
|
+
export declare function applySetData(target: Data, payload: Data): void;
|
|
22
|
+
/** 挂上的页面 */
|
|
23
|
+
export interface MountedPage {
|
|
24
|
+
/** 页面数据 */
|
|
25
|
+
readonly data: {
|
|
26
|
+
root: {
|
|
27
|
+
cn: PageNode[];
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/** 等 Vue 的更新与 setData 都完成 */
|
|
31
|
+
settle(): Promise<void>;
|
|
32
|
+
/** 按深度优先找第一个满足条件的节点 */
|
|
33
|
+
find(predicate: (node: PageNode) => boolean): PageNode | undefined;
|
|
34
|
+
/** 页面上的全部文字 */
|
|
35
|
+
text(): string;
|
|
36
|
+
/** 点一下(touchstart → touchend → tap,与真机的顺序一致),等更新完成 */
|
|
37
|
+
tap(node: PageNode): Promise<void>;
|
|
38
|
+
/** 派发一个原生事件(input、scroll…),detail 是小程序事件的 detail */
|
|
39
|
+
dispatch(node: PageNode, type: string, detail?: unknown): Promise<void>;
|
|
40
|
+
/** 派发一次触摸(touchstart / touchmove / touchend),触点在 (x, y) */
|
|
41
|
+
touch(node: PageNode, type: string, x: number, y: number): Promise<void>;
|
|
42
|
+
/** 页面的生命周期:显示、隐藏、滚到底 */
|
|
43
|
+
lifecycle(event: 'show' | 'hide' | 'reachBottom'): Promise<void>;
|
|
44
|
+
/** 卸载 */
|
|
45
|
+
unmount(): void;
|
|
46
|
+
}
|
|
47
|
+
/** 把组件挂成一个页面 */
|
|
48
|
+
export declare function mountPage(component: Component, query?: Record<string, string>): MountedPage;
|
|
49
|
+
//#endregion
|
|
50
|
+
export {};
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { n as pageOptions } from "./page-upnBSowU.js";
|
|
2
|
+
import { nextTick } from "vue";
|
|
3
|
+
//#region src/testing.ts
|
|
4
|
+
/** 按小程序的规则应用 setData:键是 a.b[0].c 这样的路径,中间缺的对象、数组自动补上 */
|
|
5
|
+
function applySetData(target, payload) {
|
|
6
|
+
for (const [path, value] of Object.entries(payload)) {
|
|
7
|
+
const keys = path.match(/[^.[\]]+/g);
|
|
8
|
+
let current = target;
|
|
9
|
+
keys.slice(0, -1).forEach((key, index) => {
|
|
10
|
+
if (current[key] === void 0 || current[key] === null) current[key] = /^\d+$/.test(keys[index + 1]) ? [] : {};
|
|
11
|
+
current = current[key];
|
|
12
|
+
});
|
|
13
|
+
current[keys.at(-1)] = structuredClone(value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** 节点的 st 是样式字典的编号:换回样式串,断言照常写 node.st.includes(…) */
|
|
17
|
+
function resolveStyles(data) {
|
|
18
|
+
const dictionary = data.ss ?? {};
|
|
19
|
+
const walk = (node) => {
|
|
20
|
+
if (typeof node.st === "string" && node.st in dictionary) node.st = dictionary[node.st];
|
|
21
|
+
node.cn?.forEach(walk);
|
|
22
|
+
};
|
|
23
|
+
walk(data.root);
|
|
24
|
+
}
|
|
25
|
+
/** 把组件挂成一个页面 */
|
|
26
|
+
function mountPage(component, query = {}) {
|
|
27
|
+
const options = pageOptions(component);
|
|
28
|
+
const page = {
|
|
29
|
+
data: structuredClone(options.data),
|
|
30
|
+
setData(payload, callback) {
|
|
31
|
+
applySetData(page.data, payload);
|
|
32
|
+
resolveStyles(page.data);
|
|
33
|
+
callback?.();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
options.onLoad.call(page, query);
|
|
37
|
+
const settle = async () => {
|
|
38
|
+
for (let round = 0; round < 3; round++) {
|
|
39
|
+
await nextTick();
|
|
40
|
+
await Promise.resolve();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const all = (nodes = page.data.root.cn) => nodes.flatMap((node) => [node, ...all(node.cn ?? [])]);
|
|
44
|
+
/** 从节点往上到根的每一层(含自己) */
|
|
45
|
+
const ancestors = (node, nodes = page.data.root.cn ?? []) => {
|
|
46
|
+
for (const child of nodes) {
|
|
47
|
+
if (child === node) return [node];
|
|
48
|
+
const path = ancestors(node, child.cn ?? []);
|
|
49
|
+
if (path.length) return [...path, child];
|
|
50
|
+
}
|
|
51
|
+
return [];
|
|
52
|
+
};
|
|
53
|
+
let timeStamp = 0;
|
|
54
|
+
const fire = async (node, event) => {
|
|
55
|
+
const stamp = ++timeStamp;
|
|
56
|
+
const targets = event.type.startsWith("touch") ? ancestors(node) : [node];
|
|
57
|
+
for (const target of targets) options.eh.call(page, {
|
|
58
|
+
...event,
|
|
59
|
+
timeStamp: stamp,
|
|
60
|
+
currentTarget: { dataset: { s: target.s } }
|
|
61
|
+
});
|
|
62
|
+
await settle();
|
|
63
|
+
};
|
|
64
|
+
const dispatch = (node, type, detail) => fire(node, {
|
|
65
|
+
type,
|
|
66
|
+
detail
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
data: page.data,
|
|
70
|
+
settle,
|
|
71
|
+
find: (predicate) => all().find(predicate),
|
|
72
|
+
text: () => all().map((node) => node.v ?? "").join(""),
|
|
73
|
+
async tap(node) {
|
|
74
|
+
await this.touch(node, "touchstart", 0, 0);
|
|
75
|
+
await this.touch(node, "touchend", 0, 0);
|
|
76
|
+
await fire(node, {
|
|
77
|
+
type: "tap",
|
|
78
|
+
changedTouches: [{
|
|
79
|
+
clientX: 0,
|
|
80
|
+
clientY: 0
|
|
81
|
+
}]
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
dispatch,
|
|
85
|
+
touch(node, type, x, y) {
|
|
86
|
+
const point = {
|
|
87
|
+
identifier: 0,
|
|
88
|
+
clientX: x,
|
|
89
|
+
clientY: y,
|
|
90
|
+
pageX: x,
|
|
91
|
+
pageY: y
|
|
92
|
+
};
|
|
93
|
+
return fire(node, {
|
|
94
|
+
type,
|
|
95
|
+
touches: type === "touchend" ? [] : [point],
|
|
96
|
+
changedTouches: [point]
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
async lifecycle(event) {
|
|
100
|
+
if (event === "show") options.onShow.call(page);
|
|
101
|
+
else if (event === "hide") options.onHide.call(page);
|
|
102
|
+
else options.onReachBottom.call(page);
|
|
103
|
+
await settle();
|
|
104
|
+
},
|
|
105
|
+
unmount: () => options.onUnload.call(page)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { applySetData, mountPage };
|
package/package.json
CHANGED
|
@@ -1,6 +1,74 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerodep-dev/vue-tsx-pro-mini",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Render vue-tsx-pro components in WeChat and Alipay mini programs: a Vue renderer over setData with generic templates, runtime styles resolved inline",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"vtp-mini": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"template"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/kenconnet666/vue-tsx-pro.git",
|
|
17
|
+
"directory": "packages/mini"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"vue",
|
|
21
|
+
"vue3",
|
|
22
|
+
"miniprogram",
|
|
23
|
+
"wechat",
|
|
24
|
+
"alipay",
|
|
25
|
+
"renderer",
|
|
26
|
+
"tsx"
|
|
27
|
+
],
|
|
28
|
+
"sideEffects": [
|
|
29
|
+
"./src/globals.ts",
|
|
30
|
+
"./dist/globals.js"
|
|
31
|
+
],
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"@vue-tsx-pro/source": "./src/index.ts",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./globals": {
|
|
39
|
+
"@vue-tsx-pro/source": "./src/globals.ts",
|
|
40
|
+
"types": "./dist/globals.d.ts",
|
|
41
|
+
"default": "./dist/globals.js"
|
|
42
|
+
},
|
|
43
|
+
"./testing": {
|
|
44
|
+
"@vue-tsx-pro/source": "./src/testing.ts",
|
|
45
|
+
"types": "./dist/testing.d.ts",
|
|
46
|
+
"default": "./dist/testing.js"
|
|
47
|
+
},
|
|
48
|
+
"./build": {
|
|
49
|
+
"@vue-tsx-pro/source": "./src/build/index.ts",
|
|
50
|
+
"types": "./dist/build.d.ts",
|
|
51
|
+
"default": "./dist/build.js"
|
|
52
|
+
},
|
|
53
|
+
"./package.json": "./package.json"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@zerodep-dev/vue-tsx-pro-tsx": "^0.4.1",
|
|
57
|
+
"vite": "^8.0.0",
|
|
58
|
+
"vue": "^3.5.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/node": "24.19.2",
|
|
62
|
+
"@zerodep-dev/vue-tsx-pro-tsx": "^0.4.1",
|
|
63
|
+
"tsdown": "0.23.1",
|
|
64
|
+
"typescript": "7.1.0-beta",
|
|
65
|
+
"vite": "8.3.4",
|
|
66
|
+
"vitest": "5.0.5",
|
|
67
|
+
"vue": "3.5.44"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "tsdown",
|
|
71
|
+
"typecheck": "tsc -p . --incremental --tsBuildInfoFile node_modules/.cache/typecheck.tsbuildinfo",
|
|
72
|
+
"test": "vitest run --configLoader native"
|
|
73
|
+
}
|
|
6
74
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
6
|
+
<title>vtp-mobile-app</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
margin: 0;
|
|
10
|
+
}
|
|
11
|
+
</style>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="app"></div>
|
|
15
|
+
<script type="module" src="/src/main.ts"></script>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// 小程序:npm run dev:wx(监听)、npm run build:mini(两端)、npm run open:wx(在开发者工具里打开)
|
|
2
|
+
import { defineMiniConfig } from '@zerodep-dev/vue-tsx-pro-mini/build';
|
|
3
|
+
import { vueTsxPro } from '@zerodep-dev/vue-tsx-pro-tsx/vite';
|
|
4
|
+
|
|
5
|
+
export default defineMiniConfig({
|
|
6
|
+
plugins: vueTsxPro(),
|
|
7
|
+
// 换成自己的 AppID(或微信的「小程序测试号」):touristappid 在开发者工具里会报 APPID_ERROR
|
|
8
|
+
appId: 'touristappid',
|
|
9
|
+
// 微信报告系统的深浅色,ConfigProvider 的 theme="system" 跟着切换
|
|
10
|
+
darkmode: true,
|
|
11
|
+
window: { navigationBarTitleText: 'vtp-mobile-app' },
|
|
12
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vtp-mobile-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "vite",
|
|
7
|
+
"build": "vite build",
|
|
8
|
+
"dev:wx": "vtp-mini dev wx",
|
|
9
|
+
"dev:my": "vtp-mini dev my",
|
|
10
|
+
"build:mini": "vtp-mini build",
|
|
11
|
+
"open:wx": "vtp-mini open wx",
|
|
12
|
+
"open:my": "vtp-mini open my",
|
|
13
|
+
"typecheck": "tsc -p ."
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@lucide/icons": "^1.59.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^24.0.0",
|
|
20
|
+
"typescript": "^7.0.2"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// 首页:H5(main.ts)与两端小程序(app.ts)共用这一份
|
|
2
|
+
import { Plus } from '@lucide/icons';
|
|
3
|
+
import { _component } from '@zerodep-dev/vue-tsx-pro-tsx';
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Cell,
|
|
7
|
+
CellGroup,
|
|
8
|
+
ConfigProvider,
|
|
9
|
+
showToast,
|
|
10
|
+
} from '@zerodep-dev/vue-tsx-pro-mobile';
|
|
11
|
+
|
|
12
|
+
export const HomePage = _component(() => {
|
|
13
|
+
let count = _state(0);
|
|
14
|
+
return () => (
|
|
15
|
+
<ConfigProvider theme="system">
|
|
16
|
+
<CellGroup title="开始">
|
|
17
|
+
<Cell title="计数" value={String(count)} />
|
|
18
|
+
<Cell title="提示" arrow onClick={() => showToast('你好')} />
|
|
19
|
+
</CellGroup>
|
|
20
|
+
<Button icon={Plus} onClick={() => count++}>
|
|
21
|
+
加一
|
|
22
|
+
</Button>
|
|
23
|
+
</ConfigProvider>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2023",
|
|
4
|
+
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"allowImportingTsExtensions": true,
|
|
8
|
+
"jsx": "preserve",
|
|
9
|
+
"jsxImportSource": "@zerodep-dev/vue-tsx-pro-tsx",
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"types": ["node", "vite/client", "@zerodep-dev/vue-tsx-pro-tsx/runes"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["src", "*.ts"]
|
|
16
|
+
}
|