@zhin.js/core 1.0.1 → 1.0.2
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/CHANGELOG.md +7 -0
- package/lib/app.d.ts +1 -1
- package/lib/app.d.ts.map +1 -1
- package/lib/app.js +1 -1
- package/lib/app.js.map +1 -1
- package/lib/component.d.ts +22 -102
- package/lib/component.d.ts.map +1 -1
- package/lib/component.js +438 -242
- package/lib/component.js.map +1 -1
- package/lib/jsx-runtime.d.ts +12 -0
- package/lib/jsx-runtime.d.ts.map +1 -0
- package/lib/jsx-runtime.js +11 -0
- package/lib/jsx-runtime.js.map +1 -0
- package/lib/jsx.d.ts +32 -0
- package/lib/jsx.d.ts.map +1 -0
- package/lib/jsx.js +57 -0
- package/lib/jsx.js.map +1 -0
- package/lib/message.d.ts +9 -6
- package/lib/message.d.ts.map +1 -1
- package/lib/message.js.map +1 -1
- package/lib/plugin.d.ts +2 -2
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +2 -2
- package/lib/plugin.js.map +1 -1
- package/lib/types.d.ts +3 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +3 -1
- package/lib/utils.js.map +1 -1
- package/package.json +10 -2
- package/src/app.ts +3 -3
- package/src/component.ts +523 -280
- package/src/jsx-runtime.ts +12 -0
- package/src/jsx.d.ts +52 -0
- package/src/jsx.ts +92 -0
- package/src/message.ts +7 -4
- package/src/plugin.ts +4 -4
- package/src/types.ts +3 -1
- package/src/utils.ts +6 -5
- package/tests/component-new.test.ts +348 -0
- package/tests/expression-evaluation.test.ts +258 -0
- package/tests/plugin.test.ts +26 -17
- package/tests/component.test.ts +0 -656
package/lib/component.js
CHANGED
|
@@ -1,273 +1,469 @@
|
|
|
1
1
|
import { getValueWithRuntime, compiler, segment } from './utils.js';
|
|
2
|
+
// 组件匹配符号
|
|
2
3
|
export const CapWithChild = Symbol('CapWithChild');
|
|
3
4
|
export const CapWithClose = Symbol('CapWithClose');
|
|
5
|
+
// 组件定义函数 - 简化版,只支持函数式组件
|
|
6
|
+
export function defineComponent(component, name = component.name) {
|
|
7
|
+
if (name) {
|
|
8
|
+
// 创建一个新的函数来避免修改只读属性
|
|
9
|
+
const namedComponent = component;
|
|
10
|
+
Object.defineProperty(namedComponent, 'name', {
|
|
11
|
+
value: name,
|
|
12
|
+
writable: false,
|
|
13
|
+
enumerable: false,
|
|
14
|
+
configurable: true
|
|
15
|
+
});
|
|
16
|
+
return namedComponent;
|
|
17
|
+
}
|
|
18
|
+
return component;
|
|
19
|
+
}
|
|
20
|
+
// 组件匹配函数
|
|
21
|
+
export function matchComponent(comp, template) {
|
|
22
|
+
// 使用更复杂的正则表达式来正确处理大括号内的内容
|
|
23
|
+
const selfClosingRegex = new RegExp(`<${comp.name}((?:[^>]|{[^}]*})*)?/>`);
|
|
24
|
+
const closingRegex = new RegExp(`<${comp.name}((?:[^>]|{[^}]*})*)?>([^<]*?)</${comp.name}>`);
|
|
25
|
+
let match = template.match(selfClosingRegex);
|
|
26
|
+
if (!match) {
|
|
27
|
+
match = template.match(closingRegex);
|
|
28
|
+
}
|
|
29
|
+
return match ? match[0] : '';
|
|
30
|
+
}
|
|
31
|
+
// 属性解析函数 - 支持 children
|
|
32
|
+
export function getProps(comp, template, context) {
|
|
33
|
+
// 1. 首先匹配组件标签,支持自闭合和闭合标签
|
|
34
|
+
const selfClosingRegex = new RegExp(`<${comp.name}((?:[^>]|{[^}]*})*)?/>`);
|
|
35
|
+
const closingRegex = new RegExp(`<${comp.name}((?:[^>]|{[^}]*})*)?>([^<]*?)</${comp.name}>`);
|
|
36
|
+
let match = template.match(selfClosingRegex);
|
|
37
|
+
let isSelfClosing = true;
|
|
38
|
+
let children = '';
|
|
39
|
+
if (!match) {
|
|
40
|
+
match = template.match(closingRegex);
|
|
41
|
+
isSelfClosing = false;
|
|
42
|
+
if (match) {
|
|
43
|
+
children = match[2] || '';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!match) {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
const attributesString = match[1] || '';
|
|
50
|
+
// 2. 解析属性,支持多种格式
|
|
51
|
+
const props = {};
|
|
52
|
+
// 如果有属性字符串,解析属性
|
|
53
|
+
if (attributesString.trim()) {
|
|
54
|
+
// 使用手动解析来处理复杂的嵌套结构
|
|
55
|
+
let i = 0;
|
|
56
|
+
while (i < attributesString.length) {
|
|
57
|
+
// 跳过空白字符
|
|
58
|
+
while (i < attributesString.length && /\s/.test(attributesString[i])) {
|
|
59
|
+
i++;
|
|
60
|
+
}
|
|
61
|
+
if (i >= attributesString.length)
|
|
62
|
+
break;
|
|
63
|
+
// 解析属性名
|
|
64
|
+
let key = '';
|
|
65
|
+
while (i < attributesString.length && /[a-zA-Z0-9_$\-]/.test(attributesString[i])) {
|
|
66
|
+
key += attributesString[i];
|
|
67
|
+
i++;
|
|
68
|
+
}
|
|
69
|
+
if (!key) {
|
|
70
|
+
i++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// 跳过空白字符
|
|
74
|
+
while (i < attributesString.length && /\s/.test(attributesString[i])) {
|
|
75
|
+
i++;
|
|
76
|
+
}
|
|
77
|
+
// 检查是否有等号
|
|
78
|
+
if (i < attributesString.length && attributesString[i] === '=') {
|
|
79
|
+
i++; // 跳过等号
|
|
80
|
+
// 跳过空白字符
|
|
81
|
+
while (i < attributesString.length && /\s/.test(attributesString[i])) {
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
if (i >= attributesString.length) {
|
|
85
|
+
props[key] = true;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
// 解析属性值
|
|
89
|
+
const value = parseAttributeValue(attributesString, i, context);
|
|
90
|
+
props[key] = value.value;
|
|
91
|
+
i = value.nextIndex;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// 没有等号,是布尔属性
|
|
95
|
+
props[key] = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// 3. 处理 children(如果不是自闭合标签)
|
|
100
|
+
if (!isSelfClosing && children.trim()) {
|
|
101
|
+
props.children = children;
|
|
102
|
+
}
|
|
103
|
+
// 4. 处理 kebab-case 到 camelCase 的转换
|
|
104
|
+
const camelCaseProps = {};
|
|
105
|
+
for (const [key, value] of Object.entries(props)) {
|
|
106
|
+
const camelKey = key.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
107
|
+
camelCaseProps[camelKey] = value;
|
|
108
|
+
}
|
|
109
|
+
return camelCaseProps;
|
|
110
|
+
}
|
|
4
111
|
/**
|
|
5
|
-
*
|
|
6
|
-
* 用于自定义消息结构和复用UI片段。
|
|
7
|
-
* @template T 组件props类型
|
|
8
|
-
* @template D 组件data类型
|
|
9
|
-
* @template P 组件props配置类型
|
|
112
|
+
* 解析属性值
|
|
10
113
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
[
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
114
|
+
function parseAttributeValue(str, startIndex, context) {
|
|
115
|
+
let i = startIndex;
|
|
116
|
+
// 处理引号包围的字符串
|
|
117
|
+
if (str[i] === '"' || str[i] === "'") {
|
|
118
|
+
const quote = str[i];
|
|
119
|
+
i++; // 跳过开始引号
|
|
120
|
+
let value = '';
|
|
121
|
+
while (i < str.length && str[i] !== quote) {
|
|
122
|
+
if (str[i] === '\\' && i + 1 < str.length) {
|
|
123
|
+
// 处理转义字符
|
|
124
|
+
i++;
|
|
125
|
+
value += str[i];
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
value += str[i];
|
|
129
|
+
}
|
|
130
|
+
i++;
|
|
131
|
+
}
|
|
132
|
+
if (i < str.length) {
|
|
133
|
+
i++; // 跳过结束引号
|
|
134
|
+
}
|
|
135
|
+
// 检查引号内是否包含表达式(大括号)
|
|
136
|
+
if (value.includes('{') && value.includes('}')) {
|
|
137
|
+
// 包含表达式,进行求值
|
|
138
|
+
const evaluatedValue = evaluateQuotedExpression(value, context);
|
|
139
|
+
return { value: evaluatedValue, nextIndex: i };
|
|
140
|
+
}
|
|
141
|
+
return { value, nextIndex: i };
|
|
18
142
|
}
|
|
19
|
-
|
|
20
|
-
|
|
143
|
+
// 处理大括号包围的表达式
|
|
144
|
+
if (str[i] === '{') {
|
|
145
|
+
let braceCount = 0;
|
|
146
|
+
let value = '';
|
|
147
|
+
i++; // 跳过开始大括号
|
|
148
|
+
while (i < str.length) {
|
|
149
|
+
if (str[i] === '{') {
|
|
150
|
+
braceCount++;
|
|
151
|
+
}
|
|
152
|
+
else if (str[i] === '}') {
|
|
153
|
+
if (braceCount === 0) {
|
|
154
|
+
i++; // 跳过结束大括号
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
braceCount--;
|
|
158
|
+
}
|
|
159
|
+
value += str[i];
|
|
160
|
+
i++;
|
|
161
|
+
}
|
|
162
|
+
return { value: parseExpressionValue(value, context), nextIndex: i };
|
|
21
163
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.$options = $options;
|
|
28
|
-
this.formatProps();
|
|
29
|
-
this[CapWithChild] = new RegExp(`<${$options.name}([^>]*)?>([^<])*?</${$options.name}>`);
|
|
30
|
-
this[CapWithClose] = new RegExp(`<${$options.name}([^>]*)?/>`);
|
|
164
|
+
// 处理无引号的值
|
|
165
|
+
let value = '';
|
|
166
|
+
while (i < str.length && !/\s/.test(str[i]) && str[i] !== '>') {
|
|
167
|
+
value += str[i];
|
|
168
|
+
i++;
|
|
31
169
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
170
|
+
return { value: parseUnquotedValue(value, context), nextIndex: i };
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 求值引号内的表达式
|
|
174
|
+
*/
|
|
175
|
+
function evaluateQuotedExpression(quotedValue, context) {
|
|
176
|
+
if (!context)
|
|
177
|
+
return quotedValue;
|
|
178
|
+
// 处理引号内的表达式,将 {expr} 格式转换为 ${expr} 格式
|
|
179
|
+
let result = quotedValue;
|
|
180
|
+
const expressionRegex = /\{([^}]+)\}/g;
|
|
181
|
+
let match;
|
|
182
|
+
while ((match = expressionRegex.exec(quotedValue)) !== null) {
|
|
183
|
+
const expr = match[1];
|
|
184
|
+
try {
|
|
185
|
+
const value = context.getValue(expr);
|
|
186
|
+
if (value !== undefined && value !== expr) {
|
|
187
|
+
const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
188
|
+
result = result.replace(match[0], stringValue);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
// 如果求值失败,保持原始表达式
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 解析表达式值
|
|
199
|
+
*/
|
|
200
|
+
function parseExpressionValue(expr, context) {
|
|
201
|
+
expr = expr.trim();
|
|
202
|
+
// 处理字符串字面量
|
|
203
|
+
if ((expr.startsWith('"') && expr.endsWith('"')) ||
|
|
204
|
+
(expr.startsWith("'") && expr.endsWith("'"))) {
|
|
205
|
+
return expr.slice(1, -1);
|
|
38
206
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* @returns 匹配到的标签内容
|
|
43
|
-
*/
|
|
44
|
-
match(template) {
|
|
45
|
-
let [match] = this[CapWithChild].exec(template) || [];
|
|
46
|
-
if (match)
|
|
47
|
-
return match;
|
|
48
|
-
[match] = this[CapWithClose].exec(template) || [];
|
|
49
|
-
return match;
|
|
207
|
+
// 处理数字
|
|
208
|
+
if (/^-?\d+(\.\d+)?$/.test(expr)) {
|
|
209
|
+
return parseFloat(expr);
|
|
50
210
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
211
|
+
// 处理布尔值
|
|
212
|
+
if (expr === 'true')
|
|
213
|
+
return true;
|
|
214
|
+
if (expr === 'false')
|
|
215
|
+
return false;
|
|
216
|
+
if (expr === 'null')
|
|
217
|
+
return null;
|
|
218
|
+
if (expr === 'undefined')
|
|
219
|
+
return undefined;
|
|
220
|
+
// 处理数组
|
|
221
|
+
if (expr.startsWith('[') && expr.endsWith(']')) {
|
|
222
|
+
try {
|
|
223
|
+
return JSON.parse(expr);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// 如果JSON解析失败,尝试手动解析简单数组
|
|
227
|
+
const items = expr.slice(1, -1).split(',').map(item => parseExpressionValue(item.trim(), context));
|
|
228
|
+
return items;
|
|
57
229
|
}
|
|
58
230
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
231
|
+
// 处理对象 - 改进的嵌套大括号处理
|
|
232
|
+
if (expr.startsWith('{') && expr.endsWith('}')) {
|
|
233
|
+
try {
|
|
234
|
+
return JSON.parse(expr);
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// 如果JSON解析失败,尝试手动解析简单对象
|
|
238
|
+
try {
|
|
239
|
+
return parseSimpleObject(expr);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// 如果都失败,返回原始字符串
|
|
243
|
+
return expr;
|
|
244
|
+
}
|
|
71
245
|
}
|
|
72
|
-
return this.$props.push({
|
|
73
|
-
name,
|
|
74
|
-
type: value.type,
|
|
75
|
-
default: value.default,
|
|
76
|
-
});
|
|
77
246
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
247
|
+
// 处理表达式 - 在沙盒中执行
|
|
248
|
+
if (context) {
|
|
249
|
+
try {
|
|
250
|
+
const result = context.getValue(expr);
|
|
251
|
+
// 如果结果是 undefined,说明表达式被阻止或求值失败,返回原始表达式
|
|
252
|
+
if (result === undefined) {
|
|
253
|
+
return expr;
|
|
254
|
+
}
|
|
85
255
|
return result;
|
|
86
|
-
for (const [_, key, __, value] of matchedArr) {
|
|
87
|
-
Object.defineProperty(result, key, {
|
|
88
|
-
enumerable: true,
|
|
89
|
-
writable: false,
|
|
90
|
-
value,
|
|
91
|
-
});
|
|
92
256
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return '';
|
|
98
|
-
const matched = template.match(/<[^>]+>([^<]*?)<\/[^?]+>/);
|
|
99
|
-
if (!matched)
|
|
100
|
-
return '';
|
|
101
|
-
return matched[1];
|
|
257
|
+
catch (error) {
|
|
258
|
+
// 如果执行失败,返回原始表达式
|
|
259
|
+
return expr;
|
|
260
|
+
}
|
|
102
261
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
262
|
+
// 如果没有上下文,返回原始表达式
|
|
263
|
+
return expr;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 解析简单对象(处理嵌套大括号和方括号)
|
|
267
|
+
*/
|
|
268
|
+
function parseSimpleObject(objStr) {
|
|
269
|
+
const result = {};
|
|
270
|
+
let i = 1; // 跳过开始的 {
|
|
271
|
+
let depth = 0;
|
|
272
|
+
let bracketDepth = 0;
|
|
273
|
+
let key = '';
|
|
274
|
+
let value = '';
|
|
275
|
+
let inKey = true;
|
|
276
|
+
let inString = false;
|
|
277
|
+
let stringChar = '';
|
|
278
|
+
while (i < objStr.length - 1) { // 跳过结束的 }
|
|
279
|
+
const char = objStr[i];
|
|
280
|
+
if (!inString) {
|
|
281
|
+
if (char === '{') {
|
|
282
|
+
depth++;
|
|
283
|
+
value += char;
|
|
113
284
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return `${char}${later.toUpperCase()}`;
|
|
118
|
-
});
|
|
119
|
-
if (key !== newKey) {
|
|
120
|
-
Object.defineProperty(props, newKey, {
|
|
121
|
-
value: Reflect.get(props, key),
|
|
122
|
-
enumerable: true,
|
|
123
|
-
});
|
|
124
|
-
Reflect.deleteProperty(props, key);
|
|
285
|
+
else if (char === '}') {
|
|
286
|
+
depth--;
|
|
287
|
+
value += char;
|
|
125
288
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
${name}:list[${name}]
|
|
145
|
-
}\n
|
|
146
|
-
}))\n
|
|
289
|
+
else if (char === '[') {
|
|
290
|
+
bracketDepth++;
|
|
291
|
+
value += char;
|
|
292
|
+
}
|
|
293
|
+
else if (char === ']') {
|
|
294
|
+
bracketDepth--;
|
|
295
|
+
value += char;
|
|
296
|
+
}
|
|
297
|
+
else if (char === ':' && depth === 0 && bracketDepth === 0) {
|
|
298
|
+
inKey = false;
|
|
299
|
+
i++;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
else if (char === ',' && depth === 0 && bracketDepth === 0) {
|
|
303
|
+
// 处理键值对
|
|
304
|
+
if (key.trim() && value.trim()) {
|
|
305
|
+
const parsedValue = parseExpressionValue(value.trim());
|
|
306
|
+
result[key.trim()] = parsedValue;
|
|
147
307
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
.replace(`vFor="${expression}"`, '')
|
|
154
|
-
.replace(`vFor='${expression}'`, '');
|
|
155
|
-
return (await Promise.all(fn(this.render.bind(this), list, newTpl, context))).join('');
|
|
308
|
+
key = '';
|
|
309
|
+
value = '';
|
|
310
|
+
inKey = true;
|
|
311
|
+
i++;
|
|
312
|
+
continue;
|
|
156
313
|
}
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
314
|
+
else if (char === '"' || char === "'") {
|
|
315
|
+
inString = true;
|
|
316
|
+
stringChar = char;
|
|
317
|
+
if (inKey) {
|
|
318
|
+
key += char;
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
value += char;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
if (inKey) {
|
|
326
|
+
key += char;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
value += char;
|
|
330
|
+
}
|
|
161
331
|
}
|
|
162
332
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
333
|
+
else {
|
|
334
|
+
if (char === stringChar) {
|
|
335
|
+
inString = false;
|
|
336
|
+
}
|
|
337
|
+
if (inKey) {
|
|
338
|
+
key += char;
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
value += char;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
i++;
|
|
345
|
+
}
|
|
346
|
+
// 处理最后一个键值对
|
|
347
|
+
if (key.trim() && value.trim()) {
|
|
348
|
+
const parsedValue = parseExpressionValue(value.trim());
|
|
349
|
+
result[key.trim()] = parsedValue;
|
|
176
350
|
}
|
|
351
|
+
return result;
|
|
177
352
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
353
|
+
/**
|
|
354
|
+
* 解析无引号的值
|
|
355
|
+
*/
|
|
356
|
+
function parseUnquotedValue(value, context) {
|
|
357
|
+
// 检查是否是大括号表达式
|
|
358
|
+
if (value.startsWith('{') && value.endsWith('}')) {
|
|
359
|
+
const expr = value.slice(1, -1); // 移除大括号
|
|
360
|
+
return parseExpressionValue(expr, context);
|
|
361
|
+
}
|
|
362
|
+
// 处理布尔值
|
|
363
|
+
if (value === 'true')
|
|
364
|
+
return true;
|
|
365
|
+
if (value === 'false')
|
|
366
|
+
return false;
|
|
367
|
+
// 处理数字
|
|
368
|
+
if (/^-?\d+(\.\d+)?$/.test(value)) {
|
|
369
|
+
return parseFloat(value);
|
|
370
|
+
}
|
|
371
|
+
// 处理 null/undefined
|
|
372
|
+
if (value === 'null')
|
|
373
|
+
return null;
|
|
374
|
+
if (value === 'undefined')
|
|
375
|
+
return undefined;
|
|
376
|
+
// 其他情况作为字符串处理
|
|
377
|
+
return value;
|
|
185
378
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
379
|
+
// 创建组件上下文的工厂函数
|
|
380
|
+
export function createComponentContext(props = {}, parent, root = '') {
|
|
381
|
+
return {
|
|
382
|
+
// 基础渲染能力
|
|
383
|
+
render: async (template, context) => {
|
|
384
|
+
// 这里需要实现渲染逻辑
|
|
385
|
+
return template;
|
|
386
|
+
},
|
|
387
|
+
// 数据访问(只读)
|
|
388
|
+
props: Object.freeze({ ...props }),
|
|
389
|
+
// 父组件上下文(只读)
|
|
390
|
+
parent: parent ? Object.freeze(parent) : undefined,
|
|
391
|
+
// 根模板(只读)
|
|
392
|
+
root,
|
|
393
|
+
// 子组件内容(React 概念)
|
|
394
|
+
children: undefined,
|
|
395
|
+
getValue: (template) => getValueWithRuntime(template, props),
|
|
396
|
+
compile: (template) => compiler(template, props),
|
|
198
397
|
};
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
398
|
+
}
|
|
399
|
+
export async function renderComponent(component, template, context) {
|
|
400
|
+
const props = getProps(component, template, context);
|
|
401
|
+
return component(props, context);
|
|
402
|
+
}
|
|
403
|
+
// 渲染函数 - 支持新的组件系统
|
|
404
|
+
export async function renderComponents(componentMap, options, customContext) {
|
|
405
|
+
if (!componentMap.size)
|
|
406
|
+
return options;
|
|
407
|
+
const components = [...Array.from(componentMap.values()), Fetch, Fragment];
|
|
408
|
+
// 创建根上下文
|
|
409
|
+
const rootContext = customContext || createComponentContext(options, undefined, typeof options.content === 'string' ? options.content : segment.toString(options.content));
|
|
410
|
+
// 实现渲染逻辑
|
|
411
|
+
const renderWithContext = async (template, context) => {
|
|
412
|
+
let result = template;
|
|
413
|
+
let hasChanges = true;
|
|
414
|
+
let iterations = 0;
|
|
415
|
+
const maxIterations = 10; // 防止无限循环
|
|
416
|
+
// 编译模板
|
|
417
|
+
result = context.compile(result);
|
|
418
|
+
// 递归处理所有组件,直到没有更多组件需要渲染
|
|
419
|
+
while (hasChanges && iterations < maxIterations) {
|
|
420
|
+
hasChanges = false;
|
|
421
|
+
iterations++;
|
|
222
422
|
for (const comp of components) {
|
|
223
|
-
const match = comp
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const content = segment.from(output);
|
|
233
|
-
return {
|
|
234
|
-
...options,
|
|
235
|
-
content
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
Component.render = render;
|
|
239
|
-
Component.Template = defineComponent({
|
|
240
|
-
name: 'template',
|
|
241
|
-
render(props, context) {
|
|
242
|
-
const keys = Object.keys(props);
|
|
243
|
-
if (!keys.length)
|
|
244
|
-
keys.push('#default');
|
|
245
|
-
for (const key of Object.keys(props)) {
|
|
246
|
-
if (key.startsWith('#')) {
|
|
247
|
-
context.parent.$slots[key.slice(1)] = (async (p) => {
|
|
248
|
-
return await context.render(context.children || '', { ...context, ...p });
|
|
249
|
-
});
|
|
423
|
+
const match = matchComponent(comp, result);
|
|
424
|
+
if (match) {
|
|
425
|
+
// 创建组件特定的上下文
|
|
426
|
+
const componentContext = createComponentContext(context.props, context, result);
|
|
427
|
+
const rendered = await renderComponent(comp, match, componentContext);
|
|
428
|
+
const renderedString = typeof rendered === 'string' ? rendered : segment.toString(rendered);
|
|
429
|
+
result = result.replace(match, renderedString);
|
|
430
|
+
hasChanges = true;
|
|
431
|
+
break; // 处理一个组件后重新开始循环
|
|
250
432
|
}
|
|
251
433
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
434
|
+
}
|
|
435
|
+
return result;
|
|
436
|
+
};
|
|
437
|
+
// 更新根上下文的渲染函数
|
|
438
|
+
rootContext.render = async (template, context) => {
|
|
439
|
+
return await renderWithContext(template, rootContext);
|
|
440
|
+
};
|
|
441
|
+
// 渲染模板
|
|
442
|
+
const output = await renderWithContext(rootContext.root, rootContext);
|
|
443
|
+
const content = typeof output === 'string' ? segment.from(output) : output;
|
|
444
|
+
return {
|
|
445
|
+
...options,
|
|
446
|
+
content
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
// 内置组件
|
|
450
|
+
export const Fragment = defineComponent(async (props, context) => {
|
|
451
|
+
let children = props.children || '';
|
|
452
|
+
if (Array.isArray(children)) {
|
|
453
|
+
return children.join('');
|
|
454
|
+
}
|
|
455
|
+
if (typeof children === 'string') {
|
|
456
|
+
try {
|
|
457
|
+
const parsed = JSON.parse(segment.unescape(children));
|
|
458
|
+
return context.render(parsed || '', context);
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
return context.render(children || '', context);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return String(children);
|
|
465
|
+
}, 'Fragment');
|
|
466
|
+
export const Fetch = defineComponent(async ({ url }) => {
|
|
467
|
+
return await fetch(url).then((r) => r.text());
|
|
468
|
+
}, 'fetch');
|
|
273
469
|
//# sourceMappingURL=component.js.map
|