@proteus-vue/compiler 0.3.0-beta.0 → 0.3.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +218 -0
- package/dist/es5.d.ts +11 -0
- package/dist/fluid-layout.d.ts +45 -0
- package/dist/index.d.ts +16 -4
- package/dist/index.js +21704 -1806
- package/dist/ir/build.d.ts +14 -0
- package/dist/overrides.d.ts +2 -0
- package/dist/platform-macros.d.ts +20 -0
- package/dist/platform-variant.d.ts +72 -0
- package/dist/script.d.ts +51 -0
- package/dist/sfc-macros.d.ts +44 -0
- package/dist/svg-lower.d.ts +157 -0
- package/dist/tags.d.ts +13 -0
- package/dist/transforms/types.d.ts +6 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate.d.ts +30 -0
- package/dist/vue-compat.d.ts +43 -0
- package/package.json +20 -12
- package/dist/explain.js +0 -59
- package/dist/overrides.js +0 -34
- package/dist/script.js +0 -318
- package/dist/style.js +0 -96
- package/dist/tags.js +0 -49
- package/dist/template.js +0 -337
- package/dist/trace.js +0 -18
- package/dist/transforms/registry.js +0 -48
- package/dist/transforms/script.js +0 -179
- package/dist/transforms/style.js +0 -74
- package/dist/transforms/template.js +0 -371
- package/dist/transforms/types.js +0 -7
- package/dist/transforms/validate.js +0 -50
- package/dist/types.js +0 -4
- package/dist/validate.js +0 -54
|
@@ -1,371 +0,0 @@
|
|
|
1
|
-
// src/compiler/transforms/template.ts
|
|
2
|
-
// template 阶段编译规则注册表 —— 每条规则一份 AI 说明书
|
|
3
|
-
// 映射表与 src/compiler/tags.ts 同源引用(防漂移),registry 测试校验覆盖完整性
|
|
4
|
-
import { TAG_MAP, EVENT_MAP, SEMANTIC_CLASS } from '../tags';
|
|
5
|
-
/** 表驱动规则工厂:从 TAG_MAP 取同源映射(改 tags.ts 自动生效,测试防遗漏) */
|
|
6
|
-
function tagRule(id, title, tags, extra) {
|
|
7
|
-
return {
|
|
8
|
-
id,
|
|
9
|
-
phase: 'template',
|
|
10
|
-
status: 'implemented',
|
|
11
|
-
title,
|
|
12
|
-
mapping: Object.fromEntries(tags.map((t) => [t, TAG_MAP[t]]).filter(([, v]) => v !== undefined)),
|
|
13
|
-
...extra,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
export const TEMPLATE_RULES = [
|
|
17
|
-
// ============ 标签映射(TAG_MAP) ============
|
|
18
|
-
tagRule('tag/div-to-view', 'div → view', ['div'], {
|
|
19
|
-
description: '块级容器 div → view(小程序通用容器)',
|
|
20
|
-
why: '小程序没有 div,view 是最通用容器标签;业务代码照写标准 HTML(§0.3 原则 1)',
|
|
21
|
-
when: 'template 中出现 <div> 时',
|
|
22
|
-
example: { before: '<div class="home">…</div>', after: '<view class="home">…</view>' },
|
|
23
|
-
verify: 'tests/mp-transform.test.ts「标准标签映射」',
|
|
24
|
-
source: 'src/compiler/template.ts → serializeElement(TAG_MAP 查表)',
|
|
25
|
-
decision: '#57(样式标签选择器映射)',
|
|
26
|
-
}),
|
|
27
|
-
tagRule('tag/inline-to-text', 'span → text', ['span'], {
|
|
28
|
-
description: '行内文本 span → text(小程序最小文本节点)',
|
|
29
|
-
why: '小程序无 span,text 是行内文本容器;text 默认不换行、可被 text 嵌套',
|
|
30
|
-
when: 'template 中出现 <span> 时',
|
|
31
|
-
example: { before: '<span>hi</span>', after: '<text>hi</text>' },
|
|
32
|
-
verify: 'tests/mp-transform.test.ts「标准标签映射」',
|
|
33
|
-
source: 'src/compiler/template.ts → serializeElement(TAG_MAP 查表)',
|
|
34
|
-
}),
|
|
35
|
-
tagRule('tag/heading-to-text', 'h1–h6 → text', ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], {
|
|
36
|
-
description: '标题 h1-h6 → text,并自动附加 proteus-h1~h6 基础类(见 semantic/base-class)',
|
|
37
|
-
why: '小程序无标题标签;语义(大字号/加粗)由基础类还原(#58),视觉对齐 Web UA 样式',
|
|
38
|
-
when: 'template 中出现 <h1>~<h6> 时',
|
|
39
|
-
example: { before: '<h1>标题</h1>', after: '<text class="proteus-h1">标题</text>' },
|
|
40
|
-
verify: 'tests/mp-transform.test.ts「语义标签自动附加基础类」',
|
|
41
|
-
source: 'src/compiler/template.ts → serializeElement + src/compiler/style.ts BASE_SEMANTIC_WXSS',
|
|
42
|
-
decision: '#58 / #59',
|
|
43
|
-
}),
|
|
44
|
-
tagRule('tag/para-to-text', 'p → text', ['p'], {
|
|
45
|
-
description: '段落 p → text,并自动附加 proteus-p 基础类(段距对齐 Web)',
|
|
46
|
-
why: 'p 映射为 text 后无 UA 默认段距,基础类注入 margin: 0 0 1em 还原 Web 折叠间距(#59)',
|
|
47
|
-
when: 'template 中出现 <p> 时',
|
|
48
|
-
example: { before: '<p>段落</p>', after: '<text class="proteus-p">段落</text>' },
|
|
49
|
-
verify: 'tests/mp-transform.test.ts「标准标签映射」',
|
|
50
|
-
source: 'src/compiler/template.ts → serializeElement',
|
|
51
|
-
decision: '#59(margin 单边 em)',
|
|
52
|
-
}),
|
|
53
|
-
tagRule('tag/link-to-view', 'a → view', ['a'], {
|
|
54
|
-
description: '链接 a → view(带 href 时升级为导航链接,见 nav/navigate-link)',
|
|
55
|
-
why: '小程序无 a 标签;导航语义由 data-url + bindtap="proteusNavigateTo" 承担,样式语义由 proteus-a 基础类承担',
|
|
56
|
-
when: 'template 中出现 <a> 时',
|
|
57
|
-
example: { before: '<a href="/pages/user/index">用户</a>', after: '<view class="proteus-a" data-url="/pages/user/index" bindtap="proteusNavigateTo">用户</view>' },
|
|
58
|
-
verify: 'tests/mp-transform.test.ts 导航链接用例',
|
|
59
|
-
source: 'src/compiler/template.ts → serializeElement(isNavLink 分支)',
|
|
60
|
-
decision: '#24 / #30(导航链接)/ #58(基础类)',
|
|
61
|
-
}),
|
|
62
|
-
tagRule('tag/image', 'img → image', ['img'], {
|
|
63
|
-
description: '图片 img → image;:src 绑定经 directive/v-bind 转为 src="{{url}}"',
|
|
64
|
-
why: '小程序图片标签是 image',
|
|
65
|
-
when: 'template 中出现 <img> 时',
|
|
66
|
-
example: { before: '<img :src="url" />', after: '<image src="{{url}}" />' },
|
|
67
|
-
verify: 'tests/mp-transform.test.ts「标准标签映射」',
|
|
68
|
-
source: 'src/compiler/template.ts → serializeElement(TAG_MAP 查表)',
|
|
69
|
-
}),
|
|
70
|
-
tagRule('tag/passthrough', '同名标签保留', ['button', 'input', 'textarea', 'video', 'canvas', 'scroll-view', 'slot'], {
|
|
71
|
-
description: 'button / input / textarea / video / canvas / scroll-view / slot 同名保留(小程序原生即有)',
|
|
72
|
-
why: '这些标签在小程序原生存在,无需映射;input/textarea 同时是 v-model 的目标(directive/v-model)',
|
|
73
|
-
when: 'template 中出现以上标签时',
|
|
74
|
-
example: { before: '<button>go</button>', after: '<button>go</button>' },
|
|
75
|
-
verify: 'tests/mp-transform.test.ts 事件/表单用例',
|
|
76
|
-
source: 'src/compiler/template.ts → serializeElement(TAG_MAP 查表)',
|
|
77
|
-
}),
|
|
78
|
-
tagRule('tag/router-link', 'router-link → view', ['router-link'], {
|
|
79
|
-
description: 'Vue Router 的 router-link → view + 导航链接(to 属性 → data-url)',
|
|
80
|
-
why: '小程序无 Vue Router 组件;统一转导航链接(决策 #24:<a href> / <router-link> 同为导航入口)',
|
|
81
|
-
when: 'template 中出现 <router-link to="..."> 且无 @click 时',
|
|
82
|
-
example: { before: '<router-link to="/pages/user/index">用户</router-link>', after: '<view data-url="/pages/user/index" bindtap="proteusNavigateTo">用户</view>' },
|
|
83
|
-
verify: 'tests/mp-transform.test.ts 导航链接用例',
|
|
84
|
-
source: 'src/compiler/template.ts → serializeElement(isNavLink 分支)',
|
|
85
|
-
decision: '#24',
|
|
86
|
-
}),
|
|
87
|
-
tagRule('tag/rich-text', 'v-html 容器 → rich-text', [], {
|
|
88
|
-
description: '带 v-html 的容器元素 → rich-text(nodes="{{expr}}")',
|
|
89
|
-
why: '小程序无 innerHTML,富文本用 rich-text 组件渲染 HTML 节点',
|
|
90
|
-
when: '元素上有 v-html 指令时(v-html 覆盖标签映射)',
|
|
91
|
-
example: { before: '<div v-html="html"></div>', after: '<rich-text nodes="{{html}}" />' },
|
|
92
|
-
verify: 'tests/mp-transform.test.ts v-html 用例',
|
|
93
|
-
source: 'src/compiler/template.ts → serializeElement(hasVHtml 分支)',
|
|
94
|
-
}),
|
|
95
|
-
tagRule('tag/unknown-kebab', '未注册标签 kebab-case 原样输出', [], {
|
|
96
|
-
description: 'TAG_MAP 未覆盖的标签按 kebab-case 原样输出(组件 / 自定义元素逃生舱)',
|
|
97
|
-
why: '白名单映射 + 未知标签保守保留:标准 Vue 组件体系(原则 9)与原生组件逃生舱(痛点 #11 对策)依赖此通道',
|
|
98
|
-
when: '标签不在 TAG_MAP 且非 router-link 时',
|
|
99
|
-
example: { before: '<custom-comp foo="bar" />', after: '<custom-comp foo="bar" />' },
|
|
100
|
-
verify: 'tests/mp-transform.test.ts 未注册标签用例',
|
|
101
|
-
source: 'src/compiler/template.ts → serializeElement(TAG_MAP[node.tag] ?? kebabCase)',
|
|
102
|
-
}),
|
|
103
|
-
// ============ 语义基础类 ============
|
|
104
|
-
{
|
|
105
|
-
id: 'semantic/base-class',
|
|
106
|
-
phase: 'template',
|
|
107
|
-
status: 'implemented',
|
|
108
|
-
title: '语义标签自动附加 proteus-* 基础类',
|
|
109
|
-
description: 'h1-h6/p/a 映射时自动附加 proteus-h1~h6 / proteus-p / proteus-a 类(与用户 class 合并、与 :class 插值拼接)',
|
|
110
|
-
why: 'Web 端 h1-h6/p/a 有浏览器 UA 默认样式,小程序 text/view 没有;基础类 + 基础 WXSS(style/semantic-base-wxss)还原两端视觉一致(#58)',
|
|
111
|
-
when: '语义标签且非 v-html 容器时',
|
|
112
|
-
example: {
|
|
113
|
-
before: '<h1 class="title">a</h1>',
|
|
114
|
-
after: '<text class="proteus-h1 title">a</text>',
|
|
115
|
-
},
|
|
116
|
-
verify: 'tests/mp-transform.test.ts「语义标签自动附加基础类」;golden fixture showcase.wxml',
|
|
117
|
-
source: 'src/compiler/template.ts → serializeElement(baseClass 分支)+ src/compiler/tags.ts SEMANTIC_CLASS',
|
|
118
|
-
decision: '#58 / #59',
|
|
119
|
-
mapping: { ...SEMANTIC_CLASS },
|
|
120
|
-
},
|
|
121
|
-
// ============ 事件映射(EVENT_MAP) ============
|
|
122
|
-
{
|
|
123
|
-
id: 'event/click-to-tap',
|
|
124
|
-
phase: 'template',
|
|
125
|
-
status: 'implemented',
|
|
126
|
-
title: '@click → bindtap(EVENT_MAP 全表)',
|
|
127
|
-
description: '标准事件 → 小程序事件:click→tap、input/change/submit/focus/blur/touch*/longpress/confirm 同名保留',
|
|
128
|
-
why: '小程序无 click,点击事件是 tap;其余事件命名一致(EVENT_MAP 集中在 tags.ts 与样式侧共用)',
|
|
129
|
-
when: '元素上有 @事件 指令时',
|
|
130
|
-
example: { before: '<button @click="handleTap">go</button>', after: '<button bindtap="handleTap">go</button>' },
|
|
131
|
-
verify: 'tests/mp-transform.test.ts「事件映射」',
|
|
132
|
-
source: 'src/compiler/template.ts → serializeElement(on 分支)+ src/compiler/tags.ts EVENT_MAP',
|
|
133
|
-
mapping: { ...EVENT_MAP },
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
id: 'event/modifier-catch',
|
|
137
|
-
phase: 'template',
|
|
138
|
-
status: 'implemented',
|
|
139
|
-
title: '.stop / .prevent 修饰符 → catch 前缀',
|
|
140
|
-
description: '@click.stop / @click.prevent → catchtap(阻止冒泡);其余修饰符忽略',
|
|
141
|
-
why: '小程序无事件修饰符语法,catch* 事件天然阻止冒泡,等价 .stop 语义;.prevent 无对等机制,映射为 catch 兜底',
|
|
142
|
-
when: '事件指令带 stop 或 prevent 修饰符时',
|
|
143
|
-
example: { before: '<a @click.stop="stopFn">s</a>', after: '<a catchtap="stopFn">s</a>' },
|
|
144
|
-
verify: 'tests/mp-transform.test.ts「事件映射」',
|
|
145
|
-
source: 'src/compiler/template.ts → serializeElement(on 分支 isCatch)',
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
id: 'event/handler-simple-ref',
|
|
149
|
-
phase: 'template',
|
|
150
|
-
title: '事件处理器仅支持简单方法引用',
|
|
151
|
-
description: '仅支持 handleTap / handleTap($event);复杂表达式编译期警告并原样输出',
|
|
152
|
-
why: 'MVP 收缩(原则 10):小程序事件处理器必须是 Page methods 中的方法名,内联表达式无法静态编译',
|
|
153
|
-
when: '事件指令的表达式不是简单方法引用时',
|
|
154
|
-
example: {
|
|
155
|
-
before: '@click="count > 0 ? go() : back()"',
|
|
156
|
-
after: '编译期警告:不是简单方法引用,原样输出(产物需人工处理)',
|
|
157
|
-
},
|
|
158
|
-
verify: 'tests/mp-transform.test.ts 事件警告用例',
|
|
159
|
-
source: 'src/compiler/template.ts → cleanHandler',
|
|
160
|
-
status: 'limitation',
|
|
161
|
-
},
|
|
162
|
-
// ============ 指令映射 ============
|
|
163
|
-
{
|
|
164
|
-
id: 'directive/v-if',
|
|
165
|
-
phase: 'template',
|
|
166
|
-
status: 'implemented',
|
|
167
|
-
title: 'v-if → wx:if',
|
|
168
|
-
description: 'v-if="show" → wx:if="{{show}}"',
|
|
169
|
-
why: '小程序条件渲染指令是 wx:if',
|
|
170
|
-
when: '元素上有 v-if 指令时',
|
|
171
|
-
example: { before: '<p v-if="show">a</p>', after: '<p wx:if="{{show}}">a</p>' },
|
|
172
|
-
verify: 'tests/mp-transform.test.ts「v-if / v-else」',
|
|
173
|
-
source: 'src/compiler/template.ts → serializeElement(if 分支)',
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
id: 'directive/v-else-if',
|
|
177
|
-
phase: 'template',
|
|
178
|
-
status: 'implemented',
|
|
179
|
-
title: 'v-else-if → wx:elif',
|
|
180
|
-
description: 'v-else-if="cond" → wx:elif="{{cond}}"',
|
|
181
|
-
why: '小程序条件链指令是 wx:elif',
|
|
182
|
-
when: '元素上有 v-else-if 指令时',
|
|
183
|
-
example: { before: '<p v-else-if="b">c</p>', after: '<p wx:elif="{{b}}">c</p>' },
|
|
184
|
-
verify: 'tests/mp-transform.test.ts「v-if / v-else」',
|
|
185
|
-
source: 'src/compiler/template.ts → serializeElement(else-if 分支)',
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
id: 'directive/v-else',
|
|
189
|
-
phase: 'template',
|
|
190
|
-
status: 'implemented',
|
|
191
|
-
title: 'v-else → wx:else',
|
|
192
|
-
description: 'v-else → wx:else(无值)',
|
|
193
|
-
why: '小程序条件链指令是 wx:else',
|
|
194
|
-
when: '元素上有 v-else 指令时',
|
|
195
|
-
example: { before: '<p v-else>b</p>', after: '<p wx:else>b</p>' },
|
|
196
|
-
verify: 'tests/mp-transform.test.ts「v-if / v-else」',
|
|
197
|
-
source: 'src/compiler/template.ts → serializeElement(else 分支)',
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
id: 'directive/v-for',
|
|
201
|
-
phase: 'template',
|
|
202
|
-
status: 'implemented',
|
|
203
|
-
title: 'v-for → wx:for / wx:for-item / wx:for-index',
|
|
204
|
-
description: 'v-for="(item, idx) in list" → wx:for="{{list}}" + wx:for-item + wx:for-index(支持 in/of)',
|
|
205
|
-
why: '小程序循环指令是 wx:for,且需显式声明 item/index 变量名',
|
|
206
|
-
when: '元素上有 v-for 指令时',
|
|
207
|
-
example: {
|
|
208
|
-
before: '<div v-for="(item, idx) in list" :key="idx">{{ item }}</div>',
|
|
209
|
-
after: '<view wx:for="{{list}}" wx:for-item="item" wx:for-index="idx" wx:key="idx">{{ item }}</view>',
|
|
210
|
-
},
|
|
211
|
-
verify: 'tests/mp-transform.test.ts「v-for」',
|
|
212
|
-
source: 'src/compiler/template.ts → serializeElement(for 分支)+ parseForExpr',
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
id: 'directive/v-bind',
|
|
216
|
-
phase: 'template',
|
|
217
|
-
status: 'implemented',
|
|
218
|
-
title: '普通 :prop 绑定 → prop="{{expr}}"',
|
|
219
|
-
description: ':src / :href / 任意属性绑定 → 属性="{{表达式}}",静态属性原样保留',
|
|
220
|
-
why: '小程序属性绑定语法是 {{expr}};静态属性(如 placeholder="x")直接透传',
|
|
221
|
-
when: '元素上有 v-bind 且 arg 不是 class/style/key 时',
|
|
222
|
-
example: { before: '<img :src="url" />', after: '<image src="{{url}}" />' },
|
|
223
|
-
verify: 'tests/mp-transform.test.ts「标准标签映射」',
|
|
224
|
-
source: 'src/compiler/template.ts → serializeElement(bind 分支)',
|
|
225
|
-
},
|
|
226
|
-
{
|
|
227
|
-
id: 'directive/v-bind-class',
|
|
228
|
-
phase: 'template',
|
|
229
|
-
status: 'implemented',
|
|
230
|
-
title: ':class 绑定(对象语法 → 三元拼接)',
|
|
231
|
-
description: ':class="{ active: on }" → {{(on?\'active \':\'\')}};数组语法编译期警告(MVP 限制)',
|
|
232
|
-
why: '小程序无 class 对象语法,编译期为三元表达式拼接;数组语法暂不支持(MVP 收缩)',
|
|
233
|
-
when: '元素上有 :class 绑定且带语义基础类或对象/数组表达式时',
|
|
234
|
-
example: {
|
|
235
|
-
before: '<p :class="{ active: on }">b</p>',
|
|
236
|
-
after: '<text class="proteus-p {{(on?\'active \':\'\')}}">b</text>',
|
|
237
|
-
},
|
|
238
|
-
verify: 'tests/mp-transform.test.ts「语义标签自动附加基础类」',
|
|
239
|
-
source: 'src/compiler/template.ts → formatClassBinding',
|
|
240
|
-
decision: '#61(多对一映射用基础类隔离)',
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
id: 'directive/v-bind-style',
|
|
244
|
-
phase: 'template',
|
|
245
|
-
status: 'implemented',
|
|
246
|
-
title: ':style 绑定(对象语法 → prop:{{expr}} 拼接)',
|
|
247
|
-
description: ':style="{ color: c }" → style="color:{{c}}";属性名 camelCase → kebab-case',
|
|
248
|
-
why: '小程序 style 属性支持内联插值,逐属性编译可静态验证',
|
|
249
|
-
when: '元素上有 :style 绑定且为对象语法时',
|
|
250
|
-
example: { before: ':style="{ backgroundColor: bg }"', after: 'style="background-color:{{bg}}"' },
|
|
251
|
-
verify: 'tests/mp-transform.test.ts :style 用例',
|
|
252
|
-
source: 'src/compiler/template.ts → formatStyleBinding',
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
id: 'directive/v-bind-key',
|
|
256
|
-
phase: 'template',
|
|
257
|
-
status: 'implemented',
|
|
258
|
-
title: ':key → wx:key(仅简单标识符)',
|
|
259
|
-
description: ':key="idx" → wx:key="idx";非简单标识符编译期警告并忽略',
|
|
260
|
-
why: '小程序列表复用标识是 wx:key,仅接受简单标识符',
|
|
261
|
-
when: '元素上有 :key 绑定(通常在 v-for 内)时',
|
|
262
|
-
example: { before: ':key="idx"', after: 'wx:key="idx"' },
|
|
263
|
-
verify: 'tests/mp-transform.test.ts「v-for」',
|
|
264
|
-
source: 'src/compiler/template.ts → serializeElement(key 分支)',
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
id: 'directive/v-model',
|
|
268
|
-
phase: 'template',
|
|
269
|
-
status: 'implemented',
|
|
270
|
-
title: 'v-model → value + bindinput 自动 handler',
|
|
271
|
-
description: 'input/textarea 的 v-model="x" → value="{{x}}" + bindinput="proteusOnXInput"(handler 由 script 阶段注入 setData)',
|
|
272
|
-
why: '小程序无 v-model 语法,需双向绑定的两半:value 绑定 + 输入事件回写(script/vmodel-handler)',
|
|
273
|
-
when: 'input 或 textarea 上有 v-model 指令时',
|
|
274
|
-
example: {
|
|
275
|
-
before: '<input v-model="name" />',
|
|
276
|
-
after: '<input value="{{name}}" bindinput="proteusOnNameInput" />',
|
|
277
|
-
},
|
|
278
|
-
verify: 'tests/mp-transform.test.ts v-model 用例',
|
|
279
|
-
source: 'src/compiler/template.ts → serializeElement(model 分支)',
|
|
280
|
-
decision: '#29(方法名避免 __ 前缀)',
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
id: 'directive/v-html',
|
|
284
|
-
phase: 'template',
|
|
285
|
-
status: 'implemented',
|
|
286
|
-
title: 'v-html → rich-text nodes',
|
|
287
|
-
description: 'v-html="html" → rich-text nodes="{{html}}"(容器标签映射为 rich-text)',
|
|
288
|
-
why: '小程序无 innerHTML,富文本用 rich-text 组件(原生能力兜底,痛点 #11 对策)',
|
|
289
|
-
when: '元素上有 v-html 指令时',
|
|
290
|
-
example: { before: '<div v-html="html"></div>', after: '<rich-text nodes="{{html}}" />' },
|
|
291
|
-
verify: 'tests/mp-transform.test.ts v-html 用例',
|
|
292
|
-
source: 'src/compiler/template.ts → serializeElement(hasVHtml / html 分支)',
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
id: 'directive/v-show-limit',
|
|
296
|
-
phase: 'template',
|
|
297
|
-
title: 'v-show 编译期警告(MVP 不支持)',
|
|
298
|
-
description: 'v-show 出现时编译期警告「请改用 v-if」,指令忽略',
|
|
299
|
-
why: '小程序无 v-show 对等机制(display 切换需运行期操作),MVP 收缩暂不支持(文档已知限制)',
|
|
300
|
-
when: '元素上有 v-show 指令时',
|
|
301
|
-
example: { before: '<p v-show="show">a</p>', after: '警告后按无指令输出(无 display 切换语义)' },
|
|
302
|
-
verify: 'tests/mp-transform.test.ts 警告用例',
|
|
303
|
-
source: 'src/compiler/template.ts → serializeElement(show 分支)',
|
|
304
|
-
status: 'limitation',
|
|
305
|
-
},
|
|
306
|
-
// ============ 导航链接 ============
|
|
307
|
-
{
|
|
308
|
-
id: 'nav/navigate-link',
|
|
309
|
-
phase: 'template',
|
|
310
|
-
status: 'implemented',
|
|
311
|
-
title: '<a href> / <router-link to> → 导航链接',
|
|
312
|
-
description: '带 href/to 的导航链接 → data-url + data-route-type + bindtap="proteusNavigateTo"(handler 由 script/nav-handler 注入)',
|
|
313
|
-
why: 'Web 端 <a> 走浏览器导航,小程序端需转 data-url + 点击跳转(决策 #24);保留前导 / 为绝对路径(#30 真机教训:无前导 / 会相对当前页目录解析导致双重前缀)',
|
|
314
|
-
when: 'a 或 router-link 且无 @click、含 href/to(静态或 :bind)时',
|
|
315
|
-
example: {
|
|
316
|
-
before: '<a href="/pages/user/index">用户</a>',
|
|
317
|
-
after: '<view class="proteus-a" data-url="/pages/user/index" bindtap="proteusNavigateTo">用户</view>',
|
|
318
|
-
},
|
|
319
|
-
verify: 'tests/mp-transform.test.ts 导航链接用例;真机验证归档决策 #30',
|
|
320
|
-
source: 'src/compiler/template.ts → serializeElement(isNavLink / hasNavTarget 分支)',
|
|
321
|
-
decision: '#24 / #30 / #29',
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
id: 'nav/route-type',
|
|
325
|
-
phase: 'template',
|
|
326
|
-
status: 'implemented',
|
|
327
|
-
title: '导航链接 route-type 属性 → data-route-type',
|
|
328
|
-
description: '<a route-type="halfScreen"> → data-route-type="halfScreen"(proteusNavigateTo 透传为 wx.navigateTo 的 routeType)',
|
|
329
|
-
why: 'Skyline 自定义路由转场从导航链接发起时,需把 routeType 传到运行期(决策 #44:routeType 双端同 API)',
|
|
330
|
-
when: '导航链接元素上有 route-type 属性时',
|
|
331
|
-
example: {
|
|
332
|
-
before: '<a href="/pages/user/profile" route-type="halfScreen">资料</a>',
|
|
333
|
-
after: '<view data-url="/pages/user/profile" data-route-type="halfScreen" bindtap="proteusNavigateTo">资料</view>',
|
|
334
|
-
},
|
|
335
|
-
verify: 'tests/mp-transform.test.ts 导航链接用例',
|
|
336
|
-
source: 'src/compiler/template.ts → serializeElement(route-type 分支)',
|
|
337
|
-
decision: '#28 / #44',
|
|
338
|
-
},
|
|
339
|
-
// ============ 通用节点 ============
|
|
340
|
-
{
|
|
341
|
-
id: 'node/interpolation',
|
|
342
|
-
phase: 'template',
|
|
343
|
-
status: 'implemented',
|
|
344
|
-
title: '插值 {{ expr }} 保留',
|
|
345
|
-
description: '{{ title }} → {{ title }}(表达式原样透传,文本节点紧凑单行输出)',
|
|
346
|
-
why: '小程序插值语法同为 {{expr}},无需转换;纯文本子节点紧凑单行保证产物可读(决策 #15)',
|
|
347
|
-
when: '模板出现 {{ }} 插值或纯文本子节点时',
|
|
348
|
-
example: { before: '<p>tapped {{ count }} times</p>', after: '<text class="proteus-p">tapped {{ count }} times</text>' },
|
|
349
|
-
verify: 'golden fixture basic.wxml',
|
|
350
|
-
source: 'src/compiler/template.ts → serializeNode(TEXT / INTERPOLATION 分支)',
|
|
351
|
-
decision: '#15',
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
id: 'annotation/line-note',
|
|
355
|
-
phase: 'template',
|
|
356
|
-
status: 'implemented',
|
|
357
|
-
title: 'PROTEUS_DEBUG 源码行号注释',
|
|
358
|
-
description: 'annotateLines=true 时 WXML 每个元素前注入 <!-- @行号 标签 -->,产物可反查源码位置',
|
|
359
|
-
why: '反编译黑盒机制(#17):默认关闭,npm run debug:mp 开启——AI/人拿到产物即可定位到源码行',
|
|
360
|
-
when: '编译选项 annotateLines=true(PROTEUS_DEBUG=1 构建)时',
|
|
361
|
-
example: { before: '第 26 行 <h1>{{ title }}</h1>', after: '<!-- @26 h1 -->\n<text class="proteus-h1">{{ title }}</text>' },
|
|
362
|
-
verify: 'tests/mp-transform.test.ts annotateLines 用例;PROJECT_MEMORY #17',
|
|
363
|
-
source: 'src/compiler/template.ts → serializeElement(lineNote 分支)',
|
|
364
|
-
decision: '#17',
|
|
365
|
-
},
|
|
366
|
-
];
|
|
367
|
-
// 追踪键(防漂移):标签 → 规则 ID,由 tag/* 规则的 mapping 反推——实现侧 trace 引用同一份数据
|
|
368
|
-
// 只取 tag/ 前缀规则:semantic/base-class(mapping 含 h1-p/a)与 event/click-to-tap(mapping 含 input/click)
|
|
369
|
-
// 的键是另一维度的映射,不得混入标签追踪(后定义覆盖会污染)。
|
|
370
|
-
// tests/explain.test.ts 校验所有 trace 事件 ruleId 均可解析
|
|
371
|
-
export const TAG_RULE_BY_TAG = Object.fromEntries(TEMPLATE_RULES.filter((r) => r.id.startsWith('tag/')).flatMap((r) => r.mapping ? Object.keys(r.mapping).map((t) => [t, r.id]) : []));
|
package/dist/transforms/types.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// src/compiler/transforms/types.ts
|
|
2
|
-
// 编译规则 AI 说明书 —— 透明定位的核心数据结构
|
|
3
|
-
// 每条编译规则 = 一份结构化自描述(人可读 + 机器可读):
|
|
4
|
-
// AI 可枚举全部规则(能力清单)、查询单条规则(what/why/when/how-verify)、
|
|
5
|
-
// 由 source 字段直接跳读实现源码。未来(阶段二)每条规则增加 apply() 后,
|
|
6
|
-
// 本注册表即升级为分派层,可输出 explainTransform(source) 决策 trace。
|
|
7
|
-
export {};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
export const VALIDATE_RULES = [
|
|
2
|
-
{
|
|
3
|
-
id: 'validate/js-syntax',
|
|
4
|
-
phase: 'validate',
|
|
5
|
-
status: 'implemented',
|
|
6
|
-
title: 'JS 产物语法自校验',
|
|
7
|
-
description: 'new Function(js) 仅解析不执行,语法错误 → 校验失败并携带错误信息',
|
|
8
|
-
why: '反编译黑盒机制(决策 #17):坏产物当场报错指明文件,绝不静默输出不可用的产物(对比 uni-app 产物无法定位问题)',
|
|
9
|
-
when: '每次整包编译后(assertValidResult 内)',
|
|
10
|
-
example: {
|
|
11
|
-
before: '// 若产物 js 含语法错误',
|
|
12
|
-
after: 'CompilerError: [proteus-compiler] xxx.vue: js 产物语法错误:Unexpected token ...',
|
|
13
|
-
},
|
|
14
|
-
verify: 'tests/mp-transform.test.ts 校验用例(构造坏产物断言抛错)',
|
|
15
|
-
source: 'src/compiler/validate.ts → validateJs',
|
|
16
|
-
decision: '#17',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
id: 'validate/wxml-pairing',
|
|
20
|
-
phase: 'validate',
|
|
21
|
-
status: 'implemented',
|
|
22
|
-
title: 'WXML 标签配对自校验',
|
|
23
|
-
description: '栈式扫描 WXML 标签配对(先剥离注释,避免行号注释干扰),未闭合/错配 → 校验失败',
|
|
24
|
-
why: '反编译黑盒机制(决策 #17):模板转换出错的常见形态就是标签不配对,编译期拦截比真机报错好定位',
|
|
25
|
-
when: '每次整包编译后(assertValidResult 内)',
|
|
26
|
-
example: {
|
|
27
|
-
before: '// 若产物 wxml 标签不配对',
|
|
28
|
-
after: 'CompilerError: [proteus-compiler] xxx.vue: wxml 产物结构错误:</view> 与 <text> 不匹配(位置 N)',
|
|
29
|
-
},
|
|
30
|
-
verify: 'tests/mp-transform.test.ts 校验用例',
|
|
31
|
-
source: 'src/compiler/validate.ts → validateWxml',
|
|
32
|
-
decision: '#17',
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: 'validate/compiler-error',
|
|
36
|
-
phase: 'validate',
|
|
37
|
-
status: 'implemented',
|
|
38
|
-
title: '坏产物抛 CompilerError 指明文件',
|
|
39
|
-
description: '校验失败 → 抛 CompilerError(携带源文件名,消息含 [proteus-compiler] 前缀)',
|
|
40
|
-
why: '反黑盒机制的统一错误通道:AI/开发者拿到错误即可定位到具体文件(错误 = 可操作的反馈,而非黑盒失败)',
|
|
41
|
-
when: 'js 或 wxml 校验不通过时',
|
|
42
|
-
example: {
|
|
43
|
-
before: '// 静默输出坏产物(反模式)',
|
|
44
|
-
after: 'throw new CompilerError(filename, message)',
|
|
45
|
-
},
|
|
46
|
-
verify: 'tests/mp-transform.test.ts 校验用例',
|
|
47
|
-
source: 'src/compiler/validate.ts → CompilerError + assertValidResult',
|
|
48
|
-
decision: '#17',
|
|
49
|
-
},
|
|
50
|
-
];
|
package/dist/types.js
DELETED
package/dist/validate.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/** 编译产物校验错误(携带源文件名,便于定位) */
|
|
2
|
-
export class CompilerError extends Error {
|
|
3
|
-
constructor(filename, message) {
|
|
4
|
-
super(`[proteus-compiler] ${filename}: ${message}`);
|
|
5
|
-
this.filename = filename;
|
|
6
|
-
this.name = 'CompilerError';
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
/** JS 语法校验:new Function 仅解析不执行(产物为 Page()/Component() 调用,无 import/export) */
|
|
10
|
-
export function validateJs(js) {
|
|
11
|
-
try {
|
|
12
|
-
// eslint-disable-next-line no-new-func
|
|
13
|
-
new Function(js);
|
|
14
|
-
return { ok: true };
|
|
15
|
-
}
|
|
16
|
-
catch (err) {
|
|
17
|
-
return { ok: false, error: err.message };
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
/** WXML 标签配对校验(先剥离注释,避免行号注释中的标签文本干扰) */
|
|
21
|
-
export function validateWxml(wxml) {
|
|
22
|
-
const withoutComments = wxml.replace(/<!--[\s\S]*?-->/g, '');
|
|
23
|
-
const tagRe = /<\/?([a-zA-Z][\w-]*)(?:"[^"]*"|'[^']*'|[^>"'])*\/?>/g;
|
|
24
|
-
const stack = [];
|
|
25
|
-
let m;
|
|
26
|
-
while ((m = tagRe.exec(withoutComments))) {
|
|
27
|
-
const full = m[0];
|
|
28
|
-
const name = m[1];
|
|
29
|
-
if (full.startsWith('</')) {
|
|
30
|
-
const top = stack.pop();
|
|
31
|
-
if (top !== name) {
|
|
32
|
-
return { ok: false, error: `</${name}> 与 <${top ?? '(无)'}> 不匹配(位置 ${m.index})` };
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
else if (!full.endsWith('/>')) {
|
|
36
|
-
stack.push(name);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (stack.length) {
|
|
40
|
-
return { ok: false, error: `<${stack[stack.length - 1]}> 未闭合` };
|
|
41
|
-
}
|
|
42
|
-
return { ok: true };
|
|
43
|
-
}
|
|
44
|
-
/** 校验整包编译结果,失败抛 CompilerError */
|
|
45
|
-
export function assertValidResult(result, filename) {
|
|
46
|
-
const jsCheck = validateJs(result.js);
|
|
47
|
-
if (!jsCheck.ok) {
|
|
48
|
-
throw new CompilerError(filename, `js 产物语法错误:${jsCheck.error}`);
|
|
49
|
-
}
|
|
50
|
-
const wxmlCheck = validateWxml(result.wxml);
|
|
51
|
-
if (!wxmlCheck.ok) {
|
|
52
|
-
throw new CompilerError(filename, `wxml 产物结构错误:${wxmlCheck.error}`);
|
|
53
|
-
}
|
|
54
|
-
}
|