@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.
@@ -0,0 +1,276 @@
1
+ // src/style-safety/index.ts
2
+ import { STYLE_PROP_LEVELS } from "@proteus-vue/contracts/style";
3
+
4
+ // src/style-safety/reachability.ts
5
+ var DYNAMIC_UNKNOWN = "unknown";
6
+ var LITERAL_RE = /^-?(?:\d+(?:\.\d+)?|\.\d+)$/;
7
+ var IDENT_RE = /^[A-Za-z_$][\w$]*$/;
8
+ function findTopLevel(input, seps) {
9
+ let depth = 0;
10
+ let quote = "";
11
+ for (let i = 0; i < input.length; i++) {
12
+ const ch = input[i];
13
+ if (quote) {
14
+ if (ch === quote && input[i - 1] !== "\\") quote = "";
15
+ continue;
16
+ }
17
+ if (ch === "'" || ch === '"' || ch === "`") {
18
+ quote = ch;
19
+ continue;
20
+ }
21
+ if (ch === "(" || ch === "[" || ch === "{") depth++;
22
+ else if (ch === ")" || ch === "]" || ch === "}") depth = Math.max(0, depth - 1);
23
+ if (depth === 0 && seps.indexOf(ch) >= 0) return i;
24
+ }
25
+ return -1;
26
+ }
27
+ function parseLiteral(s) {
28
+ if (LITERAL_RE.test(s)) return Number(s);
29
+ if (s === "true") return true;
30
+ if (s === "false") return false;
31
+ if (s === "null") return null;
32
+ if (s.startsWith("'") && s.endsWith("'") && s.length >= 2 || s.startsWith('"') && s.endsWith('"') && s.length >= 2) {
33
+ return s.slice(1, -1);
34
+ }
35
+ return void 0;
36
+ }
37
+ function dynamic(source) {
38
+ return { values: [], isFullyStatic: false, dynamicSources: [source] };
39
+ }
40
+ function deriveReachableValues(expr, constants = {}) {
41
+ const t = expr.trim();
42
+ if (!t) return dynamic(DYNAMIC_UNKNOWN);
43
+ const lit = parseLiteral(t);
44
+ if (lit !== void 0) return { values: [lit], isFullyStatic: true, dynamicSources: [] };
45
+ if (IDENT_RE.test(t)) {
46
+ const v = constants[t];
47
+ if (v !== void 0) return { values: [v], isFullyStatic: true, dynamicSources: [] };
48
+ return dynamic(t);
49
+ }
50
+ const qIdx = findTopLevel(t, ["?"]);
51
+ if (qIdx >= 0) {
52
+ const rest = t.slice(qIdx + 1);
53
+ const colonIdx = findTopLevel(rest, [":"]);
54
+ if (colonIdx >= 0) {
55
+ const a = deriveReachableValues(rest.slice(0, colonIdx), constants);
56
+ const b = deriveReachableValues(rest.slice(colonIdx + 1), constants);
57
+ return {
58
+ values: a.values.concat(b.values),
59
+ isFullyStatic: a.isFullyStatic && b.isFullyStatic,
60
+ dynamicSources: a.dynamicSources.concat(b.dynamicSources)
61
+ };
62
+ }
63
+ }
64
+ for (const op of ["+", "-", "*", "/"]) {
65
+ const idx = findTopLevel(t, [op]);
66
+ if (idx <= 0 || idx >= t.length - 1) continue;
67
+ const left = deriveReachableValues(t.slice(0, idx), constants);
68
+ const right = deriveReachableValues(t.slice(idx + 1), constants);
69
+ if (left.isFullyStatic && right.isFullyStatic && left.values.length === 1 && right.values.length === 1) {
70
+ const l = left.values[0];
71
+ const r = right.values[0];
72
+ if (typeof l === "number" && typeof r === "number") {
73
+ const folded = op === "+" ? l + r : op === "-" ? l - r : op === "*" ? l * r : l / r;
74
+ if (Number.isFinite(folded)) return { values: [folded], isFullyStatic: true, dynamicSources: [] };
75
+ }
76
+ }
77
+ return dynamic("binary");
78
+ }
79
+ return dynamic(t);
80
+ }
81
+
82
+ // src/style-safety/constants.ts
83
+ function evalLiteral(expr) {
84
+ try {
85
+ return Function(`"use strict"; return (${expr})`)();
86
+ } catch {
87
+ return void 0;
88
+ }
89
+ }
90
+ var TOP_LEVEL_CONST_RE = /^(?:export\s+)?const\s+([A-Za-z_$][\w$]*)\s*=\s*(.+)$/gm;
91
+ function isLineStart(source, index) {
92
+ const lineStart = source.lastIndexOf("\n", index) + 1;
93
+ return source.slice(lineStart, index).trim() === "";
94
+ }
95
+ var RUNTIME_VALUE_RE = /^(ref|reactive|computed|inject|defineProps|defineEmits|withDefaults|watch|onMounted|use[A-Z]|import|require)\s*\(/;
96
+ function extractScriptConstants(scriptSource) {
97
+ const constants = {};
98
+ let m;
99
+ while ((m = TOP_LEVEL_CONST_RE.exec(scriptSource)) !== null) {
100
+ if (!isLineStart(scriptSource, m.index)) continue;
101
+ const expr = m[2].trim();
102
+ if (RUNTIME_VALUE_RE.test(expr)) continue;
103
+ const value = evalLiteral(expr);
104
+ if (value !== void 0 && typeof value !== "function") constants[m[1]] = value;
105
+ }
106
+ return constants;
107
+ }
108
+
109
+ // src/style-safety/index.ts
110
+ var STYLE_BINDING_RE = /(?::|v-bind:)(style)\s*=\s*(["'])([\s\S]*?)\2/g;
111
+ function splitTopLevel(input) {
112
+ const out = [];
113
+ let depth = 0;
114
+ let quote = "";
115
+ let cur = "";
116
+ for (let i = 0; i < input.length; i++) {
117
+ const ch = input[i];
118
+ if (quote) {
119
+ cur += ch;
120
+ if (ch === quote && input[i - 1] !== "\\") quote = "";
121
+ continue;
122
+ }
123
+ if (ch === "'" || ch === '"' || ch === "`") {
124
+ quote = ch;
125
+ cur += ch;
126
+ continue;
127
+ }
128
+ if (ch === "(" || ch === "[" || ch === "{") depth++;
129
+ else if (ch === ")" || ch === "]" || ch === "}") depth = Math.max(0, depth - 1);
130
+ if (ch === "," && depth === 0) {
131
+ out.push(cur);
132
+ cur = "";
133
+ continue;
134
+ }
135
+ cur += ch;
136
+ }
137
+ if (cur.trim()) out.push(cur);
138
+ return out;
139
+ }
140
+ function findTopLevelColon(s) {
141
+ let depth = 0;
142
+ let quote = "";
143
+ for (let i = 0; i < s.length; i++) {
144
+ const ch = s[i];
145
+ if (quote) {
146
+ if (ch === quote && s[i - 1] !== "\\") quote = "";
147
+ continue;
148
+ }
149
+ if (ch === "'" || ch === '"' || ch === "`") {
150
+ quote = ch;
151
+ continue;
152
+ }
153
+ if (ch === "(" || ch === "[" || ch === "{") depth++;
154
+ else if (ch === ")" || ch === "]" || ch === "}") depth = Math.max(0, depth - 1);
155
+ if (ch === ":" && depth === 0) return i;
156
+ }
157
+ return -1;
158
+ }
159
+ function tryStaticValue(expr) {
160
+ const t = expr.trim();
161
+ if (t.startsWith("'") && t.endsWith("'") && t.length >= 2 || t.startsWith('"') && t.endsWith('"') && t.length >= 2) {
162
+ return t.slice(1, -1);
163
+ }
164
+ if (/^-?\d+(?:\.\d+)?$/.test(t)) return Number(t);
165
+ if (t === "true") return true;
166
+ if (t === "false") return false;
167
+ if (t === "null") return null;
168
+ return void 0;
169
+ }
170
+ function parseStyleObject(expr) {
171
+ const trimmed = expr.trim();
172
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return null;
173
+ const inner = trimmed.slice(1, -1);
174
+ const props = [];
175
+ for (const item of splitTopLevel(inner)) {
176
+ const seg = item.trim();
177
+ if (!seg) continue;
178
+ const colon = findTopLevelColon(seg);
179
+ if (colon < 0) continue;
180
+ let key = seg.slice(0, colon).trim();
181
+ if (key.startsWith("[")) continue;
182
+ const keyStr = /^(['"])(.*)\1$/.exec(key);
183
+ key = keyStr ? keyStr[2] : key;
184
+ if (!key.startsWith("--")) {
185
+ key = key.replace(/-([a-z])/g, (_m, c) => c.toUpperCase());
186
+ }
187
+ const valueExpr = seg.slice(colon + 1).trim();
188
+ props.push({ key, valueExpr, staticValue: tryStaticValue(valueExpr) });
189
+ }
190
+ return props;
191
+ }
192
+ function analyzeStyleBindings(templateSource, constants = {}) {
193
+ const violations = [];
194
+ let staticChecked = 0;
195
+ let dynamic2 = 0;
196
+ let m;
197
+ STYLE_BINDING_RE.lastIndex = 0;
198
+ while ((m = STYLE_BINDING_RE.exec(templateSource)) !== null) {
199
+ const expr = m[3];
200
+ const line = templateSource.slice(0, m.index).split("\n").length;
201
+ const props = parseStyleObject(expr);
202
+ if (props === null) {
203
+ dynamic2++;
204
+ violations.push({ code: "STS006", prop: ":style", value: expr, line, message: ":style \u542B\u52A8\u6001\u6E90\uFF08\u8FD0\u884C\u65F6 validateStyle \u6821\u9A8C\u515C\u5E95\uFF09" });
205
+ continue;
206
+ }
207
+ for (const { key, valueExpr, staticValue } of props) {
208
+ if (key.startsWith("--")) {
209
+ if (staticValue !== void 0) staticChecked++;
210
+ else dynamic2++;
211
+ continue;
212
+ }
213
+ const level = STYLE_PROP_LEVELS[key];
214
+ if (level === void 0) {
215
+ dynamic2++;
216
+ violations.push({ code: "STS001", prop: key, value: valueExpr, line, message: `${key} \u4E0D\u5728\u767D\u540D\u5355\uFF08\u2192 \u6539\u7528 p-* \u8BED\u4E49\u7EC4\u4EF6\u6216\u767D\u540D\u5355\u5C5E\u6027\uFF09` });
217
+ continue;
218
+ }
219
+ if (level === "FORBIDDEN") {
220
+ staticChecked++;
221
+ violations.push({ code: "STS004", prop: key, value: valueExpr, line, message: `${key} \u7981\u7528\uFF08\u2192 <p-flex> / <p-stack>\uFF09` });
222
+ continue;
223
+ }
224
+ if (level === "SEMANTIC_ONLY") {
225
+ staticChecked++;
226
+ violations.push({ code: "STS003", prop: key, value: valueExpr, line, message: `${key} \u5FC5\u987B\u7528\u8BED\u4E49\u7EC4\u4EF6\uFF08\u2192 <p-glass> / <p-filter>\uFF09` });
227
+ continue;
228
+ }
229
+ if (staticValue !== void 0) {
230
+ staticChecked++;
231
+ if (!isStaticValueValid(level, staticValue)) {
232
+ violations.push({ code: "STS002", prop: key, value: valueExpr, line, message: `${key} \u9759\u6001\u503C\u7C7B\u578B\u975E\u6CD5: ${String(staticValue)}` });
233
+ }
234
+ } else {
235
+ const { isFullyStatic } = deriveReachableValues(valueExpr, constants);
236
+ if (isFullyStatic) staticChecked++;
237
+ else dynamic2++;
238
+ }
239
+ }
240
+ }
241
+ const total = staticChecked + dynamic2;
242
+ return { violations, stats: { staticChecked, dynamic: dynamic2, coverage: total > 0 ? staticChecked / total : 1 } };
243
+ }
244
+ function isStaticValueValid(level, value) {
245
+ switch (level) {
246
+ case "Length": {
247
+ if (typeof value === "number") return Number.isFinite(value);
248
+ if (typeof value === "string") return /^-?(?:\d+(?:\.\d+)?|\.\d+)(?:px|rpx|rem|%)?$/.test(value.trim());
249
+ return false;
250
+ }
251
+ case "Opacity":
252
+ return typeof value === "number" && value >= 0 && value <= 1;
253
+ case "Integer":
254
+ return typeof value === "number" && Number.isInteger(value);
255
+ case "Color":
256
+ return typeof value === "string" && (/^#[0-9a-fA-F]{3,8}$/.test(value) || /^rgba?\(/i.test(value) || value === "transparent" || value === "inherit");
257
+ case "FlexNumber":
258
+ return typeof value === "number" && Number.isFinite(value) || value === "auto";
259
+ case "FlexAlign":
260
+ return ["flex-start", "flex-end", "center", "stretch", "baseline", "auto"].indexOf(String(value)) >= 0;
261
+ case "FlexJustify":
262
+ return ["flex-start", "flex-end", "center", "space-between", "space-around", "space-evenly"].indexOf(String(value)) >= 0;
263
+ case "Transform":
264
+ return typeof value === "string" && /^(translate|scale|rotate|skew)/i.test(value);
265
+ case "TransformOrigin":
266
+ return typeof value === "string";
267
+ default:
268
+ return true;
269
+ }
270
+ }
271
+ export {
272
+ analyzeStyleBindings,
273
+ deriveReachableValues,
274
+ extractScriptConstants,
275
+ tryStaticValue
276
+ };
@@ -0,0 +1,8 @@
1
+ export interface ReachableSet {
2
+ values: unknown[];
3
+ isFullyStatic: boolean;
4
+ dynamicSources: string[];
5
+ }
6
+ export declare const DYNAMIC_UNKNOWN = "unknown";
7
+ /** 可达值集推导(05 §2)——expr 为 :style 值表达式字符串 */
8
+ export declare function deriveReachableValues(expr: string, constants?: Record<string, unknown>): ReachableSet;
@@ -0,0 +1,3 @@
1
+ import type { StyleTransformOptions } from './types';
2
+ /** style 源码 → WXSS(纯函数,独立可测) */
3
+ export declare function transformStyleToWxss(source: string, opts?: StyleTransformOptions): string;
package/dist/style.js ADDED
@@ -0,0 +1,96 @@
1
+ import { resolveOverrides } from './overrides';
2
+ // 选择器中的标签名 → 小程序标签(.links a → .links view、h1 → text)
3
+ // 命中条件:标签名必须位于选择器起始或组合器之后(空格 / > + ~ , ( 之后),
4
+ // 前面不能是字母/数字/连字符/类名/ID(.a、#input、tag-a、data-input 均不误伤)
5
+ // 正则按生效映射生成(config customTags / mapping 覆盖后新标签也能重写)
6
+ function makeTagSelectorRe(tagMap, semanticClass) {
7
+ const names = [...Object.keys(tagMap), ...Object.keys(semanticClass)].sort((a, b) => b.length - a.length);
8
+ return new RegExp(`(?<![\\w-.#:\\[*])(?:${names.join('|')})(?=[\\s.#:\\[>+,~)(\\u0000]|$)`, 'g');
9
+ }
10
+ /** 重写单个选择器:先屏蔽属性选择器 [..](引号内容可能含标签字样),重写标签后再还原 */
11
+ // 占位符 \u0000 需在 lookahead 集合中(属性选择器也是标签名的合法后继)
12
+ function rewriteTagSelectors(selector, res, tagRe) {
13
+ const attrs = [];
14
+ const masked = selector.replace(/\[[^\]]*\]/g, (m) => {
15
+ attrs.push(m);
16
+ return `\u0000${attrs.length - 1}\u0000`;
17
+ });
18
+ const rewritten = masked.replace(tagRe, (tag) => {
19
+ // 语义标签(h1-h6/p/a)→ 基础类选择器:模板已附加 proteus-* 类,可精确区分;
20
+ // 若映射为标签会撞选择器(.card h3 与 .card p 都变 .card text,后写覆盖先写 → h3 被染灰)
21
+ const semantic = res.semanticClass[tag];
22
+ if (semantic)
23
+ return `.${semantic}`;
24
+ return res.tagMap[tag] ?? tag;
25
+ });
26
+ return rewritten.replace(/\u0000(\d+)\u0000/g, (_m, i) => attrs[Number(i)]);
27
+ }
28
+ /** 仅重写每条规则的选择器部分(声明块与 @media/@keyframes 骨架原样保留) */
29
+ function rewriteSelectorTags(css, res, tagRe) {
30
+ return css.replace(/([^{}]+)\{/g, (_m, sel) => `${rewriteTagSelectors(sel, res, tagRe)}{`);
31
+ }
32
+ // Web UA 语义基础样式(对齐 HTML 标准附录 D 默认样式表,rpx 直接书写不过 px2rpx):
33
+ // h1-h6/p/a 在 Web 有浏览器默认样式,映射为 text/view 后无默认样式,注入基础类还原语义;
34
+ // margin 用 em(相对自身字号)且只设底部单边——Web 相邻段落 margin 折叠取 max,
35
+ // 而 Skyline 自研引擎不折叠,单边 bottom 在两端主流组合(段落连续 / 标题后接段落)下
36
+ // 视觉间距一致(如 p→p 均为 1em、h1→p 均为 0.67emₕ₁);用户样式特异性更高可覆盖
37
+ const BASE_SEMANTIC_WXSS = [
38
+ '.proteus-h1 { display: block; font-size: 64rpx; font-weight: 700; margin: 0 0 0.67em; }',
39
+ '.proteus-h2 { display: block; font-size: 48rpx; font-weight: 700; margin: 0 0 0.83em; }',
40
+ '.proteus-h3 { display: block; font-size: 36rpx; font-weight: 700; margin: 0 0 1em; }',
41
+ '.proteus-h4 { display: block; font-size: 32rpx; font-weight: 700; margin: 0 0 1.33em; }',
42
+ '.proteus-h5 { display: block; font-size: 28rpx; font-weight: 700; margin: 0 0 1.67em; }',
43
+ '.proteus-h6 { display: block; font-size: 24rpx; font-weight: 700; margin: 0 0 2.33em; }',
44
+ '.proteus-p { display: block; margin: 0 0 1em; }',
45
+ '.proteus-a { color: #1a7af8; text-decoration: underline; }',
46
+ ].join('\n');
47
+ /** 统计选择器重写前源 CSS 中的标签选择器处数(语义标签与普通标签分开计数) */
48
+ function countSelectorRewrites(css, res) {
49
+ const semanticKeys = Object.keys(res.semanticClass);
50
+ const tagKeys = Object.keys(res.tagMap).filter((k) => !semanticKeys.includes(k));
51
+ const lookahead = '[\\s.#:\\[>+,~)(\\u0000]|$';
52
+ const semanticRe = new RegExp(`(?<![\\w-.\\#:\\[*])(?:${semanticKeys.join('|')})(?=${lookahead})`, 'g');
53
+ const tagRePlain = new RegExp(`(?<![\\w-.\\#:\\[*])(?:${tagKeys.join('|')})(?=${lookahead})`, 'g');
54
+ return { tag: (css.match(tagRePlain) ?? []).length, semantic: (css.match(semanticRe) ?? []).length };
55
+ }
56
+ /** style 源码 → WXSS(纯函数,独立可测) */
57
+ export function transformStyleToWxss(source, opts = { px2rpx: true, rpxRatio: 2 }) {
58
+ const trace = opts.trace;
59
+ // ★底线循环 ①③:生效映射 + 禁用集(config rules 即时生效)
60
+ const res = resolveOverrides(opts.rules);
61
+ const tagRe = makeTagSelectorRe(res.tagMap, res.semanticClass);
62
+ const injectBase = !res.disabled.has('style/semantic-base-wxss');
63
+ let css = injectBase ? `${BASE_SEMANTIC_WXSS}\n${source}` : source;
64
+ if (injectBase)
65
+ trace?.add('style/semantic-base-wxss', { before: 'h1-h6/p/a 无 UA 样式', after: '.proteus-h1~h6/.proteus-p/.proteus-a 基础 WXSS(注入在用户样式之前)' });
66
+ // 1. 标签选择器映射(与模板标签映射一一对应,避免元素已映射而样式匹配不到)
67
+ const doSelectorRewrite = !res.disabled.has('style/selector-tag') && !res.disabled.has('style/selector-semantic');
68
+ const counts = doSelectorRewrite ? countSelectorRewrites(css, res) : { tag: 0, semantic: 0 };
69
+ if (doSelectorRewrite)
70
+ css = rewriteSelectorTags(css, res, tagRe);
71
+ if (counts.tag > 0)
72
+ trace?.add('style/selector-tag', { before: `选择器含 HTML 标签(${counts.tag} 处)`, after: '映射为小程序标签(div → view)' });
73
+ if (counts.semantic > 0)
74
+ trace?.add('style/selector-semantic', { before: `h1-h6/p/a 选择器(${counts.semantic} 处)`, after: '.proteus-* 类选择器(避免同特异性覆盖)' });
75
+ // 2. px → rpx
76
+ const doPx2rpx = opts.px2rpx && !res.disabled.has('style/px-to-rpx');
77
+ const pxCount = (css.match(/(\d+(?:\.\d+)?)px\b/g) ?? []).length;
78
+ if (doPx2rpx) {
79
+ css = css.replace(/(\d+(?:\.\d+)?)px\b/g, (_m, n) => `${Number(n) * opts.rpxRatio}rpx`);
80
+ if (pxCount > 0)
81
+ trace?.add('style/px-to-rpx', { before: `${pxCount} 处 px`, after: `${pxCount} 处 rpx(rpxRatio=${opts.rpxRatio})` });
82
+ }
83
+ // 3. Skyline 不支持的属性编译期警告
84
+ const unsupported = [];
85
+ if (!res.disabled.has('style/skyline-unsupported')) {
86
+ if (/float\s*:/.test(css))
87
+ unsupported.push('float');
88
+ if (/position\s*:\s*fixed\b/.test(css))
89
+ unsupported.push('position: fixed');
90
+ }
91
+ for (const u of unsupported) {
92
+ console.warn(`[mp-transform] WXSS 检测到 Skyline 不支持的属性:${u}(编译期警告)`);
93
+ trace?.add('style/skyline-unsupported', { before: u, after: '编译期警告(不阻断构建)' });
94
+ }
95
+ return css;
96
+ }
package/dist/tags.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare const TAG_MAP: Record<string, string>;
2
+ export declare const EVENT_MAP: Record<string, string>;
3
+ export declare const SEMANTIC_CLASS: Record<string, string>;
package/dist/tags.js ADDED
@@ -0,0 +1,49 @@
1
+ // src/compiler/tags.ts
2
+ // 标签 / 事件映射表 —— 模板转换(template.ts)与样式选择器重写(style.ts)共用
3
+ // 业务代码写标准 HTML 标签,编译器统一映射到小程序标签(§0.3 原则 1)
4
+ export const TAG_MAP = {
5
+ div: 'view',
6
+ span: 'text',
7
+ p: 'text',
8
+ h1: 'text',
9
+ h2: 'text',
10
+ h3: 'text',
11
+ h4: 'text',
12
+ h5: 'text',
13
+ h6: 'text',
14
+ img: 'image',
15
+ a: 'view',
16
+ button: 'button',
17
+ input: 'input',
18
+ textarea: 'textarea',
19
+ video: 'video',
20
+ canvas: 'canvas',
21
+ 'scroll-view': 'scroll-view',
22
+ slot: 'slot',
23
+ };
24
+ export const EVENT_MAP = {
25
+ click: 'tap',
26
+ input: 'input',
27
+ change: 'change',
28
+ submit: 'submit',
29
+ focus: 'focus',
30
+ blur: 'blur',
31
+ touchstart: 'touchstart',
32
+ touchmove: 'touchmove',
33
+ touchend: 'touchend',
34
+ longpress: 'longpress',
35
+ confirm: 'confirm',
36
+ };
37
+ // 语义标签 → 基础样式类名
38
+ // Web 端 h1-h6/p/a 有浏览器 UA 默认样式(大标题/加粗/链接色),小程序 text/view 没有默认样式;
39
+ // 映射时给语义标签附加 proteus-* 类,样式侧注入基础 WXSS 还原 Web 语义(用户样式特异性更高可覆盖)
40
+ export const SEMANTIC_CLASS = {
41
+ h1: 'proteus-h1',
42
+ h2: 'proteus-h2',
43
+ h3: 'proteus-h3',
44
+ h4: 'proteus-h4',
45
+ h5: 'proteus-h5',
46
+ h6: 'proteus-h6',
47
+ p: 'proteus-p',
48
+ a: 'proteus-a',
49
+ };
@@ -0,0 +1,3 @@
1
+ import type { TemplateTransformOptions, TemplateTransformResult } from './types';
2
+ /** template 源码 → WXML(纯函数,独立可测) */
3
+ export declare function transformTemplateToWxml(source: string, opts?: TemplateTransformOptions): TemplateTransformResult;