@proteus-vue/compiler 0.3.0-beta.0
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/README.md +74 -0
- package/dist/explain.d.ts +25 -0
- package/dist/explain.js +59 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +3396 -0
- package/dist/overrides.d.ts +14 -0
- package/dist/overrides.js +34 -0
- package/dist/script.d.ts +15 -0
- package/dist/script.js +318 -0
- package/dist/style-safety/constants.d.ts +6 -0
- package/dist/style-safety/index.d.ts +25 -0
- package/dist/style-safety/index.js +276 -0
- package/dist/style-safety/reachability.d.ts +8 -0
- package/dist/style.d.ts +3 -0
- package/dist/style.js +96 -0
- package/dist/tags.d.ts +3 -0
- package/dist/tags.js +49 -0
- package/dist/template.d.ts +3 -0
- package/dist/template.js +337 -0
- package/dist/trace.d.ts +6 -0
- package/dist/trace.js +18 -0
- package/dist/transforms/registry.d.ts +18 -0
- package/dist/transforms/registry.js +48 -0
- package/dist/transforms/script.d.ts +2 -0
- package/dist/transforms/script.js +179 -0
- package/dist/transforms/style.d.ts +2 -0
- package/dist/transforms/style.js +74 -0
- package/dist/transforms/template.d.ts +3 -0
- package/dist/transforms/template.js +371 -0
- package/dist/transforms/types.d.ts +50 -0
- package/dist/transforms/types.js +7 -0
- package/dist/transforms/validate.d.ts +2 -0
- package/dist/transforms/validate.js +50 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.js +4 -0
- package/dist/validate.d.ts +18 -0
- package/dist/validate.js +54 -0
- package/package.json +42 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3396 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { parse as sfcParse2 } from "@vue/compiler-sfc";
|
|
3
|
+
|
|
4
|
+
// src/template.ts
|
|
5
|
+
import { parse as domParse, NodeTypes } from "@vue/compiler-dom";
|
|
6
|
+
|
|
7
|
+
// src/tags.ts
|
|
8
|
+
var TAG_MAP = {
|
|
9
|
+
div: "view",
|
|
10
|
+
span: "text",
|
|
11
|
+
p: "text",
|
|
12
|
+
h1: "text",
|
|
13
|
+
h2: "text",
|
|
14
|
+
h3: "text",
|
|
15
|
+
h4: "text",
|
|
16
|
+
h5: "text",
|
|
17
|
+
h6: "text",
|
|
18
|
+
img: "image",
|
|
19
|
+
a: "view",
|
|
20
|
+
button: "button",
|
|
21
|
+
input: "input",
|
|
22
|
+
textarea: "textarea",
|
|
23
|
+
video: "video",
|
|
24
|
+
canvas: "canvas",
|
|
25
|
+
"scroll-view": "scroll-view",
|
|
26
|
+
slot: "slot"
|
|
27
|
+
};
|
|
28
|
+
var EVENT_MAP = {
|
|
29
|
+
click: "tap",
|
|
30
|
+
input: "input",
|
|
31
|
+
change: "change",
|
|
32
|
+
submit: "submit",
|
|
33
|
+
focus: "focus",
|
|
34
|
+
blur: "blur",
|
|
35
|
+
touchstart: "touchstart",
|
|
36
|
+
touchmove: "touchmove",
|
|
37
|
+
touchend: "touchend",
|
|
38
|
+
longpress: "longpress",
|
|
39
|
+
confirm: "confirm",
|
|
40
|
+
scroll: "scroll"
|
|
41
|
+
// scroll-view 内置滚动事件(v0.4 虚拟列表)
|
|
42
|
+
};
|
|
43
|
+
var SEMANTIC_CLASS = {
|
|
44
|
+
h1: "proteus-h1",
|
|
45
|
+
h2: "proteus-h2",
|
|
46
|
+
h3: "proteus-h3",
|
|
47
|
+
h4: "proteus-h4",
|
|
48
|
+
h5: "proteus-h5",
|
|
49
|
+
h6: "proteus-h6",
|
|
50
|
+
p: "proteus-p",
|
|
51
|
+
a: "proteus-a"
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/transforms/template.ts
|
|
55
|
+
function tagRule(id, title, tags, extra) {
|
|
56
|
+
return {
|
|
57
|
+
id,
|
|
58
|
+
phase: "template",
|
|
59
|
+
status: "implemented",
|
|
60
|
+
title,
|
|
61
|
+
mapping: Object.fromEntries(tags.map((t) => [t, TAG_MAP[t]]).filter(([, v]) => v !== void 0)),
|
|
62
|
+
...extra
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
var TEMPLATE_RULES = [
|
|
66
|
+
// ============ 标签映射(TAG_MAP) ============
|
|
67
|
+
tagRule("tag/div-to-view", "div \u2192 view", ["div"], {
|
|
68
|
+
description: "\u5757\u7EA7\u5BB9\u5668 div \u2192 view\uFF08\u5C0F\u7A0B\u5E8F\u901A\u7528\u5BB9\u5668\uFF09",
|
|
69
|
+
why: "\u5C0F\u7A0B\u5E8F\u6CA1\u6709 div\uFF0Cview \u662F\u6700\u901A\u7528\u5BB9\u5668\u6807\u7B7E\uFF1B\u4E1A\u52A1\u4EE3\u7801\u7167\u5199\u6807\u51C6 HTML\uFF08\xA70.3 \u539F\u5219 1\uFF09",
|
|
70
|
+
when: "template \u4E2D\u51FA\u73B0 <div> \u65F6",
|
|
71
|
+
example: { before: '<div class="home">\u2026</div>', after: '<view class="home">\u2026</view>' },
|
|
72
|
+
verify: "tests/mp-transform.test.ts\u300C\u6807\u51C6\u6807\u7B7E\u6620\u5C04\u300D",
|
|
73
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08TAG_MAP \u67E5\u8868\uFF09",
|
|
74
|
+
decision: "#57\uFF08\u6837\u5F0F\u6807\u7B7E\u9009\u62E9\u5668\u6620\u5C04\uFF09"
|
|
75
|
+
}),
|
|
76
|
+
tagRule("tag/inline-to-text", "span \u2192 text", ["span"], {
|
|
77
|
+
description: "\u884C\u5185\u6587\u672C span \u2192 text\uFF08\u5C0F\u7A0B\u5E8F\u6700\u5C0F\u6587\u672C\u8282\u70B9\uFF09",
|
|
78
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 span\uFF0Ctext \u662F\u884C\u5185\u6587\u672C\u5BB9\u5668\uFF1Btext \u9ED8\u8BA4\u4E0D\u6362\u884C\u3001\u53EF\u88AB text \u5D4C\u5957",
|
|
79
|
+
when: "template \u4E2D\u51FA\u73B0 <span> \u65F6",
|
|
80
|
+
example: { before: "<span>hi</span>", after: "<text>hi</text>" },
|
|
81
|
+
verify: "tests/mp-transform.test.ts\u300C\u6807\u51C6\u6807\u7B7E\u6620\u5C04\u300D",
|
|
82
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08TAG_MAP \u67E5\u8868\uFF09"
|
|
83
|
+
}),
|
|
84
|
+
tagRule("tag/heading-to-text", "h1\u2013h6 \u2192 text", ["h1", "h2", "h3", "h4", "h5", "h6"], {
|
|
85
|
+
description: "\u6807\u9898 h1-h6 \u2192 text\uFF0C\u5E76\u81EA\u52A8\u9644\u52A0 proteus-h1~h6 \u57FA\u7840\u7C7B\uFF08\u89C1 semantic/base-class\uFF09",
|
|
86
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0\u6807\u9898\u6807\u7B7E\uFF1B\u8BED\u4E49\uFF08\u5927\u5B57\u53F7/\u52A0\u7C97\uFF09\u7531\u57FA\u7840\u7C7B\u8FD8\u539F\uFF08#58\uFF09\uFF0C\u89C6\u89C9\u5BF9\u9F50 Web UA \u6837\u5F0F",
|
|
87
|
+
when: "template \u4E2D\u51FA\u73B0 <h1>~<h6> \u65F6",
|
|
88
|
+
example: { before: "<h1>\u6807\u9898</h1>", after: '<text class="proteus-h1">\u6807\u9898</text>' },
|
|
89
|
+
verify: "tests/mp-transform.test.ts\u300C\u8BED\u4E49\u6807\u7B7E\u81EA\u52A8\u9644\u52A0\u57FA\u7840\u7C7B\u300D",
|
|
90
|
+
source: "src/compiler/template.ts \u2192 serializeElement + src/compiler/style.ts BASE_SEMANTIC_WXSS",
|
|
91
|
+
decision: "#58 / #59"
|
|
92
|
+
}),
|
|
93
|
+
tagRule("tag/para-to-text", "p \u2192 text", ["p"], {
|
|
94
|
+
description: "\u6BB5\u843D p \u2192 text\uFF0C\u5E76\u81EA\u52A8\u9644\u52A0 proteus-p \u57FA\u7840\u7C7B\uFF08\u6BB5\u8DDD\u5BF9\u9F50 Web\uFF09",
|
|
95
|
+
why: "p \u6620\u5C04\u4E3A text \u540E\u65E0 UA \u9ED8\u8BA4\u6BB5\u8DDD\uFF0C\u57FA\u7840\u7C7B\u6CE8\u5165 margin: 0 0 1em \u8FD8\u539F Web \u6298\u53E0\u95F4\u8DDD\uFF08#59\uFF09",
|
|
96
|
+
when: "template \u4E2D\u51FA\u73B0 <p> \u65F6",
|
|
97
|
+
example: { before: "<p>\u6BB5\u843D</p>", after: '<text class="proteus-p">\u6BB5\u843D</text>' },
|
|
98
|
+
verify: "tests/mp-transform.test.ts\u300C\u6807\u51C6\u6807\u7B7E\u6620\u5C04\u300D",
|
|
99
|
+
source: "src/compiler/template.ts \u2192 serializeElement",
|
|
100
|
+
decision: "#59\uFF08margin \u5355\u8FB9 em\uFF09"
|
|
101
|
+
}),
|
|
102
|
+
tagRule("tag/link-to-view", "a \u2192 view", ["a"], {
|
|
103
|
+
description: "\u94FE\u63A5 a \u2192 view\uFF08\u5E26 href \u65F6\u5347\u7EA7\u4E3A\u5BFC\u822A\u94FE\u63A5\uFF0C\u89C1 nav/navigate-link\uFF09",
|
|
104
|
+
why: '\u5C0F\u7A0B\u5E8F\u65E0 a \u6807\u7B7E\uFF1B\u5BFC\u822A\u8BED\u4E49\u7531 data-url + bindtap="proteusNavigateTo" \u627F\u62C5\uFF0C\u6837\u5F0F\u8BED\u4E49\u7531 proteus-a \u57FA\u7840\u7C7B\u627F\u62C5',
|
|
105
|
+
when: "template \u4E2D\u51FA\u73B0 <a> \u65F6",
|
|
106
|
+
example: { before: '<a href="/pages/user/index">\u7528\u6237</a>', after: '<view class="proteus-a" data-url="/pages/user/index" bindtap="proteusNavigateTo">\u7528\u6237</view>' },
|
|
107
|
+
verify: "tests/mp-transform.test.ts \u5BFC\u822A\u94FE\u63A5\u7528\u4F8B",
|
|
108
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08isNavLink \u5206\u652F\uFF09",
|
|
109
|
+
decision: "#24 / #30\uFF08\u5BFC\u822A\u94FE\u63A5\uFF09/ #58\uFF08\u57FA\u7840\u7C7B\uFF09"
|
|
110
|
+
}),
|
|
111
|
+
tagRule("tag/image", "img \u2192 image", ["img"], {
|
|
112
|
+
description: '\u56FE\u7247 img \u2192 image\uFF1B:src \u7ED1\u5B9A\u7ECF directive/v-bind \u8F6C\u4E3A src="{{url}}"',
|
|
113
|
+
why: "\u5C0F\u7A0B\u5E8F\u56FE\u7247\u6807\u7B7E\u662F image",
|
|
114
|
+
when: "template \u4E2D\u51FA\u73B0 <img> \u65F6",
|
|
115
|
+
example: { before: '<img :src="url" />', after: '<image src="{{url}}" />' },
|
|
116
|
+
verify: "tests/mp-transform.test.ts\u300C\u6807\u51C6\u6807\u7B7E\u6620\u5C04\u300D",
|
|
117
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08TAG_MAP \u67E5\u8868\uFF09"
|
|
118
|
+
}),
|
|
119
|
+
tagRule("tag/passthrough", "\u540C\u540D\u6807\u7B7E\u4FDD\u7559", ["button", "input", "textarea", "video", "canvas", "scroll-view", "slot"], {
|
|
120
|
+
description: "button / input / textarea / video / canvas / scroll-view / slot \u540C\u540D\u4FDD\u7559\uFF08\u5C0F\u7A0B\u5E8F\u539F\u751F\u5373\u6709\uFF09",
|
|
121
|
+
why: "\u8FD9\u4E9B\u6807\u7B7E\u5728\u5C0F\u7A0B\u5E8F\u539F\u751F\u5B58\u5728\uFF0C\u65E0\u9700\u6620\u5C04\uFF1Binput/textarea \u540C\u65F6\u662F v-model \u7684\u76EE\u6807\uFF08directive/v-model\uFF09",
|
|
122
|
+
when: "template \u4E2D\u51FA\u73B0\u4EE5\u4E0A\u6807\u7B7E\u65F6",
|
|
123
|
+
example: { before: "<button>go</button>", after: "<button>go</button>" },
|
|
124
|
+
verify: "tests/mp-transform.test.ts \u4E8B\u4EF6/\u8868\u5355\u7528\u4F8B",
|
|
125
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08TAG_MAP \u67E5\u8868\uFF09"
|
|
126
|
+
}),
|
|
127
|
+
tagRule("tag/router-link", "router-link \u2192 view", ["router-link"], {
|
|
128
|
+
description: "Vue Router \u7684 router-link \u2192 view + \u5BFC\u822A\u94FE\u63A5\uFF08to \u5C5E\u6027 \u2192 data-url\uFF09",
|
|
129
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 Vue Router \u7EC4\u4EF6\uFF1B\u7EDF\u4E00\u8F6C\u5BFC\u822A\u94FE\u63A5\uFF08\u51B3\u7B56 #24\uFF1A<a href> / <router-link> \u540C\u4E3A\u5BFC\u822A\u5165\u53E3\uFF09",
|
|
130
|
+
when: 'template \u4E2D\u51FA\u73B0 <router-link to="..."> \u4E14\u65E0 @click \u65F6',
|
|
131
|
+
example: { before: '<router-link to="/pages/user/index">\u7528\u6237</router-link>', after: '<view data-url="/pages/user/index" bindtap="proteusNavigateTo">\u7528\u6237</view>' },
|
|
132
|
+
verify: "tests/mp-transform.test.ts \u5BFC\u822A\u94FE\u63A5\u7528\u4F8B",
|
|
133
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08isNavLink \u5206\u652F\uFF09",
|
|
134
|
+
decision: "#24"
|
|
135
|
+
}),
|
|
136
|
+
tagRule("tag/rich-text", "v-html \u5BB9\u5668 \u2192 rich-text", [], {
|
|
137
|
+
description: '\u5E26 v-html \u7684\u5BB9\u5668\u5143\u7D20 \u2192 rich-text\uFF08nodes="{{expr}}"\uFF09',
|
|
138
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 innerHTML\uFF0C\u5BCC\u6587\u672C\u7528 rich-text \u7EC4\u4EF6\u6E32\u67D3 HTML \u8282\u70B9",
|
|
139
|
+
when: "\u5143\u7D20\u4E0A\u6709 v-html \u6307\u4EE4\u65F6\uFF08v-html \u8986\u76D6\u6807\u7B7E\u6620\u5C04\uFF09",
|
|
140
|
+
example: { before: '<div v-html="html"></div>', after: '<rich-text nodes="{{html}}" />' },
|
|
141
|
+
verify: "tests/mp-transform.test.ts v-html \u7528\u4F8B",
|
|
142
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08hasVHtml \u5206\u652F\uFF09"
|
|
143
|
+
}),
|
|
144
|
+
tagRule("tag/unknown-kebab", "\u672A\u6CE8\u518C\u6807\u7B7E kebab-case \u539F\u6837\u8F93\u51FA", [], {
|
|
145
|
+
description: "TAG_MAP \u672A\u8986\u76D6\u7684\u6807\u7B7E\u6309 kebab-case \u539F\u6837\u8F93\u51FA\uFF08\u7EC4\u4EF6 / \u81EA\u5B9A\u4E49\u5143\u7D20\u9003\u751F\u8231\uFF09",
|
|
146
|
+
why: "\u767D\u540D\u5355\u6620\u5C04 + \u672A\u77E5\u6807\u7B7E\u4FDD\u5B88\u4FDD\u7559\uFF1A\u6807\u51C6 Vue \u7EC4\u4EF6\u4F53\u7CFB\uFF08\u539F\u5219 9\uFF09\u4E0E\u539F\u751F\u7EC4\u4EF6\u9003\u751F\u8231\uFF08\u75DB\u70B9 #11 \u5BF9\u7B56\uFF09\u4F9D\u8D56\u6B64\u901A\u9053",
|
|
147
|
+
when: "\u6807\u7B7E\u4E0D\u5728 TAG_MAP \u4E14\u975E router-link \u65F6",
|
|
148
|
+
example: { before: '<custom-comp foo="bar" />', after: '<custom-comp foo="bar" />' },
|
|
149
|
+
verify: "tests/mp-transform.test.ts \u672A\u6CE8\u518C\u6807\u7B7E\u7528\u4F8B",
|
|
150
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08TAG_MAP[node.tag] ?? kebabCase\uFF09"
|
|
151
|
+
}),
|
|
152
|
+
// ============ 语义基础类 ============
|
|
153
|
+
{
|
|
154
|
+
id: "semantic/base-class",
|
|
155
|
+
phase: "template",
|
|
156
|
+
status: "implemented",
|
|
157
|
+
title: "\u8BED\u4E49\u6807\u7B7E\u81EA\u52A8\u9644\u52A0 proteus-* \u57FA\u7840\u7C7B",
|
|
158
|
+
description: "h1-h6/p/a \u6620\u5C04\u65F6\u81EA\u52A8\u9644\u52A0 proteus-h1~h6 / proteus-p / proteus-a \u7C7B\uFF08\u4E0E\u7528\u6237 class \u5408\u5E76\u3001\u4E0E :class \u63D2\u503C\u62FC\u63A5\uFF09",
|
|
159
|
+
why: "Web \u7AEF h1-h6/p/a \u6709\u6D4F\u89C8\u5668 UA \u9ED8\u8BA4\u6837\u5F0F\uFF0C\u5C0F\u7A0B\u5E8F text/view \u6CA1\u6709\uFF1B\u57FA\u7840\u7C7B + \u57FA\u7840 WXSS\uFF08style/semantic-base-wxss\uFF09\u8FD8\u539F\u4E24\u7AEF\u89C6\u89C9\u4E00\u81F4\uFF08#58\uFF09",
|
|
160
|
+
when: "\u8BED\u4E49\u6807\u7B7E\u4E14\u975E v-html \u5BB9\u5668\u65F6",
|
|
161
|
+
example: {
|
|
162
|
+
before: '<h1 class="title">a</h1>',
|
|
163
|
+
after: '<text class="proteus-h1 title">a</text>'
|
|
164
|
+
},
|
|
165
|
+
verify: "tests/mp-transform.test.ts\u300C\u8BED\u4E49\u6807\u7B7E\u81EA\u52A8\u9644\u52A0\u57FA\u7840\u7C7B\u300D\uFF1Bgolden fixture showcase.wxml",
|
|
166
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08baseClass \u5206\u652F\uFF09+ src/compiler/tags.ts SEMANTIC_CLASS",
|
|
167
|
+
decision: "#58 / #59",
|
|
168
|
+
mapping: { ...SEMANTIC_CLASS }
|
|
169
|
+
},
|
|
170
|
+
// ============ 事件映射(EVENT_MAP) ============
|
|
171
|
+
{
|
|
172
|
+
id: "event/click-to-tap",
|
|
173
|
+
phase: "template",
|
|
174
|
+
status: "implemented",
|
|
175
|
+
title: "@click \u2192 bindtap\uFF08EVENT_MAP \u5168\u8868\uFF09",
|
|
176
|
+
description: "\u6807\u51C6\u4E8B\u4EF6 \u2192 \u5C0F\u7A0B\u5E8F\u4E8B\u4EF6\uFF1Aclick\u2192tap\u3001input/change/submit/focus/blur/touch*/longpress/confirm \u540C\u540D\u4FDD\u7559",
|
|
177
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 click\uFF0C\u70B9\u51FB\u4E8B\u4EF6\u662F tap\uFF1B\u5176\u4F59\u4E8B\u4EF6\u547D\u540D\u4E00\u81F4\uFF08EVENT_MAP \u96C6\u4E2D\u5728 tags.ts \u4E0E\u6837\u5F0F\u4FA7\u5171\u7528\uFF09",
|
|
178
|
+
when: "\u5143\u7D20\u4E0A\u6709 @\u4E8B\u4EF6 \u6307\u4EE4\u65F6",
|
|
179
|
+
example: { before: '<button @click="handleTap">go</button>', after: '<button bindtap="handleTap">go</button>' },
|
|
180
|
+
verify: "tests/mp-transform.test.ts\u300C\u4E8B\u4EF6\u6620\u5C04\u300D",
|
|
181
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08on \u5206\u652F\uFF09+ src/compiler/tags.ts EVENT_MAP",
|
|
182
|
+
mapping: { ...EVENT_MAP }
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: "event/modifier-catch",
|
|
186
|
+
phase: "template",
|
|
187
|
+
title: ".stop / .prevent \u4FEE\u9970\u7B26 \u2192 catch \u524D\u7F00",
|
|
188
|
+
description: "@click.stop / @click.prevent \u2192 catchtap\uFF08\u963B\u6B62\u5192\u6CE1\uFF09\uFF1B\u5176\u4F59\u4FEE\u9970\u7B26\u5FFD\u7565",
|
|
189
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0\u4E8B\u4EF6\u4FEE\u9970\u7B26\u8BED\u6CD5\uFF0Ccatch* \u4E8B\u4EF6\u5929\u7136\u963B\u6B62\u5192\u6CE1\uFF0C\u7B49\u4EF7 .stop \u8BED\u4E49\uFF1B.prevent \u65E0\u5BF9\u7B49\u673A\u5236\uFF0C\u6620\u5C04\u4E3A catch \u515C\u5E95",
|
|
190
|
+
when: "\u4E8B\u4EF6\u6307\u4EE4\u5E26 stop \u6216 prevent \u4FEE\u9970\u7B26\u65F6",
|
|
191
|
+
example: { before: '<a @click.stop="stopFn">s</a>', after: '<a catchtap="stopFn">s</a>' },
|
|
192
|
+
verify: "tests/mp-transform.test.ts\u300C\u4E8B\u4EF6\u6620\u5C04\u300D",
|
|
193
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08on \u5206\u652F isCatch\uFF09",
|
|
194
|
+
status: "implemented"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: "event/modifier-self-once",
|
|
198
|
+
phase: "template",
|
|
199
|
+
status: "implemented",
|
|
200
|
+
title: ".self / .once \u4FEE\u9970\u7B26 \u2192 \u5305\u88C5\u65B9\u6CD5\uFF08v0.3 \u5C3E\uFF09",
|
|
201
|
+
description: '@click.self="fn" \u2192 bindtap="proteusSelfFn"\uFF08e.target === e.currentTarget \u624D\u89E6\u53D1\uFF09\uFF1B@click.once="fn" \u2192 bindtap="proteusOnceFn"\uFF08data \u6807\u8BB0\u9996\u6B21\u89E6\u53D1\u540E\u4E0D\u518D\u89E6\u53D1\uFF09\uFF1B\u4EC5\u5BF9\u7B80\u5355\u65B9\u6CD5\u540D handler \u5305\u88C5',
|
|
202
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 .self/.once \u539F\u751F\u8BED\u4E49\uFF0C\u7F16\u8BD1\u671F\u751F\u6210\u5305\u88C5\u65B9\u6CD5\uFF08script \u4FA7\uFF09\uFF1Aself \u7528\u4E8B\u4EF6\u6E90\u5224\u65AD\u3001once \u7528 data \u6807\u8BB0\uFF1B\u952E\u4F4D\u4FEE\u9970\u7B26\uFF08@keyup.enter\uFF09\u65E0\u5BF9\u7B49\u952E\u76D8\u4E8B\u4EF6 \u2192 \u7F16\u8BD1\u671F\u8B66\u544A\uFF08input \u8BF7\u7528 @confirm\uFF09",
|
|
203
|
+
when: "\u4E8B\u4EF6\u6307\u4EE4\u5E26 self \u6216 once \u4FEE\u9970\u7B26\uFF08\u4E14 handler \u662F\u7B80\u5355\u65B9\u6CD5\u540D\uFF09\u65F6",
|
|
204
|
+
example: {
|
|
205
|
+
before: '@click.self="handleTap" / @click.once="handleTap"',
|
|
206
|
+
after: 'bindtap="proteusSelfHandleTap" / bindtap="proteusOnceHandleTap"\uFF08\u5305\u88C5\u65B9\u6CD5\u751F\u6210\u4E8E Page methods\uFF09'
|
|
207
|
+
},
|
|
208
|
+
verify: "tests/mp-transform.test.ts \u4E8B\u4EF6\u4FEE\u9970\u7B26\u7528\u4F8B",
|
|
209
|
+
source: "packages/compiler/src/template.ts\uFF08on \u5206\u652F\uFF09+ script.ts\uFF08proteusSelf/Once \u751F\u6210\uFF09",
|
|
210
|
+
decision: "#88\uFF08v0.3 \u5C3E\u6307\u4EE4\u8865\u5168\uFF09"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: "event/handler-simple-ref",
|
|
214
|
+
phase: "template",
|
|
215
|
+
title: "\u4E8B\u4EF6\u5904\u7406\u5668\u4EC5\u652F\u6301\u7B80\u5355\u65B9\u6CD5\u5F15\u7528",
|
|
216
|
+
description: "\u4EC5\u652F\u6301 handleTap / handleTap($event)\uFF1B\u590D\u6742\u8868\u8FBE\u5F0F\u7F16\u8BD1\u671F\u8B66\u544A\u5E76\u539F\u6837\u8F93\u51FA",
|
|
217
|
+
why: "MVP \u6536\u7F29\uFF08\u539F\u5219 10\uFF09\uFF1A\u5C0F\u7A0B\u5E8F\u4E8B\u4EF6\u5904\u7406\u5668\u5FC5\u987B\u662F Page methods \u4E2D\u7684\u65B9\u6CD5\u540D\uFF0C\u5185\u8054\u8868\u8FBE\u5F0F\u65E0\u6CD5\u9759\u6001\u7F16\u8BD1",
|
|
218
|
+
when: "\u4E8B\u4EF6\u6307\u4EE4\u7684\u8868\u8FBE\u5F0F\u4E0D\u662F\u7B80\u5355\u65B9\u6CD5\u5F15\u7528\u65F6",
|
|
219
|
+
example: {
|
|
220
|
+
before: '@click="count > 0 ? go() : back()"',
|
|
221
|
+
after: "\u7F16\u8BD1\u671F\u8B66\u544A\uFF1A\u4E0D\u662F\u7B80\u5355\u65B9\u6CD5\u5F15\u7528\uFF0C\u539F\u6837\u8F93\u51FA\uFF08\u4EA7\u7269\u9700\u4EBA\u5DE5\u5904\u7406\uFF09"
|
|
222
|
+
},
|
|
223
|
+
verify: "tests/mp-transform.test.ts \u4E8B\u4EF6\u8B66\u544A\u7528\u4F8B",
|
|
224
|
+
source: "src/compiler/template.ts \u2192 cleanHandler",
|
|
225
|
+
status: "limitation"
|
|
226
|
+
},
|
|
227
|
+
// ============ 指令映射 ============
|
|
228
|
+
{
|
|
229
|
+
id: "directive/v-if",
|
|
230
|
+
phase: "template",
|
|
231
|
+
status: "implemented",
|
|
232
|
+
title: "v-if \u2192 wx:if",
|
|
233
|
+
description: 'v-if="show" \u2192 wx:if="{{show}}"',
|
|
234
|
+
why: "\u5C0F\u7A0B\u5E8F\u6761\u4EF6\u6E32\u67D3\u6307\u4EE4\u662F wx:if",
|
|
235
|
+
when: "\u5143\u7D20\u4E0A\u6709 v-if \u6307\u4EE4\u65F6",
|
|
236
|
+
example: { before: '<p v-if="show">a</p>', after: '<p wx:if="{{show}}">a</p>' },
|
|
237
|
+
verify: "tests/mp-transform.test.ts\u300Cv-if / v-else\u300D",
|
|
238
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08if \u5206\u652F\uFF09"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: "directive/v-else-if",
|
|
242
|
+
phase: "template",
|
|
243
|
+
status: "implemented",
|
|
244
|
+
title: "v-else-if \u2192 wx:elif",
|
|
245
|
+
description: 'v-else-if="cond" \u2192 wx:elif="{{cond}}"',
|
|
246
|
+
why: "\u5C0F\u7A0B\u5E8F\u6761\u4EF6\u94FE\u6307\u4EE4\u662F wx:elif",
|
|
247
|
+
when: "\u5143\u7D20\u4E0A\u6709 v-else-if \u6307\u4EE4\u65F6",
|
|
248
|
+
example: { before: '<p v-else-if="b">c</p>', after: '<p wx:elif="{{b}}">c</p>' },
|
|
249
|
+
verify: "tests/mp-transform.test.ts\u300Cv-if / v-else\u300D",
|
|
250
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08else-if \u5206\u652F\uFF09"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: "directive/v-else",
|
|
254
|
+
phase: "template",
|
|
255
|
+
status: "implemented",
|
|
256
|
+
title: "v-else \u2192 wx:else",
|
|
257
|
+
description: "v-else \u2192 wx:else\uFF08\u65E0\u503C\uFF09",
|
|
258
|
+
why: "\u5C0F\u7A0B\u5E8F\u6761\u4EF6\u94FE\u6307\u4EE4\u662F wx:else",
|
|
259
|
+
when: "\u5143\u7D20\u4E0A\u6709 v-else \u6307\u4EE4\u65F6",
|
|
260
|
+
example: { before: "<p v-else>b</p>", after: "<p wx:else>b</p>" },
|
|
261
|
+
verify: "tests/mp-transform.test.ts\u300Cv-if / v-else\u300D",
|
|
262
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08else \u5206\u652F\uFF09"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: "directive/v-for",
|
|
266
|
+
phase: "template",
|
|
267
|
+
status: "implemented",
|
|
268
|
+
title: "v-for \u2192 wx:for / wx:for-item / wx:for-index",
|
|
269
|
+
description: 'v-for="(item, idx) in list" \u2192 wx:for="{{list}}" + wx:for-item + wx:for-index\uFF08\u652F\u6301 in/of\uFF09',
|
|
270
|
+
why: "\u5C0F\u7A0B\u5E8F\u5FAA\u73AF\u6307\u4EE4\u662F wx:for\uFF0C\u4E14\u9700\u663E\u5F0F\u58F0\u660E item/index \u53D8\u91CF\u540D",
|
|
271
|
+
when: "\u5143\u7D20\u4E0A\u6709 v-for \u6307\u4EE4\u65F6",
|
|
272
|
+
example: {
|
|
273
|
+
before: '<div v-for="(item, idx) in list" :key="idx">{{ item }}</div>',
|
|
274
|
+
after: '<view wx:for="{{list}}" wx:for-item="item" wx:for-index="idx" wx:key="idx">{{ item }}</view>'
|
|
275
|
+
},
|
|
276
|
+
verify: "tests/mp-transform.test.ts\u300Cv-for\u300D",
|
|
277
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08for \u5206\u652F\uFF09+ parseForExpr"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: "directive/v-bind",
|
|
281
|
+
phase: "template",
|
|
282
|
+
status: "implemented",
|
|
283
|
+
title: '\u666E\u901A :prop \u7ED1\u5B9A \u2192 prop="{{expr}}"',
|
|
284
|
+
description: ':src / :href / \u4EFB\u610F\u5C5E\u6027\u7ED1\u5B9A \u2192 \u5C5E\u6027="{{\u8868\u8FBE\u5F0F}}"\uFF0C\u9759\u6001\u5C5E\u6027\u539F\u6837\u4FDD\u7559',
|
|
285
|
+
why: '\u5C0F\u7A0B\u5E8F\u5C5E\u6027\u7ED1\u5B9A\u8BED\u6CD5\u662F {{expr}}\uFF1B\u9759\u6001\u5C5E\u6027\uFF08\u5982 placeholder="x"\uFF09\u76F4\u63A5\u900F\u4F20',
|
|
286
|
+
when: "\u5143\u7D20\u4E0A\u6709 v-bind \u4E14 arg \u4E0D\u662F class/style/key \u65F6",
|
|
287
|
+
example: { before: '<img :src="url" />', after: '<image src="{{url}}" />' },
|
|
288
|
+
verify: "tests/mp-transform.test.ts\u300C\u6807\u51C6\u6807\u7B7E\u6620\u5C04\u300D",
|
|
289
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08bind \u5206\u652F\uFF09"
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: "directive/v-bind-class",
|
|
293
|
+
phase: "template",
|
|
294
|
+
status: "implemented",
|
|
295
|
+
title: ":class \u7ED1\u5B9A\uFF08\u5BF9\u8C61/\u6570\u7EC4\u8BED\u6CD5 \u2192 \u4E09\u5143\u62FC\u63A5\uFF09",
|
|
296
|
+
description: `:class="{ active: on }" \u2192 {{(on?'active ':'')}}\uFF1B\u6570\u7EC4\u8BED\u6CD5\uFF08v0.3\uFF09\u2192 \u9010\u9879\u62FC\u63A5\uFF08\u5B57\u7B26\u4E32/\u5BF9\u8C61/\u7B80\u5355\u53D8\u91CF/\u4E09\u5143\uFF09`,
|
|
297
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 class \u5BF9\u8C61/\u6570\u7EC4\u8BED\u6CD5\uFF0C\u7F16\u8BD1\u671F\u4E3A\u4E09\u5143\u8868\u8FBE\u5F0F\u62FC\u63A5\uFF1B\u6570\u7EC4\u8BED\u6CD5 v0.3 \u8865\u9F50\uFF08splitTopLevel \u9876\u5C42\u9017\u53F7\u5206\u5272\uFF0C\u8DF3\u8FC7\u5B57\u7B26\u4E32/\u62EC\u53F7\u5185\u9017\u53F7\uFF09",
|
|
298
|
+
when: "\u5143\u7D20\u4E0A\u6709 :class \u7ED1\u5B9A\u4E14\u5E26\u8BED\u4E49\u57FA\u7840\u7C7B\u6216\u5BF9\u8C61/\u6570\u7EC4\u8868\u8FBE\u5F0F\u65F6",
|
|
299
|
+
example: {
|
|
300
|
+
before: '<p :class="[activeClass, { active: on }]">b</p>',
|
|
301
|
+
after: `<text class="proteus-p {{((activeClass)?(activeClass)+' ':'')+(on?'active ':'')}}">b</text>`
|
|
302
|
+
},
|
|
303
|
+
verify: "tests/mp-transform.test.ts\u300C\u8BED\u4E49\u6807\u7B7E\u81EA\u52A8\u9644\u52A0\u57FA\u7840\u7C7B\u300D+ :class \u6570\u7EC4\u7528\u4F8B",
|
|
304
|
+
source: "packages/compiler/src/template.ts \u2192 formatClassBinding + splitTopLevel",
|
|
305
|
+
decision: "#61 / #77\uFF08v0.3 \u6570\u7EC4\u8BED\u6CD5\uFF09"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
id: "directive/v-bind-style",
|
|
309
|
+
phase: "template",
|
|
310
|
+
status: "implemented",
|
|
311
|
+
title: ":style \u7ED1\u5B9A\uFF08\u5BF9\u8C61\u8BED\u6CD5 \u2192 prop:{{expr}} \u62FC\u63A5\uFF09",
|
|
312
|
+
description: ':style="{ color: c }" \u2192 style="color:{{c}}"\uFF1B\u5C5E\u6027\u540D camelCase \u2192 kebab-case',
|
|
313
|
+
why: "\u5C0F\u7A0B\u5E8F style \u5C5E\u6027\u652F\u6301\u5185\u8054\u63D2\u503C\uFF0C\u9010\u5C5E\u6027\u7F16\u8BD1\u53EF\u9759\u6001\u9A8C\u8BC1",
|
|
314
|
+
when: "\u5143\u7D20\u4E0A\u6709 :style \u7ED1\u5B9A\u4E14\u4E3A\u5BF9\u8C61\u8BED\u6CD5\u65F6",
|
|
315
|
+
example: { before: ':style="{ backgroundColor: bg }"', after: 'style="background-color:{{bg}}"' },
|
|
316
|
+
verify: "tests/mp-transform.test.ts :style \u7528\u4F8B",
|
|
317
|
+
source: "src/compiler/template.ts \u2192 formatStyleBinding"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
id: "directive/v-bind-key",
|
|
321
|
+
phase: "template",
|
|
322
|
+
status: "implemented",
|
|
323
|
+
title: ":key \u2192 wx:key\uFF08\u4EC5\u7B80\u5355\u6807\u8BC6\u7B26\uFF09",
|
|
324
|
+
description: ':key="idx" \u2192 wx:key="idx"\uFF1B\u975E\u7B80\u5355\u6807\u8BC6\u7B26\u7F16\u8BD1\u671F\u8B66\u544A\u5E76\u5FFD\u7565',
|
|
325
|
+
why: "\u5C0F\u7A0B\u5E8F\u5217\u8868\u590D\u7528\u6807\u8BC6\u662F wx:key\uFF0C\u4EC5\u63A5\u53D7\u7B80\u5355\u6807\u8BC6\u7B26",
|
|
326
|
+
when: "\u5143\u7D20\u4E0A\u6709 :key \u7ED1\u5B9A\uFF08\u901A\u5E38\u5728 v-for \u5185\uFF09\u65F6",
|
|
327
|
+
example: { before: ':key="idx"', after: 'wx:key="idx"' },
|
|
328
|
+
verify: "tests/mp-transform.test.ts\u300Cv-for\u300D",
|
|
329
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08key \u5206\u652F\uFF09"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
id: "directive/v-model",
|
|
333
|
+
phase: "template",
|
|
334
|
+
status: "implemented",
|
|
335
|
+
title: "v-model \u2192 value + bindinput \u81EA\u52A8 handler",
|
|
336
|
+
description: 'input/textarea \u7684 v-model="x" \u2192 value="{{x}}" + bindinput="proteusOnXInput"\uFF08handler \u7531 script \u9636\u6BB5\u6CE8\u5165 setData\uFF09',
|
|
337
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 v-model \u8BED\u6CD5\uFF0C\u9700\u53CC\u5411\u7ED1\u5B9A\u7684\u4E24\u534A\uFF1Avalue \u7ED1\u5B9A + \u8F93\u5165\u4E8B\u4EF6\u56DE\u5199\uFF08script/vmodel-handler\uFF09",
|
|
338
|
+
when: "input \u6216 textarea \u4E0A\u6709 v-model \u6307\u4EE4\u65F6",
|
|
339
|
+
example: {
|
|
340
|
+
before: '<input v-model="name" />',
|
|
341
|
+
after: '<input value="{{name}}" bindinput="proteusOnNameInput" />'
|
|
342
|
+
},
|
|
343
|
+
verify: "tests/mp-transform.test.ts v-model \u7528\u4F8B",
|
|
344
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08model \u5206\u652F\uFF09",
|
|
345
|
+
decision: "#29\uFF08\u65B9\u6CD5\u540D\u907F\u514D __ \u524D\u7F00\uFF09"
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
id: "directive/v-html",
|
|
349
|
+
phase: "template",
|
|
350
|
+
status: "implemented",
|
|
351
|
+
title: "v-html \u2192 rich-text nodes",
|
|
352
|
+
description: 'v-html="html" \u2192 rich-text nodes="{{html}}"\uFF08\u5BB9\u5668\u6807\u7B7E\u6620\u5C04\u4E3A rich-text\uFF09',
|
|
353
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 innerHTML\uFF0C\u5BCC\u6587\u672C\u7528 rich-text \u7EC4\u4EF6\uFF08\u539F\u751F\u80FD\u529B\u515C\u5E95\uFF0C\u75DB\u70B9 #11 \u5BF9\u7B56\uFF09",
|
|
354
|
+
when: "\u5143\u7D20\u4E0A\u6709 v-html \u6307\u4EE4\u65F6",
|
|
355
|
+
example: { before: '<div v-html="html"></div>', after: '<rich-text nodes="{{html}}" />' },
|
|
356
|
+
verify: "tests/mp-transform.test.ts v-html \u7528\u4F8B",
|
|
357
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08hasVHtml / html \u5206\u652F\uFF09"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
id: "directive/v-show",
|
|
361
|
+
phase: "template",
|
|
362
|
+
status: "implemented",
|
|
363
|
+
title: "v-show \u2192 hidden \u5C5E\u6027",
|
|
364
|
+
description: 'v-show="show" \u2192 hidden="{{!show}}"\uFF08\u5C0F\u7A0B\u5E8F hidden \u5C5E\u6027 = display:none\uFF0C\u5143\u7D20\u59CB\u7EC8\u6E32\u67D3\uFF09',
|
|
365
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 v-show \u6307\u4EE4\uFF0Chidden \u5C5E\u6027\u8BED\u4E49\u5BF9\u9F50\uFF08display:none \u5207\u6362\uFF09\uFF1B\u5143\u7D20\u4FDD\u7559\u5728\u6587\u6863\u6D41\u4E2D\uFF0C\u4E0E v-if \u7684\u79FB\u9664\u4E0D\u540C\uFF08v0.3 \u8865\u9F50\uFF0C\u539F\u4E3A MVP \u9650\u5236\uFF09",
|
|
366
|
+
when: "\u5143\u7D20\u4E0A\u6709 v-show \u6307\u4EE4\u65F6",
|
|
367
|
+
example: { before: '<p v-show="show">a</p>', after: '<p hidden="{{!show}}">a</p>' },
|
|
368
|
+
verify: "tests/mp-transform.test.ts v-show \u7528\u4F8B",
|
|
369
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08show \u5206\u652F\uFF09",
|
|
370
|
+
decision: "#76\uFF08v0.3 \u6307\u4EE4\u8865\u5168\uFF09"
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: "directive/custom",
|
|
374
|
+
phase: "template",
|
|
375
|
+
status: "implemented",
|
|
376
|
+
title: "\u81EA\u5B9A\u4E49\u6307\u4EE4\u5265\u79BB\uFF08\u65E0\u5BF9\u7B49\u8B66\u544A\uFF09",
|
|
377
|
+
description: "v-focus \u7B49\u81EA\u5B9A\u4E49\u6307\u4EE4\u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u673A\u5236\u2014\u2014\u7F16\u8BD1\u671F\u8B66\u544A\u5E76\u5265\u79BB\uFF08\u903B\u8F91\u4E0D\u6267\u884C\uFF09\uFF0C\u4E0D\u518D\u9759\u9ED8",
|
|
378
|
+
why: "\u53CD\u9ED1\u76D2\uFF08vue-compat Batch A\uFF0C\u51B3\u7B56 #116\uFF09\uFF1A\u5E73\u53F0\u65E0\u5BF9\u7B49\u80FD\u529B\u5FC5\u987B\u7F16\u8BD1\u671F\u663E\u5F0F\u8B66\u544A",
|
|
379
|
+
when: "\u5143\u7D20\u4E0A\u6709\u975E\u5185\u7F6E v-xxx \u6307\u4EE4\uFF08\u4E0D\u5728 if/else/for/show/model/html/text/on/bind/slot/pre/once/cloak\uFF09",
|
|
380
|
+
example: { before: "<input v-focus />", after: "<input /> + \u8B66\u544A\uFF08\u5DF2\u5265\u79BB\uFF09" },
|
|
381
|
+
verify: "tests/vue-compat.test.ts \u81EA\u5B9A\u4E49\u6307\u4EE4\u7528\u4F8B",
|
|
382
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08switch default \u5206\u652F\uFF09",
|
|
383
|
+
decision: "#116"
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
id: "template/is-component",
|
|
387
|
+
phase: "template",
|
|
388
|
+
status: "implemented",
|
|
389
|
+
title: "\u52A8\u6001\u7EC4\u4EF6 <component :is> \u65E0\u5BF9\u7B49\u8B66\u544A",
|
|
390
|
+
description: "<component :is> \u52A8\u6001\u7EC4\u4EF6\u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u673A\u5236\u2014\u2014\u8B66\u544A\uFF08\u4EA7\u7269\u4E3A\u65E0\u6548\u6807\u7B7E\uFF09\uFF0C\u5EFA\u8BAE v-if/v-else \u6761\u4EF6\u6E32\u67D3",
|
|
391
|
+
why: "\u53CD\u9ED1\u76D2\uFF08vue-compat Batch A\uFF0C\u51B3\u7B56 #116\uFF09\uFF1A\u4E0D\u518D\u9759\u9ED8\u8F93\u51FA\u65E0\u6548\u4EA7\u7269",
|
|
392
|
+
when: "\u6A21\u677F\u51FA\u73B0 <component> \u6807\u7B7E",
|
|
393
|
+
example: { before: '<component :is="which" />', after: "\u8B66\u544A + \u539F\u6837\u8F93\u51FA\uFF08\u65E0\u6548\u6807\u7B7E\uFF09" },
|
|
394
|
+
verify: "tests/vue-compat.test.ts \u52A8\u6001\u7EC4\u4EF6\u7528\u4F8B",
|
|
395
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08component \u5206\u652F\uFF09",
|
|
396
|
+
decision: "#116"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: "event/inline-expression",
|
|
400
|
+
phase: "template",
|
|
401
|
+
status: "implemented",
|
|
402
|
+
title: "\u5185\u8054\u4E8B\u4EF6\u8868\u8FBE\u5F0F \u2192 \u5305\u88C5\u65B9\u6CD5\uFF08vue-compat Batch B\uFF09",
|
|
403
|
+
description: '@click="count++"\uFF08\u81EA\u589E/\u81EA\u51CF\uFF09\u4E0E @click="fn(1)"\uFF08\u7B80\u5355\u65B9\u6CD5\u8C03\u7528\uFF09\u2192 \u751F\u6210 proteusInlineXxx \u5305\u88C5\u65B9\u6CD5\uFF08setData \u66F4\u65B0 / this.fn(1)\uFF09\uFF0C\u4EA7\u7269\u53EF\u8FD0\u884C\uFF1B\u590D\u6742\u8868\u8FBE\u5F0F\u4ECD\u8B66\u544A',
|
|
404
|
+
why: "Vue \u5E38\u89C1\u5199\u6CD5\u652F\u6301\uFF08\u51B3\u7B56 #116 Batch B\uFF09\uFF1A\u4E0D\u518D\u539F\u6837\u8F93\u51FA\u65E0\u6548 bindtap\uFF1B\u5BF9\u9F50 ref \u91CD\u5199\uFF08this.data.x \xB1 1\uFF0C\u51B3\u7B56 #36\uFF09",
|
|
405
|
+
when: "\u4E8B\u4EF6\u5904\u7406\u5668\u4E3A\u81EA\u589E/\u81EA\u51CF\u6216\u7B80\u5355\u65B9\u6CD5\u8C03\u7528\uFF08\u65E0 . \u94FE\uFF09\u65F6",
|
|
406
|
+
example: { before: '@click="count++"', after: 'bindtap="proteusInlineIncCount" + \u65B9\u6CD5 setData({ count: this.data.count + 1 })' },
|
|
407
|
+
verify: "tests/vue-compat.test.ts \u5185\u8054\u8868\u8FBE\u5F0F\u7528\u4F8B",
|
|
408
|
+
source: "packages/compiler/src/template.ts \u2192 tryInlineHandler + script.ts \u2192 inlineHandlers \u6CE8\u5165",
|
|
409
|
+
decision: "#116"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
id: "slot/scoped-slot",
|
|
413
|
+
phase: "template",
|
|
414
|
+
status: "implemented",
|
|
415
|
+
title: "\u4F5C\u7528\u57DF\u63D2\u69FD\u8B66\u544A\uFF08\u2605Batch 7\uFF1AMP/Skyline \u5E73\u53F0\u9650\u5236\u786E\u8BA4 + \u66FF\u4EE3\u6A21\u5F0F\uFF09",
|
|
416
|
+
description: '<slot :item="x"> \u4F5C\u7528\u57DF\u63D2\u69FD\u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u673A\u5236\uFF08\u7236\u4FA7\u62FF\u4E0D\u5230\u5B50\u6570\u636E\uFF09\u2014\u2014\u2605\u5E73\u53F0\u9650\u5236\uFF1A\u5FAE\u4FE1\u65E0\u6A21\u677F\u4F20\u53C2\u673A\u5236\uFF08webview \u7684 template import \u65E0\u6CD5\u52A8\u6001\u9009\u62E9\u591A\u7236\u6A21\u677F\uFF0CSkyline \u4E0D\u652F\u6301\u8DE8\u6587\u4EF6\u6A21\u677F\uFF09\uFF0Cuni-app/Taro MP \u7AEF\u540C\u6837\u4E0D\u5B8C\u6574\u2014\u2014\u7F16\u8BD1\u671F\u8B66\u544A + \u66FF\u4EE3\u6A21\u5F0F\uFF1A\u5B50\u7EC4\u4EF6 props \u63A5\u6536\u6570\u636E + \u81EA\u5B9A\u4E49\u4E8B\u4EF6\u56DE\u8C03\u4F20\u6570\u636E\uFF08<MyList :items :item-tap>\uFF09',
|
|
417
|
+
why: "\u53CD\u9ED1\u76D2\uFF08vue-compat-advance Batch 1/7\uFF0C\u51B3\u7B56 #117\uFF09\uFF1A\u4E0D\u518D\u9759\u9ED8\u8F93\u51FA\u65E0\u6548\u5C5E\u6027\uFF1B\u8FD0\u884C\u65F6\u7B49\u4EF7\u53D7 MP \u5E73\u53F0\u80FD\u529B\u9650\u5236\uFF08\u975E\u5F85\u529E\uFF0C\u66FF\u4EE3\u6A21\u5F0F props+\u4E8B\u4EF6\u5DF2\u7531\u7EC4\u4EF6\u7CFB\u7EDF\u5B8C\u6574\u652F\u6301\uFF09",
|
|
418
|
+
when: "<slot> \u5143\u7D20\u4E0A\u6709 v-bind \u5C5E\u6027\uFF08\u975E class/style/name\uFF09",
|
|
419
|
+
example: { before: '<slot :item="item" />', after: "<slot /> + \u8B66\u544A\uFF08\u66FF\u4EE3\uFF1Aprops \u4F20\u5B50 + triggerEvent \u4E8B\u4EF6\u56DE\u8C03\uFF09" },
|
|
420
|
+
verify: "tests/vue-compat-advance.test.ts \u4F5C\u7528\u57DF\u63D2\u69FD\u7528\u4F8B",
|
|
421
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08bind \u5206\u652F slot \u68C0\u6D4B\uFF09",
|
|
422
|
+
decision: "#117"
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
id: "transition/component",
|
|
426
|
+
phase: "template",
|
|
427
|
+
status: "implemented",
|
|
428
|
+
title: "<transition> \u2192 \u88C5\u9970\u5F0F\u8FDB\u5165\u52A8\u753B\uFF08\u5B50\u5143\u7D20\u6CE8\u5165\u52A8\u753B class\uFF09",
|
|
429
|
+
description: '<transition name="fade"> \u88C5\u9970\u5F0F\u5904\u7406\uFF1A\u8FC7\u6E21\u6807\u7B7E\u4E0D\u8F93\u51FA\uFF0C\u5B50\u5143\u7D20\u6CE8\u5165 class="proteus-transition-{name}"\uFF08\u8FDB\u5165\u52A8\u753B\u7531\u91CD\u5EFA\u81EA\u52A8\u64AD\u653E\uFF09\uFF1Bwxss \u6309 usesTransition \u6309\u9700\u6CE8\u5165 keyframes\uFF08fade/slide-up/scale\uFF09',
|
|
430
|
+
why: "Vue <transition> \u8FD0\u884C\u65F6\u7B49\u4EF7\uFF08vue-compat-advance Batch 2\uFF0C\u51B3\u7B56 #117\uFF09\uFF1AMP \u65E0\u539F\u751F Transition\uFF0C\u7F16\u8BD1\u6CE8\u5165\u52A8\u753B class + keyframes \u8865\u4F4D\uFF1B\u4E0E\u8DEF\u7531 routeType \u8F6C\u573A\u4E92\u8865\uFF08\u5143\u7D20\u7EA7 vs \u9875\u9762\u7EA7\uFF09",
|
|
431
|
+
when: "\u6A21\u677F\u51FA\u73B0 <transition> \u6807\u7B7E",
|
|
432
|
+
example: { before: '<transition name="fade"><view v-if="on">X</view></transition>', after: '<view class="proteus-transition-fade" wx:if="{{__tv0}}">X</view>' },
|
|
433
|
+
verify: "tests/vue-compat-advance.test.ts Transition \u7528\u4F8B",
|
|
434
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08transition \u5206\u652F\uFF09+ style.ts TRANSITION_WXSS",
|
|
435
|
+
decision: "#117"
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
id: "transition/leave-state",
|
|
439
|
+
phase: "template",
|
|
440
|
+
status: "implemented",
|
|
441
|
+
title: "<transition> \u79BB\u5F00\u52A8\u753B\u72B6\u6001\u673A\uFF08\u88F8 ref v-if \u5EF6\u8FDF\u79FB\u9664\uFF09",
|
|
442
|
+
description: 'transition \u76F4\u63A5\u5B50\u5143\u7D20 v-if \u4E3A\u88F8 ref \u540D\u65F6\u542F\u7528\u79BB\u5F00\u52A8\u753B\uFF1Av-if \u6539\u5199 wx:if="{{__tv{i}}}"\uFF08\u663E\u793A\u72B6\u6001\uFF0C\u521D\u59CB = ref \u521D\u59CB\u503C\uFF09+ class \u63D2\u503C {{__tl{i} ? "...-leave" : ""}}\uFF08\u79BB\u5F00\u4E2D\u5207\u6362 leave \u52A8\u753B\uFF09\uFF1Bscript \u751F\u6210 proteusTransitionToggle{i}()\uFF08ref \u5199\u5165\u70B9\u6CE8\u5165\uFF1Aon \u53D8 false \u2192 __tl{i}=true \u64AD\u79BB\u5F00\u52A8\u753B + setTimeout \u65F6\u957F\u540E __tv{i}=false \u5EF6\u8FDF\u79FB\u9664\uFF1Bon \u53D8 true \u2192 \u53D6\u6D88\u5B9A\u65F6\u5668\u6062\u590D\u8FDB\u5165\u52A8\u753B\uFF09\uFF1Bwxss \u6309\u9700\u6CE8\u5165 leave class + keyframes\uFF08forwards \u4FDD\u6301\u672B\u5E27\uFF09',
|
|
443
|
+
why: "Vue transition \u79BB\u5F00\u8BED\u4E49\uFF1Av-if \u53D8 false \u5148\u64AD\u79BB\u5F00\u52A8\u753B\u518D\u79FB\u9664\uFF08vue-compat-advance Batch 5\uFF09\uFF1BMP wx:if \u7ACB\u5373\u79FB\u9664\u65E0\u52A8\u753B\u2014\u2014\u7F16\u8BD1\u671F\u72B6\u6001\u673A\u8865\u4F4D\uFF1B\u4EC5\u88F8 ref v-if \u652F\u6301\uFF08\u590D\u6742\u8868\u8FBE\u5F0F\u4FDD\u6301 Batch 2 \u73B0\u72B6\uFF09",
|
|
444
|
+
when: "<transition> \u76F4\u63A5\u5B50\u5143\u7D20\u5E26 v-if \u4E14\u8868\u8FBE\u5F0F\u4E3A\u88F8 ref \u540D",
|
|
445
|
+
example: { before: '<transition name="fade"><view v-if="on">X</view></transition>', after: `<view wx:if="{{__tv0}}" class="proteus-transition-fade {{__tl0 ? 'proteus-transition-fade-leave' : ''}}">X</view> + data __tv0/__tl0 + proteusTransitionToggle0()` },
|
|
446
|
+
verify: "tests/vue-compat-advance.test.ts Batch 5 \u7528\u4F8B",
|
|
447
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08v-if \u5206\u652F + class \u6CE8\u5165\uFF09+ script.ts\uFF08data/\u65B9\u6CD5/\u5199\u5165\u70B9\u6CE8\u5165\uFF09+ style.ts TRANSITION_WXSS",
|
|
448
|
+
decision: "#117"
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
id: "template/template-ref",
|
|
452
|
+
phase: "template",
|
|
453
|
+
status: "implemented",
|
|
454
|
+
title: "\u6A21\u677F ref \u65E0\u5BF9\u7B49\u8B66\u544A",
|
|
455
|
+
description: 'ref="el" \u6A21\u677F ref \u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u7ED1\u5B9A\uFF08\u6C38\u4E0D\u8D4B\u503C\uFF09\u2014\u2014\u8B66\u544A\uFF0C\u5EFA\u8BAE this.selectComponent("#id")',
|
|
456
|
+
why: "\u53CD\u9ED1\u76D2\uFF08vue-compat Batch A\uFF0C\u51B3\u7B56 #116\uFF09\uFF1A\u4E0D\u518D\u9759\u9ED8\u65E0\u6548",
|
|
457
|
+
when: "\u5143\u7D20\u4E0A\u6709 ref \u5C5E\u6027",
|
|
458
|
+
example: { before: '<input ref="el" />', after: "<input /> + \u8B66\u544A" },
|
|
459
|
+
verify: "tests/vue-compat.test.ts \u6A21\u677F ref \u7528\u4F8B",
|
|
460
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08ref \u5C5E\u6027\u5206\u652F\uFF09",
|
|
461
|
+
decision: "#116"
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
id: "template/no-peer",
|
|
465
|
+
phase: "template",
|
|
466
|
+
status: "implemented",
|
|
467
|
+
title: "\u5E73\u53F0\u65E0\u5BF9\u7B49\u7EC4\u4EF6\u8B66\u544A\uFF08Transition/Teleport \u7B49\uFF09",
|
|
468
|
+
description: "transition/transition-group/teleport/suspense/keep-alive \u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u7EC4\u4EF6\u2014\u2014\u8B66\u544A\uFF08\u539F\u6837\u8F93\u51FA\u4E0D\u751F\u6548\uFF09",
|
|
469
|
+
why: "\u53CD\u9ED1\u76D2\uFF08vue-compat Batch A\uFF0C\u51B3\u7B56 #116\uFF09\uFF1A\u8F6C\u573A\u8BF7\u7528\u8DEF\u7531 routeType\uFF0C\u7F13\u5B58/\u4F20\u9001\u8BF7\u79FB\u9664",
|
|
470
|
+
when: "\u6A21\u677F\u51FA\u73B0\u8FD9\u4E9B\u6807\u7B7E",
|
|
471
|
+
example: { before: '<transition name="fade">\u2026</transition>', after: "\u8B66\u544A + \u539F\u6837\u8F93\u51FA" },
|
|
472
|
+
verify: "tests/vue-compat.test.ts \u65E0\u5BF9\u7B49\u7EC4\u4EF6\u7528\u4F8B",
|
|
473
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08no-peer \u5206\u652F\uFF09",
|
|
474
|
+
decision: "#116"
|
|
475
|
+
},
|
|
476
|
+
// ============ 导航链接 ============
|
|
477
|
+
{
|
|
478
|
+
id: "nav/navigate-link",
|
|
479
|
+
phase: "template",
|
|
480
|
+
status: "implemented",
|
|
481
|
+
title: "<a href> / <router-link to> \u2192 \u5BFC\u822A\u94FE\u63A5",
|
|
482
|
+
description: '\u5E26 href/to \u7684\u5BFC\u822A\u94FE\u63A5 \u2192 data-url + data-route-type + bindtap="proteusNavigateTo"\uFF08handler \u7531 script/nav-handler \u6CE8\u5165\uFF09',
|
|
483
|
+
why: "Web \u7AEF <a> \u8D70\u6D4F\u89C8\u5668\u5BFC\u822A\uFF0C\u5C0F\u7A0B\u5E8F\u7AEF\u9700\u8F6C data-url + \u70B9\u51FB\u8DF3\u8F6C\uFF08\u51B3\u7B56 #24\uFF09\uFF1B\u4FDD\u7559\u524D\u5BFC / \u4E3A\u7EDD\u5BF9\u8DEF\u5F84\uFF08#30 \u771F\u673A\u6559\u8BAD\uFF1A\u65E0\u524D\u5BFC / \u4F1A\u76F8\u5BF9\u5F53\u524D\u9875\u76EE\u5F55\u89E3\u6790\u5BFC\u81F4\u53CC\u91CD\u524D\u7F00\uFF09",
|
|
484
|
+
when: "a \u6216 router-link \u4E14\u65E0 @click\u3001\u542B href/to\uFF08\u9759\u6001\u6216 :bind\uFF09\u65F6",
|
|
485
|
+
example: {
|
|
486
|
+
before: '<a href="/pages/user/index">\u7528\u6237</a>',
|
|
487
|
+
after: '<view class="proteus-a" data-url="/pages/user/index" bindtap="proteusNavigateTo">\u7528\u6237</view>'
|
|
488
|
+
},
|
|
489
|
+
verify: "tests/mp-transform.test.ts \u5BFC\u822A\u94FE\u63A5\u7528\u4F8B\uFF1B\u771F\u673A\u9A8C\u8BC1\u5F52\u6863\u51B3\u7B56 #30",
|
|
490
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08isNavLink / hasNavTarget \u5206\u652F\uFF09",
|
|
491
|
+
decision: "#24 / #30 / #29"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
id: "nav/route-type",
|
|
495
|
+
phase: "template",
|
|
496
|
+
status: "implemented",
|
|
497
|
+
title: "\u5BFC\u822A\u94FE\u63A5 route-type \u5C5E\u6027 \u2192 data-route-type",
|
|
498
|
+
description: '<a route-type="halfScreen"> \u2192 data-route-type="halfScreen"\uFF08proteusNavigateTo \u900F\u4F20\u4E3A wx.navigateTo \u7684 routeType\uFF09',
|
|
499
|
+
why: "Skyline \u81EA\u5B9A\u4E49\u8DEF\u7531\u8F6C\u573A\u4ECE\u5BFC\u822A\u94FE\u63A5\u53D1\u8D77\u65F6\uFF0C\u9700\u628A routeType \u4F20\u5230\u8FD0\u884C\u671F\uFF08\u51B3\u7B56 #44\uFF1ArouteType \u53CC\u7AEF\u540C API\uFF09",
|
|
500
|
+
when: "\u5BFC\u822A\u94FE\u63A5\u5143\u7D20\u4E0A\u6709 route-type \u5C5E\u6027\u65F6",
|
|
501
|
+
example: {
|
|
502
|
+
before: '<a href="/pages/user/profile" route-type="halfScreen">\u8D44\u6599</a>',
|
|
503
|
+
after: '<view data-url="/pages/user/profile" data-route-type="halfScreen" bindtap="proteusNavigateTo">\u8D44\u6599</view>'
|
|
504
|
+
},
|
|
505
|
+
verify: "tests/mp-transform.test.ts \u5BFC\u822A\u94FE\u63A5\u7528\u4F8B",
|
|
506
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08route-type \u5206\u652F\uFF09",
|
|
507
|
+
decision: "#28 / #44"
|
|
508
|
+
},
|
|
509
|
+
// ============ 通用节点 ============
|
|
510
|
+
{
|
|
511
|
+
id: "node/interpolation",
|
|
512
|
+
phase: "template",
|
|
513
|
+
status: "implemented",
|
|
514
|
+
title: "\u63D2\u503C {{ expr }} \u4FDD\u7559",
|
|
515
|
+
description: "{{ title }} \u2192 {{ title }}\uFF08\u8868\u8FBE\u5F0F\u539F\u6837\u900F\u4F20\uFF0C\u6587\u672C\u8282\u70B9\u7D27\u51D1\u5355\u884C\u8F93\u51FA\uFF09",
|
|
516
|
+
why: "\u5C0F\u7A0B\u5E8F\u63D2\u503C\u8BED\u6CD5\u540C\u4E3A {{expr}}\uFF0C\u65E0\u9700\u8F6C\u6362\uFF1B\u7EAF\u6587\u672C\u5B50\u8282\u70B9\u7D27\u51D1\u5355\u884C\u4FDD\u8BC1\u4EA7\u7269\u53EF\u8BFB\uFF08\u51B3\u7B56 #15\uFF09",
|
|
517
|
+
when: "\u6A21\u677F\u51FA\u73B0 {{ }} \u63D2\u503C\u6216\u7EAF\u6587\u672C\u5B50\u8282\u70B9\u65F6",
|
|
518
|
+
example: { before: "<p>tapped {{ count }} times</p>", after: '<text class="proteus-p">tapped {{ count }} times</text>' },
|
|
519
|
+
verify: "golden fixture basic.wxml",
|
|
520
|
+
source: "src/compiler/template.ts \u2192 serializeNode\uFF08TEXT / INTERPOLATION \u5206\u652F\uFF09",
|
|
521
|
+
decision: "#15"
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
id: "annotation/line-note",
|
|
525
|
+
phase: "template",
|
|
526
|
+
status: "implemented",
|
|
527
|
+
title: "PROTEUS_DEBUG \u6E90\u7801\u884C\u53F7\u6CE8\u91CA",
|
|
528
|
+
description: "annotateLines=true \u65F6 WXML \u6BCF\u4E2A\u5143\u7D20\u524D\u6CE8\u5165 <!-- @\u884C\u53F7 \u6807\u7B7E -->\uFF0C\u4EA7\u7269\u53EF\u53CD\u67E5\u6E90\u7801\u4F4D\u7F6E",
|
|
529
|
+
why: "\u53CD\u7F16\u8BD1\u9ED1\u76D2\u673A\u5236\uFF08#17\uFF09\uFF1A\u9ED8\u8BA4\u5173\u95ED\uFF0Cnpm run debug:mp \u5F00\u542F\u2014\u2014AI/\u4EBA\u62FF\u5230\u4EA7\u7269\u5373\u53EF\u5B9A\u4F4D\u5230\u6E90\u7801\u884C",
|
|
530
|
+
when: "\u7F16\u8BD1\u9009\u9879 annotateLines=true\uFF08PROTEUS_DEBUG=1 \u6784\u5EFA\uFF09\u65F6",
|
|
531
|
+
example: { before: "\u7B2C 26 \u884C <h1>{{ title }}</h1>", after: '<!-- @26 h1 -->\n<text class="proteus-h1">{{ title }}</text>' },
|
|
532
|
+
verify: "tests/mp-transform.test.ts annotateLines \u7528\u4F8B\uFF1BPROJECT_MEMORY #17",
|
|
533
|
+
source: "src/compiler/template.ts \u2192 serializeElement\uFF08lineNote \u5206\u652F\uFF09",
|
|
534
|
+
decision: "#17"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
id: "template/scope-attr",
|
|
538
|
+
phase: "template",
|
|
539
|
+
status: "implemented",
|
|
540
|
+
title: "scoped CSS\uFF1A\u7528\u6237 class \u4E0E scopeId \u62FC\u63A5\u4E3A\u5355\u4E00\u7C7B\uFF08\u26052026-08 \u771F\u673A\u91CD\u6784\uFF1A\u7C7B\u540D\u540E\u7F00\uFF09",
|
|
541
|
+
description: "<style scoped> \u5B58\u5728\u65F6\uFF0C\u6A21\u677F\u5143\u7D20 class \u503C token \u8FFD\u52A0 -scopeId\uFF08.box \u2192 box-data-v-xxx\uFF09\uFF1B:class \u5B57\u7B26\u4E32\u5B57\u9762\u91CF/\u5BF9\u8C61\u952E\u540C\u6837\u540E\u7F00\uFF08\u52A8\u6001\u53D8\u91CF\u7C7B\u540D\u7F16\u8BD1\u671F\u8B66\u544A\uFF09\uFF1B\u6837\u5F0F\u4FA7\u9009\u62E9\u5668 .box-data-v-xxx \u5339\u914D",
|
|
542
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 scoped CSS \u539F\u751F\u673A\u5236\uFF0C\u7F16\u8BD1\u671F\u7C7B\u540D\u540E\u7F00\u7B49\u4EF7\uFF08v0.3\uFF0C\u51B3\u7B56 #77\uFF09\uFF1B\u26052026-08 \u771F\u673A\u91CD\u6784\uFF1ASkyline \u4E0D\u652F\u6301\u5C5E\u6027\u9009\u62E9\u5668/\u590D\u5408\u7C7B\u9009\u62E9\u5668 \u2192 \u7C7B\u540D\u62FC\u63A5\u4E3A\u552F\u4E00\u5355\u7C7B\u9009\u62E9\u5668\u8DEF\u5F84",
|
|
543
|
+
when: "SFC \u542B <style scoped>\uFF08compileVueSfc \u751F\u6210 scopeId \u5E76\u6CE8\u5165\uFF09\u65F6",
|
|
544
|
+
example: { before: '<div class="card">\u2026</div>', after: '<view class="card-data-v-abc123">\u2026</view>' },
|
|
545
|
+
verify: "tests/mp-transform.test.ts scoped CSS \u7528\u4F8B",
|
|
546
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08scopeSuffix \u5206\u652F\uFF09",
|
|
547
|
+
decision: "#77\uFF08v0.3 scoped CSS\uFF09+ 2026-08 Skyline \u771F\u673A\u91CD\u6784\uFF08\u7C7B\u540D\u540E\u7F00\uFF09",
|
|
548
|
+
// ★分派层示范:输入 { tag, scopeId } → 输出作用域后缀片段(AI 覆盖可改后缀名)
|
|
549
|
+
apply: (ctx) => {
|
|
550
|
+
const input = ctx.input;
|
|
551
|
+
ctx.output = input.scopeId;
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
id: "component/root-class",
|
|
556
|
+
phase: "template",
|
|
557
|
+
status: "implemented",
|
|
558
|
+
title: "\u7EC4\u4EF6\u6807\u7B7E class \u900F\u4F20\uFF1Aroot-class \u5C5E\u6027 \u2192 \u7EC4\u4EF6\u6839\u8282\u70B9 {{rootClass}}\uFF08Vue class \u7EE7\u627F\u8BED\u4E49\uFF09",
|
|
559
|
+
description: '\u81EA\u5B9A\u4E49\u7EC4\u4EF6\u6807\u7B7E\uFF08\u975E\u539F\u751F\u57FA\u7840\u6807\u7B7E\uFF09\u7684 class\uFF08scope class + \u7528\u6237 class + :class \u7ED1\u5B9A\uFF09\u5408\u5E76\u4E3A\u5355\u4E2A root-class \u5C5E\u6027\u53D1\u5C04\uFF1B\u7EC4\u4EF6\u6A21\u677F\u6839\u8282\u70B9 class \u8FFD\u52A0 {{rootClass}}\uFF1Bscript \u4FA7\u7EC4\u4EF6\u6CE8\u5165 rootClass property\uFF08value: ""\uFF09',
|
|
560
|
+
why: "Vue \u7684 class \u7EE7\u627F\u8BED\u4E49\uFF08\u7236\u7EC4\u4EF6 class \u4F5C\u7528\u4E8E\u5B50\u7EC4\u4EF6\u6839\u8282\u70B9\uFF09\u5728\u5FAE\u4FE1\u65E0\u539F\u751F\u5BF9\u7B49\u2014\u2014\u7EC4\u4EF6 host \u8282\u70B9 class \u5408\u5E76\u540E\uFF0C\u9875\u9762 wxss \u65E0\u6CD5\u53EF\u9760\u4F5C\u7528\uFF08\u771F\u673A\u5B9E\u6D4B\uFF1Ap-view \u5916\u5C42\u5BB9\u5668 box \u6837\u5F0F\u4E0D\u751F\u6548\uFF0C\u5373\u4F7F styleIsolation: apply-shared\uFF09\u2014\u2014\u7F16\u8BD1\u671F\u7B49\u4EF7\uFF1Aclass \u7ECF root-class \u5C5E\u6027\u4F20\u5165\u7EC4\u4EF6\uFF0C\u6839\u8282\u70B9\u7ED1\u5B9A {{rootClass}}\uFF0C\u914D\u5408 apply-shared \u8BA9\u9875\u9762\u6837\u5F0F\u4F5C\u7528\u7EC4\u4EF6\u6839\u8282\u70B9",
|
|
561
|
+
when: "\u6A21\u677F\u51FA\u73B0\u975E\u5C0F\u7A0B\u5E8F\u539F\u751F\u6807\u7B7E\uFF08\u7EC4\u4EF6\u6807\u7B7E\uFF0C\u5982 p-view/counter\uFF09\u4E14\u89C4\u5219\u672A\u7981\u7528\u65F6\uFF1B\u7EC4\u4EF6\u6A21\u5F0F\u6839\u8282\u70B9\u7ED1\u5B9A",
|
|
562
|
+
example: { before: '<p-view class="box">\u2026</p-view>', after: '<p-view root-class="data-v-abc123 box" />\uFF08\u7EC4\u4EF6\u6839\u8282\u70B9 class="\u2026 {{rootClass}}"\uFF09' },
|
|
563
|
+
verify: "tests/mp-transform.test.ts \u7EC4\u4EF6 class \u900F\u4F20\u7528\u4F8B",
|
|
564
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08isComponentTag \u5206\u652F\uFF09+ transformTemplateToWxml\uFF08isComponentRoot\uFF09\uFF1Bpackages/compiler/src/script.ts \u2192 transformScriptToPage\uFF08properties.rootClass\uFF09",
|
|
565
|
+
decision: "2026-08 \u771F\u673A\u5B9E\u6D4B\uFF08Skyline\uFF09"
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
id: "layout/auto-flex-row",
|
|
569
|
+
phase: "template",
|
|
570
|
+
status: "implemented",
|
|
571
|
+
title: "\u884C\u5185\u573A\u666F\u81EA\u52A8 flex row\uFF08Skyline \u65E0 inline \u5E03\u5C40\uFF09",
|
|
572
|
+
description: "\u5BB9\u5668\u76F4\u63A5\u5B50\u5143\u7D20\u540C\u65F6\u542B text \u4E0E\u884C\u5185\u63A7\u4EF6\uFF08switch/slider/icon/image/button/input/textarea/checkbox/radio/label/navigator/progress\uFF09\u2192 \u81EA\u52A8\u9644\u52A0 proteus-flex-row \u7C7B\uFF08display:flex;row;align-items:center\uFF09\uFF1BBASE \u6CE8\u5165\u5BF9\u5E94\u89C4\u5219",
|
|
573
|
+
why: "Skyline \u5F15\u64CE\u4E0D\u652F\u6301 inline \u5E03\u5C40\uFF08\u5B98\u65B9 Inline \xD7 \u5F00\u53D1\u4E2D\uFF09\u2014\u2014text \u5929\u751F block \u5360\u6EE1\u4E00\u884C\uFF0C\u884C\u5185\u6392\u5E03\uFF08text + switch \u540C\u884C\uFF09\u552F\u4E00\u8DEF\u5F84\u662F flex row\uFF08\u7528\u6237\u5B9E\u6D4B\uFF09\uFF1B\u81EA\u52A8\u68C0\u6D4B\u514D\u5F00\u53D1\u8005\u624B\u52A8\u5305 flex\uFF08\u53CC\u7AEF\u4E00\u81F4\uFF09",
|
|
574
|
+
when: "\u5BB9\u5668\u5B50\u5143\u7D20\u542B text \u4E14\u542B\u884C\u5185\u63A7\u4EF6\uFF08rules.disabled \u53EF\u5173\uFF09",
|
|
575
|
+
example: { before: "<view><switch/><text>\u5F00\u5173</text></view>", after: '<view class="proteus-flex-row"><switch/><text>\u5F00\u5173</text></view>' },
|
|
576
|
+
verify: "tests/mp-transform.test.ts \u884C\u5185 flex \u7528\u4F8B",
|
|
577
|
+
source: "packages/compiler/src/template.ts \u2192 serializeElement\uFF08autoFlexRow \u5224\u5B9A\uFF09+ style.ts \u2192 BASE_SEMANTIC_WXSS",
|
|
578
|
+
decision: "2026-08 \u7528\u6237\u51B3\u7B56\uFF08Skyline inline \u9650\u5236\uFF09"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
id: "component/progress-degrade",
|
|
582
|
+
phase: "template",
|
|
583
|
+
status: "implemented",
|
|
584
|
+
title: "progress \u964D\u7EA7\u81EA\u5B9A\u4E49 view \u8FDB\u5EA6\u6761\uFF08Skyline \u5B98\u65B9\u4E0D\u652F\u6301 progress\uFF09",
|
|
585
|
+
description: "\u5C0F\u7A0B\u5E8F\u8BED\u4E49 <progress> \u7F16\u8BD1\u4E3A\u81EA\u5B9A\u4E49 view \u7ED3\u6784\uFF08track/inner/info \u4E09\u8282\u70B9\uFF09\u2014\u2014percent\u2192\u5BBD\u5EA6\u3001active-color/color\u2192\u586B\u5145\u8272\u3001stroke-width\u2192\u9AD8\u5EA6\u3001show-info\uFF08\u65E0\u503C=\u771F\uFF09\u2192\u767E\u5206\u6BD4\u6587\u5B57\uFF1B\u9759\u6001\u5C5E\u6027\u76F4\u51FA\u3001\u7ED1\u5B9A\u63D2\u503C\uFF1BBASE \u6CE8\u5165 .proteus-progress \u6837\u5F0F",
|
|
586
|
+
why: "Skyline \u7EC4\u4EF6\u652F\u6301\u8868 progress \u6682\u4E0D\u8003\u8651\uFF08\u771F\u673A\u5B9E\u6D4B\u4E0D\u6E32\u67D3\uFF09\u2014\u2014\u964D\u7EA7\u81EA\u5B9A\u4E49\u7ED3\u6784\u53CC\u7AEF\u4E00\u81F4 + Skyline \u53EF\u7528\uFF0816-progress-skyline-degrade\uFF09",
|
|
587
|
+
when: "\u6A21\u677F\u51FA\u73B0 <progress> \u4E14\u89C4\u5219\u672A\u7981\u7528\u65F6",
|
|
588
|
+
example: { before: '<progress :percent="70" show-info />', after: '<view class="proteus-progress"><view class="proteus-progress-track"><view class="proteus-progress-inner" style="width:{{70}}%;background-color:#07c160"></view></view><text wx:if="{{true}}" class="proteus-progress-info">{{70}}%</text></view>' },
|
|
589
|
+
verify: "tests/mp-transform.test.ts progress \u964D\u7EA7\u7528\u4F8B",
|
|
590
|
+
source: "packages/compiler/src/template.ts \u2192 serializeProgress + style.ts \u2192 BASE_SEMANTIC_WXSS",
|
|
591
|
+
decision: "2026-08 \u771F\u673A\u5B9E\u6D4B\uFF08Skyline \u4E0D\u652F\u6301 progress\uFF09"
|
|
592
|
+
}
|
|
593
|
+
];
|
|
594
|
+
var TAG_RULE_BY_TAG = Object.fromEntries(
|
|
595
|
+
TEMPLATE_RULES.filter((r) => r.id.startsWith("tag/")).flatMap(
|
|
596
|
+
(r) => r.mapping ? Object.keys(r.mapping).map((t) => [t, r.id]) : []
|
|
597
|
+
)
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
// src/transforms/script.ts
|
|
601
|
+
var SCRIPT_RULES = [
|
|
602
|
+
{
|
|
603
|
+
id: "script/const-to-data",
|
|
604
|
+
phase: "script",
|
|
605
|
+
status: "implemented",
|
|
606
|
+
title: "\u9876\u5C42 const\uFF08ref/reactive/\u5B57\u9762\u91CF\uFF09\u2192 data",
|
|
607
|
+
description: "\u96F6\u7F29\u8FDB\u9876\u5C42 const \u2192 data \u5B57\u6BB5\uFF1Bref(0)/reactive({...})/\u5B57\u9762\u91CF\u5728\u6784\u5EFA\u671F\u9759\u6001\u6C42\u503C\uFF0C\u591A\u884C\u6570\u7EC4/\u5BF9\u8C61\u5B57\u9762\u91CF\u5B8C\u6574\u63D0\u53D6",
|
|
608
|
+
why: "\u5C0F\u7A0B\u5E8F\u9875\u9762\u72B6\u6001\u5728 data \u4E2D\uFF0C\u54CD\u5E94\u5F0F\u58F0\u660E\uFF08ref/reactive\uFF09\u7F16\u8BD1\u671F\u4E3A\u521D\u59CB\u503C\u6C42\u503C\uFF08\u51B3\u7B56 #60\uFF1A\u62EC\u53F7\u5E73\u8861\u626B\u63CF\u652F\u6301\u591A\u884C\u5B57\u9762\u91CF\uFF1B\u53EA\u63D0\u53D6\u96F6\u7F29\u8FDB\u9876\u5C42 const\uFF0C\u4E0D\u8BEF\u53D6\u51FD\u6570\u4F53\u5185\u5C40\u90E8 const\uFF09",
|
|
609
|
+
when: "script \u9876\u5C42\u51FA\u73B0 const \u58F0\u660E\u4E14\u521D\u59CB\u503C\u4E3A\u5B57\u9762\u91CF/ref/reactive/shallowRef/readonly \u65F6",
|
|
610
|
+
example: {
|
|
611
|
+
before: 'const count = ref(0)\nconst cards = ref([{ title: "a" }])',
|
|
612
|
+
after: 'data: {\n count: 0,\n cards: [{ "title": "a" }],\n}'
|
|
613
|
+
},
|
|
614
|
+
verify: "tests/mp-transform.test.ts data \u63D0\u53D6\u7528\u4F8B\uFF1Bgolden fixture showcase.js\uFF08\u591A\u884C\u6570\u7EC4\uFF09",
|
|
615
|
+
source: "packages/compiler/src/script.ts \u2192 extractData + extractInitializer",
|
|
616
|
+
decision: "#60 / #22"
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
id: "script/computed-to-data",
|
|
620
|
+
phase: "script",
|
|
621
|
+
status: "implemented",
|
|
622
|
+
title: "computed \u8BFB\u8DEF\u5F84 \u2192 data \u6D3E\u751F\u5B57\u6BB5\uFF08v0.3\uFF09",
|
|
623
|
+
description: "\u9876\u5C42 const x = computed(() => expr) \u2192 data \u4E0D\u5B58\uFF1BonLoad \u521D\u59CB\u5316 + \u4F9D\u8D56 ref \u5199\u5165 setData \u65F6\u540C\u6B65\u91CD\u7B97\uFF08expr \u4E2D x.value \u2192 this.data.x\uFF09",
|
|
624
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 computed \u6982\u5FF5\uFF0C\u7F16\u8BD1\u671F\u628A getter \u8F6C data \u6D3E\u751F\uFF1A\u9759\u6001\u63D0\u53D6 getter \u4E2D\u7684 ref \u4F9D\u8D56 \u2192 \u4F9D\u8D56\u5199\u5165\u65F6\u628A\u91CD\u7B97\u8868\u8FBE\u5F0F\u5408\u5E76\u8FDB\u540C\u4E00 setData\uFF08v0.3 \u5148\u505A\u8BFB\u8DEF\u5F84\uFF0Cwatch/\u5199\u8DEF\u5F84\u540E\u7EED\uFF09",
|
|
625
|
+
when: "script \u9876\u5C42\u51FA\u73B0 computed(() => \u8868\u8FBE\u5F0F)\uFF08\u4EC5\u652F\u6301\u7BAD\u5934\u7B80\u5199 + \u8868\u8FBE\u5F0F\u4F53\uFF1B\u5757\u4F53/function \u5F62\u5F0F\u7F16\u8BD1\u671F\u8B66\u544A\uFF09",
|
|
626
|
+
example: {
|
|
627
|
+
before: "const double = computed(() => count.value * 2)",
|
|
628
|
+
after: "data \u4E0D\u542B double\uFF1Bthis.setData({ count: ..., double: this.data.count * 2 })\uFF08count \u5199\u5165\u65F6\u5408\u5E76\uFF0ConLoad \u521D\u59CB\u5316\u4E00\u6B21\uFF09"
|
|
629
|
+
},
|
|
630
|
+
verify: "tests/mp-transform.test.ts computed \u7528\u4F8B",
|
|
631
|
+
source: "packages/compiler/src/script.ts \u2192 extractComputedFromInit + computedPatch + computedInitLine",
|
|
632
|
+
decision: "#76\uFF08v0.3\uFF09"
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
id: "script/watch-to-methods",
|
|
636
|
+
phase: "script",
|
|
637
|
+
status: "implemented",
|
|
638
|
+
title: "watch \u2192 proteusWatchX \u65B9\u6CD5\uFF08v0.3\uFF09",
|
|
639
|
+
description: "watch(ref, (newVal, oldVal) => { body }) \u2192 methods \u751F\u6210 proteusWatchX\uFF1B\u4F9D\u8D56 ref \u5199\u5165 setData \u540E\u81EA\u52A8\u8C03\u7528\uFF08\u65E7\u503C\u5728\u5199\u5165\u524D\u4FDD\u5B58\uFF09\uFF1Bimmediate: true \u2192 onLoad \u521D\u59CB\u5316\u8C03\u7528\u4E00\u6B21",
|
|
640
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 watch \u6982\u5FF5\uFF0C\u7F16\u8BD1\u671F\u6A21\u62DF\uFF1A\u4F9D\u8D56\u9759\u6001\u63D0\u53D6\uFF08\u5355 ref\uFF09+ \u5199\u5165\u70B9\u8054\u52A8\uFF08setData \u540E\u8C03\u7528\u56DE\u8C03\uFF0CnewVal/oldVal \u7531\u7F16\u8BD1\u671F\u4FDD\u5B58\uFF09\uFF1BMVP\uFF1A\u4EC5\u5355 ref \u76F4\u63A5\u5F15\u7528 + \u7BAD\u5934\u51FD\u6570\u56DE\u8C03\uFF08\u6570\u7EC4\u6E90/\u51FD\u6570\u6E90/function \u56DE\u8C03\u8B66\u544A\uFF09",
|
|
641
|
+
when: "script \u9876\u5C42\u51FA\u73B0 watch(refName, callback) \u65F6",
|
|
642
|
+
example: {
|
|
643
|
+
before: "watch(count, (n, o) => {\n log(n, o)\n})",
|
|
644
|
+
after: "proteusWatchCount(n, o) {\n log(n, o)\n},\n// count \u5199\u5165\uFF1Aconst oldCount = this.data.count; ...setData(...); this.proteusWatchCount(this.data.count, oldCount)"
|
|
645
|
+
},
|
|
646
|
+
verify: "tests/mp-transform.test.ts watch \u7528\u4F8B",
|
|
647
|
+
source: "packages/compiler/src/script.ts \u2192 extractWatch + watchTail + immediateWatchLine",
|
|
648
|
+
decision: "#78\uFF08v0.3\uFF09"
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
id: "script/watch-props",
|
|
652
|
+
phase: "script",
|
|
653
|
+
status: "implemented",
|
|
654
|
+
title: "watch props \u6E90 \u2192 WeChat observers\uFF08\u7EC4\u4EF6\u5C5E\u6027\u76D1\u542C\uFF09",
|
|
655
|
+
description: "watch(props.x, (n, o) => { body }) \u6216 watch(() => props.x, ...) \u2192 Component observers: { x(n, o) { body } }\uFF08\u5C5E\u6027\u53D8\u5316\u89E6\u53D1\u56DE\u8C03\uFF1BWeb \u7AEF\u5373\u6807\u51C6 Vue watch\uFF0C\u5168\u54CD\u5E94\u5F0F\uFF09\uFF1Bimmediate: true \u2192 \u53E6\u751F\u6210 proteusWatchPropX \u65B9\u6CD5 + attached \u521D\u59CB\u5316\u8C03\u7528\u4E00\u6B21",
|
|
656
|
+
why: "\u7EC4\u4EF6\u9700\u8981\u54CD\u5E94\u81EA\u8EAB\u5C5E\u6027\u53D8\u5316\uFF08\u5217\u8868 items \u5206\u9875/\u52A0\u8F7D\u66F4\u591A\u3001\u5F39\u5C42 visible v-model\u3001\u8868\u5355 value \u540C\u6B65\u7B49\uFF09\uFF1B\u5C0F\u7A0B\u5E8F\u65E0\u54CD\u5E94\u5F0F\u7CFB\u7EDF\uFF0C\u5C5E\u6027\u53D8\u5316\u552F\u4E00\u901A\u9053\u662F observers",
|
|
657
|
+
when: "\u7EC4\u4EF6 script \u9876\u5C42\u51FA\u73B0 watch(props.x, callback) / watch(() => props.x, callback) \u65F6\uFF08MVP\uFF1A\u5355\u5C5E\u6027\u6E90\uFF09",
|
|
658
|
+
example: {
|
|
659
|
+
before: "watch(() => props.items, () => {\n calc()\n})",
|
|
660
|
+
after: "observers: {\n items(n, o) {\n calc()\n },\n}"
|
|
661
|
+
},
|
|
662
|
+
verify: "tests/mp-transform.test.ts props \u6E90 watch \u7528\u4F8B",
|
|
663
|
+
source: "packages/compiler/src/script.ts \u2192 extractWatch\uFF08propField \u5206\u652F\uFF09+ observers \u53D1\u5C04",
|
|
664
|
+
decision: "\u7EC4\u4EF6\u5E93 B3\uFF08p-list-view items \u54CD\u5E94\u5F0F\uFF09"
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
id: "script/module-import",
|
|
668
|
+
phase: "script",
|
|
669
|
+
status: "implemented",
|
|
670
|
+
title: "\u8DE8\u6A21\u5757\u5F15\u7528\uFF1Aimport \u2192 require \u8F6C\u6362\uFF08module-plan B0\uFF09/ \u5265\u79BB\u8B66\u544A",
|
|
671
|
+
description: 'setup \u9876\u5C42 import\uFF1A\u76F8\u5BF9\u8DEF\u5F84\u5171\u4EAB\u6A21\u5757\uFF08moduleImports \u547D\u4E2D\uFF09\u2192 \u4EA7\u7269\u9876\u90E8 const { x } = require("<\u76F8\u5BF9\u4EA7\u7269\u8DEF\u5F84>.js")\uFF08named/default/namespace/side \u56DB\u5F62\u6001\uFF09\uFF1Bvue/@proteus-vue/*/type/.vue \u8DF3\u8FC7\uFF08\u7F16\u8BD1\u5668\u9759\u6001/usingComponents/\u7EAF\u7C7B\u578B\uFF09\uFF1B\u65E0\u6CD5\u89E3\u6790\u7684\u8DEF\u5F84 \u2192 \u5265\u79BB + \u8B66\u544A',
|
|
672
|
+
why: "\u53CD\u9ED1\u76D2\uFF08vue-compat Batch A\uFF0C\u51B3\u7B56 #116\uFF09\uFF1Aimport \u5265\u79BB\u4E0D\u518D\u9759\u9ED8\uFF0C\u4EA7\u7269\u672A\u5B9A\u4E49\u5F15\u7528\u663E\u5F0F\u63D0\u793A\uFF1B\u2605module-plan B0\uFF1A\u76F8\u5BF9\u8DEF\u5F84\u5171\u4EAB\u6A21\u5757\u7F16\u8BD1\u4E3A\u72EC\u7ACB\u4EA7\u7269 + require \u8F6C\u6362\uFF0C\u8DE8\u6A21\u5757\u5F15\u7528\u771F\u6B63\u53EF\u7528\uFF08\u540E\u7EED Pinia/API/Component \u57FA\u5EFA\u7684\u5730\u57FA\uFF09",
|
|
673
|
+
when: "<script setup> \u9876\u5C42\u51FA\u73B0 import \u8BED\u53E5",
|
|
674
|
+
example: { before: 'import { formatTime } from "../utils/format"', after: 'const { formatTime } = require("../utils/format.js")\uFF08moduleImports \u547D\u4E2D\uFF09\uFF1B\u672A\u6536\u5F55\u8DEF\u5F84 \u2192 \u8B66\u544A + \u5265\u79BB' },
|
|
675
|
+
verify: "tests/module-import.test.ts B0 \u7528\u4F8B + tests/vue-compat.test.ts import \u7528\u4F8B",
|
|
676
|
+
source: "packages/compiler/src/script.ts \u2192 extractImports + transformScriptToPage\uFF08require \u751F\u6210\uFF09",
|
|
677
|
+
decision: "#116"
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
id: "script/runtime-init",
|
|
681
|
+
phase: "script",
|
|
682
|
+
status: "implemented",
|
|
683
|
+
title: "\u51FD\u6570\u8C03\u7528\u521D\u59CB\u5316 \u2192 \u8FD0\u884C\u65F6\u521D\u59CB\u5316\u5B9E\u4F8B\u5C5E\u6027\uFF08module-plan B0\uFF09",
|
|
684
|
+
description: "\u9876\u5C42 const x = fn()\uFF08\u9759\u6001\u6C42\u503C\u5931\u8D25\uFF09\u2192 data \u4E0D\u542B\u8BE5\u5B57\u6BB5\uFF1BonLoad/attached \u6CE8\u5165 this.x = fn()\uFF08\u5B9E\u4F8B\u5C5E\u6027\uFF0CES5 \u5B89\u5168\uFF09\uFF1B\u6A21\u677F\u7ED1\u5B9A\u4E0D\u652F\u6301\uFF08\u8BFB this.data.x undefined\uFF09\u2014\u2014\u5171\u4EAB\u903B\u8F91\u8BF7\u7528\u6A21\u5757 import",
|
|
685
|
+
why: "\u65E7\u884C\u4E3A\uFF1A\u51FD\u6570\u8C03\u7528\u521D\u59CB\u5316\u9759\u6001\u6C42\u503C\u5931\u8D25 \u2192 data.x = undefined \u4E14\u8C03\u7528\u4E22\u5931\uFF08\u9759\u9ED8\u574F\uFF09\uFF1BB0 \u914D\u5408\u8DE8\u6A21\u5757 require \u8BA9 useStore()/createX() \u771F\u5B9E\u6267\u884C",
|
|
686
|
+
when: "\u9876\u5C42 const \u521D\u59CB\u503C\u4E3A\u51FD\u6570\u8C03\u7528\u4E14\u65E0\u6CD5\u9759\u6001\u6C42\u503C\uFF08\u975E inject/ref/computed\uFF09",
|
|
687
|
+
example: { before: "const store = usePlayerStore()", after: "onLoad: this.store = usePlayerStore()\uFF08data \u4E0D\u542B store\uFF09" },
|
|
688
|
+
verify: "tests/module-import.test.ts + tests/vue-compat.test.ts \u8FD0\u884C\u65F6\u521D\u59CB\u5316\u7528\u4F8B",
|
|
689
|
+
source: "packages/compiler/src/script.ts \u2192 extractData\uFF08runtimeInits\uFF09+ runtimeInitLine",
|
|
690
|
+
decision: "#116"
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
id: "script/store-binding",
|
|
694
|
+
phase: "script",
|
|
695
|
+
status: "implemented",
|
|
696
|
+
title: "Pinia store \u6A21\u677F\u7ED1\u5B9A\uFF08pinia-plan 12 P1\uFF1A$subscribe \u2192 setData \u540C\u6B65\uFF09",
|
|
697
|
+
description: "\u6A21\u677F {{ store.<field> }} \u5F15\u7528\uFF08template \u6536\u96C6 storeBindings\uFF09\u2192 onLoad \u6CE8\u5165\uFF1A\u521D\u59CB setData({ field: this.store.field }) + store.$subscribe \u8BA2\u9605\uFF08\u53D8\u66F4 \u2192 setData\uFF0C\u6A21\u677F\u5B57\u6BB5\u5B9E\u65F6\u540C\u6B65\uFF09\uFF1Bstore \u53D8\u91CF = useXxxStore() \u7684 runtimeInit \u5B9E\u4F8B\u5C5E\u6027\uFF1B\u5D4C\u5957 store.current.title \u2192 current.title\uFF08store \u524D\u7F00\u5265\u79BB\uFF09",
|
|
698
|
+
why: "MP \u6A21\u677F\u7ED1\u5B9A\u8BFB this.data\u2014\u2014store \u662F\u5B9E\u4F8B\u5C5E\u6027\u8BFB\u4E0D\u5230\uFF1BPinia $subscribe \u8BA2\u9605 + setData \u540C\u6B65\uFF08\u5BF9\u9F50 store \u6865 connectPageStore \u6A21\u5F0F\uFF09\uFF0C\u8BA9 pinia-demo MP \u7AEF\u72B6\u6001\u663E\u793A\u53EF\u7528\uFF08pinia-plan 12\uFF09",
|
|
699
|
+
when: "\u6A21\u677F\u51FA\u73B0 store.<field> \u5F15\u7528\u4E14 script \u6709 const store = useXxxStore()",
|
|
700
|
+
example: { before: "{{ store.current.title }}", after: "{{ current.title }} + onLoad: this.store.$subscribe(() => this.setData({ current: this.store.current }))" },
|
|
701
|
+
verify: "tests/pinia-mp-compile.test.ts\uFF08P1 \u7528\u4F8B\uFF09",
|
|
702
|
+
source: "packages/compiler/src/template.ts\uFF08rewriteStoreRefs\uFF09+ script.ts\uFF08storeBindingLine\uFF09",
|
|
703
|
+
decision: "#116"
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
id: "script/define-props",
|
|
707
|
+
phase: "script",
|
|
708
|
+
status: "implemented",
|
|
709
|
+
title: "defineProps \u2192 Component properties\uFF08v0.3 \u7EC4\u4EF6\u7CFB\u7EDF + v0.3 \u5C3E\u6CDB\u578B\uFF09",
|
|
710
|
+
description: "\u7EC4\u4EF6\u6A21\u5F0F\uFF1AdefineProps({ label: String, initial: { type: Number, default: 0 } }) \u6216 TS \u6CDB\u578B defineProps<{ label: string; count?: number }>() \u2192 properties\uFF08type + \u9ED8\u8BA4\u503C value\uFF09\uFF1Bprops.xxx \u8BBF\u95EE\u91CD\u5199\u4E3A this.data.xxx",
|
|
711
|
+
why: "\u5C0F\u7A0B\u5E8F\u7EC4\u4EF6\u7528 Component({ properties }) \u58F0\u660E\u5916\u90E8\u5C5E\u6027\uFF1BVue \u7EC4\u4EF6 props \u7F16\u8BD1\u671F\u6620\u5C04\uFF08v0.3\uFF0C\u51B3\u7B56 #79\uFF09\uFF1B\u5BF9\u8C61\u5F62\u5F0F\uFF08type/default\uFF09+ TS \u6CDB\u578B\u5F62\u5F0F\uFF08string/number/boolean/object/Array/\u8054\u5408\u6620\u5C04\uFF09",
|
|
712
|
+
when: "\u7EC4\u4EF6\u6587\u4EF6\uFF08isComponent\uFF09\u542B defineProps \u65F6",
|
|
713
|
+
example: { before: "const props = defineProps<{ initial: number; label: string }>()", after: 'properties: {\n initial: { type: Number, value: 0 },\n label: { type: String, value: "" },\n}' },
|
|
714
|
+
verify: "tests/mp-transform.test.ts \u7EC4\u4EF6\u7CFB\u7EDF\u7528\u4F8B",
|
|
715
|
+
source: "packages/compiler/src/script.ts \u2192 extractProps + mapTsType",
|
|
716
|
+
decision: "#79 / #91\uFF08v0.3 \u5C3E\u6CDB\u578B\uFF09"
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
id: "script/define-emits",
|
|
720
|
+
phase: "script",
|
|
721
|
+
status: "implemented",
|
|
722
|
+
title: "defineEmits + emit() \u2192 triggerEvent\uFF08v0.3 \u7EC4\u4EF6\u7CFB\u7EDF\uFF09",
|
|
723
|
+
description: '\u7EC4\u4EF6\u6A21\u5F0F\uFF1Aemit("xxx", payload) \u2192 this.triggerEvent("xxx", payload)\uFF1B\u7236\u7EC4\u4EF6 @xxx\uFF08\u975E EVENT_MAP\uFF09\u2192 bind:xxx',
|
|
724
|
+
why: "\u5C0F\u7A0B\u5E8F\u7EC4\u4EF6\u5411\u7236\u7EC4\u4EF6\u901A\u4FE1\u7528 triggerEvent\uFF1BVue emit \u7F16\u8BD1\u671F\u6620\u5C04\uFF08v0.3\uFF0C\u51B3\u7B56 #79\uFF09\uFF1BMVP\uFF1A\u7EA6\u5B9A emit \u53D8\u91CF\u540D",
|
|
725
|
+
when: "\u7EC4\u4EF6\u6587\u4EF6\u542B defineEmits \u58F0\u660E\u4E14\u65B9\u6CD5\u4F53\u8C03\u7528 emit(...) \u65F6",
|
|
726
|
+
example: { before: "emit('change', count.value)", after: "this.triggerEvent('change', this.data.count)" },
|
|
727
|
+
verify: "tests/mp-transform.test.ts \u7EC4\u4EF6\u7CFB\u7EDF\u7528\u4F8B",
|
|
728
|
+
source: "packages/compiler/src/script.ts \u2192 rewriteRefAccess\uFF08emit \u91CD\u5199\uFF09+ template.ts\uFF08bind: \u5192\u53F7\u5F62\u5F0F\uFF09",
|
|
729
|
+
decision: "#79\uFF08v0.3 \u7EC4\u4EF6\u7CFB\u7EDF\uFF09"
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
id: "script/define-expose",
|
|
733
|
+
phase: "script",
|
|
734
|
+
status: "implemented",
|
|
735
|
+
title: "defineExpose \u2192 no-op + \u6821\u9A8C\uFF08v0.3 \u5C3E\uFF09",
|
|
736
|
+
description: "\u7EC4\u4EF6\u6A21\u5F0F\uFF1AdefineExpose({ ... }) \u7F16\u8BD1\u671F no-op\u2014\u2014\u5C0F\u7A0B\u5E8F\u7EC4\u4EF6 methods \u5929\u7136\u53EF\u88AB selectComponent \u8BBF\u95EE\uFF1B\u6821\u9A8C\u58F0\u660E\u6210\u5458\uFF1Aref \u503C\u66B4\u9732\u65E0\u5BF9\u7B49\u673A\u5236 \u2192 \u8B66\u544A",
|
|
737
|
+
why: "\u5C0F\u7A0B\u5E8F\u7EC4\u4EF6\u5916\u90E8\u8BBF\u95EE\uFF08selectComponent + \u65B9\u6CD5\u8C03\u7528\uFF09\u5929\u7136\u8986\u76D6 Vue \u7684 defineExpose \u65B9\u6CD5\u66B4\u9732\u8BED\u4E49\uFF1Bref \u503C\u66B4\u9732\u9700\u65B9\u6CD5\u5305\u88C5\uFF08v0.3 \u5C3E\uFF0C\u51B3\u7B56 #91\uFF09",
|
|
738
|
+
when: "\u7EC4\u4EF6\u6587\u4EF6\u542B defineExpose \u65F6",
|
|
739
|
+
example: { before: "defineExpose({ reset })", after: "no-op\uFF08reset \u5DF2\u5728 methods\uFF0C\u5916\u90E8 selectComponent \u53EF\u8C03\uFF09" },
|
|
740
|
+
verify: "tests/mp-transform.test.ts defineExpose \u7528\u4F8B",
|
|
741
|
+
source: "packages/compiler/src/script.ts \u2192 checkDefineExpose",
|
|
742
|
+
decision: "#91\uFF08v0.3 \u5C3E\u7EC4\u4EF6\u6536\u5C3E\uFF09"
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
id: "script/function-to-methods",
|
|
746
|
+
phase: "script",
|
|
747
|
+
status: "implemented",
|
|
748
|
+
title: "\u9876\u5C42 function \u58F0\u660E \u2192 methods",
|
|
749
|
+
description: "\u9876\u5C42 function handleTap() {...} \u2192 methods \u4E2D\u7684\u5BF9\u8C61\u7B80\u5199 handleTap() {...}",
|
|
750
|
+
why: "\u5C0F\u7A0B\u5E8F\u9875\u9762\u903B\u8F91\u5728 methods \u4E2D\uFF1B\u5BF9\u8C61\u5B57\u9762\u91CF\u5185\u4E0D\u80FD\u8F93\u51FA\u88F8 function \u58F0\u660E\uFF08\u51B3\u7B56 #14\uFF1A\u4EA7\u7269\u65B9\u6CD5\u7528\u5BF9\u8C61\u7B80\u5199\uFF09",
|
|
751
|
+
when: "script \u9876\u5C42\u51FA\u73B0 function \u58F0\u660E\u65F6",
|
|
752
|
+
example: {
|
|
753
|
+
before: "function handleTap() {\n count.value++\n}",
|
|
754
|
+
after: "handleTap() {\n this.setData({ count: (this.data.count === undefined || this.data.count === null ? 0 : this.data.count) + 1 })\n},"
|
|
755
|
+
},
|
|
756
|
+
verify: "tests/mp-transform.test.ts methods \u63D0\u53D6\u7528\u4F8B",
|
|
757
|
+
source: "src/compiler/script.ts \u2192 extractMethods",
|
|
758
|
+
decision: "#14"
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
id: "script/arrow-to-methods",
|
|
762
|
+
phase: "script",
|
|
763
|
+
status: "implemented",
|
|
764
|
+
title: "const \u7BAD\u5934\u51FD\u6570 \u2192 methods",
|
|
765
|
+
description: "const fn = (params) => {...} \u2192 methods \u4E2D\u7684 fn(params) {...}\uFF08\u652F\u6301 async\uFF09",
|
|
766
|
+
why: "\u5C0F\u7A0B\u5E8F\u9875\u9762\u903B\u8F91\u5728 methods \u4E2D\uFF1Bconst \u7BAD\u5934\u51FD\u6570\u540C\u6837\u63D0\u53D6\u4E3A\u65B9\u6CD5",
|
|
767
|
+
when: "script \u9876\u5C42\u51FA\u73B0 const \u7BAD\u5934\u51FD\u6570\uFF08\u542B async\uFF09\u65F6",
|
|
768
|
+
example: {
|
|
769
|
+
before: "const load = async () => {\n const r = await fetchData()\n}",
|
|
770
|
+
after: "load() {\n const r = await fetchData()\n},"
|
|
771
|
+
},
|
|
772
|
+
verify: "tests/mp-transform.test.ts methods \u63D0\u53D6\u7528\u4F8B",
|
|
773
|
+
source: "src/compiler/script.ts \u2192 extractMethods\uFF08arrowRe \u5206\u652F\uFF09"
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
id: "script/lifecycle-map",
|
|
777
|
+
phase: "script",
|
|
778
|
+
status: "implemented",
|
|
779
|
+
title: "\u751F\u547D\u5468\u671F\u6620\u5C04 onMounted \u2192 onReady / onUnmounted \u2192 onUnload",
|
|
780
|
+
description: "onMounted(() => {...}) \u2192 onReady() {...}\uFF1BonUnmounted \u2192 onUnload\uFF1BonLoad \u900F\u4F20",
|
|
781
|
+
why: "Vue \u7EC4\u4EF6\u751F\u547D\u5468\u671F\u4E0E\u5C0F\u7A0B\u5E8F\u9875\u9762\u751F\u547D\u5468\u671F\u4E0D\u540C\u540D\uFF0C\u7F16\u8BD1\u671F\u6620\u5C04\u5230\u5C0F\u7A0B\u5E8F\u94A9\u5B50",
|
|
782
|
+
when: "script \u51FA\u73B0 onMounted / onUnmounted / onLoad \u8C03\u7528\u65F6",
|
|
783
|
+
example: { before: "onMounted(() => { doInit() })", after: "onReady() {\n doInit()\n}," },
|
|
784
|
+
verify: "tests/runtime.test.ts + mp-transform.test.ts \u751F\u547D\u5468\u671F\u7528\u4F8B",
|
|
785
|
+
source: "src/compiler/script.ts \u2192 extractLifecycles"
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
id: "script/ref-read",
|
|
789
|
+
phase: "script",
|
|
790
|
+
status: "implemented",
|
|
791
|
+
title: "\u65B9\u6CD5\u5185 ref \u8BFB\u53D6 \u2192 this.data.name",
|
|
792
|
+
description: "\u65B9\u6CD5/\u751F\u547D\u5468\u671F\u4F53\u5185\u7684 name.value \u8BFB\u53D6 \u2192 this.data.name",
|
|
793
|
+
why: "\u5C0F\u7A0B\u5E8F\u8FD0\u884C\u671F\u72B6\u6001\u5728 this.data \u4E2D\uFF0C\u7F16\u8BD1\u671F\u628A setup ref \u8BBF\u95EE\u91CD\u5199\u4E3A data \u8BBF\u95EE\uFF08\u51B3\u7B56 #22\uFF09",
|
|
794
|
+
when: "\u65B9\u6CD5\u6216\u751F\u547D\u5468\u671F\u4F53\u5185\u51FA\u73B0 ref.value \u8BFB\u53D6\u65F6",
|
|
795
|
+
example: { before: "const v = count.value", after: "const v = this.data.count" },
|
|
796
|
+
verify: "tests/mp-transform.test.ts ref \u91CD\u5199\u7528\u4F8B",
|
|
797
|
+
source: "src/compiler/script.ts \u2192 rewriteRefAccess",
|
|
798
|
+
decision: "#22"
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
id: "script/ref-write",
|
|
802
|
+
phase: "script",
|
|
803
|
+
status: "implemented",
|
|
804
|
+
title: "\u65B9\u6CD5\u5185 ref \u8D4B\u503C \u2192 this.setData",
|
|
805
|
+
description: "\u65B9\u6CD5/\u751F\u547D\u5468\u671F\u4F53\u5185\u7684 name.value = expr \u2192 this.setData({ name: expr })",
|
|
806
|
+
why: "\u5C0F\u7A0B\u5E8F\u66F4\u65B0\u89C6\u56FE\u7684\u552F\u4E00\u901A\u9053\u662F setData\uFF0C\u7F16\u8BD1\u671F\u628A\u8D4B\u503C\u91CD\u5199\u4E3A setData \u8C03\u7528\uFF08\u51B3\u7B56 #22\uFF1B\u6392\u9664 ==/===/\u590D\u5408\u8D4B\u503C\uFF09",
|
|
807
|
+
when: "\u65B9\u6CD5\u6216\u751F\u547D\u5468\u671F\u4F53\u5185\u51FA\u73B0 name.value = expr \u65F6",
|
|
808
|
+
example: { before: "count.value = count.value + 1", after: "this.setData({ count: this.data.count + 1 })" },
|
|
809
|
+
verify: "tests/mp-transform.test.ts ref \u91CD\u5199\u7528\u4F8B",
|
|
810
|
+
source: "src/compiler/script.ts \u2192 rewriteRefAccess",
|
|
811
|
+
decision: "#22"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
id: "script/ref-incdec",
|
|
815
|
+
phase: "script",
|
|
816
|
+
status: "implemented",
|
|
817
|
+
title: "\u65B9\u6CD5\u5185 ref \u81EA\u589E/\u81EA\u51CF \u2192 this.setData",
|
|
818
|
+
description: "name.value++ / -- / ++name.value \u2192 this.setData({ name: (null \u68C0\u67E5 ? 0 : this.data.name) + 1 })",
|
|
819
|
+
why: "\u81EA\u589E/\u81EA\u51CF\u65E0\u8868\u8FBE\u5F0F\u53EF\u63D0\u53D6\uFF0C\u7F16\u8BD1\u671F\u4E3A\u663E\u5F0F setData \u5E76\u505A null \u515C\u5E95\uFF08\u51B3\u7B56 #22\uFF1B\u4E0D\u7528 ?? \u8FD0\u7B97\u7B26\u2014\u2014\u771F\u673A\u9884\u89C8\u62A5 SyntaxError\uFF0C\u51B3\u7B56 #36\uFF09",
|
|
820
|
+
when: "\u65B9\u6CD5\u6216\u751F\u547D\u5468\u671F\u4F53\u5185\u51FA\u73B0 name.value \u7684\u81EA\u589E/\u81EA\u51CF\u65F6",
|
|
821
|
+
example: {
|
|
822
|
+
before: "count.value++",
|
|
823
|
+
after: "this.setData({ count: (this.data.count === undefined || this.data.count === null ? 0 : this.data.count) + 1 })"
|
|
824
|
+
},
|
|
825
|
+
verify: "tests/mp-transform.test.ts ref \u91CD\u5199\u7528\u4F8B\uFF1BWeb E2E tap \u8BA1\u6570\u7528\u4F8B",
|
|
826
|
+
source: "src/compiler/script.ts \u2192 rewriteRefAccess + numOrZero",
|
|
827
|
+
decision: "#22 / #36"
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
id: "script/vmodel-handler",
|
|
831
|
+
phase: "script",
|
|
832
|
+
status: "implemented",
|
|
833
|
+
title: "v-model \u81EA\u52A8 handler \u6CE8\u5165",
|
|
834
|
+
description: '\u6A21\u677F\u51FA\u73B0 v-model="x" \u65F6\u6CE8\u5165 proteusOnXInput(e) { this.setData({ x: e.detail.value }) }',
|
|
835
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 v-model\uFF0C\u56DE\u5199\u65B9\u5411\u7531\u81EA\u52A8 handler \u627F\u62C5\uFF08\u51B3\u7B56 #29\uFF1A\u65B9\u6CD5\u540D\u907F\u514D __ \u524D\u7F00\uFF0C\u5FAE\u4FE1\u4FDD\u7559 _ \u524D\u7F00\u53EF\u80FD\u5BFC\u81F4\u7ED1\u5B9A\u5931\u6548\uFF09",
|
|
836
|
+
when: "template \u8F6C\u6362\u8FD4\u56DE\u7684 vModelBindings \u975E\u7A7A\u65F6",
|
|
837
|
+
example: {
|
|
838
|
+
before: '\uFF08\u6A21\u677F\uFF09<input v-model="name" />',
|
|
839
|
+
after: "proteusOnNameInput(e) { this.setData({ name: e.detail.value }) },"
|
|
840
|
+
},
|
|
841
|
+
verify: "tests/mp-transform.test.ts v-model \u7528\u4F8B",
|
|
842
|
+
source: "src/compiler/script.ts \u2192 transformScriptToPage\uFF08vModelBindings \u5FAA\u73AF\uFF09",
|
|
843
|
+
decision: "#29"
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
id: "script/nav-handler",
|
|
847
|
+
phase: "script",
|
|
848
|
+
status: "implemented",
|
|
849
|
+
title: "\u5BFC\u822A\u94FE\u63A5\u81EA\u52A8 handler \u6CE8\u5165\uFF08proteusNavigateTo\uFF09",
|
|
850
|
+
description: "\u6A21\u677F\u51FA\u73B0\u5BFC\u822A\u94FE\u63A5\u65F6\u6CE8\u5165 proteusNavigateTo(e)\uFF1A\u8BFB data-url \u2192 wx.navigateTo\uFF08routeType \u900F\u4F20\uFF0Cfail \u964D\u7EA7\u666E\u901A\u8DF3\u8F6C\uFF09",
|
|
851
|
+
why: "\u5C0F\u7A0B\u5E8F\u5BFC\u822A\u7EDF\u4E00\u8D70 wx.navigateTo\uFF1B\u4FDD\u7559\u524D\u5BFC / \u4E3A\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u51B3\u7B56 #30 \u771F\u673A\u6839\u56E0\uFF09\uFF1Bfail \u964D\u7EA7\u4FDD\u8BC1\u81EA\u5B9A\u4E49\u8DEF\u7531\u5931\u8D25\u4ECD\u53EF\u8DF3\u8F6C\uFF08#28\uFF09\uFF1B\u65B9\u6CD5\u540D\u907F\u5F00 __ \u524D\u7F00\uFF08#29\uFF09",
|
|
852
|
+
when: "template \u8F6C\u6362\u8FD4\u56DE usesNavigate=true \u65F6",
|
|
853
|
+
example: {
|
|
854
|
+
before: '\uFF08\u6A21\u677F\uFF09<a href="/pages/user/index">\u7528\u6237</a>',
|
|
855
|
+
after: 'proteusNavigateTo(e) {\n const ds = e.currentTarget.dataset\n const url = String(ds.url || "")\n if (!url) return\n const nav = { url: url, fail: ... }\n if (ds.routeType) nav.routeType = ds.routeType\n wx.navigateTo(nav)\n},'
|
|
856
|
+
},
|
|
857
|
+
verify: "tests/mp-transform.test.ts \u5BFC\u822A\u94FE\u63A5\u7528\u4F8B\uFF1B\u771F\u673A\u9A8C\u8BC1\u5F52\u6863 #30",
|
|
858
|
+
source: "src/compiler/script.ts \u2192 transformScriptToPage\uFF08usesNavigate \u5206\u652F\uFF09",
|
|
859
|
+
decision: "#24 / #28 / #29 / #30"
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
id: "page/scroll-bridge",
|
|
863
|
+
phase: "script",
|
|
864
|
+
status: "implemented",
|
|
865
|
+
title: "\u9875\u9762\u6EDA\u52A8 API \u6865\u63A5\uFF0815-page-scroll-container\uFF1ASkyline \u9875\u9762\u4E0D\u6EDA\u52A8\uFF0C\u9875\u9762\u94A9\u5B50\u9760 scroll-view \u4E8B\u4EF6\u89E6\u53D1\uFF09",
|
|
866
|
+
description: "\u9875\u9762\u58F0\u660E onPageScroll/onReachBottom/onPullDownRefresh \u65F6\uFF1A\u81EA\u52A8\u5305\u88C5 scroll-view \u7ED1\u5B9A\u5BF9\u5E94\u4E8B\u4EF6\uFF08template \u4FA7\uFF09+ \u751F\u6210\u6865\u63A5\u65B9\u6CD5\uFF08\u8F7D\u8377\u5F52\u4E00\uFF1Ascroll-view e.detail.scrollTop \u2192 onPageScroll { scrollTop }\uFF09\uFF1B\u624B\u52A8 scroll-view \u573A\u666F\u7F16\u8BD1\u671F\u6B67\u4E49\u8B66\u544A",
|
|
867
|
+
why: "Skyline \u9875\u9762\u672C\u8EAB\u4E0D\u6EDA\u52A8\uFF08\u6EDA\u52A8\u5FC5\u987B scroll-view\uFF09\u2014\u2014\u9875\u9762\u7EA7\u6EDA\u52A8\u94A9\u5B50\u4E0D\u4F1A\u88AB\u89E6\u53D1\uFF1B\u6865\u63A5\u540E onPageScroll/onReachBottom/onPullDownRefresh \u8BED\u4E49\u4FDD\u7559\uFF08\u4E0E Web \u7AEF\u4E00\u81F4\uFF09",
|
|
868
|
+
when: "\u9875\u9762\uFF08\u975E\u7EC4\u4EF6\uFF09\u6A21\u5F0F + \u58F0\u660E\u6EDA\u52A8\u751F\u547D\u5468\u671F + \u81EA\u52A8\u5305\u6EDA\u52A8\u5BB9\u5668\uFF08rules.disabled \u53EF\u5173\uFF09",
|
|
869
|
+
example: { before: "function onPageScroll(e) { \u2026 }", after: '\u81EA\u52A8\u5305\u88C5 scroll-view bindscroll="proteusPageScroll" + proteusPageScroll(e) \u8F7D\u8377\u5F52\u4E00\u8C03\u7528 onPageScroll' },
|
|
870
|
+
verify: "tests/mp-transform.test.ts \u9875\u9762\u6EDA\u52A8\u6865\u63A5\u7528\u4F8B",
|
|
871
|
+
source: "packages/compiler/src/{template,script}.ts\uFF08pageScrollHooks + proteusPageScroll \u7B49\uFF09",
|
|
872
|
+
decision: "2026-08 \u771F\u673A\u5B9E\u6D4B\uFF0815-page-scroll-container\uFF09"
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
id: "script/onload-params",
|
|
876
|
+
phase: "script",
|
|
877
|
+
status: "implemented",
|
|
878
|
+
title: "\u9ED8\u8BA4 onLoad \u53C2\u6570\u81EA\u52A8 decode",
|
|
879
|
+
description: "\u65E0\u663E\u5F0F onLoad \u65F6\u6CE8\u5165\u9ED8\u8BA4\u5B9E\u73B0\uFF1A\u904D\u5386 options \u2192 decodeURIComponent \u2192 \u7ED3\u6784\u5316\u503C\uFF08{/[ \u5F00\u5934\uFF09JSON.parse \u2192 setData",
|
|
880
|
+
why: '\u8DEF\u7531\u53C2\u6570\u7ECF query \u4F20\u9012\uFF0C\u9875\u9762\u9700\u8FD8\u539F\u4E3A\u539F\u59CB\u7C7B\u578B\uFF08\u51B3\u7B56 #19\uFF1A\u4EC5\u5BF9\u7ED3\u6784\u5316\u503C JSON.parse\uFF0C\u666E\u901A\u6807\u91CF\u4FDD\u6301\u5B57\u7B26\u4E32\u2014\u2014\u5BF9\u9F50 P3 \u5951\u7EA6 options.id === "1"\uFF09',
|
|
881
|
+
when: "script \u65E0\u663E\u5F0F onLoad \u65F6\uFF08\u6709\u663E\u5F0F onLoad \u5219\u900F\u4F20\uFF09",
|
|
882
|
+
example: {
|
|
883
|
+
before: "\uFF08\u65E0 onLoad\uFF09",
|
|
884
|
+
after: "onLoad(options) {\n const params = {}\n const keys = Object.keys(options || {})\n for (let i = 0; i < keys.length; i++) {\n ...\n }\n this.setData(params)\n},"
|
|
885
|
+
},
|
|
886
|
+
verify: "tests/runtime.test.ts \u53C2\u6570 decode \u7528\u4F8B",
|
|
887
|
+
source: "src/compiler/script.ts \u2192 transformScriptToPage\uFF08onLoad \u5206\u652F\uFF09",
|
|
888
|
+
decision: "#19 / #32\uFF08ES5 \u5B89\u5168\uFF1A\u7D22\u5F15\u5FAA\u73AF Object.keys\uFF09"
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
id: "script/component-mode",
|
|
892
|
+
phase: "script",
|
|
893
|
+
status: "implemented",
|
|
894
|
+
title: "\u7EC4\u4EF6\u6A21\u5F0F \u2192 Component() \u6784\u9020\u5668",
|
|
895
|
+
description: "isComponent=true\uFF08components \u76EE\u5F55\u4E0B\uFF09\u2192 Component({ data, methods, ... })\uFF0C\u9875\u9762 \u2192 Page({ ... })",
|
|
896
|
+
why: "\u5C0F\u7A0B\u5E8F\u9875\u9762\u7528 Page()\uFF0C\u7EC4\u4EF6\u7528 Component()\uFF08\u4E0D\u540C\u6784\u9020\u5668\u5F62\u6001\uFF0CisComponent \u5206\u652F\u5DF2\u5728 compiler/index.ts \u900F\u4F20\uFF09",
|
|
897
|
+
when: "\u6587\u4EF6\u8DEF\u5F84\u5305\u542B components/ \u76EE\u5F55\u65F6",
|
|
898
|
+
example: { before: "\uFF08\u7EC4\u4EF6 SFC\uFF09", after: "Component({ data: {...}, ... })" },
|
|
899
|
+
verify: "tests/mp-transform.test.ts \u7EC4\u4EF6\u5F62\u6001\u7528\u4F8B",
|
|
900
|
+
source: "src/compiler/script.ts \u2192 transformScriptToPage\uFF08isComponent \u5206\u652F\uFF09"
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
id: "script/es5-safe",
|
|
904
|
+
phase: "script",
|
|
905
|
+
status: "implemented",
|
|
906
|
+
title: "\u751F\u6210\u4EE3\u7801 ES5 \u5B89\u5168",
|
|
907
|
+
description: "\u4EA7\u7269\u89C4\u907F ?? / ?. / \u6570\u7EC4\u89E3\u6784 / \u5BF9\u8C61\u5C55\u5F00\uFF08\u663E\u5F0F null \u68C0\u67E5\u4E09\u5143\u3001\u7D22\u5F15\u5FAA\u73AF Object.keys\u3001\u76F4\u63A5\u5C5E\u6027\u8D4B\u503C\uFF09",
|
|
908
|
+
why: "\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u5C06\u9875\u9762 JS \u8F6C ES5 \u65F6\u4F9D\u8D56 babel helper \u6A21\u5757\uFF0Chelper \u4E0D\u5728\u5305\u5185\u4F1A\u62A5\u9519\uFF08#32\uFF1AarrayWithHoles \u672A\u5B9A\u4E49\uFF09\uFF1B\u771F\u673A\u9884\u89C8\u76F4\u63A5\u62A5 ?? \u8BED\u6CD5\u9519\u8BEF\uFF08#36\uFF09",
|
|
909
|
+
when: "\u6240\u6709\u751F\u6210\u7684 JS \u4EA7\u7269\uFF08\u65B9\u6CD5\u4F53 / onLoad / \u81EA\u52A8 handler\uFF09",
|
|
910
|
+
example: {
|
|
911
|
+
before: "// \u7981\u6B62\u51FA\u73B0\u5728\u4EA7\u7269\u4E2D\nconst [a, b] = arr\nconst v = x ?? 0",
|
|
912
|
+
after: "// \u4EA7\u7269\u5B9E\u9645\u5F62\u6001\nconst a = arr[0], b = arr[1]\nconst v = (x === undefined || x === null ? 0 : x)"
|
|
913
|
+
},
|
|
914
|
+
verify: "grep \u626B\u63CF\u4EA7\u7269\u96F6\u6B8B\u7559\uFF08\u51B3\u7B56 #36 \u9A8C\u8BC1\u65B9\u5F0F\uFF09",
|
|
915
|
+
source: "src/compiler/script.ts \u5404\u5904\uFF08numOrZero / onLoad \u5B9E\u73B0 / nav handler\uFF09",
|
|
916
|
+
decision: "#32 / #36"
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
id: "script/provide-inject",
|
|
920
|
+
phase: "script",
|
|
921
|
+
status: "implemented",
|
|
922
|
+
title: "provide/inject \u2192 getApp().__proteusProvides \u5168\u5C40\u6CE8\u518C\u8868\u6865\uFF08vue-compat-advance Batch 3/4\uFF09",
|
|
923
|
+
description: '\u9876\u5C42 provide("key", expr) / const x = inject("key"[, default]) \u7F16\u8BD1\u4E3A\u6CE8\u518C\u8868\u8BFB\u5199\uFF1A\u9875\u9762 onLoad \u5355\u51FD\u6570\u5408\u5E76\u5757\uFF08pageId \u547D\u540D\u7A7A\u95F4\u89E3\u6790\u4E00\u6B21 + provide \u6CE8\u518C + inject setData\uFF09\uFF1B\u7EC4\u4EF6 provide \u653E created\uFF08\u5148\u4E8E\u5B50\u7EC4\u4EF6 attached \u6CE8\u5165\uFF09\u3001inject \u653E attached\uFF1Bprovide \u503C\u8868\u8FBE\u5F0F\u91CD\u5199\u88F8 ref \u540D / ref.value \u2192 this.data.<name>\uFF1Binject \u652F\u6301\u9ED8\u8BA4\u503C\uFF08undefined \u65F6\u53D6\u9ED8\u8BA4\uFF09\uFF1B\u2605Batch 4 \u54CD\u5E94\u5F0F\u8054\u52A8\uFF1A\u88F8 ref \u63D0\u4F9B\uFF08provide("key", refName)\uFF09\u2192 ref \u5199\u5165\u70B9\u6CE8\u5165 proteusSyncProvide\uFF08\u540C\u6B65\u6CE8\u518C\u8868\u503C + \u901A\u77E5\u8BA2\u9605\u8005\uFF09\uFF0Cinject \u4FA7\u8BA2\u9605 __subs[key]\uFF08\u503C\u53D8\u5316 setData \u5237\u65B0\uFF09+ detached/onUnload \u53D6\u6D88\uFF1B\u2605Batch 6 \u9875\u9762\u7EA7\u9694\u79BB\uFF1A\u6CE8\u518C\u8868\u6309 pageId \u547D\u540D\u7A7A\u95F4\uFF08\u9875\u9762 onLoad __seq \u751F\u6210 + \u7EC4\u4EF6 getCurrentPages \u6808\u9876\u89E3\u6790\uFF0ConUnload \u5220\u9664\u547D\u540D\u7A7A\u95F4\u9632\u6CC4\u6F0F\uFF09\uFF1B.value/\u5B57\u9762\u91CF\u63D0\u4F9B\u4FDD\u6301\u9759\u6001\u5FEB\u7167\uFF08\u5BF9\u9F50 Vue \u8BED\u4E49\uFF1A\u4F20 ref \u8054\u52A8\u3001\u4F20\u503C\u5FEB\u7167\uFF09',
|
|
924
|
+
why: "\u5C0F\u7A0B\u5E8F\u7EC4\u4EF6\u6811\u65E0 provide/inject \u673A\u5236\uFF08\u51B3\u7B56 #117\uFF09\uFF1A\u5168\u5C40\u6CE8\u518C\u8868\u6865\u8BA9\u9875\u9762\u5411\u7EC4\u4EF6\u4F20\u503C\uFF08\u542B\u6DF1\u5C42\u5D4C\u5957\u7EC4\u4EF6\uFF09\uFF1BMVP \u503C\u5FEB\u7167\uFF08\u975E\u54CD\u5E94\u5F0F\u8054\u52A8\uFF09+ \u5168\u5C40\u6CE8\u518C\u8868\uFF08\u91CD\u540D key \u540E\u5199\u8986\u76D6\uFF09\uFF0C\u9875\u9762\u7EA7\u9694\u79BB/\u54CD\u5E94\u5F0F\u4E3A\u540E\u7EED",
|
|
925
|
+
when: 'script \u9876\u5C42\u51FA\u73B0 provide("key", value) \u8C03\u7528\u6216 const x = inject("key")\uFF08\u4EC5\u96F6\u7F29\u8FDB\u5355\u884C\uFF1Bprovide \u503C\u652F\u6301\u5B57\u9762\u91CF/\u88F8 ref/ref.value\uFF09',
|
|
926
|
+
example: {
|
|
927
|
+
before: 'provide("user", userInfo)\nconst theme = inject("theme", "light")',
|
|
928
|
+
after: 'onLoad: const provides = (getApp().__proteusProvides || (getApp().__proteusProvides = {})); provides["user"] = this.data.userInfo; if (!provides.__subs) provides.__subs = {}; ...; this.setData({ theme: (provides["theme"] === undefined ? "light" : provides["theme"]) })\uFF1BuserInfo \u5199\u5165\u70B9\u8FFD\u52A0 this.proteusSyncProvide("user", "userInfo")\uFF08\u901A\u77E5\u8BA2\u9605\u8005 setData \u5237\u65B0\uFF09'
|
|
929
|
+
},
|
|
930
|
+
verify: "tests/vue-compat-advance.test.ts Batch 3/4 \u7528\u4F8B",
|
|
931
|
+
source: "packages/compiler/src/script.ts \u2192 extractProvideInject + buildProvideInject + rewriteRefAccess\uFF08\u5199\u5165\u70B9\u6CE8\u5165\uFF09+ transformScriptToPage\uFF08created/attached/onLoad \u6CE8\u5165\u70B9 + proteusSyncProvide/proteusUnsubscribeProvide \u751F\u6210\uFF09",
|
|
932
|
+
decision: "#117"
|
|
933
|
+
}
|
|
934
|
+
];
|
|
935
|
+
|
|
936
|
+
// src/transforms/style.ts
|
|
937
|
+
var STYLE_RULES = [
|
|
938
|
+
{
|
|
939
|
+
id: "style/px-to-rpx",
|
|
940
|
+
phase: "style",
|
|
941
|
+
status: "implemented",
|
|
942
|
+
title: "px \u2192 rpx\uFF08\u4EC5 MP \u7AEF\u7F16\u8BD1\u671F\u751F\u6548\uFF09",
|
|
943
|
+
description: "CSS \u6570\u503C px \u2192 rpx\uFF08rpxRatio \u9ED8\u8BA4 2\uFF1A48px \u2192 96rpx\uFF09\uFF1BWeb \u7AEF\u6C38\u4E0D\u8F6C\u6362\uFF08Vite \u539F\u751F\u5904\u7406\uFF09",
|
|
944
|
+
why: "\u5C0F\u7A0B\u5E8F rpx \u662F\u5C4F\u5E55\u7B49\u6BD4\u5355\u4F4D\uFF08750 \u8BBE\u8BA1\u7A3F\uFF09\uFF0C\u8DE8\u7AEF CSS \u4E00\u81F4\u6027\u7684\u7F16\u8BD1\u671F\u5438\u6536\uFF08\u51B3\u7B56 #9\uFF1AMP \u7AEF px\u2192rpx\uFF0CWeb \u7AEF\u4FDD\u6301\u6807\u51C6 CSS\uFF09",
|
|
945
|
+
when: "style \u4E2D\u51FA\u73B0\u6570\u503C px\uFF08style.px2rpx=true\uFF09\u65F6",
|
|
946
|
+
example: { before: "padding: 48px;", after: "padding: 96rpx;" },
|
|
947
|
+
verify: "tests/mp-transform.test.ts px\u2192rpx \u7528\u4F8B",
|
|
948
|
+
source: "src/compiler/style.ts \u2192 transformStyleToWxss\uFF08px2rpx \u5206\u652F\uFF09",
|
|
949
|
+
decision: "#9",
|
|
950
|
+
// ★分派层:AI 覆盖此实现(如改换算公式/单位映射)→ 编译输出即时变化,无需改 style.ts
|
|
951
|
+
apply: (ctx) => {
|
|
952
|
+
const input = ctx.input;
|
|
953
|
+
const ratio = Number(ctx.options?.rpxRatio ?? 2);
|
|
954
|
+
ctx.output = input.replace(/(\d+(?:\.\d+)?)px\b/g, (_m, n) => `${Number(n) * ratio}rpx`);
|
|
955
|
+
}
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
id: "style/selector-tag",
|
|
959
|
+
phase: "style",
|
|
960
|
+
status: "implemented",
|
|
961
|
+
title: "\u9009\u62E9\u5668 HTML \u6807\u7B7E \u2192 \u5C0F\u7A0B\u5E8F\u6807\u7B7E",
|
|
962
|
+
description: "\u9009\u62E9\u5668\u4E2D\u7684\u6807\u7B7E\u540D\u91CD\u5199\u4E3A\u5C0F\u7A0B\u5E8F\u6807\u7B7E\uFF08.links a \u2192 .links view\u3001div > p \u2192 view > .proteus-p\uFF09\uFF1B\u5C5E\u6027\u9009\u62E9\u5668/\u7C7B\u540D/ID/\u957F\u6807\u8BC6\u7B26\u4E0D\u8BEF\u4F24",
|
|
963
|
+
why: "\u6A21\u677F\u5DF2\u628A div/a/h1 \u7B49\u6620\u5C04\u4E3A view/text\uFF0C\u82E5\u6837\u5F0F\u9009\u62E9\u5668\u4E0D\u91CD\u5199\u5219\u5339\u914D\u4E0D\u5230\u5143\u7D20\uFF08\u51B3\u7B56 #57 \u4FEE\u590D\u7684 bug\uFF09\uFF1B\u547D\u4E2D\u6761\u4EF6=\u6807\u7B7E\u4F4D\u4E8E\u9009\u62E9\u5668\u8D77\u59CB\u6216\u7EC4\u5408\u5668\u4E4B\u540E\uFF0C\u907F\u514D .a/#input/tag-a \u8BEF\u4F24",
|
|
964
|
+
when: "style \u9009\u62E9\u5668\u4E2D\u542B TAG_MAP \u4E2D\u7684\u6807\u7B7E\u540D\u65F6",
|
|
965
|
+
example: { before: ".links a { color: #1a7af8; }", after: ".links .proteus-a { color: #1a7af8; }" },
|
|
966
|
+
verify: "tests/mp-transform.test.ts \u9009\u62E9\u5668\u91CD\u5199\u7528\u4F8B\uFF1B\u4EA7\u7269\u9A8C\u8BC1 .links a \u2192 .links view \u5BF9\u9F50 wxml\uFF08\u51B3\u7B56 #57\uFF09",
|
|
967
|
+
source: "src/compiler/style.ts \u2192 rewriteTagSelectors + TAG_SELECTOR_RE",
|
|
968
|
+
decision: "#57",
|
|
969
|
+
mapping: { ...TAG_MAP }
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
id: "style/selector-semantic",
|
|
973
|
+
phase: "style",
|
|
974
|
+
status: "implemented",
|
|
975
|
+
title: "\u8BED\u4E49\u6807\u7B7E\u9009\u62E9\u5668 \u2192 proteus-* \u7C7B\u9009\u62E9\u5668",
|
|
976
|
+
description: "h1-h6/p/a \u9009\u62E9\u5668\u6620\u5C04\u4E3A\u57FA\u7840\u7C7B\u9009\u62E9\u5668\uFF08.card h3 \u2192 .card .proteus-h3\u3001.links a \u2192 .links .proteus-a\uFF09\uFF0C\u800C\u975E\u6807\u7B7E",
|
|
977
|
+
why: "h3 \u4E0E p \u90FD\u6620\u5C04\u4E3A text\uFF0C\u82E5\u90FD\u6620\u5C04\u4E3A\u6807\u7B7E\uFF0C\u540C\u7279\u5F02\u6027\u89C4\u5219\u540E\u5199\u8986\u76D6\u5148\u5199\uFF08\u51B3\u7B56 #61 \u4FEE\u590D\uFF1A.card p \u7684 color \u66FE\u6C61\u67D3 h3\uFF09\uFF1B\u6A21\u677F\u4FA7\u5DF2\u9644\u52A0 proteus-* \u7C7B\u6545\u7CBE\u786E\u5339\u914D",
|
|
978
|
+
when: "\u9009\u62E9\u5668\u4E2D\u542B h1-h6/p/a \u6807\u7B7E\u65F6",
|
|
979
|
+
example: { before: ".card h3 { font-weight: 700; }", after: ".card .proteus-h3 { font-weight: 700; }" },
|
|
980
|
+
verify: "tests/mp-transform.test.ts \u9009\u62E9\u5668\u91CD\u5199\u7528\u4F8B\uFF08showcase.wxss \u4E24\u6761\u89C4\u5219\u72EC\u7ACB\uFF09",
|
|
981
|
+
source: "src/compiler/style.ts \u2192 rewriteTagSelectors\uFF08semantic \u5206\u652F\uFF09+ src/compiler/tags.ts SEMANTIC_CLASS",
|
|
982
|
+
decision: "#61",
|
|
983
|
+
mapping: { ...SEMANTIC_CLASS }
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
id: "style/semantic-base-wxss",
|
|
987
|
+
phase: "style",
|
|
988
|
+
status: "implemented",
|
|
989
|
+
title: "\u6CE8\u5165\u8BED\u4E49\u57FA\u7840 WXSS\uFF08h1-h6/p/a \u89C6\u89C9\u8FD8\u539F\uFF09",
|
|
990
|
+
description: "\u4EA7\u7269 WXSS \u5934\u90E8\u6CE8\u5165 .proteus-h1~h6/.proteus-p/.proteus-a \u57FA\u7840\u6837\u5F0F\uFF08\u5BF9\u9F50 HTML \u6807\u51C6\u9644\u5F55 D\uFF1A\u5B57\u53F7/\u5B57\u91CD/\u5355\u8FB9 em \u6BB5\u8DDD/\u94FE\u63A5\u8272\uFF09\uFF0C\u4F4D\u4E8E\u7528\u6237\u6837\u5F0F\u4E4B\u524D",
|
|
991
|
+
why: "Web \u6D4F\u89C8\u5668\u6709 UA \u9ED8\u8BA4\u6837\u5F0F\uFF0C\u5C0F\u7A0B\u5E8F text/view \u6CA1\u6709\uFF1B\u6CE8\u5165\u57FA\u7840\u7C7B\u8FD8\u539F\u4E24\u7AEF\u89C6\u89C9\u4E00\u81F4\uFF08\u51B3\u7B56 #58\uFF09\uFF1Bmargin \u7528\u5355\u8FB9 bottom + em \u76F8\u5BF9\u81EA\u8EAB\u5B57\u53F7\u2014\u2014Skyline \u81EA\u7814\u5F15\u64CE\u4E0D\u6298\u53E0 margin\uFF0C\u5355\u8FB9 em \u4E0E Web \u6298\u53E0\u5728\u4E3B\u6D41\u7A0B\u7EC4\u5408\u4E0B\u89C6\u89C9\u4E00\u81F4\uFF08\u51B3\u7B56 #59\uFF09",
|
|
992
|
+
when: "\u6BCF\u6B21 style \u8F6C\u6362\uFF08\u4F5C\u4E3A\u4EA7\u7269\u524D\u7F00\uFF09",
|
|
993
|
+
example: {
|
|
994
|
+
before: "// \u6E90\u7801\u65E0\u6837\u5F0F\u65F6\u4EA7\u7269\u4ECD\u542B",
|
|
995
|
+
after: ".proteus-h1 { display: block; font-size: 64rpx; font-weight: 700; margin: 0 0 0.67em; }\n.proteus-p { display: block; margin: 0 0 1em; }\n.proteus-a { color: #1a7af8; text-decoration: underline; }"
|
|
996
|
+
},
|
|
997
|
+
verify: 'golden fixture showcase.wxss\uFF1B\u4EA7\u7269\u9A8C\u8BC1 <text class="proteus-h1"> + .proteus-h1\uFF08\u51B3\u7B56 #58\uFF09',
|
|
998
|
+
source: "src/compiler/style.ts \u2192 BASE_SEMANTIC_WXSS",
|
|
999
|
+
decision: "#58 / #59"
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
id: "style/default-scoped",
|
|
1003
|
+
phase: "style",
|
|
1004
|
+
status: "implemented",
|
|
1005
|
+
title: "\u9ED8\u8BA4 scoped\uFF1A<style> \u65E0\u6807\u8BB0\u6309\u5C40\u90E8\u4F5C\u7528\u57DF\u5904\u7406\uFF08\u26052026-08 \u7528\u6237\u51B3\u7B56\uFF09",
|
|
1006
|
+
description: "\u975E <style global> \u7684 style \u5757\u5747\u6309 scoped \u5904\u7406\uFF08\u7C7B\u540D\u540E\u7F00\u62FC\u63A5\uFF09\uFF1B<style> \u65E0 scoped/global \u6807\u8BB0\u65F6\u7F16\u8BD1\u671F\u8B66\u544A\uFF1B<style global>\uFF08Proteus \u6269\u5C55\uFF09\u663E\u5F0F\u5168\u5C40\uFF08\u4E0D\u4F5C\u7528\u57DF\u5316\uFF09\uFF1B\u540C\u6587\u4EF6 scoped+global \u5206\u7EC4\u8F93\u51FA\uFF08global \u5728\u524D\uFF09",
|
|
1007
|
+
why: "Vue \u6807\u51C6 <style> \u4E3A\u5168\u5C40\u2014\u2014Web \u7AEF\u6CC4\u6F0F\u5230\u6240\u6709\u9875\u9762\uFF08\u771F\u673A\u5B9E\u6D4B\uFF1Aconfig-demo \u65E0 scoped \u7684\u7070\u8272\u80CC\u666F\u4E32\u5230\u5185\u7F6E\u7EC4\u4EF6\u6F14\u793A\u9875\uFF09\uFF1B\u7528\u6237\u51B3\u7B56\u9ED8\u8BA4\u5C40\u90E8\u66F4\u5B89\u5168\uFF08\u8DE8\u7AEF\u4E00\u81F4\uFF1AWeb \u7AEF vite \u63D2\u4EF6\u540C\u6B65\u6539\u5199 <style> \u2192 <style scoped>\uFF09",
|
|
1008
|
+
when: "\u6BCF\u6B21 SFC \u7F16\u8BD1\uFF08style \u5757\u5904\u7406\uFF09",
|
|
1009
|
+
example: { before: "<style>.box { color: red; }</style>", after: "\u6309 scoped \u5904\u7406\uFF08.box-data-v-x\uFF09+ \u8B66\u544A\u63D0\u793A\u5168\u5C40\u51FA\u53E3 <style global>" },
|
|
1010
|
+
verify: "tests/mp-transform.test.ts \u9ED8\u8BA4 scoped \u7528\u4F8B",
|
|
1011
|
+
source: "packages/compiler/src/index.ts \u2192 compileVueSfc\uFF08scopedStyles/globalStyles \u5206\u7EC4\uFF09",
|
|
1012
|
+
decision: "2026-08 \u7528\u6237\u51B3\u7B56\uFF08\u9ED8\u8BA4 scoped\uFF09"
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
id: "style/skyline-unsupported",
|
|
1016
|
+
phase: "style",
|
|
1017
|
+
status: "implemented",
|
|
1018
|
+
title: "Skyline \u4E0D\u652F\u6301\u5C5E\u6027\u7F16\u8BD1\u671F\u8B66\u544A",
|
|
1019
|
+
description: "float\u3001position: fixed \u51FA\u73B0\u65F6\u7F16\u8BD1\u671F\u8B66\u544A\uFF08\u4E0D\u963B\u65AD\u6784\u5EFA\uFF09",
|
|
1020
|
+
why: "Skyline \u81EA\u7814\u6E32\u67D3\u5F15\u64CE\u4E0D\u652F\u6301\u8FD9\u4E9B\u5E03\u5C40\u5C5E\u6027\uFF0C\u7F16\u8BD1\u671F\u8B66\u544A\u8BA9\u5F00\u53D1\u8005\u63D0\u524D\u77E5\u9053\uFF08\u53CD\u9ED1\u76D2\u539F\u5219\uFF1A\u8B66\u544A\u53EF\u89C1\u3001\u53EF\u7EDF\u8BA1\uFF09",
|
|
1021
|
+
when: "WXSS \u4E2D\u51FA\u73B0 float: \u6216 position: fixed \u65F6",
|
|
1022
|
+
example: { before: ".banner { position: fixed; }", after: "\u8B66\u544A\uFF1AWXSS \u68C0\u6D4B\u5230 Skyline \u4E0D\u652F\u6301\u7684\u5C5E\u6027\uFF1Aposition: fixed\uFF08\u7F16\u8BD1\u671F\u8B66\u544A\uFF09" },
|
|
1023
|
+
verify: "tests/mp-transform.test.ts \u8B66\u544A\u7528\u4F8B",
|
|
1024
|
+
source: "src/compiler/style.ts \u2192 transformStyleToWxss\uFF08unsupported \u5206\u652F\uFF09"
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
id: "style/scoped-css",
|
|
1028
|
+
phase: "style",
|
|
1029
|
+
status: "implemented",
|
|
1030
|
+
title: "scoped CSS\uFF1A\u9009\u62E9\u5668\u7C7B\u540D\u540E\u7F00\u62FC\u63A5\uFF08\u26052026-08 \u771F\u673A\u91CD\u6784\uFF1A\u5355\u7C7B\u9009\u62E9\u5668\uFF09",
|
|
1031
|
+
description: "<style scoped> \u5B58\u5728\u65F6\uFF1A\u9009\u62E9\u5668\u5185\u6BCF\u4E2A\u7C7B token \u8FFD\u52A0 -scopeId\uFF08.box \u2192 .box-data-v-xxx \u5355\u4E00\u7C7B\u9009\u62E9\u5668\uFF09\uFF1B:deep(X) \u53BB\u5305\u88C5\u540E\u7EDF\u4E00\u540E\u7F00\uFF1B@keyframes \u5E27/\u4F2A\u7C7B\u4F2A\u5143\u7D20/@\u89C4\u5219\u4E0D\u5904\u7406\uFF1B\u9017\u53F7\u5217\u8868\u9010\u6761",
|
|
1032
|
+
why: "\u5C0F\u7A0B\u5E8F\u65E0 scoped CSS \u539F\u751F\u673A\u5236\uFF0C\u7F16\u8BD1\u671F\u7C7B\u540D\u540E\u7F00\u7B49\u4EF7\uFF08v0.3\uFF0C\u51B3\u7B56 #77\uFF09\uFF1B\u26052026-08 \u771F\u673A\u91CD\u6784\uFF1ASkyline glass-easel \u4E0D\u652F\u6301\u5C5E\u6027\u9009\u62E9\u5668 [data-v]\uFF08f48460c \u6539 class \u590D\u5408\u4ECD\u4E0D\u5339\u914D\u2014\u2014.a.b \u590D\u5408\u7C7B\u9009\u62E9\u5668\u4E5F\u9759\u9ED8\u4E22\u5F03\uFF0C\u7EC4\u4EF6\u81EA\u8EAB wxss \u5339\u914D\u81EA\u8EAB\u6839\u8282\u70B9\u90FD\u5931\u6548\uFF09\u2192 \u7C7B\u540D\u540E\u7F00\u662F\u552F\u4E00 Skyline \u786E\u5B9A\u652F\u6301\u7684\u8DEF\u5F84\uFF08\u5355\u7C7B\u9009\u62E9\u5668 \u2713\uFF09",
|
|
1033
|
+
when: "SFC \u542B <style scoped> \u4E14\u89C4\u5219\u672A\u88AB\u7981\u7528\u65F6",
|
|
1034
|
+
example: { before: ".card { color: red; }", after: ".card-data-v-abc123 { color: red; }" },
|
|
1035
|
+
verify: "tests/mp-transform.test.ts scoped CSS \u7528\u4F8B",
|
|
1036
|
+
source: "packages/compiler/src/style.ts \u2192 transformStyleToWxss\uFF08scopeId \u5206\u652F\uFF09",
|
|
1037
|
+
decision: "#77\uFF08v0.3 scoped CSS\uFF09+ 2026-08 Skyline \u771F\u673A\u91CD\u6784\uFF08\u7C7B\u540D\u540E\u7F00\uFF09"
|
|
1038
|
+
},
|
|
1039
|
+
{
|
|
1040
|
+
id: "transition/animation-wxss",
|
|
1041
|
+
phase: "style",
|
|
1042
|
+
status: "implemented",
|
|
1043
|
+
title: "<transition> \u8FDB\u5165\u52A8\u753B keyframes \u6CE8\u5165",
|
|
1044
|
+
description: "wxss \u5C3E\u90E8\u6CE8\u5165 proteus-transition-* \u8FDB\u5165\u52A8\u753B\uFF08fade/slide-up/scale keyframes\uFF09\u2014\u2014\u914D\u5408\u6A21\u677F\u4FA7 transition \u88C5\u9970 class",
|
|
1045
|
+
why: "vue-compat-advance Batch 2\uFF08\u51B3\u7B56 #117\uFF09\uFF1A<transition> \u8FDB\u5165\u52A8\u753B\u8FD0\u884C\u65F6\u7B49\u4EF7\uFF08\u5143\u7D20 wx:if \u91CD\u5EFA\u65F6 animation \u81EA\u52A8\u64AD\u653E\uFF09\uFF1B\u79BB\u5F00\u52A8\u753B MP \u65E0\u94A9\u5B50",
|
|
1046
|
+
when: "\u4EFB\u4E00\u9875\u9762\u4F7F\u7528 <transition>\uFF08\u9ED8\u8BA4\u6CE8\u5165\uFF1Brules.disabled \u53EF\u5173\uFF09",
|
|
1047
|
+
example: { before: '<transition name="fade">\u2026', after: "wxss \u542B .proteus-transition-fade + @keyframes proteus-fade-in" },
|
|
1048
|
+
verify: "tests/vue-compat-advance.test.ts Transition \u7528\u4F8B",
|
|
1049
|
+
source: "packages/compiler/src/style.ts \u2192 TRANSITION_WXSS \u6CE8\u5165",
|
|
1050
|
+
decision: "#117"
|
|
1051
|
+
}
|
|
1052
|
+
];
|
|
1053
|
+
|
|
1054
|
+
// src/transforms/validate.ts
|
|
1055
|
+
var VALIDATE_RULES = [
|
|
1056
|
+
{
|
|
1057
|
+
id: "validate/js-syntax",
|
|
1058
|
+
phase: "validate",
|
|
1059
|
+
status: "implemented",
|
|
1060
|
+
title: "JS \u4EA7\u7269\u8BED\u6CD5\u81EA\u6821\u9A8C",
|
|
1061
|
+
description: "new Function(js) \u4EC5\u89E3\u6790\u4E0D\u6267\u884C\uFF0C\u8BED\u6CD5\u9519\u8BEF \u2192 \u6821\u9A8C\u5931\u8D25\u5E76\u643A\u5E26\u9519\u8BEF\u4FE1\u606F",
|
|
1062
|
+
why: "\u53CD\u7F16\u8BD1\u9ED1\u76D2\u673A\u5236\uFF08\u51B3\u7B56 #17\uFF09\uFF1A\u574F\u4EA7\u7269\u5F53\u573A\u62A5\u9519\u6307\u660E\u6587\u4EF6\uFF0C\u7EDD\u4E0D\u9759\u9ED8\u8F93\u51FA\u4E0D\u53EF\u7528\u7684\u4EA7\u7269\uFF08\u5BF9\u6BD4 uni-app \u4EA7\u7269\u65E0\u6CD5\u5B9A\u4F4D\u95EE\u9898\uFF09",
|
|
1063
|
+
when: "\u6BCF\u6B21\u6574\u5305\u7F16\u8BD1\u540E\uFF08assertValidResult \u5185\uFF09",
|
|
1064
|
+
example: {
|
|
1065
|
+
before: "// \u82E5\u4EA7\u7269 js \u542B\u8BED\u6CD5\u9519\u8BEF",
|
|
1066
|
+
after: "CompilerError: [proteus-compiler] xxx.vue: js \u4EA7\u7269\u8BED\u6CD5\u9519\u8BEF\uFF1AUnexpected token ..."
|
|
1067
|
+
},
|
|
1068
|
+
verify: "tests/mp-transform.test.ts \u6821\u9A8C\u7528\u4F8B\uFF08\u6784\u9020\u574F\u4EA7\u7269\u65AD\u8A00\u629B\u9519\uFF09",
|
|
1069
|
+
source: "src/compiler/validate.ts \u2192 validateJs",
|
|
1070
|
+
decision: "#17"
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
id: "validate/wxml-pairing",
|
|
1074
|
+
phase: "validate",
|
|
1075
|
+
status: "implemented",
|
|
1076
|
+
title: "WXML \u6807\u7B7E\u914D\u5BF9\u81EA\u6821\u9A8C",
|
|
1077
|
+
description: "\u6808\u5F0F\u626B\u63CF WXML \u6807\u7B7E\u914D\u5BF9\uFF08\u5148\u5265\u79BB\u6CE8\u91CA\uFF0C\u907F\u514D\u884C\u53F7\u6CE8\u91CA\u5E72\u6270\uFF09\uFF0C\u672A\u95ED\u5408/\u9519\u914D \u2192 \u6821\u9A8C\u5931\u8D25",
|
|
1078
|
+
why: "\u53CD\u7F16\u8BD1\u9ED1\u76D2\u673A\u5236\uFF08\u51B3\u7B56 #17\uFF09\uFF1A\u6A21\u677F\u8F6C\u6362\u51FA\u9519\u7684\u5E38\u89C1\u5F62\u6001\u5C31\u662F\u6807\u7B7E\u4E0D\u914D\u5BF9\uFF0C\u7F16\u8BD1\u671F\u62E6\u622A\u6BD4\u771F\u673A\u62A5\u9519\u597D\u5B9A\u4F4D",
|
|
1079
|
+
when: "\u6BCF\u6B21\u6574\u5305\u7F16\u8BD1\u540E\uFF08assertValidResult \u5185\uFF09",
|
|
1080
|
+
example: {
|
|
1081
|
+
before: "// \u82E5\u4EA7\u7269 wxml \u6807\u7B7E\u4E0D\u914D\u5BF9",
|
|
1082
|
+
after: "CompilerError: [proteus-compiler] xxx.vue: wxml \u4EA7\u7269\u7ED3\u6784\u9519\u8BEF\uFF1A</view> \u4E0E <text> \u4E0D\u5339\u914D\uFF08\u4F4D\u7F6E N\uFF09"
|
|
1083
|
+
},
|
|
1084
|
+
verify: "tests/mp-transform.test.ts \u6821\u9A8C\u7528\u4F8B",
|
|
1085
|
+
source: "src/compiler/validate.ts \u2192 validateWxml",
|
|
1086
|
+
decision: "#17"
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
id: "validate/compiler-error",
|
|
1090
|
+
phase: "validate",
|
|
1091
|
+
status: "implemented",
|
|
1092
|
+
title: "\u574F\u4EA7\u7269\u629B CompilerError \u6307\u660E\u6587\u4EF6",
|
|
1093
|
+
description: "\u6821\u9A8C\u5931\u8D25 \u2192 \u629B CompilerError\uFF08\u643A\u5E26\u6E90\u6587\u4EF6\u540D\uFF0C\u6D88\u606F\u542B [proteus-compiler] \u524D\u7F00\uFF09",
|
|
1094
|
+
why: "\u53CD\u9ED1\u76D2\u673A\u5236\u7684\u7EDF\u4E00\u9519\u8BEF\u901A\u9053\uFF1AAI/\u5F00\u53D1\u8005\u62FF\u5230\u9519\u8BEF\u5373\u53EF\u5B9A\u4F4D\u5230\u5177\u4F53\u6587\u4EF6\uFF08\u9519\u8BEF = \u53EF\u64CD\u4F5C\u7684\u53CD\u9988\uFF0C\u800C\u975E\u9ED1\u76D2\u5931\u8D25\uFF09",
|
|
1095
|
+
when: "js \u6216 wxml \u6821\u9A8C\u4E0D\u901A\u8FC7\u65F6",
|
|
1096
|
+
example: {
|
|
1097
|
+
before: "// \u9759\u9ED8\u8F93\u51FA\u574F\u4EA7\u7269\uFF08\u53CD\u6A21\u5F0F\uFF09",
|
|
1098
|
+
after: "throw new CompilerError(filename, message)"
|
|
1099
|
+
},
|
|
1100
|
+
verify: "tests/mp-transform.test.ts \u6821\u9A8C\u7528\u4F8B",
|
|
1101
|
+
source: "src/compiler/validate.ts \u2192 CompilerError + assertValidResult",
|
|
1102
|
+
decision: "#17"
|
|
1103
|
+
}
|
|
1104
|
+
];
|
|
1105
|
+
|
|
1106
|
+
// src/transforms/registry.ts
|
|
1107
|
+
var TRANSFORM_RULES = [
|
|
1108
|
+
...TEMPLATE_RULES,
|
|
1109
|
+
...SCRIPT_RULES,
|
|
1110
|
+
...STYLE_RULES,
|
|
1111
|
+
...VALIDATE_RULES
|
|
1112
|
+
];
|
|
1113
|
+
var byId = new Map(TRANSFORM_RULES.map((r) => [r.id, r]));
|
|
1114
|
+
function listTransformRules(phase) {
|
|
1115
|
+
return phase ? TRANSFORM_RULES.filter((r) => r.phase === phase) : [...TRANSFORM_RULES];
|
|
1116
|
+
}
|
|
1117
|
+
function getTransformRule(id) {
|
|
1118
|
+
return byId.get(id);
|
|
1119
|
+
}
|
|
1120
|
+
function formatTransformRule(rule) {
|
|
1121
|
+
const lines = [
|
|
1122
|
+
`## ${rule.id}\uFF08${rule.phase}\uFF09`,
|
|
1123
|
+
`**${rule.title}** \`[${rule.status}]\``,
|
|
1124
|
+
`- \u8F93\u5165 \u2192 \u8F93\u51FA\uFF1A${rule.description}`,
|
|
1125
|
+
`- \u4E3A\u4EC0\u4E48\uFF1A${rule.why}`,
|
|
1126
|
+
`- \u89E6\u53D1\u6761\u4EF6\uFF1A${rule.when}`,
|
|
1127
|
+
`- \u793A\u4F8B\uFF1A`,
|
|
1128
|
+
` - \u6E90\u7801\uFF1A\`${rule.example.before}\``,
|
|
1129
|
+
` - \u4EA7\u7269\uFF1A\`${rule.example.after}\``,
|
|
1130
|
+
`- \u5982\u4F55\u9A8C\u8BC1\uFF1A${rule.verify}`,
|
|
1131
|
+
`- \u5B9E\u73B0\u4F4D\u7F6E\uFF1A${rule.source}`
|
|
1132
|
+
];
|
|
1133
|
+
if (rule.decision) lines.push(`- \u51B3\u7B56\uFF1A${rule.decision}`);
|
|
1134
|
+
return lines.join("\n");
|
|
1135
|
+
}
|
|
1136
|
+
function formatTransformCatalog() {
|
|
1137
|
+
const phases = ["template", "script", "style", "validate"];
|
|
1138
|
+
const blocks = phases.map((phase) => {
|
|
1139
|
+
const rules = listTransformRules(phase);
|
|
1140
|
+
const items = rules.map((r) => `- \`${r.id}\` \`[${r.status}]\` ${r.title}${r.apply ? "\uFF08\u5DF2\u5206\u6D3E apply\uFF09" : ""}`).join("\n");
|
|
1141
|
+
return `### ${phase} \u9636\u6BB5\uFF08${rules.length} \u6761\u89C4\u5219\uFF09
|
|
1142
|
+
|
|
1143
|
+
${items}`;
|
|
1144
|
+
});
|
|
1145
|
+
return blocks.join("\n\n");
|
|
1146
|
+
}
|
|
1147
|
+
function executeRule(id, ctx) {
|
|
1148
|
+
const rule = byId.get(id);
|
|
1149
|
+
if (!rule) {
|
|
1150
|
+
throw new Error(`[proteus] executeRule \u672A\u77E5\u89C4\u5219 ID: ${id}`);
|
|
1151
|
+
}
|
|
1152
|
+
if (!rule.apply) {
|
|
1153
|
+
throw new Error(`[proteus] \u89C4\u5219 ${id} \u672A\u767B\u8BB0 apply()\uFF08\u63CF\u8FF0\u5C42\u89C4\u5219\uFF1B\u5B9E\u73B0\u89C1 ${rule.source}\u2014\u2014AI \u53EF\u767B\u8BB0 apply \u540E\u7ECF\u5206\u6D3E\u5C42\u751F\u6548\uFF09`);
|
|
1154
|
+
}
|
|
1155
|
+
rule.apply(ctx);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// src/overrides.ts
|
|
1159
|
+
function resolveOverrides(options) {
|
|
1160
|
+
const tagMap = { ...TAG_MAP };
|
|
1161
|
+
const eventMap = { ...EVENT_MAP };
|
|
1162
|
+
const semanticClass = { ...SEMANTIC_CLASS };
|
|
1163
|
+
const disabled = new Set(options?.disabled ?? []);
|
|
1164
|
+
for (const [ruleId, patch] of Object.entries(options?.mapping ?? {})) {
|
|
1165
|
+
const rule = getTransformRule(ruleId);
|
|
1166
|
+
if (!rule) {
|
|
1167
|
+
console.warn(`[proteus] \u89C4\u5219\u8986\u76D6\u5F15\u7528\u4E86\u672A\u6CE8\u518C\u7684\u89C4\u5219 ID\uFF1A${ruleId}\uFF08\u5DF2\u5FFD\u7565\uFF0C\u53EF\u7528 listTransformRules() \u67E5\u770B\u5408\u6CD5 ID\uFF09`);
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
if (rule.id.startsWith("tag/")) Object.assign(tagMap, patch);
|
|
1171
|
+
else if (rule.id === "event/click-to-tap") Object.assign(eventMap, patch);
|
|
1172
|
+
else if (rule.id === "semantic/base-class") Object.assign(semanticClass, patch);
|
|
1173
|
+
else console.warn(`[proteus] \u89C4\u5219 ${ruleId} \u4E0D\u652F\u6301 mapping \u8986\u76D6\uFF08\u4EC5 tag/* / event/click-to-tap / semantic/base-class\uFF09`);
|
|
1174
|
+
}
|
|
1175
|
+
Object.assign(tagMap, options?.customTags);
|
|
1176
|
+
for (const id of disabled) {
|
|
1177
|
+
if (!getTransformRule(id)) console.warn(`[proteus] \u89C4\u5219\u8986\u76D6\u7981\u7528\u4E86\u672A\u6CE8\u518C\u7684\u89C4\u5219 ID\uFF1A${id}\uFF08\u5DF2\u5FFD\u7565\uFF09`);
|
|
1178
|
+
}
|
|
1179
|
+
return { tagMap, eventMap, semanticClass, disabled };
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/template.ts
|
|
1183
|
+
function escapeXml(s) {
|
|
1184
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1185
|
+
}
|
|
1186
|
+
function kebabCase(s) {
|
|
1187
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
1188
|
+
}
|
|
1189
|
+
function camelToKebab(s) {
|
|
1190
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
1191
|
+
}
|
|
1192
|
+
function suffixClassValue(value, scopeId) {
|
|
1193
|
+
return value.split(/\s+/).filter(Boolean).map((n) => n.endsWith(`-${scopeId}`) ? n : `${n}-${scopeId}`).join(" ");
|
|
1194
|
+
}
|
|
1195
|
+
function suffixClassName(name, scopeId) {
|
|
1196
|
+
if (!scopeId) return name;
|
|
1197
|
+
return name.endsWith(`-${scopeId}`) ? name : `${name}-${scopeId}`;
|
|
1198
|
+
}
|
|
1199
|
+
var NATIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1200
|
+
"view",
|
|
1201
|
+
"scroll-view",
|
|
1202
|
+
"swiper",
|
|
1203
|
+
"swiper-item",
|
|
1204
|
+
"movable-view",
|
|
1205
|
+
"movable-area",
|
|
1206
|
+
"cover-view",
|
|
1207
|
+
"cover-image",
|
|
1208
|
+
"icon",
|
|
1209
|
+
"text",
|
|
1210
|
+
"rich-text",
|
|
1211
|
+
"progress",
|
|
1212
|
+
"button",
|
|
1213
|
+
"checkbox",
|
|
1214
|
+
"checkbox-group",
|
|
1215
|
+
"form",
|
|
1216
|
+
"input",
|
|
1217
|
+
"label",
|
|
1218
|
+
"picker",
|
|
1219
|
+
"picker-view",
|
|
1220
|
+
"picker-view-column",
|
|
1221
|
+
"radio",
|
|
1222
|
+
"radio-group",
|
|
1223
|
+
"slider",
|
|
1224
|
+
"switch",
|
|
1225
|
+
"textarea",
|
|
1226
|
+
"navigator",
|
|
1227
|
+
"image",
|
|
1228
|
+
"video",
|
|
1229
|
+
"camera",
|
|
1230
|
+
"live-player",
|
|
1231
|
+
"live-pusher",
|
|
1232
|
+
"map",
|
|
1233
|
+
"canvas",
|
|
1234
|
+
"web-view",
|
|
1235
|
+
"ad",
|
|
1236
|
+
"official-account",
|
|
1237
|
+
"open-data",
|
|
1238
|
+
"slot",
|
|
1239
|
+
"block",
|
|
1240
|
+
"template",
|
|
1241
|
+
"match-media",
|
|
1242
|
+
"page-container",
|
|
1243
|
+
"share-element",
|
|
1244
|
+
"sticky-header",
|
|
1245
|
+
"sticky-section",
|
|
1246
|
+
"root-portal",
|
|
1247
|
+
"channel-live",
|
|
1248
|
+
"channel-video",
|
|
1249
|
+
"list",
|
|
1250
|
+
"grid",
|
|
1251
|
+
"cell",
|
|
1252
|
+
"cell-group",
|
|
1253
|
+
"waterflow"
|
|
1254
|
+
]);
|
|
1255
|
+
var INLINE_CONTROL_TAGS = /* @__PURE__ */ new Set([
|
|
1256
|
+
"switch",
|
|
1257
|
+
"slider",
|
|
1258
|
+
"icon",
|
|
1259
|
+
"image",
|
|
1260
|
+
"button",
|
|
1261
|
+
"input",
|
|
1262
|
+
"textarea",
|
|
1263
|
+
"checkbox",
|
|
1264
|
+
"radio",
|
|
1265
|
+
"label",
|
|
1266
|
+
"navigator",
|
|
1267
|
+
"progress"
|
|
1268
|
+
]);
|
|
1269
|
+
function stripTypeAssertions(expr) {
|
|
1270
|
+
let out = expr.trim();
|
|
1271
|
+
for (let i = 0; i < 5; i++) {
|
|
1272
|
+
let changed = false;
|
|
1273
|
+
out = out.replace(/\(\s*([^()]*?)\s+as\s+[A-Za-z_$][\w$.]*(\[\])?\s*\)/g, (_m, inner) => {
|
|
1274
|
+
changed = true;
|
|
1275
|
+
return inner;
|
|
1276
|
+
});
|
|
1277
|
+
out = out.replace(/\s+as\s+[A-Za-z_$][\w$.]*(\[\])?\s*$/g, () => {
|
|
1278
|
+
changed = true;
|
|
1279
|
+
return "";
|
|
1280
|
+
});
|
|
1281
|
+
if (!changed) break;
|
|
1282
|
+
}
|
|
1283
|
+
return out.trim();
|
|
1284
|
+
}
|
|
1285
|
+
function exprContent(exp) {
|
|
1286
|
+
let out = "";
|
|
1287
|
+
if (exp == null) return "";
|
|
1288
|
+
if (typeof exp === "string") {
|
|
1289
|
+
out = exp;
|
|
1290
|
+
} else {
|
|
1291
|
+
const node = exp;
|
|
1292
|
+
if (node.type === NodeTypes.SIMPLE_EXPRESSION) {
|
|
1293
|
+
out = typeof node.content === "string" ? node.content : "";
|
|
1294
|
+
} else if (node.type === NodeTypes.INTERPOLATION) {
|
|
1295
|
+
out = exprContent(node.content);
|
|
1296
|
+
} else if (node.type === NodeTypes.COMPOUND_EXPRESSION || Array.isArray(node.children)) {
|
|
1297
|
+
out = node.children.map((c) => exprContent(c)).join("");
|
|
1298
|
+
} else if (node.type === NodeTypes.TEXT) {
|
|
1299
|
+
out = typeof node.content === "string" ? node.content : "";
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
return stripTypeAssertions(out);
|
|
1303
|
+
}
|
|
1304
|
+
function parseForExpr(exp) {
|
|
1305
|
+
const m = exp.trim().match(/^\(?\s*([\w$]+)\s*(?:,\s*([\w$]+))?\s*\)?\s+(?:in|of)\s+(.+)$/);
|
|
1306
|
+
if (!m) return { list: exp };
|
|
1307
|
+
return { list: m[3].trim(), item: m[1], index: m[2] };
|
|
1308
|
+
}
|
|
1309
|
+
function cleanHandler(exp, warnings) {
|
|
1310
|
+
const t = exp.trim();
|
|
1311
|
+
if (/^[\w$]+$/.test(t)) return t;
|
|
1312
|
+
const m = t.match(/^([\w$]+)\(\$event\)$/);
|
|
1313
|
+
if (m) return m[1];
|
|
1314
|
+
warnings.push(`\u4E8B\u4EF6\u5904\u7406\u5668 "${t}" \u4E0D\u662F\u7B80\u5355\u65B9\u6CD5\u5F15\u7528\uFF08MVP \u4EC5\u652F\u6301\u65B9\u6CD5\u540D\uFF09\uFF0C\u5DF2\u539F\u6837\u8F93\u51FA`);
|
|
1315
|
+
return t;
|
|
1316
|
+
}
|
|
1317
|
+
function formatClassBinding(exp, warnings, scopeId = "") {
|
|
1318
|
+
const t = exp.trim();
|
|
1319
|
+
const sfx = (name) => scopeId && !name.endsWith(`-${scopeId}`) ? `${name}-${scopeId}` : name;
|
|
1320
|
+
const sfxExpr = (e) => scopeId ? e.replace(/'([^']*)'/g, (_m, s) => `'${sfx(s)}'`) : e;
|
|
1321
|
+
const dynWarn = (name) => {
|
|
1322
|
+
if (scopeId) warnings.push(`:class \u52A8\u6001\u7C7B\u540D "${name}" \u65E0\u6CD5 scoped \u540E\u7F00\uFF08MP \u5355\u7C7B\u9009\u62E9\u5668\u673A\u5236\uFF09\uFF0C\u8BE5\u52A8\u6001\u7C7B\u5728\u5C0F\u7A0B\u5E8F\u65E0 scoped \u6837\u5F0F\u5339\u914D\uFF08Web \u7AEF\u6B63\u5E38\uFF09`);
|
|
1323
|
+
};
|
|
1324
|
+
if (t.startsWith("{")) {
|
|
1325
|
+
const parts = [];
|
|
1326
|
+
const re = /(['"]?)([\w-]+)\1\s*:\s*([^,}]+)/g;
|
|
1327
|
+
let m;
|
|
1328
|
+
while (m = re.exec(t)) parts.push(`(${m[3].trim()}?'${sfx(m[2])} ':'')`);
|
|
1329
|
+
if (parts.length) return `{{${parts.join("+")}}}`;
|
|
1330
|
+
}
|
|
1331
|
+
if (t.startsWith("[")) {
|
|
1332
|
+
const items = splitTopLevel(t.slice(1, -1));
|
|
1333
|
+
const parts = [];
|
|
1334
|
+
for (const item of items) {
|
|
1335
|
+
const i = item.trim();
|
|
1336
|
+
if (!i) continue;
|
|
1337
|
+
const str = i.match(/^(['"])([^'"]*)\1$/);
|
|
1338
|
+
if (str) {
|
|
1339
|
+
parts.push(`'${sfx(str[2])} '`);
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
if (i.startsWith("{")) {
|
|
1343
|
+
const inner = i.slice(1, -1);
|
|
1344
|
+
const shorthand = inner.trim().match(/^([\w$]+)$/);
|
|
1345
|
+
if (shorthand) {
|
|
1346
|
+
parts.push(`(${shorthand[1]}?'${sfx(shorthand[1])} ':'')`);
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
const re = /(['"]?)([\w-]+)\1\s*:\s*([^,}]+)/g;
|
|
1350
|
+
let m;
|
|
1351
|
+
let ok = false;
|
|
1352
|
+
while (m = re.exec(inner)) {
|
|
1353
|
+
parts.push(`(${m[3].trim()}?'${sfx(m[2])} ':'')`);
|
|
1354
|
+
ok = true;
|
|
1355
|
+
}
|
|
1356
|
+
if (ok) continue;
|
|
1357
|
+
}
|
|
1358
|
+
if (/^[\w$.]+(?:\s*\?\s*[^:]+:.+)?$/.test(i)) {
|
|
1359
|
+
if (scopeId && /^[\w$.]+$/.test(i)) dynWarn(i);
|
|
1360
|
+
parts.push(`((${sfxExpr(i)})?(${sfxExpr(i)})+' ':'')`);
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
warnings.push(`:class \u6570\u7EC4\u9879 "${i}" \u6682\u4E0D\u652F\u6301\uFF08MVP\uFF1A\u4EC5\u5B57\u7B26\u4E32/\u5BF9\u8C61/\u7B80\u5355\u53D8\u91CF/\u4E09\u5143\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
1364
|
+
}
|
|
1365
|
+
if (parts.length) return `{{${parts.join("+")}}}`;
|
|
1366
|
+
}
|
|
1367
|
+
if (/^[\w$.]+$/.test(t)) dynWarn(t);
|
|
1368
|
+
return `{{${sfxExpr(t)}}}`;
|
|
1369
|
+
}
|
|
1370
|
+
function splitTopLevel(expr) {
|
|
1371
|
+
const parts = [];
|
|
1372
|
+
let depth = 0;
|
|
1373
|
+
let quote = null;
|
|
1374
|
+
let cur = "";
|
|
1375
|
+
for (let i = 0; i < expr.length; i++) {
|
|
1376
|
+
const ch = expr[i];
|
|
1377
|
+
if (quote) {
|
|
1378
|
+
cur += ch;
|
|
1379
|
+
if (ch === quote && expr[i - 1] !== "\\") quote = null;
|
|
1380
|
+
continue;
|
|
1381
|
+
}
|
|
1382
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
1383
|
+
quote = ch;
|
|
1384
|
+
cur += ch;
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
if (ch === "(" || ch === "{" || ch === "[") {
|
|
1388
|
+
depth++;
|
|
1389
|
+
cur += ch;
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
if (ch === ")" || ch === "}" || ch === "]") {
|
|
1393
|
+
depth--;
|
|
1394
|
+
cur += ch;
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
if (ch === "," && depth === 0) {
|
|
1398
|
+
parts.push(cur);
|
|
1399
|
+
cur = "";
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
cur += ch;
|
|
1403
|
+
}
|
|
1404
|
+
if (cur.trim()) parts.push(cur);
|
|
1405
|
+
return parts;
|
|
1406
|
+
}
|
|
1407
|
+
function formatStyleBinding(exp) {
|
|
1408
|
+
const t = exp.trim();
|
|
1409
|
+
if (t.startsWith("{")) {
|
|
1410
|
+
const parts = [];
|
|
1411
|
+
const re = /(['"]?)([\w-]+)\1\s*:\s*([^,}]+)/g;
|
|
1412
|
+
let m;
|
|
1413
|
+
while (m = re.exec(t)) parts.push(`${camelToKebab(m[2])}:{{${m[3].trim()}}}`);
|
|
1414
|
+
if (parts.length) return parts.join(";");
|
|
1415
|
+
}
|
|
1416
|
+
return `{{${t}}}`;
|
|
1417
|
+
}
|
|
1418
|
+
function rewriteStoreRefs(expr, ctx) {
|
|
1419
|
+
return expr.replace(/\bstore\.([A-Za-z_$][\w$]*)/g, (m, field) => {
|
|
1420
|
+
ctx.storeBindings.add(field);
|
|
1421
|
+
return field;
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
function tryInlineHandler(exp) {
|
|
1425
|
+
const t = exp.trim();
|
|
1426
|
+
let m = t.match(/^([\w$]+)\+\+$/) ?? t.match(/^\+\+([\w$]+)$/);
|
|
1427
|
+
if (m) {
|
|
1428
|
+
return {
|
|
1429
|
+
name: `proteusInlineInc${capitalize(m[1])}`,
|
|
1430
|
+
code: `this.setData({ ${m[1]}: this.data.${m[1]} + 1 })`
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
m = t.match(/^([\w$]+)--$/) ?? t.match(/^--([\w$]+)$/);
|
|
1434
|
+
if (m) {
|
|
1435
|
+
return {
|
|
1436
|
+
name: `proteusInlineDec${capitalize(m[1])}`,
|
|
1437
|
+
code: `this.setData({ ${m[1]}: this.data.${m[1]} - 1 })`
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
m = t.match(/^([\w$]+)\(([^()]*)\)$/);
|
|
1441
|
+
if (m && /^[\w$,'"\s]*$/.test(m[2])) {
|
|
1442
|
+
const key = m[2].replace(/\W/g, "") || "NoArgs";
|
|
1443
|
+
return {
|
|
1444
|
+
name: `proteusInline${capitalize(m[1])}${key}`,
|
|
1445
|
+
code: `this.${m[1]}(${m[2]})`
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
m = t.match(/^store\.([A-Za-z_$][\w$]*)\s*\(([^()]*)\)$/);
|
|
1449
|
+
if (m) {
|
|
1450
|
+
const method = m[1];
|
|
1451
|
+
const args = m[2].trim();
|
|
1452
|
+
const key = args.replace(/[^A-Za-z0-9_$+-]/g, "").replace(/-/g, "Minus").replace(/\+/g, "Plus") || "NoArgs";
|
|
1453
|
+
return {
|
|
1454
|
+
name: `proteusStore${capitalize(method)}${key}`,
|
|
1455
|
+
code: `this.store.${method}(${args.replace(/\bstore\./g, "this.store.")})`
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
return null;
|
|
1459
|
+
}
|
|
1460
|
+
function serializeProgress(node, ctx) {
|
|
1461
|
+
const find = (name) => node.props.find(
|
|
1462
|
+
(p2) => p2.type === NodeTypes.ATTRIBUTE ? p2.name === name : exprContent(p2.arg) === name
|
|
1463
|
+
);
|
|
1464
|
+
const staticVal = (name) => {
|
|
1465
|
+
const a = find(name);
|
|
1466
|
+
return a && a.type === NodeTypes.ATTRIBUTE && a.value ? a.value.content : void 0;
|
|
1467
|
+
};
|
|
1468
|
+
const bindVal = (name) => {
|
|
1469
|
+
const d = find(name);
|
|
1470
|
+
return d && d.type === NodeTypes.DIRECTIVE && d.exp ? exprContent(d.exp) : void 0;
|
|
1471
|
+
};
|
|
1472
|
+
const attr = (name) => ({ static: staticVal(name), bind: bindVal(name) });
|
|
1473
|
+
const p = attr("percent");
|
|
1474
|
+
const c = attr("active-color") || attr("color");
|
|
1475
|
+
const sw = attr("stroke-width");
|
|
1476
|
+
const percentInner = p.bind ?? p.static ?? "0";
|
|
1477
|
+
const percentExpr = p.bind ? `{{${p.bind}}}` : p.static ?? "0";
|
|
1478
|
+
const colorExpr = c.bind ? `{{${c.bind}}}` : c.static ?? "#07c160";
|
|
1479
|
+
const swExpr = sw.bind ? `{{${sw.bind}}}` : sw.static ?? "6";
|
|
1480
|
+
const widthStyle = p.bind ? `width:{{${p.bind}}}%` : `width:${p.static ?? 0}%`;
|
|
1481
|
+
const colorStyle = c.bind ? `background-color:{{${c.bind}}}` : `background-color:${c.static ?? "#07c160"}`;
|
|
1482
|
+
const si = find("show-info");
|
|
1483
|
+
const showInfo = si ? si.type === NodeTypes.DIRECTIVE ? exprContent(si.exp) || "true" : "true" : "false";
|
|
1484
|
+
ctx.trace?.add("component/progress-degrade", {
|
|
1485
|
+
line: node.loc.start.line,
|
|
1486
|
+
before: '<progress percent="70" show-info>',
|
|
1487
|
+
after: "\u81EA\u5B9A\u4E49 view \u8FDB\u5EA6\u6761\uFF08Skyline \u4E0D\u652F\u6301\u539F\u751F progress\uFF0C16-progress-skyline-degrade\uFF09"
|
|
1488
|
+
});
|
|
1489
|
+
return `<view class="proteus-progress">
|
|
1490
|
+
<view class="proteus-progress-track" style="height:${swExpr}px">
|
|
1491
|
+
<view class="proteus-progress-inner" style="${widthStyle};${colorStyle}"></view>
|
|
1492
|
+
</view>
|
|
1493
|
+
${showInfo === "false" ? "" : `<text wx:if="{{${showInfo}}}" class="proteus-progress-info">{{${percentInner}}}%</text>
|
|
1494
|
+
`}</view>`;
|
|
1495
|
+
}
|
|
1496
|
+
function serializeElement(node, ctx) {
|
|
1497
|
+
if (node.tag === "component") {
|
|
1498
|
+
ctx.warnings.push(
|
|
1499
|
+
`<component :is> \u52A8\u6001\u7EC4\u4EF6\u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u673A\u5236\uFF08\u4EA7\u7269\u4E3A\u65E0\u6548\u6807\u7B7E\uFF09\u2014\u2014\u8BF7\u7528 v-if/v-else \u6761\u4EF6\u6E32\u67D3\uFF08vue-compat Batch A\uFF09`
|
|
1500
|
+
);
|
|
1501
|
+
ctx.trace?.add("template/is-component", { line: node.loc.start.line, before: "<component :is>", after: "\uFF08\u65E0\u6548\u6807\u7B7E\uFF0C\u8BF7\u6761\u4EF6\u6E32\u67D3\uFF09" });
|
|
1502
|
+
}
|
|
1503
|
+
const isTransition = node.tag === "transition";
|
|
1504
|
+
if (isTransition) {
|
|
1505
|
+
const nameAttr = node.props.find((p) => p.type === NodeTypes.ATTRIBUTE && p.name === "name");
|
|
1506
|
+
const tName = (nameAttr && nameAttr.value ? nameAttr.value.content : "fade") || "fade";
|
|
1507
|
+
ctx.transitionClassName = `proteus-transition-${tName}`;
|
|
1508
|
+
ctx.usesTransition = true;
|
|
1509
|
+
ctx.transitionCtx = { ref: void 0, tName, index: -1 };
|
|
1510
|
+
ctx.trace?.add("transition/component", {
|
|
1511
|
+
line: node.loc.start.line,
|
|
1512
|
+
before: `<transition name="${tName}">`,
|
|
1513
|
+
after: `\u5B50\u5143\u7D20\u6CE8\u5165 class="proteus-transition-${tName}"\uFF08\u8FDB\u5165\u52A8\u753B\u81EA\u52A8\u64AD\u653E\uFF1B\u88F8 ref v-if \u5B50\u5143\u7D20\u542F\u7528\u79BB\u5F00\u52A8\u753B\u72B6\u6001\u673A Batch 5\uFF09`
|
|
1514
|
+
});
|
|
1515
|
+
return node.children.map((c) => serializeNode(c, ctx)).join("\n");
|
|
1516
|
+
}
|
|
1517
|
+
if ((node.tag === "transition-group" || node.tag === "teleport" || node.tag === "suspense" || node.tag === "keep-alive") && !isTransition) {
|
|
1518
|
+
ctx.warnings.push(
|
|
1519
|
+
`<${node.tag}> \u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u7EC4\u4EF6\uFF08\u5DF2\u539F\u6837\u8F93\u51FA\uFF0C\u4E0D\u751F\u6548\uFF09\u2014\u2014\u7F13\u5B58/\u4F20\u9001\u8BF7\u79FB\u9664\uFF1B\u591A\u5143\u7D20\u8F6C\u573A\u8BF7\u7528\u8DEF\u7531 routeType\uFF08vue-compat Batch A\uFF09`
|
|
1520
|
+
);
|
|
1521
|
+
ctx.trace?.add("template/no-peer", { line: node.loc.start.line, before: `<${node.tag}>`, after: "\uFF08\u65E0\u5BF9\u7B49\uFF0C\u539F\u6837\u8F93\u51FA\uFF09" });
|
|
1522
|
+
}
|
|
1523
|
+
const hasVHtml = node.props.some((p) => p.type === NodeTypes.DIRECTIVE && p.name === "html");
|
|
1524
|
+
const hasClick = node.props.some((p) => p.type === NodeTypes.DIRECTIVE && p.name === "on");
|
|
1525
|
+
const isNavLink = (node.tag === "a" || node.tag === "router-link") && !hasClick;
|
|
1526
|
+
const tagRuleId = TAG_RULE_BY_TAG[node.tag];
|
|
1527
|
+
let tag = hasVHtml ? "rich-text" : ctx.tagMap[node.tag] ?? kebabCase(node.tag);
|
|
1528
|
+
if (node.tag === "router-link") tag = "view";
|
|
1529
|
+
if (tagRuleId && ctx.disabled.has(tagRuleId)) {
|
|
1530
|
+
tag = kebabCase(node.tag);
|
|
1531
|
+
ctx.warnings.push(`\u89C4\u5219 ${tagRuleId} \u5DF2\u88AB\u7981\u7528\uFF08rules.disabled\uFF09\uFF0C<${node.tag}> \u6309\u672A\u6CE8\u518C\u6807\u7B7E\u539F\u6837\u8F93\u51FA`);
|
|
1532
|
+
}
|
|
1533
|
+
if (tag === "progress" && !ctx.disabled.has("component/progress-degrade")) {
|
|
1534
|
+
return serializeProgress(node, ctx);
|
|
1535
|
+
}
|
|
1536
|
+
if (!ctx.disabled.has("tag/unknown-kebab") && !(tagRuleId && ctx.disabled.has(tagRuleId))) {
|
|
1537
|
+
ctx.trace?.add(
|
|
1538
|
+
hasVHtml ? "tag/rich-text" : node.tag === "router-link" ? "tag/router-link" : TAG_RULE_BY_TAG[node.tag] ?? "tag/unknown-kebab",
|
|
1539
|
+
{ line: node.loc.start.line, before: `<${node.tag}>`, after: `<${tag}>` }
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
const baseClass = hasVHtml ? "" : ctx.semanticClass[node.tag] ?? "";
|
|
1543
|
+
const tagDisabled = Boolean(tagRuleId) && ctx.disabled.has(tagRuleId);
|
|
1544
|
+
if (baseClass && (ctx.disabled.has("semantic/base-class") || tagDisabled)) {
|
|
1545
|
+
ctx.warnings.push(`\u8BED\u4E49\u57FA\u7840\u7C7B\u5DF2\u88AB\u7981\u7528\uFF08${tagDisabled ? `${tagRuleId} \u88AB\u7981\u7528` : "semantic/base-class \u88AB\u7981\u7528"}\uFF0Crules.disabled\uFF09\uFF0C\u4E0D\u518D\u9644\u52A0`);
|
|
1546
|
+
}
|
|
1547
|
+
const effectiveBaseClass = baseClass && !ctx.disabled.has("semantic/base-class") && !tagDisabled ? baseClass : "";
|
|
1548
|
+
if (effectiveBaseClass) {
|
|
1549
|
+
ctx.trace?.add("semantic/base-class", { line: node.loc.start.line, before: node.tag, after: effectiveBaseClass });
|
|
1550
|
+
}
|
|
1551
|
+
const isInputLike = tag === "input" || tag === "textarea";
|
|
1552
|
+
const attrs = [];
|
|
1553
|
+
let hasNavTarget = false;
|
|
1554
|
+
let scopeClass = "";
|
|
1555
|
+
if (ctx.scopeId) {
|
|
1556
|
+
const scopeCtx = { input: { tag: node.tag, scopeId: ctx.scopeId } };
|
|
1557
|
+
executeRule("template/scope-attr", scopeCtx);
|
|
1558
|
+
scopeClass = scopeCtx.output ?? ctx.scopeId;
|
|
1559
|
+
ctx.trace?.add("template/scope-attr", { line: node.loc.start.line, before: `<${node.tag}>`, after: `<${node.tag} class="${scopeClass}">` });
|
|
1560
|
+
}
|
|
1561
|
+
const scopeSuffix = scopeClass || "";
|
|
1562
|
+
let staticClass;
|
|
1563
|
+
let bindingClass;
|
|
1564
|
+
for (const prop of node.props) {
|
|
1565
|
+
if (prop.type === NodeTypes.ATTRIBUTE) {
|
|
1566
|
+
const attr = prop;
|
|
1567
|
+
if (isNavLink && (attr.name === "href" || attr.name === "to" || attr.name === "route-type")) {
|
|
1568
|
+
if (ctx.disabled.has("nav/navigate-link")) {
|
|
1569
|
+
ctx.warnings.push("\u89C4\u5219 nav/navigate-link \u5DF2\u88AB\u7981\u7528\uFF08rules.disabled\uFF09\uFF0C<a> \u6309\u666E\u901A view \u8F93\u51FA\uFF08\u65E0\u5BFC\u822A\u8BED\u4E49\uFF09");
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1572
|
+
if (attr.name === "route-type" && attr.value) {
|
|
1573
|
+
attrs.push(`data-route-type="${escapeXml(attr.value.content)}"`);
|
|
1574
|
+
ctx.trace?.add("nav/route-type", { line: node.loc.start.line, before: `route-type="${attr.value.content}"`, after: `data-route-type="${attr.value.content}"` });
|
|
1575
|
+
} else if (attr.value) {
|
|
1576
|
+
attrs.push(`data-url="${escapeXml(attr.value.content)}"`);
|
|
1577
|
+
hasNavTarget = true;
|
|
1578
|
+
}
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
if (attr.name === "class") {
|
|
1582
|
+
staticClass = attr.value ? escapeXml(attr.value.content) : "";
|
|
1583
|
+
continue;
|
|
1584
|
+
}
|
|
1585
|
+
if (attr.name === "ref") {
|
|
1586
|
+
ctx.warnings.push(
|
|
1587
|
+
`\u6A21\u677F ref="${attr.value ? attr.value.content : ""}" \u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u7ED1\u5B9A\uFF08\u6C38\u4E0D\u8D4B\u503C\uFF09\u2014\u2014\u8BF7\u7528 this.selectComponent('#id')\uFF08vue-compat Batch A\uFF09`
|
|
1588
|
+
);
|
|
1589
|
+
ctx.trace?.add("template/template-ref", { line: node.loc.start.line, before: `ref="${attr.value ? attr.value.content : ""}"`, after: "\uFF08\u5265\u79BB\uFF0C\u65E0\u5BF9\u7B49\uFF09" });
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
attrs.push(attr.value ? `${attr.name}="${escapeXml(attr.value.content)}"` : attr.name);
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
const dir = prop;
|
|
1596
|
+
if (isNavLink && dir.name === "bind" && (exprContent(dir.arg) === "href" || exprContent(dir.arg) === "to")) {
|
|
1597
|
+
const exp = exprContent(dir.exp);
|
|
1598
|
+
if (exp.trim().startsWith("{")) {
|
|
1599
|
+
ctx.warnings.push("\u8DEF\u7531\u94FE\u63A5 :to/:href \u5BF9\u8C61\u5F62\u5F0F\u6682\u4E0D\u652F\u6301\uFF08MVP\uFF09\uFF0C\u8BF7\u6539\u7528\u5B57\u7B26\u4E32\u8DEF\u5F84");
|
|
1600
|
+
} else if (!ctx.disabled.has("nav/navigate-link")) {
|
|
1601
|
+
attrs.push(`data-url="{{${exp}}}"`);
|
|
1602
|
+
hasNavTarget = true;
|
|
1603
|
+
} else {
|
|
1604
|
+
ctx.warnings.push("\u89C4\u5219 nav/navigate-link \u5DF2\u88AB\u7981\u7528\uFF08rules.disabled\uFF09\uFF0C<a> \u6309\u666E\u901A view \u8F93\u51FA\uFF08\u65E0\u5BFC\u822A\u8BED\u4E49\uFF09");
|
|
1605
|
+
}
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1608
|
+
switch (dir.name) {
|
|
1609
|
+
case "if":
|
|
1610
|
+
if (ctx.disabled.has("directive/v-if")) {
|
|
1611
|
+
ctx.warnings.push("\u89C4\u5219 directive/v-if \u5DF2\u88AB\u7981\u7528\uFF08rules.disabled\uFF09\uFF0Cv-if \u5DF2\u5FFD\u7565");
|
|
1612
|
+
break;
|
|
1613
|
+
}
|
|
1614
|
+
if (ctx.transitionCtx && /^[A-Za-z_$][\w$]*$/.test(exprContent(dir.exp))) {
|
|
1615
|
+
const ref = exprContent(dir.exp);
|
|
1616
|
+
ctx.transitionCtx.ref = ref;
|
|
1617
|
+
ctx.transitionCtx.index = ctx.transitions.length;
|
|
1618
|
+
ctx.transitions.push({ ref, tName: ctx.transitionCtx.tName, index: ctx.transitionCtx.index });
|
|
1619
|
+
attrs.push(`wx:if="{{__tv${ctx.transitionCtx.index}}}"`);
|
|
1620
|
+
ctx.trace?.add("transition/leave-state", {
|
|
1621
|
+
line: node.loc.start.line,
|
|
1622
|
+
before: `v-if="${ref}"`,
|
|
1623
|
+
after: `wx:if="{{__tv${ctx.transitionCtx.index}}}"\uFF08\u79BB\u5F00\u52A8\u753B\u72B6\u6001\u673A\uFF1A__tl${ctx.transitionCtx.index} \u64AD\u79BB\u5F00\u52A8\u753B + \u5EF6\u8FDF\u79FB\u9664\uFF09`
|
|
1624
|
+
});
|
|
1625
|
+
break;
|
|
1626
|
+
}
|
|
1627
|
+
if (ctx.transitionCtx) ctx.transitionCtx.ref = void 0;
|
|
1628
|
+
attrs.push(`wx:if="{{${exprContent(dir.exp)}}}"`);
|
|
1629
|
+
ctx.trace?.add("directive/v-if", { line: node.loc.start.line, before: `v-if="${exprContent(dir.exp)}"`, after: `wx:if="{{${exprContent(dir.exp)}}}"` });
|
|
1630
|
+
break;
|
|
1631
|
+
case "else-if":
|
|
1632
|
+
if (ctx.disabled.has("directive/v-else-if")) break;
|
|
1633
|
+
attrs.push(`wx:elif="{{${exprContent(dir.exp)}}}"`);
|
|
1634
|
+
ctx.trace?.add("directive/v-else-if", { line: node.loc.start.line, before: "v-else-if", after: "wx:elif" });
|
|
1635
|
+
break;
|
|
1636
|
+
case "else":
|
|
1637
|
+
if (ctx.disabled.has("directive/v-else")) break;
|
|
1638
|
+
attrs.push("wx:else");
|
|
1639
|
+
ctx.trace?.add("directive/v-else", { line: node.loc.start.line, before: "v-else", after: "wx:else" });
|
|
1640
|
+
break;
|
|
1641
|
+
case "for": {
|
|
1642
|
+
if (ctx.disabled.has("directive/v-for")) {
|
|
1643
|
+
ctx.warnings.push("\u89C4\u5219 directive/v-for \u5DF2\u88AB\u7981\u7528\uFF08rules.disabled\uFF09\uFF0Cv-for \u5DF2\u5FFD\u7565");
|
|
1644
|
+
break;
|
|
1645
|
+
}
|
|
1646
|
+
const f = parseForExpr(exprContent(dir.exp));
|
|
1647
|
+
attrs.push(`wx:for="{{${f.list}}}"`);
|
|
1648
|
+
if (f.item) attrs.push(`wx:for-item="${f.item}"`);
|
|
1649
|
+
if (f.index) attrs.push(`wx:for-index="${f.index}"`);
|
|
1650
|
+
ctx.trace?.add("directive/v-for", { line: node.loc.start.line, before: exprContent(dir.exp), after: `wx:for="{{${f.list}}}"` });
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
case "on": {
|
|
1654
|
+
if (ctx.disabled.has("event/click-to-tap") && ctx.disabled.has("event/modifier-catch")) {
|
|
1655
|
+
ctx.warnings.push("\u4E8B\u4EF6\u6620\u5C04\u89C4\u5219\u5DF2\u5168\u90E8\u7981\u7528\uFF08rules.disabled\uFF09\uFF0C@\u4E8B\u4EF6 \u539F\u6837\u8F93\u51FA");
|
|
1656
|
+
const handler2 = cleanHandler(exprContent(dir.exp), ctx.warnings);
|
|
1657
|
+
attrs.push(`bind${exprContent(dir.arg)}="${handler2}"`);
|
|
1658
|
+
break;
|
|
1659
|
+
}
|
|
1660
|
+
const raw = exprContent(dir.arg);
|
|
1661
|
+
const mapped = ctx.eventMap[raw] ?? raw;
|
|
1662
|
+
const mods = dir.modifiers.map(
|
|
1663
|
+
(m) => typeof m === "string" ? m : m?.content ?? ""
|
|
1664
|
+
);
|
|
1665
|
+
const isCatch = (mods.includes("stop") || mods.includes("prevent")) && !ctx.disabled.has("event/modifier-catch");
|
|
1666
|
+
const isSelf = mods.includes("self") && !isCatch;
|
|
1667
|
+
const isOnce = mods.includes("once") && !isCatch;
|
|
1668
|
+
const rawHandler = exprContent(dir.exp);
|
|
1669
|
+
const inline2 = tryInlineHandler(rawHandler);
|
|
1670
|
+
let handler;
|
|
1671
|
+
if (inline2 && !isSelf && !isOnce) {
|
|
1672
|
+
handler = inline2.name;
|
|
1673
|
+
if (!ctx.inlineHandlers.some((h) => h.name === inline2.name)) ctx.inlineHandlers.push(inline2);
|
|
1674
|
+
ctx.trace?.add("event/inline-expression", { line: node.loc.start.line, before: `@${exprContent(dir.arg)}="${rawHandler}"`, after: `${inline2.name}\uFF08\u5305\u88C5\u65B9\u6CD5\uFF09` });
|
|
1675
|
+
} else {
|
|
1676
|
+
handler = cleanHandler(rawHandler, ctx.warnings);
|
|
1677
|
+
}
|
|
1678
|
+
if (raw === "keyup" || raw === "keydown" || raw === "keypress") {
|
|
1679
|
+
const keyMods = mods.filter((m) => !["stop", "prevent", "self", "once"].includes(m));
|
|
1680
|
+
ctx.warnings.push(`@${raw}${keyMods.length ? "." + keyMods.join(".") : ""} \u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u952E\u76D8\u4E8B\u4EF6\uFF08input \u952E\u76D8\u884C\u4E3A\u8BF7\u7528 @confirm\uFF09\uFF0C\u5DF2\u539F\u6837\u8F93\u51FA`);
|
|
1681
|
+
}
|
|
1682
|
+
const isCustomEvent = !(raw in ctx.eventMap);
|
|
1683
|
+
const prefix = `${isCatch ? "catch" : "bind"}${isCustomEvent ? ":" : ""}`;
|
|
1684
|
+
if ((isSelf || isOnce) && /^[\w$]+$/.test(handler)) {
|
|
1685
|
+
if (isSelf) ctx.selfHandlers.add(handler);
|
|
1686
|
+
if (isOnce) ctx.onceHandlers.add(handler);
|
|
1687
|
+
const wrap = `${isSelf ? "proteusSelf" : "proteusOnce"}${capitalize(handler)}`;
|
|
1688
|
+
attrs.push(`${prefix}${mapped}="${wrap}"`);
|
|
1689
|
+
} else {
|
|
1690
|
+
attrs.push(`${prefix}${mapped}="${handler}"`);
|
|
1691
|
+
}
|
|
1692
|
+
ctx.trace?.add(
|
|
1693
|
+
isCatch ? "event/modifier-catch" : isSelf || isOnce ? "event/modifier-self-once" : "event/click-to-tap",
|
|
1694
|
+
{ line: node.loc.start.line, before: `@${raw}`, after: `${prefix}${mapped}` }
|
|
1695
|
+
);
|
|
1696
|
+
break;
|
|
1697
|
+
}
|
|
1698
|
+
case "bind": {
|
|
1699
|
+
const arg = exprContent(dir.arg);
|
|
1700
|
+
const exp = exprContent(dir.exp);
|
|
1701
|
+
if (node.tag === "slot" && arg !== "class" && arg !== "style" && arg !== "name") {
|
|
1702
|
+
ctx.warnings.push(
|
|
1703
|
+
`\u4F5C\u7528\u57DF\u63D2\u69FD <slot :${arg}> \u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u673A\u5236\uFF08\u7236\u4FA7\u62FF\u4E0D\u5230\u5B50\u7EC4\u4EF6\u6570\u636E\uFF1BMP/Skyline \u65E0\u6A21\u677F\u4F20\u53C2\u2014\u2014vue-compat-advance Batch 7 \u5E73\u53F0\u9650\u5236\uFF09\u2014\u2014\u66FF\u4EE3\u6A21\u5F0F\uFF1A\u5B50\u7EC4\u4EF6 props \u63A5\u6536\u6570\u636E + \u81EA\u5B9A\u4E49\u4E8B\u4EF6\u56DE\u8C03\u4F20\u6570\u636E\uFF0C\u5982 <MyList :items="items" @item-tap="onItemTap" />\uFF08props.items \u5185\u6E32\u67D3 + triggerEvent \u56DE\u4F20\uFF09`
|
|
1704
|
+
);
|
|
1705
|
+
ctx.trace?.add("slot/scoped-slot", { line: node.loc.start.line, before: `<slot :${arg}="${exp}">`, after: "\uFF08\u65E0\u6548\uFF0CMP \u4E0D\u4F20\u6570\u636E\uFF09\u2192 \u66FF\u4EE3\uFF1Aprops \u4F20\u5B50 + \u4E8B\u4EF6\u56DE\u8C03" });
|
|
1706
|
+
}
|
|
1707
|
+
if (arg === "class") {
|
|
1708
|
+
if (ctx.disabled.has("directive/v-bind-class")) break;
|
|
1709
|
+
bindingClass = formatClassBinding(exp, ctx.warnings, scopeSuffix);
|
|
1710
|
+
ctx.trace?.add("directive/v-bind-class", { line: node.loc.start.line, before: `:class="${exp}"`, after: bindingClass });
|
|
1711
|
+
} else if (arg === "style") {
|
|
1712
|
+
if (ctx.disabled.has("directive/v-bind-style")) break;
|
|
1713
|
+
attrs.push(`style="${formatStyleBinding(exp)}"`);
|
|
1714
|
+
ctx.trace?.add("directive/v-bind-style", { line: node.loc.start.line, before: `:style="${exp}"`, after: formatStyleBinding(exp) });
|
|
1715
|
+
} else if (arg === "key") {
|
|
1716
|
+
if (ctx.disabled.has("directive/v-bind-key")) break;
|
|
1717
|
+
if (/^[\w$]+$/.test(exp)) attrs.push(`wx:key="${exp}"`);
|
|
1718
|
+
else ctx.warnings.push(`:key="${exp}" \u4E0D\u662F\u7B80\u5355\u6807\u8BC6\u7B26\uFF08MVP\uFF09\uFF0Cwx:key \u5DF2\u5FFD\u7565`);
|
|
1719
|
+
ctx.trace?.add("directive/v-bind-key", { line: node.loc.start.line, before: `:key="${exp}"`, after: `wx:key="${exp}"` });
|
|
1720
|
+
} else {
|
|
1721
|
+
if (ctx.disabled.has("directive/v-bind")) break;
|
|
1722
|
+
attrs.push(`${arg}="{{${rewriteStoreRefs(exp, ctx)}}}"`);
|
|
1723
|
+
ctx.trace?.add("directive/v-bind", { line: node.loc.start.line, before: `:${arg}`, after: `${arg}="{{${rewriteStoreRefs(exp, ctx)}}}"` });
|
|
1724
|
+
}
|
|
1725
|
+
break;
|
|
1726
|
+
}
|
|
1727
|
+
case "model": {
|
|
1728
|
+
if (ctx.disabled.has("directive/v-model")) {
|
|
1729
|
+
ctx.warnings.push("\u89C4\u5219 directive/v-model \u5DF2\u88AB\u7981\u7528\uFF08rules.disabled\uFF09\uFF0Cv-model \u5DF2\u5FFD\u7565");
|
|
1730
|
+
break;
|
|
1731
|
+
}
|
|
1732
|
+
const model = exprContent(dir.exp);
|
|
1733
|
+
if (model && !ctx.vModelBindings.includes(model)) ctx.vModelBindings.push(model);
|
|
1734
|
+
if (isInputLike) attrs.push(`value="{{${model}}}"`);
|
|
1735
|
+
attrs.push(`bindinput="proteusOn${capitalize(model)}Input"`);
|
|
1736
|
+
ctx.trace?.add("directive/v-model", { line: node.loc.start.line, before: `v-model="${model}"`, after: `bindinput="proteusOn${capitalize(model)}Input"` });
|
|
1737
|
+
break;
|
|
1738
|
+
}
|
|
1739
|
+
case "html":
|
|
1740
|
+
if (ctx.disabled.has("directive/v-html")) {
|
|
1741
|
+
ctx.warnings.push("\u89C4\u5219 directive/v-html \u5DF2\u88AB\u7981\u7528\uFF08rules.disabled\uFF09\uFF0Cv-html \u5DF2\u5FFD\u7565");
|
|
1742
|
+
break;
|
|
1743
|
+
}
|
|
1744
|
+
attrs.push(`nodes="{{${exprContent(dir.exp)}}}"`);
|
|
1745
|
+
ctx.trace?.add("directive/v-html", { line: node.loc.start.line, before: "v-html", after: "rich-text nodes" });
|
|
1746
|
+
break;
|
|
1747
|
+
case "show":
|
|
1748
|
+
if (ctx.disabled.has("directive/v-show")) break;
|
|
1749
|
+
attrs.push(`hidden="{{!${exprContent(dir.exp)}}}"`);
|
|
1750
|
+
ctx.trace?.add("directive/v-show", { line: node.loc.start.line, before: `v-show="${exprContent(dir.exp)}"`, after: `hidden="{{!${exprContent(dir.exp)}}}"` });
|
|
1751
|
+
break;
|
|
1752
|
+
default:
|
|
1753
|
+
if (dir.name === "slot" || dir.name === "pre" || dir.name === "cloak") {
|
|
1754
|
+
break;
|
|
1755
|
+
}
|
|
1756
|
+
ctx.warnings.push(
|
|
1757
|
+
`\u81EA\u5B9A\u4E49\u6307\u4EE4 v-${dir.name} \u5728\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u673A\u5236\uFF08\u5DF2\u5265\u79BB\u4E14\u4E0D\u6267\u884C\uFF09\u2014\u2014\u8BF7\u6539\u7528\u65B9\u6CD5\u8C03\u7528\u6216\u6761\u4EF6\u6E32\u67D3\uFF08vue-compat Batch A\uFF09`
|
|
1758
|
+
);
|
|
1759
|
+
ctx.trace?.add("directive/custom", { line: node.loc.start.line, before: `v-${dir.name}`, after: "\uFF08\u5265\u79BB\uFF0C\u65E0\u5BF9\u7B49\uFF09" });
|
|
1760
|
+
break;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
if (hasNavTarget && !ctx.disabled.has("nav/navigate-link")) {
|
|
1764
|
+
attrs.push('bindtap="proteusNavigateTo"');
|
|
1765
|
+
ctx.usesNavigate = true;
|
|
1766
|
+
ctx.trace?.add("nav/navigate-link", { line: node.loc.start.line, before: `<${node.tag}>` + (node.tag === "a" ? " href/to" : " to"), after: 'data-url + bindtap="proteusNavigateTo"' });
|
|
1767
|
+
}
|
|
1768
|
+
let transitionAnimCls = "";
|
|
1769
|
+
let transitionLeaveExpr = "";
|
|
1770
|
+
if (ctx.transitionClassName) {
|
|
1771
|
+
transitionAnimCls = ctx.transitionClassName;
|
|
1772
|
+
ctx.transitionClassName = void 0;
|
|
1773
|
+
const tctx = ctx.transitionCtx;
|
|
1774
|
+
ctx.transitionCtx = void 0;
|
|
1775
|
+
if (tctx && tctx.ref !== void 0) {
|
|
1776
|
+
transitionLeaveExpr = `{{__tl${tctx.index} ? '${suffixClassName(`${transitionAnimCls}-leave`, scopeSuffix)}' : ''}}`;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
const suffix = (v) => scopeSuffix ? suffixClassValue(v, scopeSuffix) : v;
|
|
1780
|
+
let autoFlexRow = false;
|
|
1781
|
+
if (!ctx.disabled.has("layout/auto-flex-row")) {
|
|
1782
|
+
const childTags = node.children.filter((c) => c.type === NodeTypes.ELEMENT).map((c) => ctx.tagMap[c.tag] ?? kebabCase(c.tag));
|
|
1783
|
+
const controls = childTags.filter((t) => INLINE_CONTROL_TAGS.has(t));
|
|
1784
|
+
const textCount = childTags.filter((t) => t === "text").length;
|
|
1785
|
+
autoFlexRow = controls.length === 1 && textCount === 1;
|
|
1786
|
+
if (autoFlexRow) {
|
|
1787
|
+
ctx.trace?.add("layout/auto-flex-row", {
|
|
1788
|
+
line: node.loc.start.line,
|
|
1789
|
+
before: `<${node.tag}>\uFF081 text + 1 \u884C\u5185\u63A7\u4EF6\uFF09`,
|
|
1790
|
+
after: `\u81EA\u52A8\u9644\u52A0 proteus-flex-row\uFF08display:flex;row;align-items:center\u2014\u2014Skyline \u65E0 inline\uFF0C\u884C\u5185\u6392\u5E03\u5FC5\u987B flex row\uFF09`
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
const classStatic = [transitionAnimCls, autoFlexRow ? "proteus-flex-row" : "", effectiveBaseClass, staticClass].filter((c) => Boolean(c)).map(suffix);
|
|
1795
|
+
const classInterp = [bindingClass, transitionLeaveExpr].filter((c) => Boolean(c));
|
|
1796
|
+
if (ctx.isComponentRoot) classInterp.push("{{rootClass}}");
|
|
1797
|
+
const isComponentTag = !NATIVE_TAGS.has(tag);
|
|
1798
|
+
const rootClassEnabled = !ctx.disabled.has("component/root-class");
|
|
1799
|
+
if (classStatic.length || classInterp.length) {
|
|
1800
|
+
const clsValue = `${classStatic.join(" ")}${classInterp.length ? `${classStatic.length ? " " : ""}${classInterp.join(" ")}` : ""}`;
|
|
1801
|
+
attrs.push(isComponentTag && rootClassEnabled ? `root-class="${clsValue}"` : `class="${clsValue}"`);
|
|
1802
|
+
if (isComponentTag && rootClassEnabled) {
|
|
1803
|
+
ctx.trace?.add("component/root-class", { line: node.loc.start.line, before: `<${node.tag} class="${clsValue}">`, after: `<${node.tag} root-class="${clsValue}">` });
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
const attrStr = attrs.length ? ` ${attrs.join(" ")}` : "";
|
|
1807
|
+
const lineNote = ctx.annotateLines ? `<!-- @${node.loc.start.line} ${node.tag} -->
|
|
1808
|
+
` : "";
|
|
1809
|
+
if (lineNote && !ctx.lineNoteTraced) {
|
|
1810
|
+
ctx.lineNoteTraced = true;
|
|
1811
|
+
ctx.trace?.add("annotation/line-note", { line: node.loc.start.line, before: `<${node.tag}>`, after: `<!-- @${node.loc.start.line} ${node.tag} -->` });
|
|
1812
|
+
}
|
|
1813
|
+
if (!node.children.length) return `${lineNote}<${tag}${attrStr} />`;
|
|
1814
|
+
const hasElementChild = node.children.some((c) => c.type === NodeTypes.ELEMENT);
|
|
1815
|
+
if (hasElementChild) {
|
|
1816
|
+
const inner = node.children.map((c) => serializeNode(c, ctx)).join("\n");
|
|
1817
|
+
return `${lineNote}<${tag}${attrStr}>
|
|
1818
|
+
${inner}
|
|
1819
|
+
</${tag}>`;
|
|
1820
|
+
}
|
|
1821
|
+
const inline = node.children.map((c) => serializeNode(c, ctx)).join("");
|
|
1822
|
+
return `${lineNote}<${tag}${attrStr}>${inline}</${tag}>`;
|
|
1823
|
+
}
|
|
1824
|
+
function serializeNode(node, ctx) {
|
|
1825
|
+
switch (node.type) {
|
|
1826
|
+
case NodeTypes.ELEMENT:
|
|
1827
|
+
return serializeElement(node, ctx);
|
|
1828
|
+
case NodeTypes.TEXT:
|
|
1829
|
+
return escapeXml(node.content);
|
|
1830
|
+
case NodeTypes.INTERPOLATION:
|
|
1831
|
+
ctx.trace?.add("node/interpolation", { line: node.loc.start.line, before: "{{ expr }}", after: "{{ expr }}\uFF08\u539F\u6837\u4FDD\u7559\uFF09" });
|
|
1832
|
+
return `{{ ${rewriteStoreRefs(node.content.content, ctx)} }}`;
|
|
1833
|
+
case NodeTypes.COMMENT:
|
|
1834
|
+
return `<!-- ${node.content} -->`;
|
|
1835
|
+
default:
|
|
1836
|
+
return "";
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
function capitalize(s) {
|
|
1840
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
1841
|
+
}
|
|
1842
|
+
function transformTemplateToWxml(source, opts = { px2rpx: true, rpxRatio: 2, annotateLines: false }) {
|
|
1843
|
+
const ctx = {
|
|
1844
|
+
vModelBindings: [],
|
|
1845
|
+
warnings: [],
|
|
1846
|
+
annotateLines: opts.annotateLines ?? false,
|
|
1847
|
+
filename: opts.filename,
|
|
1848
|
+
usesNavigate: false,
|
|
1849
|
+
trace: opts.trace,
|
|
1850
|
+
// ★底线循环 ①③:生效配置 = tags.ts 常量 + config 覆盖(规则改写/禁用即时生效)
|
|
1851
|
+
...resolveOverrides(opts.rules),
|
|
1852
|
+
scopeId: opts.scopeId,
|
|
1853
|
+
selfHandlers: /* @__PURE__ */ new Set(),
|
|
1854
|
+
onceHandlers: /* @__PURE__ */ new Set(),
|
|
1855
|
+
inlineHandlers: [],
|
|
1856
|
+
usesTransition: false,
|
|
1857
|
+
transitions: [],
|
|
1858
|
+
storeBindings: /* @__PURE__ */ new Set()
|
|
1859
|
+
};
|
|
1860
|
+
const root = domParse(source, { onError: () => void 0 });
|
|
1861
|
+
const isComp = opts.isComponent === true;
|
|
1862
|
+
const autoScroll = opts.autoScrollContainer !== false && !isComp;
|
|
1863
|
+
const topTags = root.children.filter((c) => c.type === NodeTypes.ELEMENT).map((c) => c.tag);
|
|
1864
|
+
const alreadyScroll = topTags.length === 1 && (topTags[0] === "scroll-view" || topTags[0] === "p-scroll-view");
|
|
1865
|
+
let firstEl = isComp;
|
|
1866
|
+
const inner = root.children.map((c) => {
|
|
1867
|
+
if (firstEl && c.type === NodeTypes.ELEMENT) {
|
|
1868
|
+
firstEl = false;
|
|
1869
|
+
return serializeNode(c, { ...ctx, isComponentRoot: true });
|
|
1870
|
+
}
|
|
1871
|
+
return serializeNode(c, ctx);
|
|
1872
|
+
}).join("\n");
|
|
1873
|
+
let wxml = inner;
|
|
1874
|
+
if (autoScroll && !alreadyScroll) {
|
|
1875
|
+
const hooks = opts.pageScrollHooks ?? {};
|
|
1876
|
+
const bindAttrs = ["scroll-y", 'class="proteus-page-scroll"'];
|
|
1877
|
+
if (hooks.hasOnPageScroll) bindAttrs.push('bindscroll="proteusPageScroll"');
|
|
1878
|
+
if (hooks.hasOnReachBottom) bindAttrs.push('bindscrolltolower="proteusReachBottom"');
|
|
1879
|
+
if (hooks.hasOnPullDownRefresh) {
|
|
1880
|
+
bindAttrs.push('refresher-enabled="{{true}}" refresher-triggered="{{__proteusRefreshing}}" bindrefresherrefresh="proteusPullDownRefresh"');
|
|
1881
|
+
}
|
|
1882
|
+
if (hooks.hasPageScrollTo) {
|
|
1883
|
+
bindAttrs.push('scroll-top="{{__proteusPageScrollTop}}" scroll-with-animation');
|
|
1884
|
+
}
|
|
1885
|
+
wxml = `<scroll-view ${bindAttrs.join(" ")}>
|
|
1886
|
+
${inner}
|
|
1887
|
+
</scroll-view>`;
|
|
1888
|
+
} else if (!isComp && (opts.pageScrollHooks?.hasOnPageScroll || opts.pageScrollHooks?.hasOnReachBottom)) {
|
|
1889
|
+
ctx.warnings.push(
|
|
1890
|
+
"\u6B67\u4E49\u8B66\u544A\uFF1A\u9875\u9762\u58F0\u660E onPageScroll/onReachBottom \u4E14\u9876\u5C42\u662F scroll-view\uFF08\u7528\u6237\u663E\u5F0F\u6EDA\u52A8\u5BB9\u5668\uFF09\u2014\u2014\u9875\u9762\u7EA7\u6EDA\u52A8\u94A9\u5B50\u4E0D\u4F1A\u88AB scroll-view \u89E6\u53D1\uFF0C\u8BF7\u7528 bindscroll/bindscrolltolower \u6216\u4EA4\u7531\u81EA\u52A8\u5305\u88C5\uFF0815-page-scroll-container\uFF09"
|
|
1891
|
+
);
|
|
1892
|
+
}
|
|
1893
|
+
for (const w of ctx.warnings) console.warn(`[mp-transform] ${w}`);
|
|
1894
|
+
return {
|
|
1895
|
+
wxml,
|
|
1896
|
+
vModelBindings: ctx.vModelBindings,
|
|
1897
|
+
usesNavigate: ctx.usesNavigate,
|
|
1898
|
+
selfHandlers: [...ctx.selfHandlers],
|
|
1899
|
+
onceHandlers: [...ctx.onceHandlers],
|
|
1900
|
+
inlineHandlers: ctx.inlineHandlers,
|
|
1901
|
+
usesTransition: ctx.usesTransition,
|
|
1902
|
+
// ★Batch 5:离开动画状态机列表(裸 ref v-if 的 transition 子元素)
|
|
1903
|
+
transitions: ctx.transitions,
|
|
1904
|
+
// ★pinia-plan 12 P1:模板 store 引用字段(script 生成绑定)
|
|
1905
|
+
storeBindings: [...ctx.storeBindings],
|
|
1906
|
+
// ★15-page-scroll-container:已自动包滚动容器(compileVueSfc 据此注入高度样式)
|
|
1907
|
+
pageScrollWrapped: autoScroll && !alreadyScroll,
|
|
1908
|
+
warnings: ctx.warnings
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// src/trace.ts
|
|
1913
|
+
function createTrace(phase = "template") {
|
|
1914
|
+
return {
|
|
1915
|
+
events: [],
|
|
1916
|
+
add(ruleId, opts) {
|
|
1917
|
+
this.events.push({ ruleId, phase, ...opts });
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
function lineAt(source, index) {
|
|
1922
|
+
let line = 1;
|
|
1923
|
+
for (let i = 0; i < index && i < source.length; i++) {
|
|
1924
|
+
if (source[i] === "\n") line++;
|
|
1925
|
+
}
|
|
1926
|
+
return line;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// src/script.ts
|
|
1930
|
+
function evalLiteral(expr) {
|
|
1931
|
+
try {
|
|
1932
|
+
return Function(`"use strict"; return (${expr})`)();
|
|
1933
|
+
} catch {
|
|
1934
|
+
return void 0;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
function extractProvideInject(source) {
|
|
1938
|
+
const provides = [];
|
|
1939
|
+
const injects = [];
|
|
1940
|
+
const pRe = /^provide\s*\(\s*['"]([^'"]+)['"]\s*,\s*([^)]+)\)/gm;
|
|
1941
|
+
let m;
|
|
1942
|
+
while (m = pRe.exec(source)) {
|
|
1943
|
+
const lineStart = source.lastIndexOf("\n", m.index) + 1;
|
|
1944
|
+
if (source.slice(lineStart, m.index) !== "") continue;
|
|
1945
|
+
provides.push({ key: m[1], expr: m[2].trim(), line: lineAt(source, m.index) });
|
|
1946
|
+
}
|
|
1947
|
+
const iRe = /^const\s+([A-Za-z_$][\w$]*)\s*=\s*inject\s*\(\s*['"]([^'"]+)['"]\s*(?:,\s*([^)]+))?\)/gm;
|
|
1948
|
+
while (m = iRe.exec(source)) {
|
|
1949
|
+
const lineStart = source.lastIndexOf("\n", m.index) + 1;
|
|
1950
|
+
if (source.slice(lineStart, m.index) !== "") continue;
|
|
1951
|
+
const inj = { name: m[1], key: m[2], line: lineAt(source, m.index) };
|
|
1952
|
+
if (m[3] !== void 0) inj.def = m[3].trim();
|
|
1953
|
+
injects.push(inj);
|
|
1954
|
+
}
|
|
1955
|
+
return { provides, injects };
|
|
1956
|
+
}
|
|
1957
|
+
function buildProvideInject(provides, injects, data, computeds) {
|
|
1958
|
+
const pageOpen = [
|
|
1959
|
+
"const __reg = getApp().__proteusProvides || (getApp().__proteusProvides = {})",
|
|
1960
|
+
"__reg.__seq = (__reg.__seq || 0) + 1",
|
|
1961
|
+
"this.__proteusPageId = 'p' + __reg.__seq",
|
|
1962
|
+
"const provides = (__reg[this.__proteusPageId] || (__reg[this.__proteusPageId] = {}))"
|
|
1963
|
+
].join("\n");
|
|
1964
|
+
const compOpen = [
|
|
1965
|
+
"const __pages = (typeof getCurrentPages === 'function' ? getCurrentPages() : [])",
|
|
1966
|
+
"const __pid = __pages.length ? __pages[__pages.length - 1].__proteusPageId : ''",
|
|
1967
|
+
"this.__proteusPageId = __pid || 'global'",
|
|
1968
|
+
"const __reg = getApp().__proteusProvides || (getApp().__proteusProvides = {})",
|
|
1969
|
+
"const provides = (__reg[this.__proteusPageId] || (__reg[this.__proteusPageId] = {}))"
|
|
1970
|
+
].join("\n");
|
|
1971
|
+
const provideRefs = /* @__PURE__ */ new Map();
|
|
1972
|
+
const pLines = [];
|
|
1973
|
+
for (const p of provides) {
|
|
1974
|
+
let expr = p.expr;
|
|
1975
|
+
const vm = expr.match(/^([A-Za-z_$][\w$]*)\.value$/);
|
|
1976
|
+
if (vm && (vm[1] in data || vm[1] in computeds)) expr = `this.data.${vm[1]}`;
|
|
1977
|
+
else if (expr in data) {
|
|
1978
|
+
expr = `this.data.${expr}`;
|
|
1979
|
+
provideRefs.set(p.expr, p.key);
|
|
1980
|
+
}
|
|
1981
|
+
pLines.push(`provides[${JSON.stringify(p.key)}] = ${expr}`);
|
|
1982
|
+
if (provideRefs.has(p.expr)) {
|
|
1983
|
+
pLines.push(`if (!provides.__subs) provides.__subs = {}; if (!provides.__subs[${JSON.stringify(p.key)}]) provides.__subs[${JSON.stringify(p.key)}] = []`);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
const iLines = [];
|
|
1987
|
+
for (const inj of injects) {
|
|
1988
|
+
const read = `provides[${JSON.stringify(inj.key)}]`;
|
|
1989
|
+
const value = inj.def === void 0 ? read : `(${read} === undefined ? ${inj.def} : ${read})`;
|
|
1990
|
+
iLines.push(`this.setData({ ${inj.name}: ${value} })`);
|
|
1991
|
+
}
|
|
1992
|
+
if (injects.length) {
|
|
1993
|
+
iLines.push("const __self = this");
|
|
1994
|
+
for (const inj of injects) {
|
|
1995
|
+
const read = `provides[${JSON.stringify(inj.key)}]`;
|
|
1996
|
+
iLines.push(`if (provides.__subs && provides.__subs[${JSON.stringify(inj.key)}]) {`);
|
|
1997
|
+
iLines.push(` const __sub = { k: ${JSON.stringify(inj.key)}, fn: function () { __self.setData({ ${inj.name}: ${read} }) } }`);
|
|
1998
|
+
iLines.push(" if (!this.__proteusSubs) this.__proteusSubs = []");
|
|
1999
|
+
iLines.push(" this.__proteusSubs.push(__sub)");
|
|
2000
|
+
iLines.push(` provides.__subs[${JSON.stringify(inj.key)}].push(__sub)`);
|
|
2001
|
+
iLines.push("}");
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
return {
|
|
2005
|
+
page: pLines.length || iLines.length ? `${pageOpen}
|
|
2006
|
+
${[...pLines, ...iLines].join("\n")}` : "",
|
|
2007
|
+
provide: pLines.length ? `${compOpen}
|
|
2008
|
+
${pLines.join("\n")}` : "",
|
|
2009
|
+
inject: iLines.length ? `${compOpen}
|
|
2010
|
+
${iLines.join("\n")}` : "",
|
|
2011
|
+
provideRefs
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
function extractImports(source) {
|
|
2015
|
+
const out = [];
|
|
2016
|
+
const lines = source.split("\n");
|
|
2017
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2018
|
+
const line = lines[i];
|
|
2019
|
+
const t = line.trim();
|
|
2020
|
+
if (!t.startsWith("import ")) continue;
|
|
2021
|
+
const lineNo = i + 1;
|
|
2022
|
+
let m = t.match(/^import\s+['"]([^'"]+)['"];?$/);
|
|
2023
|
+
if (m) {
|
|
2024
|
+
out.push({ source: m[1], kind: "side", names: [], line: lineNo, typeOnly: false });
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
2027
|
+
m = t.match(/^import\s+type\s+([\s\S]*?)\s+from\s+['"]([^'"]+)['"];?$/);
|
|
2028
|
+
if (m) {
|
|
2029
|
+
out.push({ source: m[2], kind: "named", names: [], line: lineNo, typeOnly: true });
|
|
2030
|
+
continue;
|
|
2031
|
+
}
|
|
2032
|
+
m = t.match(/^import\s+([A-Za-z_$][\w$]*)\s*,\s*\{([^}]*)\}\.*\s+from\s+['"]([^'"]+)['"];?$/);
|
|
2033
|
+
if (m) {
|
|
2034
|
+
const names = m[2].split(",").map((n) => n.trim()).filter(Boolean).map((n) => n.replace(/\s+as\s+[\w$]+$/, "").trim());
|
|
2035
|
+
out.push({ source: m[3], kind: "default", names: [m[1]], line: lineNo, typeOnly: false });
|
|
2036
|
+
if (names.length) out.push({ source: m[3], kind: "named", names, line: lineNo, typeOnly: false });
|
|
2037
|
+
continue;
|
|
2038
|
+
}
|
|
2039
|
+
m = t.match(/^import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s+['"]([^'"]+)['"];?$/);
|
|
2040
|
+
if (m) {
|
|
2041
|
+
out.push({ source: m[2], kind: "namespace", names: [m[1]], line: lineNo, typeOnly: false });
|
|
2042
|
+
continue;
|
|
2043
|
+
}
|
|
2044
|
+
m = t.match(/^import\s+\{([^}]*)\}\s+from\s+['"]([^'"]+)['"];?$/);
|
|
2045
|
+
if (m) {
|
|
2046
|
+
const names = m[1].split(",").map((n) => n.trim()).filter(Boolean).map((n) => n.replace(/\s+as\s+[\w$]+$/, "").trim());
|
|
2047
|
+
out.push({ source: m[2], kind: "named", names, line: lineNo, typeOnly: false });
|
|
2048
|
+
continue;
|
|
2049
|
+
}
|
|
2050
|
+
m = t.match(/^import\s+([A-Za-z_$][\w$]*)\s+from\s+['"]([^'"]+)['"];?$/);
|
|
2051
|
+
if (m) {
|
|
2052
|
+
out.push({ source: m[2], kind: "default", names: [m[1]], line: lineNo, typeOnly: false });
|
|
2053
|
+
continue;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
return out;
|
|
2057
|
+
}
|
|
2058
|
+
function extractBracedBody(source, openBraceIndex) {
|
|
2059
|
+
let depth = 0;
|
|
2060
|
+
for (let i = openBraceIndex; i < source.length; i++) {
|
|
2061
|
+
const ch = source[i];
|
|
2062
|
+
if (ch === "{") depth++;
|
|
2063
|
+
else if (ch === "}") {
|
|
2064
|
+
depth--;
|
|
2065
|
+
if (depth === 0) return source.slice(openBraceIndex + 1, i);
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
return null;
|
|
2069
|
+
}
|
|
2070
|
+
function extractInitializer(source, valueStart) {
|
|
2071
|
+
let depth = 0;
|
|
2072
|
+
let quote = null;
|
|
2073
|
+
let escaped = false;
|
|
2074
|
+
let inBlockComment = false;
|
|
2075
|
+
let i = valueStart;
|
|
2076
|
+
const len = source.length;
|
|
2077
|
+
for (; i < len; i++) {
|
|
2078
|
+
const ch = source[i];
|
|
2079
|
+
const next = source[i + 1];
|
|
2080
|
+
if (inBlockComment) {
|
|
2081
|
+
if (ch === "*" && next === "/") {
|
|
2082
|
+
inBlockComment = false;
|
|
2083
|
+
i++;
|
|
2084
|
+
}
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
if (quote) {
|
|
2088
|
+
if (escaped) {
|
|
2089
|
+
escaped = false;
|
|
2090
|
+
continue;
|
|
2091
|
+
}
|
|
2092
|
+
if (ch === "\\") {
|
|
2093
|
+
escaped = true;
|
|
2094
|
+
continue;
|
|
2095
|
+
}
|
|
2096
|
+
if (ch === quote) quote = null;
|
|
2097
|
+
continue;
|
|
2098
|
+
}
|
|
2099
|
+
if (ch === "/" && next === "*") {
|
|
2100
|
+
inBlockComment = true;
|
|
2101
|
+
i++;
|
|
2102
|
+
continue;
|
|
2103
|
+
}
|
|
2104
|
+
if (ch === "/" && next === "/") {
|
|
2105
|
+
while (i < len && source[i] !== "\n") i++;
|
|
2106
|
+
if (depth === 0) break;
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
2110
|
+
quote = ch;
|
|
2111
|
+
continue;
|
|
2112
|
+
}
|
|
2113
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
2114
|
+
depth++;
|
|
2115
|
+
continue;
|
|
2116
|
+
}
|
|
2117
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
2118
|
+
depth--;
|
|
2119
|
+
continue;
|
|
2120
|
+
}
|
|
2121
|
+
if (depth === 0 && (ch === ";" || ch === "\n")) break;
|
|
2122
|
+
}
|
|
2123
|
+
return source.slice(valueStart, i).trim();
|
|
2124
|
+
}
|
|
2125
|
+
function mapTsType(t, warnings, name) {
|
|
2126
|
+
const s = t.trim();
|
|
2127
|
+
if (s === "string") return { type: "String", value: "" };
|
|
2128
|
+
if (s === "number") return { type: "Number", value: 0 };
|
|
2129
|
+
if (s === "boolean") return { type: "Boolean", value: false };
|
|
2130
|
+
if (s === "object") return { type: "Object" };
|
|
2131
|
+
if (s.endsWith("[]") || s === "Array") return { type: "Array" };
|
|
2132
|
+
if (s.includes("|") || s.startsWith("'")) return { type: "String", value: "" };
|
|
2133
|
+
warnings.push(`prop ${name} \u7684\u7C7B\u578B ${s} \u65E0\u6CD5\u6620\u5C04\uFF08\u6CDB\u578B\u5F62\u5F0F\u652F\u6301 string/number/boolean/object/Array/\u8054\u5408\uFF09\uFF0C\u5DF2\u6309 String \u5904\u7406`);
|
|
2134
|
+
return { type: "String", value: "" };
|
|
2135
|
+
}
|
|
2136
|
+
function extractProps(source, warnings, trace) {
|
|
2137
|
+
const out = {};
|
|
2138
|
+
const add = (name, info, before) => {
|
|
2139
|
+
out[name] = info;
|
|
2140
|
+
trace?.add("script/define-props", { before, after: `properties.${name}\uFF08type: ${info.type}\uFF09` });
|
|
2141
|
+
};
|
|
2142
|
+
const m = source.match(/\bdefineProps\s*\(\s*\{/);
|
|
2143
|
+
if (m) {
|
|
2144
|
+
const body = extractBracedBody(source, (m.index ?? 0) + m[0].length - 1);
|
|
2145
|
+
if (body === null) {
|
|
2146
|
+
warnings.push("defineProps \u89E3\u6790\u5931\u8D25\uFF08MVP \u4EC5\u652F\u6301\u5BF9\u8C61\u5F62\u5F0F defineProps({...})\uFF09\uFF0C\u5DF2\u5FFD\u7565");
|
|
2147
|
+
return out;
|
|
2148
|
+
}
|
|
2149
|
+
const re2 = /(['"]?)([A-Za-z_$][\w$]*)\1\s*:\s*(\{[^}]*\}|[A-Za-z_$][\w$]*)/g;
|
|
2150
|
+
let pm2;
|
|
2151
|
+
while (pm2 = re2.exec(body)) {
|
|
2152
|
+
const name = pm2[2];
|
|
2153
|
+
const spec = pm2[3].trim();
|
|
2154
|
+
let type = "String";
|
|
2155
|
+
let value;
|
|
2156
|
+
if (spec.startsWith("{")) {
|
|
2157
|
+
const typeM = spec.match(/type\s*:\s*([A-Za-z_$][\w$]*)/);
|
|
2158
|
+
if (typeM) type = typeM[1];
|
|
2159
|
+
const defM = spec.match(/default\s*:\s*([^,}]+)/);
|
|
2160
|
+
if (defM) value = evalLiteral(defM[1].trim());
|
|
2161
|
+
} else {
|
|
2162
|
+
type = spec;
|
|
2163
|
+
}
|
|
2164
|
+
if (!["String", "Number", "Boolean", "Object", "Array", "Function"].includes(type)) {
|
|
2165
|
+
warnings.push(`prop ${name} \u7684\u7C7B\u578B ${type} \u65E0\u6CD5\u6620\u5C04\u5230\u5FAE\u4FE1 properties\uFF08MVP \u652F\u6301 String/Number/Boolean/Object/Array\uFF09\uFF0C\u5DF2\u6309 String \u5904\u7406`);
|
|
2166
|
+
type = "String";
|
|
2167
|
+
}
|
|
2168
|
+
if (typeof value === "function") {
|
|
2169
|
+
warnings.push(`prop ${name} \u7684 default \u662F\u51FD\u6570\uFF08\u5FAE\u4FE1 properties.value \u4EC5\u652F\u6301\u5B57\u9762\u91CF\uFF09\uFF0C\u5DF2\u5FFD\u7565\u9ED8\u8BA4\u503C`);
|
|
2170
|
+
value = void 0;
|
|
2171
|
+
}
|
|
2172
|
+
if (value === void 0) {
|
|
2173
|
+
if (type === "String") value = "";
|
|
2174
|
+
else if (type === "Number") value = 0;
|
|
2175
|
+
else if (type === "Boolean") value = false;
|
|
2176
|
+
}
|
|
2177
|
+
add(name, { type, value }, `defineProps({ ${name}: ... })`);
|
|
2178
|
+
}
|
|
2179
|
+
return out;
|
|
2180
|
+
}
|
|
2181
|
+
const tsM = source.match(/\bdefineProps\s*<\{([\s\S]*?)\}\s*>/);
|
|
2182
|
+
if (!tsM) return out;
|
|
2183
|
+
const re = /([A-Za-z_$][\w$]*)\s*(\?)?\s*:\s*([^;]+)/g;
|
|
2184
|
+
let pm;
|
|
2185
|
+
while (pm = re.exec(tsM[1])) {
|
|
2186
|
+
const name = pm[1];
|
|
2187
|
+
const info = mapTsType(pm[3], warnings, name);
|
|
2188
|
+
add(name, info, `defineProps<{ ${name}${pm[2] ?? ""}: ${pm[3].trim()} }>()`);
|
|
2189
|
+
}
|
|
2190
|
+
return out;
|
|
2191
|
+
}
|
|
2192
|
+
function extractWatch(source, data, warnings, trace, allowPropWatch = true) {
|
|
2193
|
+
const out = {};
|
|
2194
|
+
const re = /^watch\s*\(\s*([\s\S]*?)\s*,\s*(?:\(([^)]*)\)\s*=>|function\s*\(([^)]*)\)\s*)\s*\{/gm;
|
|
2195
|
+
let m;
|
|
2196
|
+
while (m = re.exec(source)) {
|
|
2197
|
+
const lineStart = source.lastIndexOf("\n", m.index) + 1;
|
|
2198
|
+
if (source.slice(lineStart, m.index) !== "") continue;
|
|
2199
|
+
const rawSrc = m[1].trim();
|
|
2200
|
+
const params = (m[2] ?? m[3] ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
2201
|
+
let propField;
|
|
2202
|
+
if (allowPropWatch && rawSrc.startsWith("props.")) {
|
|
2203
|
+
propField = rawSrc.slice("props.".length).trim().replace(/\.value$/, "");
|
|
2204
|
+
} else if (allowPropWatch && rawSrc.startsWith("()")) {
|
|
2205
|
+
const getter = rawSrc.replace(/^\(\)\s*=>\s*/, "").trim();
|
|
2206
|
+
const pm = getter.match(/^props\.([A-Za-z_$][\w$]*)$/);
|
|
2207
|
+
if (pm) propField = pm[1];
|
|
2208
|
+
}
|
|
2209
|
+
if (propField) {
|
|
2210
|
+
const braceIdx2 = m.index + m[0].length - 1;
|
|
2211
|
+
const body2 = extractBracedBody(source, braceIdx2);
|
|
2212
|
+
if (body2 === null) {
|
|
2213
|
+
warnings.push(`watch ${rawSrc} \u56DE\u8C03\u4F53\u89E3\u6790\u5931\u8D25\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
2214
|
+
continue;
|
|
2215
|
+
}
|
|
2216
|
+
const after2 = source.slice(braceIdx2 + body2.length + 1, braceIdx2 + body2.length + 120);
|
|
2217
|
+
const immediate2 = /immediate\s*:\s*true/.test(after2);
|
|
2218
|
+
const id2 = `Prop${capitalize2(propField)}`;
|
|
2219
|
+
if (out[id2]) {
|
|
2220
|
+
warnings.push(`watch ${rawSrc} \u4E0E\u5DF2\u6709 watch \u91CD\u540D\uFF08${id2}\uFF09\uFF0C\u540E\u8005\u8986\u76D6\u524D\u8005`);
|
|
2221
|
+
}
|
|
2222
|
+
trace?.add("script/watch-props", {
|
|
2223
|
+
line: lineAt(source, m.index),
|
|
2224
|
+
before: `watch(${rawSrc}, (${params.join(", ")}) => ...)`,
|
|
2225
|
+
after: `observers: { ${propField}(n, o) { ... }${immediate2 ? " + proteusWatchPropX \u65B9\u6CD5\uFF08attached \u521D\u59CB\u5316\u8C03\u7528\u4E00\u6B21\uFF09" : ""} }`
|
|
2226
|
+
});
|
|
2227
|
+
out[id2] = { id: id2, deps: [], params, body: body2, immediate: immediate2, line: lineAt(source, m.index), propField };
|
|
2228
|
+
continue;
|
|
2229
|
+
}
|
|
2230
|
+
let deps = [];
|
|
2231
|
+
let expr;
|
|
2232
|
+
if (rawSrc.startsWith("[")) {
|
|
2233
|
+
deps = Array.from(rawSrc.matchAll(/\b([A-Za-z_$][\w$]*)\b/g), (mm) => mm[1]);
|
|
2234
|
+
} else if (rawSrc.startsWith("()")) {
|
|
2235
|
+
const getter = rawSrc.replace(/^\(\)\s*=>\s*/, "");
|
|
2236
|
+
deps = [...new Set(Array.from(getter.matchAll(/\b([A-Za-z_$][\w$]*)\.value\b/g), (mm) => mm[1]))];
|
|
2237
|
+
expr = getter.replace(/\b([A-Za-z_$][\w$]*)\.value\b/g, "this.data.$1");
|
|
2238
|
+
} else {
|
|
2239
|
+
deps = [rawSrc];
|
|
2240
|
+
}
|
|
2241
|
+
const missing = deps.filter((d) => !(d in data));
|
|
2242
|
+
if (missing.length) {
|
|
2243
|
+
warnings.push(`watch \u4F9D\u8D56 ${missing.join("/")} \u672A\u5728\u9876\u5C42 data \u4E2D\u5B9A\u4E49\uFF08watch \u7684\u6E90\u5FC5\u987B\u662F\u672C\u6587\u4EF6\u9876\u5C42 ref/reactive\uFF09`);
|
|
2244
|
+
continue;
|
|
2245
|
+
}
|
|
2246
|
+
if (!deps.length) {
|
|
2247
|
+
warnings.push(`watch \u6E90\u65E0\u6CD5\u89E3\u6790\u4F9D\u8D56\uFF08${rawSrc.slice(0, 40)}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
const id = deps.map((d) => capitalize2(d)).join("And");
|
|
2251
|
+
const braceIdx = m.index + m[0].length - 1;
|
|
2252
|
+
const body = extractBracedBody(source, braceIdx);
|
|
2253
|
+
if (body === null) {
|
|
2254
|
+
warnings.push(`watch ${rawSrc} \u56DE\u8C03\u4F53\u89E3\u6790\u5931\u8D25\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
2255
|
+
continue;
|
|
2256
|
+
}
|
|
2257
|
+
const after = source.slice(braceIdx + body.length + 1, braceIdx + body.length + 120);
|
|
2258
|
+
const immediate = /immediate\s*:\s*true/.test(after);
|
|
2259
|
+
if (out[id]) {
|
|
2260
|
+
warnings.push(`watch ${rawSrc} \u4E0E\u5DF2\u6709 watch \u91CD\u540D\uFF08${id}\uFF09\uFF0C\u540E\u8005\u8986\u76D6\u524D\u8005`);
|
|
2261
|
+
}
|
|
2262
|
+
trace?.add("script/watch-to-methods", {
|
|
2263
|
+
line: lineAt(source, m.index),
|
|
2264
|
+
before: `watch(${rawSrc}, (${params.join(", ")}) => ...)`,
|
|
2265
|
+
after: `proteusWatch${id}\uFF08${deps.join("/")} \u5199\u5165 setData \u540E\u81EA\u52A8\u8C03\u7528${immediate ? "\uFF0Cimmediate \u521D\u59CB\u5316\u4E00\u6B21" : ""}\uFF09`
|
|
2266
|
+
});
|
|
2267
|
+
out[id] = { id, deps, params, body, immediate, line: lineAt(source, m.index), expr };
|
|
2268
|
+
}
|
|
2269
|
+
return out;
|
|
2270
|
+
}
|
|
2271
|
+
function extractComputedFromInit(name, init, data, warnings) {
|
|
2272
|
+
const arrow = init.match(/^computed\s*\(\s*\(\)\s*=>\s*([\s\S]*?)\s*\)\s*;?$/);
|
|
2273
|
+
let rawExpr;
|
|
2274
|
+
let setter;
|
|
2275
|
+
if (arrow) {
|
|
2276
|
+
rawExpr = arrow[1];
|
|
2277
|
+
if (rawExpr.trim().startsWith("{")) return null;
|
|
2278
|
+
} else {
|
|
2279
|
+
const objM = init.match(/^computed\s*\(\s*\{/);
|
|
2280
|
+
if (!objM) return null;
|
|
2281
|
+
const body = extractBracedBody(init, (objM.index ?? 0) + objM[0].length - 1);
|
|
2282
|
+
if (body === null) return null;
|
|
2283
|
+
const getM = body.match(/\bget\s*:\s*\(\)\s*=>\s*([\s\S]*?)(?=,\s*\bset\s*:|$)/);
|
|
2284
|
+
rawExpr = getM?.[1]?.trim();
|
|
2285
|
+
if (!rawExpr || rawExpr.startsWith("{")) return null;
|
|
2286
|
+
const setM = body.match(/\bset\s*:\s*\(([^)]*)\)\s*=>\s*\{([\s\S]*?)\}/);
|
|
2287
|
+
if (setM) setter = { param: setM[1].trim(), body: setM[2] };
|
|
2288
|
+
}
|
|
2289
|
+
const deps = [...new Set(Array.from(rawExpr.matchAll(/\b([A-Za-z_$][\w$]*)\.value\b/g), (mm) => mm[1]))];
|
|
2290
|
+
const missing = deps.filter((d) => !(d in data));
|
|
2291
|
+
if (missing.length) {
|
|
2292
|
+
warnings.push(
|
|
2293
|
+
`computed ${name} \u4F9D\u8D56 ${missing.join("/")} \u672A\u5728\u9876\u5C42 data \u4E2D\u5B9A\u4E49\uFF08${name} \u7684\u4F9D\u8D56\u5FC5\u987B\u662F\u672C\u6587\u4EF6\u9876\u5C42 ref/reactive\uFF09`
|
|
2294
|
+
);
|
|
2295
|
+
}
|
|
2296
|
+
const expr = rawExpr.replace(/\b([A-Za-z_$][\w$]*)\.value\b/g, "this.data.$1");
|
|
2297
|
+
return { name, deps, expr, setter };
|
|
2298
|
+
}
|
|
2299
|
+
function extractData(source, warnings, trace) {
|
|
2300
|
+
const data = {};
|
|
2301
|
+
const runtimeInits = [];
|
|
2302
|
+
const rawComputed = [];
|
|
2303
|
+
const re = /const\s+([A-Za-z_$][\w$]*)\s*=\s*/gm;
|
|
2304
|
+
let m;
|
|
2305
|
+
while (m = re.exec(source)) {
|
|
2306
|
+
const lineStart = source.lastIndexOf("\n", m.index) + 1;
|
|
2307
|
+
if (source.slice(lineStart, m.index) !== "") continue;
|
|
2308
|
+
const name = m[1];
|
|
2309
|
+
const init = extractInitializer(source, m.index + m[0].length);
|
|
2310
|
+
if (!init) continue;
|
|
2311
|
+
const line = lineAt(source, m.index);
|
|
2312
|
+
if (/^(?:defineProps\s*[<(]|defineEmits\s*\(|defineExpose\s*\()/.test(init)) continue;
|
|
2313
|
+
if (/^(?:async\s+)?(?:function\b|(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>)/.test(init)) continue;
|
|
2314
|
+
if (init.startsWith("computed(")) {
|
|
2315
|
+
rawComputed.push({ name, init, line });
|
|
2316
|
+
continue;
|
|
2317
|
+
}
|
|
2318
|
+
trace?.add("script/const-to-data", {
|
|
2319
|
+
line,
|
|
2320
|
+
before: `const ${name} = ${init.slice(0, 40)}${init.length > 40 ? "\u2026" : ""}`,
|
|
2321
|
+
after: `data.${name}`
|
|
2322
|
+
});
|
|
2323
|
+
const inner = init.match(/^(?:ref|reactive|shallowRef|readonly)\s*\(\s*([\s\S]*?)\s*\);?\s*$/);
|
|
2324
|
+
const raw = inner ? inner[1] : init;
|
|
2325
|
+
const value = evalLiteral(raw);
|
|
2326
|
+
const isCall = /^[\w$.]+\(/.test(raw.trim());
|
|
2327
|
+
if (isCall && value === void 0 && !/^inject\s*\(/.test(raw.trim())) {
|
|
2328
|
+
runtimeInits.push({ name, call: raw.trim() });
|
|
2329
|
+
trace?.add("script/runtime-init", {
|
|
2330
|
+
line,
|
|
2331
|
+
before: `const ${name} = ${raw.slice(0, 40)}`,
|
|
2332
|
+
after: `this.${name} = ${raw.trim()}\uFF08onLoad/attached \u8FD0\u884C\u65F6\u521D\u59CB\u5316\uFF0C\u5B9E\u4F8B\u5C5E\u6027\uFF1B\u6A21\u677F\u7ED1\u5B9A\u4E0D\u652F\u6301\uFF09`
|
|
2333
|
+
});
|
|
2334
|
+
warnings.push(
|
|
2335
|
+
`const ${name} \u7684\u521D\u59CB\u503C "${raw.slice(0, 40)}" \u662F\u51FD\u6570\u8C03\u7528\u2014\u2014\u5DF2\u7F16\u8BD1\u4E3A\u8FD0\u884C\u65F6\u521D\u59CB\u5316 this.${name}\uFF08onLoad/attached \u6267\u884C\uFF0C\u5B9E\u4F8B\u5C5E\u6027\uFF1A\u6A21\u677F\u7ED1\u5B9A\u4E0D\u652F\u6301\uFF0C\u903B\u8F91\u5C42\u53EF\u7528\uFF1B\u5171\u4EAB\u903B\u8F91\u8BF7\u7528\u6A21\u5757 import\uFF0C\u89C1 docs/proteus-module-plan/\uFF09`
|
|
2336
|
+
);
|
|
2337
|
+
continue;
|
|
2338
|
+
}
|
|
2339
|
+
if (value === void 0 && raw !== "undefined" && !/^inject\s*\(/.test(raw.trim())) {
|
|
2340
|
+
warnings.push(`const ${name} \u7684\u521D\u59CB\u503C "${raw.slice(0, 40)}" \u65E0\u6CD5\u9759\u6001\u6C42\u503C\uFF0Cdata.${name} \u5C06\u8BBE\u4E3A undefined\uFF08MVP \u9650\u5236\uFF1A\u4EC5\u652F\u6301\u5B57\u9762\u91CF\uFF09`);
|
|
2341
|
+
}
|
|
2342
|
+
data[name] = value;
|
|
2343
|
+
}
|
|
2344
|
+
const computed = {};
|
|
2345
|
+
for (const c of rawComputed) {
|
|
2346
|
+
const info = extractComputedFromInit(c.name, c.init, data, warnings);
|
|
2347
|
+
if (info) {
|
|
2348
|
+
computed[c.name] = info;
|
|
2349
|
+
trace?.add("script/computed-to-data", {
|
|
2350
|
+
line: c.line,
|
|
2351
|
+
before: `const ${c.name} = computed(() => ...)`,
|
|
2352
|
+
after: `\u6D3E\u751F\u5B57\u6BB5\uFF08\u4F9D\u8D56 ${info.deps.join("/") || "\u65E0"}\uFF0C\u5199\u5165\u65F6\u5408\u5E76\u91CD\u7B97\uFF09`
|
|
2353
|
+
});
|
|
2354
|
+
} else {
|
|
2355
|
+
warnings.push(`computed ${c.name} \u4EC5\u652F\u6301\u7BAD\u5934\u7B80\u5199 + \u8868\u8FBE\u5F0F\u4F53\uFF08computed(() => expr)\uFF09\uFF0C\u5DF2\u5FFD\u7565`);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
return { data, computed, runtimeInits };
|
|
2359
|
+
}
|
|
2360
|
+
function stripParamTypes(params) {
|
|
2361
|
+
return params.replace(/:\s*[^,)]+/g, "").trim();
|
|
2362
|
+
}
|
|
2363
|
+
function checkDefineExpose(source, data, warnings, trace) {
|
|
2364
|
+
const m = source.match(/\bdefineExpose\s*\(\s*\{([\s\S]*?)\}\s*\)/);
|
|
2365
|
+
if (!m) return;
|
|
2366
|
+
const names = Array.from(m[1].matchAll(/([A-Za-z_$][\w$]*)/g), (mm) => mm[1]);
|
|
2367
|
+
trace?.add("script/define-expose", {
|
|
2368
|
+
before: `defineExpose({ ${names.join(", ")} })`,
|
|
2369
|
+
after: "no-op\uFF08\u7EC4\u4EF6 methods \u5929\u7136\u53EF\u88AB selectComponent \u8BBF\u95EE\uFF09"
|
|
2370
|
+
});
|
|
2371
|
+
for (const n of names) {
|
|
2372
|
+
if (n in data) {
|
|
2373
|
+
warnings.push(`defineExpose \u66B4\u9732\u7684 ${n} \u662F ref \u503C\uFF08\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u673A\u5236\uFF0C\u5916\u90E8\u4EC5\u53EF\u8BBF\u95EE methods\uFF09\uFF0C\u5DF2\u5FFD\u7565\u2014\u2014\u8BF7\u7528\u65B9\u6CD5\u5305\u88C5`);
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
function extractMethods(source, warnings, trace, disabled) {
|
|
2378
|
+
const methods = {};
|
|
2379
|
+
const fnRe = /(async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g;
|
|
2380
|
+
let m;
|
|
2381
|
+
if (!disabled?.has("script/function-to-methods")) {
|
|
2382
|
+
while (m = fnRe.exec(source)) {
|
|
2383
|
+
const name = m[2];
|
|
2384
|
+
const isAsync = Boolean(m[1]);
|
|
2385
|
+
const params = stripParamTypes(m[3]);
|
|
2386
|
+
const body = extractBracedBody(source, m.index + m[0].length - 1);
|
|
2387
|
+
const line = lineAt(source, m.index);
|
|
2388
|
+
trace?.add("script/function-to-methods", { line, before: `function ${name}(${m[3]})`, after: `${name}(${params})` });
|
|
2389
|
+
if (body !== null) methods[name] = { src: `${isAsync ? "async " : ""}${name}(${params}) {
|
|
2390
|
+
${body}
|
|
2391
|
+
}`, line };
|
|
2392
|
+
else warnings.push(`\u51FD\u6570 ${name} \u4F53\u89E3\u6790\u5931\u8D25\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
const arrowRe = /const\s+([A-Za-z_$][\w$]*)\s*=\s*(async\s*)?\(([^)]*)\)\s*=>\s*\{/g;
|
|
2396
|
+
if (!disabled?.has("script/arrow-to-methods")) {
|
|
2397
|
+
while (m = arrowRe.exec(source)) {
|
|
2398
|
+
const name = m[1];
|
|
2399
|
+
const isAsync = Boolean(m[2]);
|
|
2400
|
+
const params = stripParamTypes(m[3]);
|
|
2401
|
+
const braceIdx = source.indexOf("{", m.index + m[0].length - 1);
|
|
2402
|
+
const body = extractBracedBody(source, braceIdx);
|
|
2403
|
+
const line = lineAt(source, m.index);
|
|
2404
|
+
trace?.add("script/arrow-to-methods", { line, before: `const ${name} = (...) =>`, after: `${name}(...)` });
|
|
2405
|
+
if (body !== null) methods[name] = { src: `${isAsync ? "async " : ""}${name}(${params}) {
|
|
2406
|
+
${body}
|
|
2407
|
+
}`, line };
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return methods;
|
|
2411
|
+
}
|
|
2412
|
+
function numOrZero(expr) {
|
|
2413
|
+
return `(${expr} === undefined || ${expr} === null ? 0 : ${expr})`;
|
|
2414
|
+
}
|
|
2415
|
+
function computedPatch(writtenRef, computeds) {
|
|
2416
|
+
const patches = Object.entries(computeds).filter(([, c]) => c.deps.includes(writtenRef)).map(([n, c]) => `${n}: ${c.expr}`);
|
|
2417
|
+
return patches.length ? `, ${patches.join(", ")}` : "";
|
|
2418
|
+
}
|
|
2419
|
+
function computedInitLine(computeds) {
|
|
2420
|
+
const entries = Object.entries(computeds);
|
|
2421
|
+
if (!entries.length) return "";
|
|
2422
|
+
return `this.setData({ ${entries.map(([n, c]) => `${n}: ${c.expr}`).join(", ")} })`;
|
|
2423
|
+
}
|
|
2424
|
+
function runtimeInitLine(inits) {
|
|
2425
|
+
return inits.map((i) => `this.${i.name} = ${i.call}`).join("\n");
|
|
2426
|
+
}
|
|
2427
|
+
function storeBindingLine(fields, storeVar) {
|
|
2428
|
+
const map = fields.map((f) => `${f}: __self.${storeVar}.${f}`).join(", ");
|
|
2429
|
+
return [
|
|
2430
|
+
"const __self = this",
|
|
2431
|
+
`this.setData({ ${map} })`,
|
|
2432
|
+
`if (this.${storeVar} && this.${storeVar}.$subscribe) {`,
|
|
2433
|
+
` this.${storeVar}.$subscribe(function () { __self.setData({ ${map} }) })`,
|
|
2434
|
+
`}`
|
|
2435
|
+
].join("\n");
|
|
2436
|
+
}
|
|
2437
|
+
function immediateWatchLine(watches) {
|
|
2438
|
+
const lines = Object.entries(watches).filter(([, w]) => w.immediate).map(([, w]) => {
|
|
2439
|
+
const single = w.deps.length === 1;
|
|
2440
|
+
const newVals = w.propField ? `this.data.${w.propField}` : single ? w.expr ?? `this.data.${w.deps[0]}` : `[${w.expr ?? w.deps.map((d) => `this.data.${d}`).join(", ")}]`;
|
|
2441
|
+
const oldVals = w.propField ? "undefined" : single ? "undefined" : `[${w.deps.map(() => "undefined").join(", ")}]`;
|
|
2442
|
+
return `this.proteusWatch${w.id}(${newVals}, ${oldVals})`;
|
|
2443
|
+
});
|
|
2444
|
+
return lines.join("\n");
|
|
2445
|
+
}
|
|
2446
|
+
function writeSetData(name, valueExpr, patch, hasWatch = false, forceWrite = false) {
|
|
2447
|
+
const needWrite = forceWrite || Boolean(patch) || hasWatch;
|
|
2448
|
+
if (!needWrite) return `this.setData({ ${name}: ${valueExpr} })`;
|
|
2449
|
+
return `this.data.${name} = ${valueExpr}; this.setData({ ${name}: this.data.${name}${patch} })`;
|
|
2450
|
+
}
|
|
2451
|
+
function watchTail(w) {
|
|
2452
|
+
if (!w) return "";
|
|
2453
|
+
const single = w.deps.length === 1;
|
|
2454
|
+
const newVals = single ? w.expr ?? `this.data.${w.deps[0]}` : `[${w.expr ?? w.deps.map((d) => `this.data.${d}`).join(", ")}]`;
|
|
2455
|
+
const oldVals = single ? `old${capitalize2(w.deps[0])}` : `[${w.deps.map((d) => `old${capitalize2(d)}`).join(", ")}]`;
|
|
2456
|
+
return `; this.proteusWatch${w.id}(${newVals}, ${oldVals})`;
|
|
2457
|
+
}
|
|
2458
|
+
function rewriteBareMethodCalls(body, methodNames) {
|
|
2459
|
+
let out = body;
|
|
2460
|
+
for (const name of methodNames) {
|
|
2461
|
+
out = out.replace(new RegExp(`(?<![\\w$.])${name}\\s*\\(`, "g"), `this.${name}(`);
|
|
2462
|
+
}
|
|
2463
|
+
return out;
|
|
2464
|
+
}
|
|
2465
|
+
function rewriteRefAccess(body, refNames, trace, disabled, computeds = {}, watches = {}, emitEnabled = false, propsVar, providedRefs, transitionToggle) {
|
|
2466
|
+
const skip = (id) => disabled?.has(id);
|
|
2467
|
+
let out = body;
|
|
2468
|
+
out = out.replace(/\s+as\s+(?:unknown|any|never)\b/g, "");
|
|
2469
|
+
out = out.replace(/\s+as\s+[A-Za-z_$][\w$]*(?:<[^;\n]*?>)?/g, "");
|
|
2470
|
+
if (emitEnabled) out = out.replace(/\bemit\s*\(/g, "this.triggerEvent(");
|
|
2471
|
+
if (propsVar) out = out.replace(new RegExp(`\\b${propsVar}\\.([A-Za-z_$][\\w$]*)`, "g"), "this.data.$1");
|
|
2472
|
+
for (const [cname, c] of Object.entries(computeds)) {
|
|
2473
|
+
if (!new RegExp(`\\b${cname}\\.value\\s*=`).test(out)) continue;
|
|
2474
|
+
if (c.setter) {
|
|
2475
|
+
out = out.replace(
|
|
2476
|
+
new RegExp(`\\b${cname}\\.value\\s*=\\s*(?!=)([^;\\n]+)`),
|
|
2477
|
+
(_m, expr) => `this.proteusSet${capitalize2(cname)}(${expr.trim()})`
|
|
2478
|
+
);
|
|
2479
|
+
} else {
|
|
2480
|
+
out = out.replace(
|
|
2481
|
+
new RegExp(`\\b${cname}\\.value\\s*=\\s*(?!=)([^;\\n]+)`),
|
|
2482
|
+
() => `/* computed ${cname} \u53EA\u8BFB\uFF08\u65E0 setter\uFF09\uFF0C\u8D4B\u503C\u5DF2\u5FFD\u7565 */`
|
|
2483
|
+
);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
for (const name of refNames) {
|
|
2487
|
+
const prop = `this.data.${name}`;
|
|
2488
|
+
const line = lineAt(body, Math.max(0, body.indexOf(name)));
|
|
2489
|
+
const patch = computedPatch(name, computeds);
|
|
2490
|
+
const w = Object.values(watches).find((ww) => ww.deps.includes(name) && !skip("script/watch-to-methods"));
|
|
2491
|
+
const oldSave = w ? `const ${w.deps.map((d) => `old${capitalize2(d)} = this.data.${d}`).join(", ")}; ` : "";
|
|
2492
|
+
const tail = watchTail(w);
|
|
2493
|
+
const sync = providedRefs && providedRefs.get(name) ? `; this.proteusSyncProvide(${JSON.stringify(providedRefs.get(name))}, ${JSON.stringify(name)})` : "";
|
|
2494
|
+
const tToggle = transitionToggle && transitionToggle.get(name) ? `; this.${transitionToggle.get(name)}()` : "";
|
|
2495
|
+
if (!skip("script/ref-incdec")) {
|
|
2496
|
+
if (new RegExp(`(\\+\\+|--)\\s*${name}\\.value`).test(body) || new RegExp(`\\b${name}\\.value\\s*(\\+\\+|--)`).test(body)) {
|
|
2497
|
+
trace?.add("script/ref-incdec", { line, before: `${name}.value++/--`, after: `this.setData({ ${name}: ...${patch || w ? " + \u6D3E\u751F/\u8054\u52A8" : ""} })` });
|
|
2498
|
+
}
|
|
2499
|
+
out = out.replace(new RegExp(`\\+\\+\\s*${name}\\.value`, "g"), `${oldSave}${writeSetData(name, `${numOrZero(prop)} + 1`, patch, Boolean(w), true)}${tail}${sync}${tToggle}`);
|
|
2500
|
+
out = out.replace(new RegExp(`--\\s*${name}\\.value`, "g"), `${oldSave}${writeSetData(name, `${numOrZero(prop)} - 1`, patch, Boolean(w), true)}${tail}${sync}${tToggle}`);
|
|
2501
|
+
out = out.replace(new RegExp(`\\b${name}\\.value\\s*\\+\\+`, "g"), `${oldSave}${writeSetData(name, `${numOrZero(prop)} + 1`, patch, Boolean(w))}${tail}${sync}${tToggle}`);
|
|
2502
|
+
out = out.replace(new RegExp(`\\b${name}\\.value\\s*--`, "g"), `${oldSave}${writeSetData(name, `${numOrZero(prop)} - 1`, patch, Boolean(w))}${tail}${sync}${tToggle}`);
|
|
2503
|
+
}
|
|
2504
|
+
if (!skip("script/ref-write")) {
|
|
2505
|
+
if (new RegExp(`\\b${name}\\.value\\s*=\\s*(?!=)`).test(out)) {
|
|
2506
|
+
trace?.add("script/ref-write", { line, before: `${name}.value = expr`, after: `this.setData({ ${name}: expr${patch || w ? " + \u6D3E\u751F/\u8054\u52A8" : ""} })` });
|
|
2507
|
+
}
|
|
2508
|
+
const assignRe = new RegExp(`\\b${name}\\.value\\s*=\\s*(?!=)`);
|
|
2509
|
+
const chunks = [];
|
|
2510
|
+
let rest = out;
|
|
2511
|
+
while (true) {
|
|
2512
|
+
const am = assignRe.exec(rest);
|
|
2513
|
+
if (!am) {
|
|
2514
|
+
chunks.push(rest);
|
|
2515
|
+
break;
|
|
2516
|
+
}
|
|
2517
|
+
chunks.push(rest.slice(0, am.index));
|
|
2518
|
+
let i = am.index + am[0].length;
|
|
2519
|
+
let depth = 0;
|
|
2520
|
+
let inStr = null;
|
|
2521
|
+
while (i < rest.length) {
|
|
2522
|
+
const ch = rest[i];
|
|
2523
|
+
if (inStr) {
|
|
2524
|
+
if (ch === "\\") {
|
|
2525
|
+
i += 2;
|
|
2526
|
+
continue;
|
|
2527
|
+
}
|
|
2528
|
+
if (ch === inStr) inStr = null;
|
|
2529
|
+
} else if (ch === '"' || ch === "'") {
|
|
2530
|
+
inStr = ch;
|
|
2531
|
+
} else if (ch === "{" || ch === "(" || ch === "[") {
|
|
2532
|
+
depth++;
|
|
2533
|
+
} else if (ch === "}" || ch === ")" || ch === "]") {
|
|
2534
|
+
depth--;
|
|
2535
|
+
} else if (depth === 0 && (ch === ";" || ch === "\n")) {
|
|
2536
|
+
break;
|
|
2537
|
+
}
|
|
2538
|
+
i++;
|
|
2539
|
+
}
|
|
2540
|
+
const expr = rest.slice(am.index + am[0].length, i).trim();
|
|
2541
|
+
chunks.push(`${oldSave}${writeSetData(name, expr, patch, Boolean(w))}${tail}${sync}${tToggle}`);
|
|
2542
|
+
rest = rest.slice(i);
|
|
2543
|
+
}
|
|
2544
|
+
out = chunks.join("");
|
|
2545
|
+
}
|
|
2546
|
+
if (!skip("script/ref-read")) {
|
|
2547
|
+
if (new RegExp(`\\b${name}\\.value\\b`).test(out)) {
|
|
2548
|
+
trace?.add("script/ref-read", { line, before: `${name}.value`, after: `this.data.${name}` });
|
|
2549
|
+
}
|
|
2550
|
+
out = out.replace(new RegExp(`\\b${name}\\.value\\b`, "g"), prop);
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
return out;
|
|
2554
|
+
}
|
|
2555
|
+
function extractLifecycles(source, trace, disabled, warnings = []) {
|
|
2556
|
+
const out = {};
|
|
2557
|
+
if (disabled?.has("script/lifecycle-map")) return out;
|
|
2558
|
+
const hooks = [
|
|
2559
|
+
{ re: /onMounted\s*\(/g, key: "onReady" },
|
|
2560
|
+
{ re: /onUnmounted\s*\(/g, key: "onUnload" },
|
|
2561
|
+
{ re: /onLoad\s*\(/g, key: "onLoad" }
|
|
2562
|
+
];
|
|
2563
|
+
const mapped = /* @__PURE__ */ new Set(["onMounted", "onUnmounted", "onLoad"]);
|
|
2564
|
+
for (const hm of source.matchAll(/\bon([A-Z][A-Za-z0-9_]*)\s*\(\s*(?:(?:\([^)]*\)\s*=>)|function)/g)) {
|
|
2565
|
+
const full = `on${hm[1]}`;
|
|
2566
|
+
if (!mapped.has(full)) {
|
|
2567
|
+
warnings.push(`\u672A\u6620\u5C04\u7684\u751F\u547D\u5468\u671F\u94A9\u5B50 ${full}() \u5DF2\u5265\u79BB\uFF08\u5C0F\u7A0B\u5E8F\u65E0\u5BF9\u7B49\u94A9\u5B50\uFF1BWeb \u7AEF\u4FDD\u7559\u539F\u751F\u8BED\u4E49\uFF09\u2014\u2014\u5982\u7EC4\u4EF6\u5185\u9700\u8981\u964D\u7EA7\u8BF4\u660E\u8BF7\u6CE8\u91CA\u6807\u6CE8`);
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
for (const h of hooks) {
|
|
2571
|
+
h.re.lastIndex = 0;
|
|
2572
|
+
const m = h.re.exec(source);
|
|
2573
|
+
if (!m) continue;
|
|
2574
|
+
const braceIdx = source.indexOf("{", m.index);
|
|
2575
|
+
const body = extractBracedBody(source, braceIdx);
|
|
2576
|
+
trace?.add("script/lifecycle-map", { line: lineAt(source, m.index), before: m[0].replace(/\s*\(/, "()"), after: h.key });
|
|
2577
|
+
if (body !== null) out[h.key] = body;
|
|
2578
|
+
}
|
|
2579
|
+
return out;
|
|
2580
|
+
}
|
|
2581
|
+
function indentBody(body) {
|
|
2582
|
+
return body.split("\n").map((l) => ` ${l}`).join("\n");
|
|
2583
|
+
}
|
|
2584
|
+
function capitalize2(s) {
|
|
2585
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
2586
|
+
}
|
|
2587
|
+
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
2588
|
+
function vlqEncode(value) {
|
|
2589
|
+
let vlq = value < 0 ? -value << 1 | 1 : value << 1;
|
|
2590
|
+
let out = "";
|
|
2591
|
+
do {
|
|
2592
|
+
let digit = vlq & 31;
|
|
2593
|
+
vlq >>>= 5;
|
|
2594
|
+
if (vlq > 0) digit |= 32;
|
|
2595
|
+
out += B64[digit];
|
|
2596
|
+
} while (vlq > 0);
|
|
2597
|
+
return out;
|
|
2598
|
+
}
|
|
2599
|
+
function buildSourceMap(js, file, source, lineMappings) {
|
|
2600
|
+
const byOut = new Map(lineMappings.map((m) => [m.out, m.src]));
|
|
2601
|
+
const lineCount = js.split("\n").length;
|
|
2602
|
+
let prevSrc = 0;
|
|
2603
|
+
const segs = [];
|
|
2604
|
+
for (let i = 0; i < lineCount; i++) {
|
|
2605
|
+
const srcLine = byOut.get(i);
|
|
2606
|
+
if (srcLine == null) {
|
|
2607
|
+
segs.push("");
|
|
2608
|
+
continue;
|
|
2609
|
+
}
|
|
2610
|
+
const src0 = srcLine - 1;
|
|
2611
|
+
segs.push(`AA${vlqEncode(src0 - prevSrc)}A`);
|
|
2612
|
+
prevSrc = src0;
|
|
2613
|
+
}
|
|
2614
|
+
return JSON.stringify({
|
|
2615
|
+
version: 3,
|
|
2616
|
+
file: file ? `${file}.js` : "page.js",
|
|
2617
|
+
sources: [file ?? "anonymous.vue"],
|
|
2618
|
+
sourcesContent: [source],
|
|
2619
|
+
names: [],
|
|
2620
|
+
mappings: segs.join(";")
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
2623
|
+
function transformScriptToPage(source, _opts = { px2rpx: true, rpxRatio: 2 }, extra = {}) {
|
|
2624
|
+
const warnings = [];
|
|
2625
|
+
const trace = extra.trace;
|
|
2626
|
+
const moduleImports = /* @__PURE__ */ new Map();
|
|
2627
|
+
for (const mi of extra.moduleImports ?? []) moduleImports.set(mi.source, mi.requirePath);
|
|
2628
|
+
const requireLines = [];
|
|
2629
|
+
const importWarnings = [];
|
|
2630
|
+
for (const imp of extractImports(source)) {
|
|
2631
|
+
if (imp.source === "vue") continue;
|
|
2632
|
+
if (imp.typeOnly) continue;
|
|
2633
|
+
if (imp.source.endsWith(".vue")) continue;
|
|
2634
|
+
const reqPath = moduleImports.get(imp.source);
|
|
2635
|
+
if (reqPath) {
|
|
2636
|
+
if (imp.kind === "named") {
|
|
2637
|
+
requireLines.push(`const { ${imp.names.join(", ")} } = require('${reqPath}')`);
|
|
2638
|
+
} else if (imp.kind === "namespace") {
|
|
2639
|
+
requireLines.push(`const ${imp.names[0]} = require('${reqPath}')`);
|
|
2640
|
+
} else if (imp.kind === "default") {
|
|
2641
|
+
requireLines.push(`const ${imp.names[0]} = require('${reqPath}').default !== undefined ? require('${reqPath}').default : require('${reqPath}')`);
|
|
2642
|
+
} else {
|
|
2643
|
+
requireLines.push(`require('${reqPath}')`);
|
|
2644
|
+
}
|
|
2645
|
+
trace?.add("script/module-import", {
|
|
2646
|
+
line: imp.line,
|
|
2647
|
+
before: `import { ${imp.names.join(", ")} } from '${imp.source}'`,
|
|
2648
|
+
after: `const { ${imp.names.join(", ")} } = require('${reqPath}')\uFF08\u8DE8\u6A21\u5757\u5F15\u7528\uFF0C\u5171\u4EAB\u6A21\u5757\u72EC\u7ACB\u4EA7\u7269\uFF09`
|
|
2649
|
+
});
|
|
2650
|
+
} else if (!extra.isComponent) {
|
|
2651
|
+
importWarnings.push(imp.source);
|
|
2652
|
+
trace?.add("script/module-import", {
|
|
2653
|
+
line: imp.line,
|
|
2654
|
+
before: `import ... from '${imp.source}'`,
|
|
2655
|
+
after: "\uFF08\u5265\u79BB\uFF1A\u65E0\u6CD5\u89E3\u6790\u7684\u8DE8\u6A21\u5757\u5F15\u7528\uFF0C\u7B26\u53F7\u5C06 undefined\uFF09"
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
if (importWarnings.length) {
|
|
2660
|
+
warnings.push(
|
|
2661
|
+
`\u68C0\u6D4B\u5230 ${importWarnings.length} \u6761\u65E0\u6CD5\u89E3\u6790\u7684 import\uFF08${importWarnings.slice(0, 3).join(", ")}${importWarnings.length > 3 ? "\u2026" : ""}\uFF09\u2014\u2014\u5C0F\u7A0B\u5E8F\u4EA7\u7269\u65E0\u6A21\u5757\u7CFB\u7EDF\uFF0C\u8DE8\u6A21\u5757\u5F15\u7528\u5C06 undefined\uFF1A\u8BF7\u6539\u7528\u672C\u5730\u6A21\u5757\u8DEF\u5F84\uFF08module-plan B0\uFF1A\u76F8\u5BF9\u8DEF\u5F84\u5171\u4EAB\u6A21\u5757\u81EA\u52A8\u7F16\u8BD1 + require\uFF09\u6216\u6846\u67B6 store \u6865`
|
|
2662
|
+
);
|
|
2663
|
+
}
|
|
2664
|
+
const disabled = resolveOverrides(extra.rules).disabled;
|
|
2665
|
+
const { data, computed, runtimeInits } = disabled.has("script/const-to-data") ? { data: {}, computed: {}, runtimeInits: [] } : extractData(source, warnings, trace);
|
|
2666
|
+
const computeds = disabled.has("script/computed-to-data") ? {} : computed;
|
|
2667
|
+
const watches = disabled.has("script/watch-to-methods") ? {} : extractWatch(source, data, warnings, trace, !disabled.has("script/watch-props"));
|
|
2668
|
+
const props = extra.isComponent && !disabled.has("script/define-props") ? extractProps(source, warnings, trace) : {};
|
|
2669
|
+
const propsVar = source.match(/const\s+([A-Za-z_$][\w$]*)\s*=\s*defineProps\s*[<(]/)?.[1];
|
|
2670
|
+
const emitEnabled = extra.isComponent && /defineEmits\s*\(/.test(source) && !disabled.has("script/define-emits");
|
|
2671
|
+
const methods = extractMethods(source, warnings, trace, disabled);
|
|
2672
|
+
if (extra.isComponent) checkDefineExpose(source, data, warnings, trace);
|
|
2673
|
+
const lifecycles = extractLifecycles(source, trace, disabled, warnings);
|
|
2674
|
+
const vModelBindings = extra.vModelBindings ?? [];
|
|
2675
|
+
const refNames = new Set(Object.keys(data));
|
|
2676
|
+
const piEnabled = !disabled.has("script/provide-inject");
|
|
2677
|
+
const { provides, injects } = piEnabled ? extractProvideInject(source) : { provides: [], injects: [] };
|
|
2678
|
+
const piBlocks = buildProvideInject(provides, injects, data, computeds);
|
|
2679
|
+
if (piEnabled && (provides.length || injects.length)) {
|
|
2680
|
+
trace?.add("script/provide-inject", {
|
|
2681
|
+
before: `${provides.map((p) => `provide(${JSON.stringify(p.key)}, \u2026)`).join("\u3001")}${provides.length && injects.length ? "\uFF1B" : ""}${injects.map((i) => `const ${i.name} = inject(${JSON.stringify(i.key)})`).join("\u3001")}`,
|
|
2682
|
+
after: "\u9875\u9762 onLoad / \u7EC4\u4EF6 created+attached \u6CE8\u5165 getApp().__proteusProvides \u5168\u5C40\u6CE8\u518C\u8868\u8BFB\u5199\uFF08MVP \u503C\u5FEB\u7167\uFF0C\u975E\u54CD\u5E94\u5F0F\uFF09"
|
|
2683
|
+
});
|
|
2684
|
+
}
|
|
2685
|
+
const providedRefs = piEnabled ? piBlocks.provideRefs : /* @__PURE__ */ new Map();
|
|
2686
|
+
const hasInjects = piEnabled && injects.length > 0;
|
|
2687
|
+
const transitions = extra.transitions ?? [];
|
|
2688
|
+
const transitionToggle = /* @__PURE__ */ new Map();
|
|
2689
|
+
for (const t of transitions) transitionToggle.set(t.ref, `proteusTransitionToggle${t.index}`);
|
|
2690
|
+
const dataExtra = {};
|
|
2691
|
+
for (const t of transitions) {
|
|
2692
|
+
dataExtra[`__tv${t.index}`] = data[t.ref];
|
|
2693
|
+
dataExtra[`__tl${t.index}`] = false;
|
|
2694
|
+
}
|
|
2695
|
+
const storeBindings = extra.storeBindings ?? [];
|
|
2696
|
+
const storeVar = runtimeInits.find((i) => i.name === "store" && /^use\w*Store\(/.test(i.call))?.name;
|
|
2697
|
+
const storeBindingInit = storeVar && storeBindings.length ? storeBindingLine(storeBindings, storeVar) : "";
|
|
2698
|
+
if (storeBindingInit) {
|
|
2699
|
+
trace?.add("script/store-binding", {
|
|
2700
|
+
before: `\u6A21\u677F {{ store.${storeBindings.join(" }} / {{ store.")} }}`,
|
|
2701
|
+
after: `onLoad\uFF1Athis.setData(\u6620\u5C04) + store.$subscribe \u2192 setData\uFF08Pinia MP \u7ED1\u5B9A\uFF0Cpinia-plan 12 P1\uFF09`
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2704
|
+
const lines = [];
|
|
2705
|
+
if (extra.file) lines.push(`// ${extra.file}\uFF08Proteus mp-transform \u7F16\u8BD1\u4EA7\u7269\uFF09`);
|
|
2706
|
+
lines.push("// AUTO-GENERATED by vite-plugin-mp-transform.ts. DO NOT EDIT.", "");
|
|
2707
|
+
if (requireLines.length) lines.push(...requireLines, "");
|
|
2708
|
+
lines.push(extra.isComponent ? "Component({" : "Page({");
|
|
2709
|
+
const methodLines = [];
|
|
2710
|
+
const methodMappings = [];
|
|
2711
|
+
const pushMethod = (line, srcLine) => {
|
|
2712
|
+
const start = methodLines.length;
|
|
2713
|
+
methodLines.push(line);
|
|
2714
|
+
if (srcLine !== void 0) {
|
|
2715
|
+
const sub = line.split("\n");
|
|
2716
|
+
for (let i = 0; i < sub.length; i++) methodMappings.push({ out: start + i, src: srcLine + i });
|
|
2717
|
+
}
|
|
2718
|
+
};
|
|
2719
|
+
const methodNames = new Set(Object.keys(methods));
|
|
2720
|
+
const bridgeHooks = {
|
|
2721
|
+
hasOnPageScroll: /onPageScroll\s*\(/.test(source),
|
|
2722
|
+
hasOnReachBottom: /onReachBottom\s*\(/.test(source),
|
|
2723
|
+
hasOnPullDownRefresh: /onPullDownRefresh\s*\(/.test(source),
|
|
2724
|
+
hasPageScrollTo: /wx\.pageScrollTo\s*\(/.test(source)
|
|
2725
|
+
};
|
|
2726
|
+
if (!extra.isComponent && !disabled.has("page/scroll-bridge")) {
|
|
2727
|
+
if (bridgeHooks.hasOnPullDownRefresh) dataExtra.__proteusRefreshing = false;
|
|
2728
|
+
if (bridgeHooks.hasPageScrollTo) dataExtra.__proteusPageScrollTop = 0;
|
|
2729
|
+
}
|
|
2730
|
+
const dataEntries = [...Object.entries(data), ...Object.entries(dataExtra)];
|
|
2731
|
+
if (dataEntries.length) {
|
|
2732
|
+
lines.push(" data: {");
|
|
2733
|
+
for (const [k, v] of dataEntries) lines.push(` ${k}: ${JSON.stringify(v)},`);
|
|
2734
|
+
lines.push(" },");
|
|
2735
|
+
}
|
|
2736
|
+
const propEntries = Object.entries(props);
|
|
2737
|
+
if (extra.isComponent) {
|
|
2738
|
+
const allProps = { ...props };
|
|
2739
|
+
if (!disabled.has("component/root-class")) {
|
|
2740
|
+
allProps.rootClass = { type: "String", value: "" };
|
|
2741
|
+
}
|
|
2742
|
+
const entries = Object.entries(allProps);
|
|
2743
|
+
if (entries.length) {
|
|
2744
|
+
lines.push(" properties: {");
|
|
2745
|
+
for (const [k, p] of entries) {
|
|
2746
|
+
lines.push(` ${k}: { type: ${p.type}${p.value !== void 0 ? `, value: ${JSON.stringify(p.value)}` : ""} },`);
|
|
2747
|
+
}
|
|
2748
|
+
lines.push(" },");
|
|
2749
|
+
}
|
|
2750
|
+
} else if (propEntries.length) {
|
|
2751
|
+
lines.push(" properties: {");
|
|
2752
|
+
for (const [k, p] of propEntries) {
|
|
2753
|
+
lines.push(` ${k}: { type: ${p.type}${p.value !== void 0 ? `, value: ${JSON.stringify(p.value)}` : ""} },`);
|
|
2754
|
+
}
|
|
2755
|
+
lines.push(" },");
|
|
2756
|
+
}
|
|
2757
|
+
const vmodelDisabled = disabled.has("script/vmodel-handler");
|
|
2758
|
+
for (const name of vModelBindings) {
|
|
2759
|
+
if (!vmodelDisabled) {
|
|
2760
|
+
methodNames.add(`proteusOn${capitalize2(name)}Input`);
|
|
2761
|
+
pushMethod(` proteusOn${capitalize2(name)}Input(e) { this.setData({ ${name}: e.detail.value }) },`);
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
if (vModelBindings.length && !vmodelDisabled) {
|
|
2765
|
+
trace?.add("script/vmodel-handler", {
|
|
2766
|
+
before: `v-model="${vModelBindings.join('", "')}"`,
|
|
2767
|
+
after: `proteusOn${vModelBindings.map(capitalize2).join(" / proteusOn")}Input\uFF08setData \u56DE\u5199\uFF09`
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2770
|
+
const propWatches = Object.values(watches).filter((w) => w.propField);
|
|
2771
|
+
if (extra.isComponent && propWatches.length) {
|
|
2772
|
+
lines.push(" observers: {");
|
|
2773
|
+
for (const w of propWatches) {
|
|
2774
|
+
const observerBody = rewriteBareMethodCalls(rewriteRefAccess(w.body, refNames, trace, disabled, computeds, watches, emitEnabled, propsVar, providedRefs, transitionToggle), methodNames);
|
|
2775
|
+
const bodyLines = observerBody.split("\n");
|
|
2776
|
+
lines.push(` ${w.propField}(n, o) {`);
|
|
2777
|
+
for (const bl of bodyLines) lines.push(` ${bl}`);
|
|
2778
|
+
lines.push(" },");
|
|
2779
|
+
}
|
|
2780
|
+
lines.push(" },");
|
|
2781
|
+
}
|
|
2782
|
+
if (extra.usesNavigate && !disabled.has("script/nav-handler")) {
|
|
2783
|
+
trace?.add("script/nav-handler", { before: "<a href> / <router-link>", after: "proteusNavigateTo(e)\uFF08data-url \u2192 wx.navigateTo\uFF09" });
|
|
2784
|
+
methodNames.add("proteusNavigateTo");
|
|
2785
|
+
methodLines.push(
|
|
2786
|
+
" proteusNavigateTo(e) {",
|
|
2787
|
+
" const ds = e.currentTarget.dataset",
|
|
2788
|
+
' const url = String(ds.url || "")',
|
|
2789
|
+
...extra.debug ? [` console.log('[proteus][nav] tap', JSON.stringify(ds), Date.now())`] : [],
|
|
2790
|
+
" if (!url) return",
|
|
2791
|
+
...extra.debug ? [` console.log('[proteus][nav] navigateTo', url, Date.now())`] : [],
|
|
2792
|
+
" const nav = {",
|
|
2793
|
+
" url: url,",
|
|
2794
|
+
...extra.debug ? [` success: function () { console.log('[proteus][nav] navigateTo success', url, Date.now()) },`] : [],
|
|
2795
|
+
" fail: function (err) {",
|
|
2796
|
+
...extra.debug ? [` console.warn('[proteus][nav] navigateTo fail', JSON.stringify(err), Date.now())`] : [],
|
|
2797
|
+
" if (ds.routeType) wx.navigateTo({ url: url })",
|
|
2798
|
+
" }",
|
|
2799
|
+
" }",
|
|
2800
|
+
" if (ds.routeType) nav.routeType = ds.routeType",
|
|
2801
|
+
" wx.navigateTo(nav)",
|
|
2802
|
+
" },"
|
|
2803
|
+
);
|
|
2804
|
+
}
|
|
2805
|
+
if (lifecycles.onReady) {
|
|
2806
|
+
lines.push(` onReady() {
|
|
2807
|
+
${indentBody(rewriteBareMethodCalls(rewriteRefAccess(lifecycles.onReady, refNames, trace, disabled, computeds, watches, emitEnabled, propsVar, providedRefs, transitionToggle), methodNames))}
|
|
2808
|
+
},`);
|
|
2809
|
+
} else if (extra.debug) {
|
|
2810
|
+
lines.push(` onReady() {
|
|
2811
|
+
console.log('[proteus][page] onReady ${extra.file ?? ""}', Date.now())
|
|
2812
|
+
},`);
|
|
2813
|
+
}
|
|
2814
|
+
const storeDisposeLine = storeVar && !extra.isComponent ? `if (this.${storeVar} && this.${storeVar}.$dispose) { this.${storeVar}.$dispose(); this.${storeVar} = null }` : "";
|
|
2815
|
+
const needsPageCleanup = hasInjects || providedRefs.size > 0 || Boolean(storeDisposeLine);
|
|
2816
|
+
const pageCleanupLine = "const __reg = getApp().__proteusProvides; if (__reg && this.__proteusPageId) delete __reg[this.__proteusPageId]";
|
|
2817
|
+
const unsubLine = hasInjects ? "this.proteusUnsubscribeProvide()" : "";
|
|
2818
|
+
if (lifecycles.onUnload) {
|
|
2819
|
+
const unloadBody = rewriteBareMethodCalls(rewriteRefAccess(lifecycles.onUnload, refNames, trace, disabled, computeds, watches, emitEnabled, propsVar, providedRefs, transitionToggle), methodNames);
|
|
2820
|
+
const isComp = extra.isComponent;
|
|
2821
|
+
const pre = isComp ? [unsubLine].filter(Boolean).join("\n") : [unsubLine, storeDisposeLine, pageCleanupLine].filter(Boolean).join("\n");
|
|
2822
|
+
const hook = isComp ? "detached" : "onUnload";
|
|
2823
|
+
lines.push(` ${hook}() {
|
|
2824
|
+
${indentBody(pre ? `${pre}
|
|
2825
|
+
${unloadBody}` : unloadBody)}
|
|
2826
|
+
},`);
|
|
2827
|
+
} else if (needsPageCleanup && !extra.isComponent) {
|
|
2828
|
+
lines.push(` onUnload() {
|
|
2829
|
+
${indentBody([unsubLine, storeDisposeLine, pageCleanupLine].filter(Boolean).join("\n"))}
|
|
2830
|
+
},`);
|
|
2831
|
+
}
|
|
2832
|
+
if (extra.isComponent) {
|
|
2833
|
+
if (piBlocks.provide) {
|
|
2834
|
+
lines.push(` created() {
|
|
2835
|
+
${indentBody(piBlocks.provide)}
|
|
2836
|
+
},`);
|
|
2837
|
+
}
|
|
2838
|
+
const initLines = [computedInitLine(computeds), runtimeInitLine(runtimeInits), storeBindingInit, immediateWatchLine(watches), piBlocks.inject].filter(Boolean);
|
|
2839
|
+
if (initLines.length) {
|
|
2840
|
+
lines.push(` attached() {
|
|
2841
|
+
${indentBody(initLines.join("\n"))}
|
|
2842
|
+
},`);
|
|
2843
|
+
}
|
|
2844
|
+
if (hasInjects && !lifecycles.onUnload) {
|
|
2845
|
+
lines.push(` detached() {
|
|
2846
|
+
this.proteusUnsubscribeProvide()
|
|
2847
|
+
},`);
|
|
2848
|
+
}
|
|
2849
|
+
} else if (lifecycles.onLoad) {
|
|
2850
|
+
const initLines = [computedInitLine(computeds), runtimeInitLine(runtimeInits), storeBindingInit, immediateWatchLine(watches), piBlocks.page].filter(Boolean);
|
|
2851
|
+
const body = rewriteBareMethodCalls(rewriteRefAccess(lifecycles.onLoad, refNames, trace, disabled, computeds, watches, emitEnabled, propsVar, providedRefs, transitionToggle), methodNames);
|
|
2852
|
+
lines.push(` onLoad(options) {
|
|
2853
|
+
${indentBody(initLines.length ? `${initLines.join("\n")}
|
|
2854
|
+
${body}` : body)}
|
|
2855
|
+
},`);
|
|
2856
|
+
} else {
|
|
2857
|
+
if (!disabled.has("script/onload-params")) {
|
|
2858
|
+
trace?.add("script/onload-params", { before: "\uFF08\u65E0\u663E\u5F0F onLoad\uFF09", after: "onLoad(options) \u2192 decodeURIComponent + JSON.parse + setData" });
|
|
2859
|
+
const initLines = [computedInitLine(computeds), runtimeInitLine(runtimeInits), storeBindingInit, immediateWatchLine(watches), piBlocks.page].filter(Boolean);
|
|
2860
|
+
lines.push(
|
|
2861
|
+
[
|
|
2862
|
+
" onLoad(options) {",
|
|
2863
|
+
...extra.debug ? [` console.log('[proteus][page] onLoad ${extra.file ?? ""}', JSON.stringify(options), Date.now())`] : [],
|
|
2864
|
+
...initLines.map((l) => l.split("\n").map((sl) => ` ${sl}`).join("\n")),
|
|
2865
|
+
" const params = {}",
|
|
2866
|
+
" const keys = Object.keys(options || {})",
|
|
2867
|
+
" for (let i = 0; i < keys.length; i++) {",
|
|
2868
|
+
" const k = keys[i]",
|
|
2869
|
+
" const v = options[k]",
|
|
2870
|
+
" const s = decodeURIComponent(v)",
|
|
2871
|
+
' try { params[k] = (s.startsWith("{") || s.startsWith("[")) ? JSON.parse(s) : s } catch { params[k] = s }',
|
|
2872
|
+
" }",
|
|
2873
|
+
" this.setData(params)",
|
|
2874
|
+
" },"
|
|
2875
|
+
].join("\n")
|
|
2876
|
+
);
|
|
2877
|
+
} else if (piBlocks.page) {
|
|
2878
|
+
lines.push(` onLoad(options) {
|
|
2879
|
+
${indentBody(piBlocks.page)}
|
|
2880
|
+
},`);
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
const lineMappings = [];
|
|
2884
|
+
const pushMapped = (line, srcLine) => {
|
|
2885
|
+
const start = lines.length;
|
|
2886
|
+
lines.push(line);
|
|
2887
|
+
const sub = line.split("\n");
|
|
2888
|
+
for (let i = 0; i < sub.length; i++) lineMappings.push({ out: start + i, src: srcLine + i });
|
|
2889
|
+
};
|
|
2890
|
+
for (const [name, m] of Object.entries(methods)) {
|
|
2891
|
+
if (extra.debug) methodLines.push(` // @${m.line} ${name}()`);
|
|
2892
|
+
const braceIdx = m.src.indexOf("{");
|
|
2893
|
+
const sig = m.src.slice(0, braceIdx + 1);
|
|
2894
|
+
const body = m.src.slice(braceIdx + 1);
|
|
2895
|
+
pushMethod(` ${sig + rewriteBareMethodCalls(rewriteRefAccess(body, refNames, trace, disabled, computeds, watches, emitEnabled, propsVar, providedRefs, transitionToggle), methodNames)},`, m.line);
|
|
2896
|
+
}
|
|
2897
|
+
if (!extra.isComponent && !disabled.has("page/scroll-bridge")) {
|
|
2898
|
+
if (bridgeHooks.hasOnPageScroll) {
|
|
2899
|
+
methodNames.add("proteusPageScroll");
|
|
2900
|
+
methodLines.push(' proteusPageScroll(e) { if (typeof this.onPageScroll === "function") this.onPageScroll({ scrollTop: e.detail.scrollTop, scrollLeft: e.detail.scrollLeft }) },');
|
|
2901
|
+
}
|
|
2902
|
+
if (bridgeHooks.hasOnReachBottom) {
|
|
2903
|
+
methodNames.add("proteusReachBottom");
|
|
2904
|
+
methodLines.push(' proteusReachBottom() { if (typeof this.onReachBottom === "function") this.onReachBottom() },');
|
|
2905
|
+
}
|
|
2906
|
+
if (bridgeHooks.hasOnPullDownRefresh) {
|
|
2907
|
+
methodNames.add("proteusPullDownRefresh");
|
|
2908
|
+
methodLines.push(
|
|
2909
|
+
' proteusPullDownRefresh() { const __r = typeof this.onPullDownRefresh === "function" ? this.onPullDownRefresh() : undefined; this.setData({ __proteusRefreshing: false }); return __r },'
|
|
2910
|
+
);
|
|
2911
|
+
}
|
|
2912
|
+
if (bridgeHooks.hasPageScrollTo) {
|
|
2913
|
+
methodNames.add("proteusPageScrollTo");
|
|
2914
|
+
methodLines.push(" proteusPageScrollTo(opts) { this.setData({ __proteusPageScrollTop: opts.scrollTop }) },");
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
for (const w of Object.values(watches)) {
|
|
2918
|
+
if (w.propField && !w.immediate) continue;
|
|
2919
|
+
methodNames.add(`proteusWatch${w.id}`);
|
|
2920
|
+
const src = `proteusWatch${w.id}(${w.params.join(", ")}) {
|
|
2921
|
+
${indentBody(rewriteBareMethodCalls(rewriteRefAccess(w.body, refNames, trace, disabled, computeds, watches, emitEnabled, propsVar, providedRefs, transitionToggle), methodNames))}
|
|
2922
|
+
},`;
|
|
2923
|
+
pushMethod(` ${src}`, w.line);
|
|
2924
|
+
}
|
|
2925
|
+
for (const [cname, c] of Object.entries(computeds)) {
|
|
2926
|
+
if (!c.setter) continue;
|
|
2927
|
+
methodNames.add(`proteusSet${capitalize2(cname)}`);
|
|
2928
|
+
const src = `proteusSet${capitalize2(cname)}(${c.setter.param}) {
|
|
2929
|
+
${indentBody(rewriteBareMethodCalls(rewriteRefAccess(c.setter.body, refNames, trace, disabled, computeds, watches, emitEnabled, propsVar, providedRefs, transitionToggle), methodNames))}
|
|
2930
|
+
},`;
|
|
2931
|
+
pushMethod(` ${src}`, 1);
|
|
2932
|
+
}
|
|
2933
|
+
for (const h of extra.selfHandlers ?? []) {
|
|
2934
|
+
methodNames.add(`proteusSelf${capitalize2(h)}`);
|
|
2935
|
+
methodLines.push(` proteusSelf${capitalize2(h)}(e) {`);
|
|
2936
|
+
methodLines.push(` if (e.target === e.currentTarget) {`);
|
|
2937
|
+
methodLines.push(` this.${h}(e)`);
|
|
2938
|
+
methodLines.push(` }`);
|
|
2939
|
+
methodLines.push(` },`);
|
|
2940
|
+
}
|
|
2941
|
+
for (const h of extra.onceHandlers ?? []) {
|
|
2942
|
+
methodNames.add(`proteusOnce${capitalize2(h)}`);
|
|
2943
|
+
methodLines.push(` proteusOnce${capitalize2(h)}(e) {`);
|
|
2944
|
+
methodLines.push(` if (!this.data.__once${capitalize2(h)}) {`);
|
|
2945
|
+
methodLines.push(` this.data.__once${capitalize2(h)} = true`);
|
|
2946
|
+
methodLines.push(` this.${h}(e)`);
|
|
2947
|
+
methodLines.push(` }`);
|
|
2948
|
+
methodLines.push(` },`);
|
|
2949
|
+
}
|
|
2950
|
+
for (const ih of extra.inlineHandlers ?? []) {
|
|
2951
|
+
if (disabled.has("event/inline-expression")) continue;
|
|
2952
|
+
methodNames.add(ih.name);
|
|
2953
|
+
pushMethod(` ${ih.name}(e) {`, 1);
|
|
2954
|
+
pushMethod(` ${ih.code}`, 1);
|
|
2955
|
+
pushMethod(` },`, 1);
|
|
2956
|
+
}
|
|
2957
|
+
if (providedRefs.size > 0) {
|
|
2958
|
+
methodNames.add("proteusSyncProvide");
|
|
2959
|
+
methodLines.push(
|
|
2960
|
+
" proteusSyncProvide(key, ref) {",
|
|
2961
|
+
" const reg = getApp().__proteusProvides",
|
|
2962
|
+
" const p = reg && this.__proteusPageId ? reg[this.__proteusPageId] : (reg || {})",
|
|
2963
|
+
" if (!p) return",
|
|
2964
|
+
" p[key] = this.data[ref]",
|
|
2965
|
+
" const subs = p.__subs && p.__subs[key]",
|
|
2966
|
+
" if (!subs) return",
|
|
2967
|
+
" for (let i = 0; i < subs.length; i++) subs[i]()",
|
|
2968
|
+
" },"
|
|
2969
|
+
);
|
|
2970
|
+
}
|
|
2971
|
+
if (hasInjects) {
|
|
2972
|
+
methodNames.add("proteusUnsubscribeProvide");
|
|
2973
|
+
methodLines.push(
|
|
2974
|
+
" proteusUnsubscribeProvide() {",
|
|
2975
|
+
" const reg = getApp().__proteusProvides",
|
|
2976
|
+
" const p = reg && this.__proteusPageId ? reg[this.__proteusPageId] : (reg || {})",
|
|
2977
|
+
" if (!p || !p.__subs || !this.__proteusSubs) return",
|
|
2978
|
+
" for (let i = 0; i < this.__proteusSubs.length; i++) {",
|
|
2979
|
+
" const s = this.__proteusSubs[i]",
|
|
2980
|
+
" const list = p.__subs[s.k]",
|
|
2981
|
+
" if (list) {",
|
|
2982
|
+
" const idx = list.indexOf(s.fn)",
|
|
2983
|
+
" if (idx >= 0) list.splice(idx, 1)",
|
|
2984
|
+
" }",
|
|
2985
|
+
" }",
|
|
2986
|
+
" this.__proteusSubs = null",
|
|
2987
|
+
" },"
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2990
|
+
for (const t of transitions) {
|
|
2991
|
+
const dur = { fade: 250, "slide-up": 320, scale: 400 }[t.tName] ?? 250;
|
|
2992
|
+
methodNames.add(`proteusTransitionToggle${t.index}`);
|
|
2993
|
+
methodLines.push(
|
|
2994
|
+
` proteusTransitionToggle${t.index}() {`,
|
|
2995
|
+
` if (this.data.${t.ref}) {`,
|
|
2996
|
+
` clearTimeout(this.__tlTimer${t.index})`,
|
|
2997
|
+
` this.__tlTimer${t.index} = null`,
|
|
2998
|
+
` this.setData({ __tv${t.index}: true, __tl${t.index}: false })`,
|
|
2999
|
+
` return`,
|
|
3000
|
+
` }`,
|
|
3001
|
+
` if (this.__tlTimer${t.index}) return`,
|
|
3002
|
+
` this.setData({ __tl${t.index}: true })`,
|
|
3003
|
+
` this.__tlTimer${t.index} = setTimeout(() => {`,
|
|
3004
|
+
` this.__tlTimer${t.index} = null`,
|
|
3005
|
+
` this.setData({ __tv${t.index}: false })`,
|
|
3006
|
+
` }, ${dur})`,
|
|
3007
|
+
` },`
|
|
3008
|
+
);
|
|
3009
|
+
}
|
|
3010
|
+
if (extra.isComponent) {
|
|
3011
|
+
lines.push(" methods: {");
|
|
3012
|
+
for (const ml of methodLines) {
|
|
3013
|
+
for (const sl of ml.split("\n")) lines.push(` ${sl}`);
|
|
3014
|
+
}
|
|
3015
|
+
lines.push(" },");
|
|
3016
|
+
} else {
|
|
3017
|
+
lines.push(...methodLines);
|
|
3018
|
+
}
|
|
3019
|
+
const methodsBase = lines.length;
|
|
3020
|
+
for (const mm of methodMappings) lineMappings.push({ out: methodsBase + mm.out, src: mm.src });
|
|
3021
|
+
lines.push("})");
|
|
3022
|
+
trace?.add("script/component-mode", {
|
|
3023
|
+
before: "SFC",
|
|
3024
|
+
after: extra.isComponent ? "Component({ ... })" : "Page({ ... })"
|
|
3025
|
+
});
|
|
3026
|
+
trace?.add("script/es5-safe", { before: "?? / ?. / \u89E3\u6784 / \u5C55\u5F00", after: "\u663E\u5F0F null \u4E09\u5143 / \u7D22\u5F15\u5FAA\u73AF / \u76F4\u63A5\u8D4B\u503C" });
|
|
3027
|
+
for (const w of warnings) console.warn(`[mp-transform] ${w}`);
|
|
3028
|
+
const js = lines.join("\n") + "\n";
|
|
3029
|
+
const jsFinal = js.replace(/\bwx\.pageScrollTo\s*\(/g, "this.proteusPageScrollTo(");
|
|
3030
|
+
const sourcemap = buildSourceMap(jsFinal, extra.file, source, lineMappings);
|
|
3031
|
+
return { js: jsFinal, warnings, sourcemap };
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
// src/style.ts
|
|
3035
|
+
function makeTagSelectorRe(tagMap, semanticClass) {
|
|
3036
|
+
const names = [...Object.keys(tagMap), ...Object.keys(semanticClass)].sort((a, b) => b.length - a.length);
|
|
3037
|
+
return new RegExp(
|
|
3038
|
+
`(?<![\\w-.#:\\[*])(?:${names.join("|")})(?=[\\s.#:\\[>+,~)(\\u0000]|$)`,
|
|
3039
|
+
"g"
|
|
3040
|
+
);
|
|
3041
|
+
}
|
|
3042
|
+
function rewriteTagSelectors(selector, res, tagRe) {
|
|
3043
|
+
const attrs = [];
|
|
3044
|
+
const masked = selector.replace(/\[[^\]]*\]/g, (m) => {
|
|
3045
|
+
attrs.push(m);
|
|
3046
|
+
return `\0${attrs.length - 1}\0`;
|
|
3047
|
+
});
|
|
3048
|
+
const rewritten = masked.replace(tagRe, (tag) => {
|
|
3049
|
+
const semantic = res.semanticClass[tag];
|
|
3050
|
+
if (semantic) return `.${semantic}`;
|
|
3051
|
+
return res.tagMap[tag] ?? tag;
|
|
3052
|
+
});
|
|
3053
|
+
return rewritten.replace(/\u0000(\d+)\u0000/g, (_m, i) => attrs[Number(i)]);
|
|
3054
|
+
}
|
|
3055
|
+
function splitTopLevelSelectors(s) {
|
|
3056
|
+
const out = [];
|
|
3057
|
+
let depth = 0;
|
|
3058
|
+
let cur = "";
|
|
3059
|
+
for (const ch of s) {
|
|
3060
|
+
if (ch === "(" || ch === "[") depth++;
|
|
3061
|
+
else if (ch === ")" || ch === "]") depth = Math.max(0, depth - 1);
|
|
3062
|
+
if (ch === "," && depth === 0) {
|
|
3063
|
+
out.push(cur);
|
|
3064
|
+
cur = "";
|
|
3065
|
+
continue;
|
|
3066
|
+
}
|
|
3067
|
+
cur += ch;
|
|
3068
|
+
}
|
|
3069
|
+
out.push(cur);
|
|
3070
|
+
return out;
|
|
3071
|
+
}
|
|
3072
|
+
function rewriteSelectorTags(css, res, tagRe) {
|
|
3073
|
+
return css.replace(/([^{}]+)\{/g, (_m, sel) => `${rewriteTagSelectors(sel, res, tagRe)}{`);
|
|
3074
|
+
}
|
|
3075
|
+
var BASE_SEMANTIC_WXSS = [
|
|
3076
|
+
// ★16-progress-skyline-degrade:<progress> 降级自定义进度条(Skyline 不支持原生 progress)
|
|
3077
|
+
".proteus-progress { display: flex; align-items: center; gap: 8px; }",
|
|
3078
|
+
".proteus-progress-track { flex: 1; background: #ebedf0; border-radius: 2px; overflow: hidden; }",
|
|
3079
|
+
".proteus-progress-inner { height: 100%; border-radius: 2px; background: #07c160; }",
|
|
3080
|
+
".proteus-progress-info { font-size: 13px; color: #666; min-width: 34px; text-align: right; }",
|
|
3081
|
+
// ★2026-08:行内场景自动 flex row(layout/auto-flex-row 模板注入 proteus-flex-row 类)——
|
|
3082
|
+
// Skyline 不支持 inline 布局,text + 行内控件同行必须 flex row(双端一致)
|
|
3083
|
+
".proteus-flex-row { display: flex; flex-direction: row; align-items: center; }",
|
|
3084
|
+
".proteus-h1 { display: block; font-size: 64rpx; font-weight: 700; margin: 0 0 0.67em; }",
|
|
3085
|
+
".proteus-h2 { display: block; font-size: 48rpx; font-weight: 700; margin: 0 0 0.83em; }",
|
|
3086
|
+
".proteus-h3 { display: block; font-size: 36rpx; font-weight: 700; margin: 0 0 1em; }",
|
|
3087
|
+
".proteus-h4 { display: block; font-size: 32rpx; font-weight: 700; margin: 0 0 1.33em; }",
|
|
3088
|
+
".proteus-h5 { display: block; font-size: 28rpx; font-weight: 700; margin: 0 0 1.67em; }",
|
|
3089
|
+
".proteus-h6 { display: block; font-size: 24rpx; font-weight: 700; margin: 0 0 2.33em; }",
|
|
3090
|
+
".proteus-p { display: block; margin: 0 0 1em; }",
|
|
3091
|
+
".proteus-a { color: #1a7af8; text-decoration: underline; }"
|
|
3092
|
+
].join("\n");
|
|
3093
|
+
var TRANSITION_WXSS = [
|
|
3094
|
+
// ★Batch 5:离开动画(__tl{i} 插值 class 切换;forwards 保持末帧直到延迟移除)
|
|
3095
|
+
".proteus-transition-fade-leave { animation: proteus-fade-out 0.25s ease forwards; }",
|
|
3096
|
+
".proteus-transition-slide-up-leave { animation: proteus-slide-up-out 0.32s cubic-bezier(0.35, 0.91, 0.33, 0.97) forwards; }",
|
|
3097
|
+
".proteus-transition-scale-leave { animation: proteus-scale-out 0.4s cubic-bezier(0.35, 0.91, 0.33, 0.97) forwards; }",
|
|
3098
|
+
".proteus-transition-fade { animation: proteus-fade-in 0.25s ease; }",
|
|
3099
|
+
".proteus-transition-slide-up { animation: proteus-slide-up-in 0.32s cubic-bezier(0.35, 0.91, 0.33, 0.97); }",
|
|
3100
|
+
".proteus-transition-scale { animation: proteus-scale-in 0.4s cubic-bezier(0.35, 0.91, 0.33, 0.97); }",
|
|
3101
|
+
"@keyframes proteus-fade-in { from { opacity: 0; } to { opacity: 1; } }",
|
|
3102
|
+
"@keyframes proteus-fade-out { from { opacity: 1; } to { opacity: 0; } }",
|
|
3103
|
+
"@keyframes proteus-slide-up-in { from { transform: translateY(20%); opacity: 0; } to { transform: translateY(0); opacity: 1; } }",
|
|
3104
|
+
"@keyframes proteus-slide-up-out { from { transform: translateY(0); opacity: 1; } to { transform: translateY(20%); opacity: 0; } }",
|
|
3105
|
+
"@keyframes proteus-scale-in { from { transform: scale(0.92) translateY(4%); opacity: 0.8; } to { transform: scale(1) translateY(0); opacity: 1; } }",
|
|
3106
|
+
"@keyframes proteus-scale-out { from { transform: scale(1) translateY(0); opacity: 1; } to { transform: scale(0.92) translateY(4%); opacity: 0.8; } }"
|
|
3107
|
+
].join("\n");
|
|
3108
|
+
function countSelectorRewrites(css, res) {
|
|
3109
|
+
const semanticKeys = Object.keys(res.semanticClass);
|
|
3110
|
+
const tagKeys = Object.keys(res.tagMap).filter((k) => !semanticKeys.includes(k));
|
|
3111
|
+
const lookahead = "[\\s.#:\\[>+,~)(\\u0000]|$";
|
|
3112
|
+
const semanticRe = new RegExp(`(?<![\\w-.\\#:\\[*])(?:${semanticKeys.join("|")})(?=${lookahead})`, "g");
|
|
3113
|
+
const tagRePlain = new RegExp(`(?<![\\w-.\\#:\\[*])(?:${tagKeys.join("|")})(?=${lookahead})`, "g");
|
|
3114
|
+
return { tag: (css.match(tagRePlain) ?? []).length, semantic: (css.match(semanticRe) ?? []).length };
|
|
3115
|
+
}
|
|
3116
|
+
function transformStyleToWxss(source, opts = { px2rpx: true, rpxRatio: 2 }) {
|
|
3117
|
+
const trace = opts.trace;
|
|
3118
|
+
const res = resolveOverrides(opts.rules);
|
|
3119
|
+
const tagRe = makeTagSelectorRe(res.tagMap, res.semanticClass);
|
|
3120
|
+
const scopeId = opts.scopeId;
|
|
3121
|
+
const doScope = Boolean(scopeId) && !res.disabled.has("style/scoped-css");
|
|
3122
|
+
if (doScope && scopeId) trace?.add("style/scoped-css", { before: "<style scoped>", after: `\u9009\u62E9\u5668\u672B\u5C3E\u8FFD\u52A0 [${scopeId}]\uFF08:deep() \u53BB\u5305\u88C5\uFF09` });
|
|
3123
|
+
const injectBase = !res.disabled.has("style/semantic-base-wxss");
|
|
3124
|
+
let css = injectBase ? `${BASE_SEMANTIC_WXSS}
|
|
3125
|
+
${source}` : source;
|
|
3126
|
+
if (injectBase) trace?.add("style/semantic-base-wxss", { before: "h1-h6/p/a \u65E0 UA \u6837\u5F0F", after: ".proteus-h1~h6/.proteus-p/.proteus-a \u57FA\u7840 WXSS\uFF08\u6CE8\u5165\u5728\u7528\u6237\u6837\u5F0F\u4E4B\u524D\uFF09" });
|
|
3127
|
+
const injectTransition = opts.usesTransition === true && !res.disabled.has("transition/animation-wxss");
|
|
3128
|
+
if (injectTransition) css = `${css}
|
|
3129
|
+
|
|
3130
|
+
${TRANSITION_WXSS}`;
|
|
3131
|
+
const doSelectorRewrite = !res.disabled.has("style/selector-tag") && !res.disabled.has("style/selector-semantic");
|
|
3132
|
+
const counts = doSelectorRewrite ? countSelectorRewrites(css, res) : { tag: 0, semantic: 0 };
|
|
3133
|
+
if (doSelectorRewrite) css = rewriteSelectorTags(css, res, tagRe);
|
|
3134
|
+
if (counts.tag > 0) trace?.add("style/selector-tag", { before: `\u9009\u62E9\u5668\u542B HTML \u6807\u7B7E\uFF08${counts.tag} \u5904\uFF09`, after: "\u6620\u5C04\u4E3A\u5C0F\u7A0B\u5E8F\u6807\u7B7E\uFF08div \u2192 view\uFF09" });
|
|
3135
|
+
if (counts.semantic > 0) trace?.add("style/selector-semantic", { before: `h1-h6/p/a \u9009\u62E9\u5668\uFF08${counts.semantic} \u5904\uFF09`, after: ".proteus-* \u7C7B\u9009\u62E9\u5668\uFF08\u907F\u514D\u540C\u7279\u5F02\u6027\u8986\u76D6\uFF09" });
|
|
3136
|
+
const doPx2rpx = opts.px2rpx && !res.disabled.has("style/px-to-rpx");
|
|
3137
|
+
const pxCount = (css.match(/(\d+(?:\.\d+)?)px\b/g) ?? []).length;
|
|
3138
|
+
if (doPx2rpx) {
|
|
3139
|
+
const pxCtx = {
|
|
3140
|
+
input: css,
|
|
3141
|
+
options: { rpxRatio: opts.rpxRatio }
|
|
3142
|
+
};
|
|
3143
|
+
executeRule("style/px-to-rpx", pxCtx);
|
|
3144
|
+
css = pxCtx.output ?? css;
|
|
3145
|
+
if (pxCount > 0) trace?.add("style/px-to-rpx", { before: `${pxCount} \u5904 px`, after: `${pxCount} \u5904 rpx\uFF08rpxRatio=${opts.rpxRatio}\uFF09` });
|
|
3146
|
+
}
|
|
3147
|
+
const unsupported = [];
|
|
3148
|
+
if (!res.disabled.has("style/skyline-unsupported")) {
|
|
3149
|
+
if (/float\s*:/.test(css)) unsupported.push("float");
|
|
3150
|
+
if (/position\s*:\s*fixed\b/.test(css)) unsupported.push("position: fixed");
|
|
3151
|
+
}
|
|
3152
|
+
for (const u of unsupported) {
|
|
3153
|
+
console.warn(`[mp-transform] WXSS \u68C0\u6D4B\u5230 Skyline \u4E0D\u652F\u6301\u7684\u5C5E\u6027\uFF1A${u}\uFF08\u7F16\u8BD1\u671F\u8B66\u544A\uFF09`);
|
|
3154
|
+
trace?.add("style/skyline-unsupported", { before: u, after: "\u7F16\u8BD1\u671F\u8B66\u544A\uFF08\u4E0D\u963B\u65AD\u6784\u5EFA\uFF09" });
|
|
3155
|
+
}
|
|
3156
|
+
if (doScope && scopeId) {
|
|
3157
|
+
css = css.replace(/:deep\(([^)]*)\)/g, "$1");
|
|
3158
|
+
css = css.replace(/([^{}]+)\{/g, (m, sel) => {
|
|
3159
|
+
const s = sel.trim();
|
|
3160
|
+
if (s.startsWith("@")) return m;
|
|
3161
|
+
const parts = splitTopLevelSelectors(s);
|
|
3162
|
+
const scoped = parts.map((p) => {
|
|
3163
|
+
const t = p.trim();
|
|
3164
|
+
if (/^(from|to|\d+(?:\.\d+)?%)$/i.test(t)) return t;
|
|
3165
|
+
return suffixClassTokens(t, scopeId);
|
|
3166
|
+
}).join(", ");
|
|
3167
|
+
return `${scoped} {`;
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
return css;
|
|
3171
|
+
}
|
|
3172
|
+
function suffixClassTokens(sel, scopeId) {
|
|
3173
|
+
const attrs = [];
|
|
3174
|
+
const masked = sel.replace(/\[[^\]]*\]/g, (m) => {
|
|
3175
|
+
attrs.push(m);
|
|
3176
|
+
return `\0${attrs.length - 1}\0`;
|
|
3177
|
+
});
|
|
3178
|
+
const out = masked.replace(
|
|
3179
|
+
/\.(-?[_a-zA-Z][-_a-zA-Z0-9]*)/g,
|
|
3180
|
+
(m, name) => name.endsWith(`-${scopeId}`) ? m : `.${name}-${scopeId}`
|
|
3181
|
+
);
|
|
3182
|
+
return out.replace(/\u0000(\d+)\u0000/g, (_m, i) => attrs[Number(i)]);
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
// src/validate.ts
|
|
3186
|
+
var CompilerError = class extends Error {
|
|
3187
|
+
constructor(filename, message) {
|
|
3188
|
+
super(`[proteus-compiler] ${filename}: ${message}`);
|
|
3189
|
+
this.filename = filename;
|
|
3190
|
+
this.name = "CompilerError";
|
|
3191
|
+
}
|
|
3192
|
+
};
|
|
3193
|
+
function validateJs(js) {
|
|
3194
|
+
try {
|
|
3195
|
+
new Function(js);
|
|
3196
|
+
return { ok: true };
|
|
3197
|
+
} catch (err) {
|
|
3198
|
+
return { ok: false, error: err.message };
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
function validateWxml(wxml) {
|
|
3202
|
+
const withoutComments = wxml.replace(/<!--[\s\S]*?-->/g, "");
|
|
3203
|
+
const tagRe = /<\/?([a-zA-Z][\w-]*)(?:"[^"]*"|'[^']*'|[^>"'])*\/?>/g;
|
|
3204
|
+
const stack = [];
|
|
3205
|
+
let m;
|
|
3206
|
+
while (m = tagRe.exec(withoutComments)) {
|
|
3207
|
+
const full = m[0];
|
|
3208
|
+
const name = m[1];
|
|
3209
|
+
if (full.startsWith("</")) {
|
|
3210
|
+
const top = stack.pop();
|
|
3211
|
+
if (top !== name) {
|
|
3212
|
+
return { ok: false, error: `</${name}> \u4E0E <${top ?? "(\u65E0)"}> \u4E0D\u5339\u914D\uFF08\u4F4D\u7F6E ${m.index}\uFF09` };
|
|
3213
|
+
}
|
|
3214
|
+
} else if (!full.endsWith("/>")) {
|
|
3215
|
+
stack.push(name);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
if (stack.length) {
|
|
3219
|
+
return { ok: false, error: `<${stack[stack.length - 1]}> \u672A\u95ED\u5408` };
|
|
3220
|
+
}
|
|
3221
|
+
return { ok: true };
|
|
3222
|
+
}
|
|
3223
|
+
function assertValidResult(result, filename) {
|
|
3224
|
+
const jsCheck = validateJs(result.js);
|
|
3225
|
+
if (!jsCheck.ok) {
|
|
3226
|
+
throw new CompilerError(filename, `js \u4EA7\u7269\u8BED\u6CD5\u9519\u8BEF\uFF1A${jsCheck.error}`);
|
|
3227
|
+
}
|
|
3228
|
+
const wxmlCheck = validateWxml(result.wxml);
|
|
3229
|
+
if (!wxmlCheck.ok) {
|
|
3230
|
+
throw new CompilerError(filename, `wxml \u4EA7\u7269\u7ED3\u6784\u9519\u8BEF\uFF1A${wxmlCheck.error}`);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
// src/explain.ts
|
|
3235
|
+
import { parse as sfcParse } from "@vue/compiler-sfc";
|
|
3236
|
+
function explainTransform(source, options = {}) {
|
|
3237
|
+
const { descriptor } = sfcParse(source, { filename: options.filename ?? "anonymous.vue" });
|
|
3238
|
+
const styleOpts = { px2rpx: options.px2rpx ?? true, rpxRatio: options.rpxRatio ?? 2 };
|
|
3239
|
+
const events = [];
|
|
3240
|
+
const tplTrace = createTrace("template");
|
|
3241
|
+
const tpl = descriptor.template?.content ?? "";
|
|
3242
|
+
const tplResult = transformTemplateToWxml(tpl, {
|
|
3243
|
+
...styleOpts,
|
|
3244
|
+
filename: options.filename,
|
|
3245
|
+
annotateLines: options.annotateLines,
|
|
3246
|
+
rules: options.rules,
|
|
3247
|
+
trace: tplTrace
|
|
3248
|
+
});
|
|
3249
|
+
events.push(...tplTrace.events);
|
|
3250
|
+
const setup = descriptor.scriptSetup?.content ?? descriptor.script?.content ?? "";
|
|
3251
|
+
const scriptTrace = createTrace("script");
|
|
3252
|
+
transformScriptToPage(setup, styleOpts, {
|
|
3253
|
+
file: options.filename,
|
|
3254
|
+
isComponent: options.isComponent,
|
|
3255
|
+
vModelBindings: tplResult.vModelBindings,
|
|
3256
|
+
usesNavigate: tplResult.usesNavigate,
|
|
3257
|
+
rules: options.rules,
|
|
3258
|
+
trace: scriptTrace
|
|
3259
|
+
});
|
|
3260
|
+
events.push(...scriptTrace.events);
|
|
3261
|
+
const styleTrace = createTrace("style");
|
|
3262
|
+
transformStyleToWxss(
|
|
3263
|
+
descriptor.styles.map((s) => s.content).join("\n"),
|
|
3264
|
+
{ ...styleOpts, rules: options.rules, trace: styleTrace }
|
|
3265
|
+
);
|
|
3266
|
+
events.push(...styleTrace.events);
|
|
3267
|
+
return { filename: options.filename, events };
|
|
3268
|
+
}
|
|
3269
|
+
function formatTransformTrace(result) {
|
|
3270
|
+
const phases = ["template", "script", "style", "validate"];
|
|
3271
|
+
const blocks = [];
|
|
3272
|
+
for (const phase of phases) {
|
|
3273
|
+
const evts = result.events.filter((e) => e.phase === phase);
|
|
3274
|
+
if (!evts.length) continue;
|
|
3275
|
+
const lines = evts.map((e) => {
|
|
3276
|
+
const line = e.line != null ? `L${e.line} ` : "";
|
|
3277
|
+
const detail = [e.before, e.after].filter(Boolean).join(" \u2192 ");
|
|
3278
|
+
return ` ${line}${e.ruleId}${detail ? `\uFF1A${detail}` : ""}`;
|
|
3279
|
+
});
|
|
3280
|
+
blocks.push(`### ${phase} \u9636\u6BB5\uFF08${evts.length} \u4E2A\u51B3\u7B56\uFF09
|
|
3281
|
+
${lines.join("\n")}`);
|
|
3282
|
+
}
|
|
3283
|
+
return `# explainTransform ${result.filename ?? ""}
|
|
3284
|
+
|
|
3285
|
+
${blocks.join("\n\n")}`;
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
// src/index.ts
|
|
3289
|
+
function scopedIdFrom(filename) {
|
|
3290
|
+
let hash = 5381;
|
|
3291
|
+
for (let i = 0; i < filename.length; i++) {
|
|
3292
|
+
hash = (hash << 5) + hash + filename.charCodeAt(i) >>> 0;
|
|
3293
|
+
}
|
|
3294
|
+
return `data-v-${hash.toString(16).slice(0, 6)}`;
|
|
3295
|
+
}
|
|
3296
|
+
function compileVueSfc(source, options = {}) {
|
|
3297
|
+
const { descriptor } = sfcParse2(source, { filename: options.filename ?? "anonymous.vue" });
|
|
3298
|
+
const styleOpts = {
|
|
3299
|
+
px2rpx: options.px2rpx ?? true,
|
|
3300
|
+
rpxRatio: options.rpxRatio ?? 2,
|
|
3301
|
+
rules: options.rules
|
|
3302
|
+
};
|
|
3303
|
+
const globalStyles = descriptor.styles.filter((s) => s.attrs?.global !== void 0);
|
|
3304
|
+
const scopedStyles = descriptor.styles.filter((s) => s.attrs?.global === void 0);
|
|
3305
|
+
const hasScoped = scopedStyles.length > 0;
|
|
3306
|
+
const scopeId = hasScoped ? scopedIdFrom(options.filename ?? "anonymous.vue") : void 0;
|
|
3307
|
+
const tplTrace = createTrace("template");
|
|
3308
|
+
const tpl = descriptor.template?.content ?? "";
|
|
3309
|
+
const setup = descriptor.scriptSetup?.content ?? descriptor.script?.content ?? "";
|
|
3310
|
+
const pageScrollHooks = {
|
|
3311
|
+
hasOnPageScroll: /onPageScroll\s*\(/.test(setup),
|
|
3312
|
+
hasOnReachBottom: /onReachBottom\s*\(/.test(setup),
|
|
3313
|
+
hasOnPullDownRefresh: /onPullDownRefresh\s*\(/.test(setup),
|
|
3314
|
+
hasPageScrollTo: /wx\.pageScrollTo\s*\(/.test(setup)
|
|
3315
|
+
};
|
|
3316
|
+
const tplResult = transformTemplateToWxml(tpl, {
|
|
3317
|
+
...styleOpts,
|
|
3318
|
+
filename: options.filename,
|
|
3319
|
+
annotateLines: options.annotateLines,
|
|
3320
|
+
scopeId,
|
|
3321
|
+
isComponent: options.isComponent,
|
|
3322
|
+
autoScrollContainer: options.autoScrollContainer,
|
|
3323
|
+
pageScrollHooks,
|
|
3324
|
+
trace: tplTrace
|
|
3325
|
+
});
|
|
3326
|
+
const scriptTrace = createTrace("script");
|
|
3327
|
+
const scriptResult = transformScriptToPage(setup, styleOpts, {
|
|
3328
|
+
file: options.filename,
|
|
3329
|
+
isComponent: options.isComponent,
|
|
3330
|
+
vModelBindings: tplResult.vModelBindings,
|
|
3331
|
+
usesNavigate: tplResult.usesNavigate,
|
|
3332
|
+
selfHandlers: tplResult.selfHandlers,
|
|
3333
|
+
onceHandlers: tplResult.onceHandlers,
|
|
3334
|
+
inlineHandlers: tplResult.inlineHandlers,
|
|
3335
|
+
debug: options.debug,
|
|
3336
|
+
rules: options.rules,
|
|
3337
|
+
transitions: tplResult.transitions,
|
|
3338
|
+
storeBindings: tplResult.storeBindings,
|
|
3339
|
+
moduleImports: options.moduleImports,
|
|
3340
|
+
trace: scriptTrace
|
|
3341
|
+
});
|
|
3342
|
+
const styleTrace = createTrace("style");
|
|
3343
|
+
const preprocess = (s) => s.lang && options.preprocessStyle ? options.preprocessStyle(s.lang, s.content) : s.content;
|
|
3344
|
+
if (!options.rules?.disabled?.includes("style/default-scoped")) {
|
|
3345
|
+
for (const s of scopedStyles) {
|
|
3346
|
+
if (!s.scoped) {
|
|
3347
|
+
const msg = `<style> \u5DF2\u6309 scoped \u5904\u7406\uFF08Proteus \u9ED8\u8BA4\u5C40\u90E8\u4F5C\u7528\u57DF\uFF1BVue \u6807\u51C6 <style> \u4E3A\u5168\u5C40\uFF0CWeb \u7AEF\u4F1A\u6CC4\u6F0F\u5230\u6240\u6709\u9875\u9762\uFF09\u2014\u2014\u5982\u9700\u5168\u5C40\u6837\u5F0F\u8BF7\u6539\u7528 <style global>`;
|
|
3348
|
+
tplResult.warnings.push(msg);
|
|
3349
|
+
console.warn(`[mp-transform] ${msg}`);
|
|
3350
|
+
break;
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
const globalWxss = transformStyleToWxss(globalStyles.map(preprocess).join("\n"), {
|
|
3355
|
+
...styleOpts,
|
|
3356
|
+
usesTransition: tplResult.usesTransition,
|
|
3357
|
+
trace: styleTrace
|
|
3358
|
+
});
|
|
3359
|
+
const scopedWxss = transformStyleToWxss(scopedStyles.map(preprocess).join("\n"), {
|
|
3360
|
+
...styleOpts,
|
|
3361
|
+
scopeId,
|
|
3362
|
+
usesTransition: tplResult.usesTransition,
|
|
3363
|
+
trace: styleTrace
|
|
3364
|
+
});
|
|
3365
|
+
const wxss = [globalWxss, scopedWxss].filter(Boolean).join("\n");
|
|
3366
|
+
const pageScrollCss = tplResult.pageScrollWrapped ? "\n.proteus-page-scroll { height: 100vh; }\n" : "";
|
|
3367
|
+
const finalWxss = `${wxss}${pageScrollCss}`;
|
|
3368
|
+
const result = {
|
|
3369
|
+
wxml: tplResult.wxml,
|
|
3370
|
+
js: scriptResult.js,
|
|
3371
|
+
wxss: finalWxss,
|
|
3372
|
+
warnings: [...tplResult.warnings, ...scriptResult.warnings],
|
|
3373
|
+
trace: [...tplTrace.events, ...scriptTrace.events, ...styleTrace.events],
|
|
3374
|
+
sourcemap: scriptResult.sourcemap
|
|
3375
|
+
};
|
|
3376
|
+
assertValidResult(result, options.filename ?? "anonymous.vue");
|
|
3377
|
+
return result;
|
|
3378
|
+
}
|
|
3379
|
+
export {
|
|
3380
|
+
CompilerError,
|
|
3381
|
+
compileVueSfc,
|
|
3382
|
+
createTrace,
|
|
3383
|
+
explainTransform,
|
|
3384
|
+
formatTransformCatalog,
|
|
3385
|
+
formatTransformRule,
|
|
3386
|
+
formatTransformTrace,
|
|
3387
|
+
getTransformRule,
|
|
3388
|
+
lineAt,
|
|
3389
|
+
listTransformRules,
|
|
3390
|
+
scopedIdFrom,
|
|
3391
|
+
transformScriptToPage,
|
|
3392
|
+
transformStyleToWxss,
|
|
3393
|
+
transformTemplateToWxml,
|
|
3394
|
+
validateJs,
|
|
3395
|
+
validateWxml
|
|
3396
|
+
};
|