bobe-dom 0.0.67 → 0.0.69
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/dist/bobe-dom.cjs +281 -0
- package/dist/bobe-dom.cjs.map +1 -0
- package/dist/bobe-dom.esm.js +28 -319
- package/dist/bobe-dom.esm.js.map +1 -1
- package/dist/{code.cjs.js → code.cjs} +3 -3
- package/dist/code.cjs.map +1 -0
- package/dist/code.esm.js +2 -2
- package/dist/code.esm.js.map +1 -1
- package/dist/iconify.cjs +442 -0
- package/dist/iconify.cjs.map +1 -0
- package/dist/iconify.d.ts +42 -0
- package/dist/iconify.esm.js +432 -0
- package/dist/iconify.esm.js.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -18
- package/dist/index.umd.js +40 -332
- package/dist/index.umd.js.map +1 -1
- package/dist/{markdown.cjs.js → markdown.cjs} +81 -15
- package/dist/markdown.cjs.map +1 -0
- package/dist/markdown.esm.js +80 -14
- package/dist/markdown.esm.js.map +1 -1
- package/dist/ssr-index.cjs +298 -0
- package/dist/ssr-index.cjs.map +1 -0
- package/dist/ssr-index.esm.js +295 -0
- package/dist/ssr-index.esm.js.map +1 -0
- package/package.json +19 -7
- package/dist/bobe-dom.cjs.js +0 -574
- package/dist/bobe-dom.cjs.js.map +0 -1
- package/dist/code.cjs.js.map +0 -1
- package/dist/markdown.cjs.js.map +0 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var bobe = require('bobe');
|
|
4
|
+
var htmlparser2 = require('htmlparser2');
|
|
5
|
+
|
|
6
|
+
(function (SSRNodeType) {
|
|
7
|
+
SSRNodeType[SSRNodeType["Element"] = 0] = "Element";
|
|
8
|
+
SSRNodeType[SSRNodeType["Text"] = 1] = "Text";
|
|
9
|
+
SSRNodeType[SSRNodeType["Anchor"] = 2] = "Anchor";
|
|
10
|
+
SSRNodeType[SSRNodeType["Root"] = 3] = "Root";
|
|
11
|
+
return SSRNodeType;
|
|
12
|
+
})({});
|
|
13
|
+
class SSRFiber {
|
|
14
|
+
parent = undefined;
|
|
15
|
+
next = undefined;
|
|
16
|
+
child = undefined;
|
|
17
|
+
html = undefined;
|
|
18
|
+
openTagEnd = undefined;
|
|
19
|
+
constructor(type, props = {}) {
|
|
20
|
+
this.type = type;
|
|
21
|
+
this.props = props;
|
|
22
|
+
}
|
|
23
|
+
querySelector(selector) {
|
|
24
|
+
const idMatch = selector.match(/#([\w-]+)/);
|
|
25
|
+
const id = idMatch ? idMatch[1] : null;
|
|
26
|
+
const classMatches = selector.match(/\.[\w-]+/g);
|
|
27
|
+
const classes = classMatches ? classMatches.map(c => c.slice(1)) : [];
|
|
28
|
+
const tag = selector.replace(/#[\w-]+/g, '').replace(/\.[\w-]+/g, '').trim() || null;
|
|
29
|
+
const walk = node => {
|
|
30
|
+
if (!node) return null;
|
|
31
|
+
if (node.type !== 'root' && node.type !== 'anchor' && node.type !== 'text') {
|
|
32
|
+
if (!tag || node.type === tag) {
|
|
33
|
+
if (!id || node.props['id'] === id) {
|
|
34
|
+
if (classes.length === 0 || classes.every(c => (node.props['class'] || '').split(/\s+/).includes(c))) {
|
|
35
|
+
return node;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return walk(node.child) || walk(node.next);
|
|
41
|
+
};
|
|
42
|
+
return walk(this.child);
|
|
43
|
+
}
|
|
44
|
+
getElementById(id) {
|
|
45
|
+
return this.querySelector(`#${id}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseHtmlToFibers(html, root) {
|
|
50
|
+
const stack = [{
|
|
51
|
+
parent: root,
|
|
52
|
+
lastChild: null
|
|
53
|
+
}];
|
|
54
|
+
const append = node => {
|
|
55
|
+
const frame = stack[stack.length - 1];
|
|
56
|
+
if (!frame.parent.child) {
|
|
57
|
+
frame.parent.child = node;
|
|
58
|
+
} else if (frame.lastChild) {
|
|
59
|
+
frame.lastChild.next = node;
|
|
60
|
+
}
|
|
61
|
+
frame.lastChild = node;
|
|
62
|
+
node.parent = frame.parent;
|
|
63
|
+
};
|
|
64
|
+
let parserRef;
|
|
65
|
+
const parser = new htmlparser2.Parser({
|
|
66
|
+
onparserinit(p) {
|
|
67
|
+
parserRef = p;
|
|
68
|
+
},
|
|
69
|
+
onopentag(name, attribs) {
|
|
70
|
+
const fiber = new SSRFiber(name, {
|
|
71
|
+
...attribs
|
|
72
|
+
});
|
|
73
|
+
fiber.openTagEnd = parserRef.endIndex;
|
|
74
|
+
append(fiber);
|
|
75
|
+
stack.push({
|
|
76
|
+
parent: fiber,
|
|
77
|
+
lastChild: null
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
ontext(text) {
|
|
81
|
+
const fiber = new SSRFiber('text', {
|
|
82
|
+
children: text
|
|
83
|
+
});
|
|
84
|
+
append(fiber);
|
|
85
|
+
},
|
|
86
|
+
onclosetag() {
|
|
87
|
+
stack.pop();
|
|
88
|
+
const top = stack[stack.length - 1];
|
|
89
|
+
top.lastChild ?? null;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
parser.write(html);
|
|
93
|
+
parser.end();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const VOID_TAGS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
|
|
97
|
+
const BOOLEAN_ATTRS = new Set(['disabled', 'readonly', 'checked', 'selected', 'hidden', 'multiple', 'required', 'autofocus', 'autoplay', 'controls', 'loop', 'muted', 'defer', 'async', 'reversed', 'open', 'itemscope', 'ismap', 'nohref', 'noshade', 'nowrap', 'compact', 'default']);
|
|
98
|
+
const ATTR_RE = /[&"<>]/g;
|
|
99
|
+
const TEXT_RE = /[&<>]/g;
|
|
100
|
+
const ATTR_MAP = {
|
|
101
|
+
'&': '&',
|
|
102
|
+
'"': '"',
|
|
103
|
+
'<': '<',
|
|
104
|
+
'>': '>'
|
|
105
|
+
};
|
|
106
|
+
const TEXT_MAP = {
|
|
107
|
+
'&': '&',
|
|
108
|
+
'<': '<',
|
|
109
|
+
'>': '>'
|
|
110
|
+
};
|
|
111
|
+
const escapeAttr = str => {
|
|
112
|
+
const s = String(str);
|
|
113
|
+
return ATTR_RE.test(s) ? s.replace(ATTR_RE, m => ATTR_MAP[m]) : s;
|
|
114
|
+
};
|
|
115
|
+
const escapeText = str => {
|
|
116
|
+
const s = String(str);
|
|
117
|
+
return TEXT_RE.test(s) ? s.replace(TEXT_RE, m => TEXT_MAP[m]) : s;
|
|
118
|
+
};
|
|
119
|
+
const createNode = name => {
|
|
120
|
+
return new SSRFiber(name);
|
|
121
|
+
};
|
|
122
|
+
const setProp = (node, key, value) => {
|
|
123
|
+
node.props[key] = value;
|
|
124
|
+
if (key === 'html') {
|
|
125
|
+
parseHtmlToFibers(value, node);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const beforeIndent = node => {
|
|
129
|
+
if (node.props.html != null) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const insertAfter = (parent, node, prev) => {
|
|
134
|
+
let next;
|
|
135
|
+
if (prev) {
|
|
136
|
+
next = prev.next;
|
|
137
|
+
prev.next = node;
|
|
138
|
+
} else {
|
|
139
|
+
next = parent.child;
|
|
140
|
+
parent.child = node;
|
|
141
|
+
}
|
|
142
|
+
node.next = next;
|
|
143
|
+
node.parent = parent;
|
|
144
|
+
};
|
|
145
|
+
const createAnchor = (name, isBefore) => {
|
|
146
|
+
return new SSRFiber('anchor', {
|
|
147
|
+
name,
|
|
148
|
+
isBefore
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
const remove = (node, prev) => {
|
|
152
|
+
const parent = node.parent,
|
|
153
|
+
next = node.next;
|
|
154
|
+
node.next = null;
|
|
155
|
+
if (prev) {
|
|
156
|
+
prev.next = next;
|
|
157
|
+
} else {
|
|
158
|
+
parent.child = next;
|
|
159
|
+
}
|
|
160
|
+
node.parent = null;
|
|
161
|
+
};
|
|
162
|
+
const firstChild = node => node.child;
|
|
163
|
+
const nextSib = node => node.next;
|
|
164
|
+
function walkFiber(root) {
|
|
165
|
+
let point = root;
|
|
166
|
+
let shouldSink = true;
|
|
167
|
+
sink: do {
|
|
168
|
+
if (point.type === 'root') {
|
|
169
|
+
point.html = '';
|
|
170
|
+
} else if (point.type === 'anchor') {
|
|
171
|
+
point.html = `<!--${point.props.name}-->`;
|
|
172
|
+
} else if (point.type === 'text') {
|
|
173
|
+
const text = point.props.children;
|
|
174
|
+
if (text != null) {
|
|
175
|
+
point.html = escapeText(point.props.children);
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
point.html = `<${point.type}`;
|
|
179
|
+
const props = point.props;
|
|
180
|
+
let text;
|
|
181
|
+
let classStr = '';
|
|
182
|
+
const dotClasses = [];
|
|
183
|
+
let idValue = null;
|
|
184
|
+
for (const key in props) {
|
|
185
|
+
const value = props[key];
|
|
186
|
+
if (key.startsWith('on') || key === 'ref') continue;
|
|
187
|
+
if (key === 'html') {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (key === 'children') {
|
|
191
|
+
text = value;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (key.startsWith('.')) {
|
|
195
|
+
if (value) dotClasses.push(key.slice(1));
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (key.startsWith('#')) {
|
|
199
|
+
if (value) idValue = key.slice(1);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (key === 'class') {
|
|
203
|
+
if (value == null) continue;
|
|
204
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
205
|
+
classStr = Object.entries(value).filter(([, v]) => !!v).map(([k]) => k).join(' ');
|
|
206
|
+
} else {
|
|
207
|
+
classStr = typeof value === 'boolean' ? value ? 'true' : '' : String(value);
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (key === 'style') {
|
|
212
|
+
if (value == null) continue;
|
|
213
|
+
point.html += ` style="${escapeAttr(String(value))}"`;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (BOOLEAN_ATTRS.has(key)) {
|
|
217
|
+
if (value !== false && value !== null && value !== undefined) {
|
|
218
|
+
point.html += ` ${key}`;
|
|
219
|
+
}
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (key.startsWith('data-') || key.startsWith('aria-')) {
|
|
223
|
+
if (value == null) continue;
|
|
224
|
+
point.html += ` ${key}="${escapeAttr(value)}"`;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (value == null) continue;
|
|
228
|
+
point.html += ` ${key}="${escapeAttr(value)}"`;
|
|
229
|
+
}
|
|
230
|
+
if (dotClasses.length) {
|
|
231
|
+
classStr = (classStr ? classStr + ' ' : '') + dotClasses.join(' ');
|
|
232
|
+
}
|
|
233
|
+
if (classStr) {
|
|
234
|
+
point.html += ` class="${escapeAttr(classStr)}"`;
|
|
235
|
+
}
|
|
236
|
+
if (idValue) {
|
|
237
|
+
point.html += ` id="${escapeAttr(idValue)}"`;
|
|
238
|
+
}
|
|
239
|
+
if (text != null && props.html == null) {
|
|
240
|
+
const content = escapeText(text);
|
|
241
|
+
point.html += `>${content}</${point.type}>`;
|
|
242
|
+
if (point.child) console.warn(`<${point.type}> has text content and child elements — children ignored`);
|
|
243
|
+
shouldSink = false;
|
|
244
|
+
} else {
|
|
245
|
+
if (VOID_TAGS.has(point.type)) {
|
|
246
|
+
point.html += `/>`;
|
|
247
|
+
if (point.child) console.warn(`<${point.type}> can't have children`);
|
|
248
|
+
shouldSink = false;
|
|
249
|
+
} else {
|
|
250
|
+
point.html += `>`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (point.child && shouldSink) {
|
|
255
|
+
point = point.child;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
do {
|
|
259
|
+
const notRoot = point !== root;
|
|
260
|
+
const notAnchor = point.type !== 'anchor';
|
|
261
|
+
const notText = point.type !== 'text';
|
|
262
|
+
if (shouldSink && notRoot && notAnchor && notText) {
|
|
263
|
+
point.html += `</${point.type}>`;
|
|
264
|
+
}
|
|
265
|
+
if (notRoot) {
|
|
266
|
+
point.parent.html += point.html;
|
|
267
|
+
}
|
|
268
|
+
shouldSink = true;
|
|
269
|
+
if (!notRoot) break sink;
|
|
270
|
+
if (point.next) {
|
|
271
|
+
point = point.next;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
point = point.parent;
|
|
275
|
+
} while (true);
|
|
276
|
+
} while (true);
|
|
277
|
+
}
|
|
278
|
+
const renderHtmlStr = ComponentClass => {
|
|
279
|
+
const root = new SSRFiber('root');
|
|
280
|
+
const render = bobe.customRender({
|
|
281
|
+
createNode,
|
|
282
|
+
setProp,
|
|
283
|
+
insertAfter,
|
|
284
|
+
createAnchor,
|
|
285
|
+
remove,
|
|
286
|
+
firstChild,
|
|
287
|
+
nextSib,
|
|
288
|
+
beforeIndent,
|
|
289
|
+
noopEffect: true
|
|
290
|
+
});
|
|
291
|
+
render(ComponentClass, root);
|
|
292
|
+
walkFiber(root);
|
|
293
|
+
return root.html;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
exports.renderHtmlStr = renderHtmlStr;
|
|
297
|
+
exports.walkFiber = walkFiber;
|
|
298
|
+
//# sourceMappingURL=ssr-index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr-index.cjs","sources":["../src/type.ts","../src/parse-html.ts","../src/render-html-str2.ts"],"sourcesContent":["export class SSRNode {\n type: SSRNodeType;\n // 链表\n parent?: SSRNode | null = null;\n firstChild: SSRNode | null = null;\n lastChild: SSRNode | null = null; // O(1) 尾插\n nextSibling: SSRNode | null = null;\n prevSibling: SSRNode | null = null;\n closed: boolean = false;\n startClosed: boolean = false;\n // innerHTML 缓存\n _innerHtml: string | null = null;\n}\n\nexport enum SSRNodeType {\n Element,\n Text,\n Anchor,\n Root\n}\n\nexport class Element extends SSRNode {\n type = SSRNodeType.Element;\n textContent: string | null = null;\n\n attrs: Record<string, string> = {};\n constructor(\n /** 标签名 */\n public value: string,\n public parent?: SSRNode | null\n ) {\n super();\n }\n}\n\nexport class Text extends SSRNode {\n type = SSRNodeType.Text;\n constructor(\n /** 文本内容 */\n public textContent: string,\n public parent?: SSRNode | null\n ) {\n super();\n }\n}\n\nexport class Anchor extends SSRNode {\n type = SSRNodeType.Anchor;\n constructor(\n /** 注释内容 */\n public value: string,\n public parent?: SSRNode | null\n ) {\n super();\n }\n}\nexport class Root extends SSRNode {\n type = SSRNodeType.Root;\n constructor(\n /** 输出 HTML */\n public value: string,\n public parent?: SSRNode | null\n ) {\n super();\n }\n}\n\nexport type SSRCtx = {\n /** 根节点 */\n root: Root;\n /** 当前节点 */\n current: SSRNode;\n};\n\n\nexport class SSRFiber {\n parent?: SSRFiber = undefined;\n next?: SSRFiber = undefined;\n child?: SSRFiber = undefined;\n html?: string = undefined;\n /** 开标签 > 在原始 HTML 字符串中的索引位置 */\n openTagEnd?: number = undefined;\n constructor(public type: any, public props: Record<any, any> = {}) {}\n\n querySelector(selector: string): SSRFiber | null {\n // 解析 selector:tag#id.class1.class2\n const idMatch = selector.match(/#([\\w-]+)/);\n const id = idMatch ? idMatch[1] : null;\n const classMatches = selector.match(/\\.[\\w-]+/g);\n const classes = classMatches ? classMatches.map(c => c.slice(1)) : [];\n const tag = selector.replace(/#[\\w-]+/g, '').replace(/\\.[\\w-]+/g, '').trim() || null;\n\n // 深度优先遍历 child → next 链表\n const walk = (node: SSRFiber | undefined): SSRFiber | null => {\n if (!node) return null;\n // 跳过非元素节点\n if (node.type !== 'root' && node.type !== 'anchor' && node.type !== 'text') {\n if (!tag || node.type === tag) {\n if (!id || node.props['id'] === id) {\n if (\n classes.length === 0 ||\n classes.every(c => (node.props['class'] || '').split(/\\s+/).includes(c))\n ) {\n return node;\n }\n }\n }\n }\n return walk(node.child) || walk(node.next);\n };\n\n return walk(this.child);\n }\n\n getElementById(id: string): SSRFiber | null {\n return this.querySelector(`#${id}`);\n }\n}\n\n","import { Parser } from 'htmlparser2';\nimport { SSRFiber } from './type';\n\n/**\n * 使用 htmlparser2 将 HTML 字符串解析为 SSRFiber,插入到 root 下。\n * 解析产生的节点通过 child/next/parent 组成树结构,挂载到 root.child 链表。\n */\nexport function parseHtmlToFibers(html: string, root: SSRFiber): void {\n // 栈:跟踪当前父节点及其最后一个子节点。root 始终在栈底\n const stack: { parent: SSRFiber; lastChild: SSRFiber | null }[] = [\n { parent: root, lastChild: null },\n ];\n // 当前层级的上一个兄弟节点\n let prevSibling: SSRFiber | null = null;\n\n const append = (node: SSRFiber) => {\n const frame = stack[stack.length - 1];\n if (!frame.parent.child) {\n frame.parent.child = node;\n } else if (frame.lastChild) {\n frame.lastChild.next = node;\n }\n frame.lastChild = node;\n node.parent = frame.parent;\n prevSibling = node;\n };\n\n // 通过 onparserinit 拿到 parser 实例,后续从 parser.endIndex 读取 > 的位置\n let parserRef: Parser;\n\n const parser = new Parser({\n onparserinit(p: Parser) {\n parserRef = p;\n },\n\n onopentag(name: string, attribs: Record<string, string>) {\n const fiber = new SSRFiber(name, { ...attribs });\n // onopentagend 在 onopentag 之前触发,已将 endIndex 写入 parser\n fiber.openTagEnd = parserRef.endIndex;\n append(fiber);\n stack.push({ parent: fiber, lastChild: null });\n prevSibling = null;\n },\n\n ontext(text: string) {\n const fiber = new SSRFiber('text', { children: text });\n append(fiber);\n },\n\n onclosetag() {\n stack.pop();\n const top = stack[stack.length - 1];\n prevSibling = top.lastChild ?? null;\n },\n });\n\n parser.write(html);\n parser.end();\n}\n","import { customRender, Store } from 'bobe';\nimport { SSRFiber } from './type';\nimport { parseHtmlToFibers } from './parse-html';\n\nconst VOID_TAGS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr'\n]);\n\nconst BOOLEAN_ATTRS = new Set([\n 'disabled',\n 'readonly',\n 'checked',\n 'selected',\n 'hidden',\n 'multiple',\n 'required',\n 'autofocus',\n 'autoplay',\n 'controls',\n 'loop',\n 'muted',\n 'defer',\n 'async',\n 'reversed',\n 'open',\n 'itemscope',\n 'ismap',\n 'nohref',\n 'noshade',\n 'nowrap',\n 'compact',\n 'default'\n]);\n\nconst ATTR_RE = /[&\"<>]/g;\nconst TEXT_RE = /[&<>]/g;\nconst ATTR_MAP: Record<string, string> = {\n '&': '&',\n '\"': '"',\n '<': '<',\n '>': '>'\n};\nconst TEXT_MAP: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>'\n};\n\nconst escapeAttr = (str: string) => {\n const s = String(str);\n return ATTR_RE.test(s) ? s.replace(ATTR_RE, m => ATTR_MAP[m]) : s;\n};\n\nconst escapeText = (str: string) => {\n const s = String(str);\n return TEXT_RE.test(s) ? s.replace(TEXT_RE, m => TEXT_MAP[m]) : s;\n};\n\nconst createNode = (name: string) => {\n return new SSRFiber(name);\n};\n\nconst setProp = (node: SSRFiber, key: string, value: any) => {\n node.props[key] = value;\n if (key === 'html') {\n parseHtmlToFibers(value, node);\n }\n};\n\nconst beforeIndent = (node: SSRFiber) => {\n // 仅 html 需要跳过缩进子节点(子内容来自 parseHtmlToFibers)\n if (node.props.html != null) {\n return false;\n }\n};\n\nconst insertAfter = (parent: SSRFiber, node: SSRFiber, prev: SSRFiber | null) => {\n let next: SSRFiber;\n if (prev) {\n next = prev.next;\n prev.next = node;\n } else {\n next = parent.child;\n parent.child = node;\n }\n node.next = next;\n node.parent = parent;\n};\n\nconst createAnchor = (name: string, isBefore?: boolean) => {\n return new SSRFiber('anchor', {\n name,\n isBefore\n });\n};\n\nconst remove = (node: SSRFiber, prev?: SSRFiber) => {\n const { parent, next } = node;\n node.next = null;\n if (prev) {\n prev.next = next;\n } else {\n parent.child = next;\n }\n node.parent = null;\n};\n\nconst firstChild = (node: SSRFiber) => node.child;\n\nconst nextSib = (node: SSRFiber) => node.next;\n\nexport function walkFiber(root: SSRFiber) {\n let point = root;\n let shouldSink = true;\n // 下沉\n sink: do {\n // begin 组装 HTML 起始部位\n if (point.type === 'root') {\n point.html = '';\n } else if (point.type === 'anchor') {\n point.html = `<!--${point.props.name}-->`;\n } else if (point.type === 'text') {\n const text = point.props.children;\n if (text != null) {\n point.html = escapeText(point.props.children);\n }\n }\n // dom 节点\n else {\n point.html = `<${point.type}`;\n const props = point.props;\n let text;\n let classStr = '';\n const dotClasses: string[] = [];\n let idValue: string | null = null;\n\n for (const key in props) {\n const value = props[key];\n if (key.startsWith('on') || key === 'ref') continue;\n // html 被解析成了子节点,则不处理\n if (key === 'html') {\n continue;\n }\n if (key === 'children') {\n text = value;\n continue;\n }\n // .xxx — class toggle,收集后与 class 合并\n if (key.startsWith('.')) {\n if (value) dotClasses.push(key.slice(1));\n continue;\n }\n // #xxx — id toggle\n if (key.startsWith('#')) {\n if (value) idValue = key.slice(1);\n continue;\n }\n if (key === 'class') {\n if (value == null) continue;\n if (typeof value === 'object' && !Array.isArray(value)) {\n classStr = Object.entries(value as Record<string, any>)\n .filter(([, v]) => !!v)\n .map(([k]) => k)\n .join(' ');\n } else {\n classStr = typeof value === 'boolean' ? (value ? 'true' : '') : String(value);\n }\n continue;\n }\n if (key === 'style') {\n if (value == null) continue;\n point.html += ` style=\"${escapeAttr(String(value))}\"`;\n continue;\n }\n // 4. 布尔属性\n if (BOOLEAN_ATTRS.has(key)) {\n if (value !== false && value !== null && value !== undefined) {\n point.html += ` ${key}`;\n }\n continue;\n }\n // 5. data-* / aria-* — 对齐 Browser:null 时不输出\n if (key.startsWith('data-') || key.startsWith('aria-')) {\n if (value == null) continue;\n point.html += ` ${key}=\"${escapeAttr(value)}\"`;\n continue;\n }\n // 6. 其余属性 — 对齐 Browser:null 时不输出\n if (value == null) continue;\n point.html += ` ${key}=\"${escapeAttr(value)}\"`;\n }\n // 合并 .xxx 类名到 class\n if (dotClasses.length) {\n classStr = (classStr ? classStr + ' ' : '') + dotClasses.join(' ');\n }\n if (classStr) {\n point.html += ` class=\"${escapeAttr(classStr)}\"`;\n }\n // 输出 #xxx 设置的 id\n if (idValue) {\n point.html += ` id=\"${escapeAttr(idValue)}\"`;\n }\n // 纯 text(无 html children)→ 内联文本,闭合标签,不下沉\n if (text != null && props.html == null) {\n const content = escapeText(text);\n point.html += `>${content}</${point.type}>`;\n if (point.child) console.warn(`<${point.type}> has text content and child elements — children ignored`);\n shouldSink = false;\n } else {\n if (VOID_TAGS.has(point.type)) {\n point.html += `/>`;\n if (point.child) console.warn(`<${point.type}> can't have children`);\n shouldSink = false;\n } else {\n point.html += `>`;\n }\n }\n }\n\n if (point.child && shouldSink) {\n point = point.child;\n continue;\n }\n // 上浮\n do {\n const notRoot = point !== root;\n const notAnchor = point.type !== 'anchor';\n const notText = point.type !== 'text';\n /**\n * shouldSink == false 时会直接进入节点的 complete 且不会遍历子节点\n * 此时标签已闭合,complete 不需要重复处理\n */\n if (shouldSink && notRoot && notAnchor && notText) {\n point.html += `</${point.type}>`;\n }\n // 把子节点的字符串加入父节点\n if (notRoot) {\n point.parent.html += point.html;\n }\n // complete\n // 上浮到根,end\n shouldSink = true;\n if (!notRoot) break sink;\n // 有兄弟节点,停止上浮\n if (point.next) {\n point = point.next;\n break;\n }\n // 无兄弟节点,继续上浮\n point = point.parent;\n } while (true);\n } while (true);\n}\n\nexport const renderHtmlStr = (ComponentClass: typeof Store) => {\n const root = new SSRFiber('root');\n const render = customRender({\n createNode,\n setProp,\n insertAfter,\n createAnchor,\n remove,\n firstChild,\n nextSib,\n beforeIndent,\n noopEffect: true\n });\n render(ComponentClass, root);\n walkFiber(root);\n return root.html;\n};\n"],"names":["SSRNodeType","SSRFiber","parent","undefined","next","child","html","openTagEnd","constructor","type","props","querySelector","selector","idMatch","match","id","classMatches","classes","map","c","slice","tag","replace","trim","walk","node","length","every","split","includes","getElementById","parseHtmlToFibers","root","stack","lastChild","append","frame","parserRef","parser","Parser","onparserinit","p","onopentag","name","attribs","fiber","endIndex","push","ontext","text","children","onclosetag","pop","top","prevSibling","write","end","VOID_TAGS","Set","BOOLEAN_ATTRS","ATTR_RE","TEXT_RE","ATTR_MAP","TEXT_MAP","escapeAttr","str","s","String","test","m","escapeText","createNode","setProp","key","value","beforeIndent","insertAfter","prev","createAnchor","isBefore","remove","firstChild","nextSib","walkFiber","point","shouldSink","sink","classStr","dotClasses","idValue","startsWith","Array","isArray","Object","entries","filter","v","k","join","has","content","console","warn","notRoot","notAnchor","notText","renderHtmlStr","ComponentClass","render","customRender","noopEffect"],"mappings":"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"}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { customRender } from 'bobe';
|
|
2
|
+
import { Parser } from 'htmlparser2';
|
|
3
|
+
|
|
4
|
+
(function (SSRNodeType) {
|
|
5
|
+
SSRNodeType[SSRNodeType["Element"] = 0] = "Element";
|
|
6
|
+
SSRNodeType[SSRNodeType["Text"] = 1] = "Text";
|
|
7
|
+
SSRNodeType[SSRNodeType["Anchor"] = 2] = "Anchor";
|
|
8
|
+
SSRNodeType[SSRNodeType["Root"] = 3] = "Root";
|
|
9
|
+
return SSRNodeType;
|
|
10
|
+
})({});
|
|
11
|
+
class SSRFiber {
|
|
12
|
+
parent = undefined;
|
|
13
|
+
next = undefined;
|
|
14
|
+
child = undefined;
|
|
15
|
+
html = undefined;
|
|
16
|
+
openTagEnd = undefined;
|
|
17
|
+
constructor(type, props = {}) {
|
|
18
|
+
this.type = type;
|
|
19
|
+
this.props = props;
|
|
20
|
+
}
|
|
21
|
+
querySelector(selector) {
|
|
22
|
+
const idMatch = selector.match(/#([\w-]+)/);
|
|
23
|
+
const id = idMatch ? idMatch[1] : null;
|
|
24
|
+
const classMatches = selector.match(/\.[\w-]+/g);
|
|
25
|
+
const classes = classMatches ? classMatches.map(c => c.slice(1)) : [];
|
|
26
|
+
const tag = selector.replace(/#[\w-]+/g, '').replace(/\.[\w-]+/g, '').trim() || null;
|
|
27
|
+
const walk = node => {
|
|
28
|
+
if (!node) return null;
|
|
29
|
+
if (node.type !== 'root' && node.type !== 'anchor' && node.type !== 'text') {
|
|
30
|
+
if (!tag || node.type === tag) {
|
|
31
|
+
if (!id || node.props['id'] === id) {
|
|
32
|
+
if (classes.length === 0 || classes.every(c => (node.props['class'] || '').split(/\s+/).includes(c))) {
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return walk(node.child) || walk(node.next);
|
|
39
|
+
};
|
|
40
|
+
return walk(this.child);
|
|
41
|
+
}
|
|
42
|
+
getElementById(id) {
|
|
43
|
+
return this.querySelector(`#${id}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseHtmlToFibers(html, root) {
|
|
48
|
+
const stack = [{
|
|
49
|
+
parent: root,
|
|
50
|
+
lastChild: null
|
|
51
|
+
}];
|
|
52
|
+
const append = node => {
|
|
53
|
+
const frame = stack[stack.length - 1];
|
|
54
|
+
if (!frame.parent.child) {
|
|
55
|
+
frame.parent.child = node;
|
|
56
|
+
} else if (frame.lastChild) {
|
|
57
|
+
frame.lastChild.next = node;
|
|
58
|
+
}
|
|
59
|
+
frame.lastChild = node;
|
|
60
|
+
node.parent = frame.parent;
|
|
61
|
+
};
|
|
62
|
+
let parserRef;
|
|
63
|
+
const parser = new Parser({
|
|
64
|
+
onparserinit(p) {
|
|
65
|
+
parserRef = p;
|
|
66
|
+
},
|
|
67
|
+
onopentag(name, attribs) {
|
|
68
|
+
const fiber = new SSRFiber(name, {
|
|
69
|
+
...attribs
|
|
70
|
+
});
|
|
71
|
+
fiber.openTagEnd = parserRef.endIndex;
|
|
72
|
+
append(fiber);
|
|
73
|
+
stack.push({
|
|
74
|
+
parent: fiber,
|
|
75
|
+
lastChild: null
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
ontext(text) {
|
|
79
|
+
const fiber = new SSRFiber('text', {
|
|
80
|
+
children: text
|
|
81
|
+
});
|
|
82
|
+
append(fiber);
|
|
83
|
+
},
|
|
84
|
+
onclosetag() {
|
|
85
|
+
stack.pop();
|
|
86
|
+
const top = stack[stack.length - 1];
|
|
87
|
+
top.lastChild ?? null;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
parser.write(html);
|
|
91
|
+
parser.end();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const VOID_TAGS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
|
|
95
|
+
const BOOLEAN_ATTRS = new Set(['disabled', 'readonly', 'checked', 'selected', 'hidden', 'multiple', 'required', 'autofocus', 'autoplay', 'controls', 'loop', 'muted', 'defer', 'async', 'reversed', 'open', 'itemscope', 'ismap', 'nohref', 'noshade', 'nowrap', 'compact', 'default']);
|
|
96
|
+
const ATTR_RE = /[&"<>]/g;
|
|
97
|
+
const TEXT_RE = /[&<>]/g;
|
|
98
|
+
const ATTR_MAP = {
|
|
99
|
+
'&': '&',
|
|
100
|
+
'"': '"',
|
|
101
|
+
'<': '<',
|
|
102
|
+
'>': '>'
|
|
103
|
+
};
|
|
104
|
+
const TEXT_MAP = {
|
|
105
|
+
'&': '&',
|
|
106
|
+
'<': '<',
|
|
107
|
+
'>': '>'
|
|
108
|
+
};
|
|
109
|
+
const escapeAttr = str => {
|
|
110
|
+
const s = String(str);
|
|
111
|
+
return ATTR_RE.test(s) ? s.replace(ATTR_RE, m => ATTR_MAP[m]) : s;
|
|
112
|
+
};
|
|
113
|
+
const escapeText = str => {
|
|
114
|
+
const s = String(str);
|
|
115
|
+
return TEXT_RE.test(s) ? s.replace(TEXT_RE, m => TEXT_MAP[m]) : s;
|
|
116
|
+
};
|
|
117
|
+
const createNode = name => {
|
|
118
|
+
return new SSRFiber(name);
|
|
119
|
+
};
|
|
120
|
+
const setProp = (node, key, value) => {
|
|
121
|
+
node.props[key] = value;
|
|
122
|
+
if (key === 'html') {
|
|
123
|
+
parseHtmlToFibers(value, node);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const beforeIndent = node => {
|
|
127
|
+
if (node.props.html != null) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const insertAfter = (parent, node, prev) => {
|
|
132
|
+
let next;
|
|
133
|
+
if (prev) {
|
|
134
|
+
next = prev.next;
|
|
135
|
+
prev.next = node;
|
|
136
|
+
} else {
|
|
137
|
+
next = parent.child;
|
|
138
|
+
parent.child = node;
|
|
139
|
+
}
|
|
140
|
+
node.next = next;
|
|
141
|
+
node.parent = parent;
|
|
142
|
+
};
|
|
143
|
+
const createAnchor = (name, isBefore) => {
|
|
144
|
+
return new SSRFiber('anchor', {
|
|
145
|
+
name,
|
|
146
|
+
isBefore
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const remove = (node, prev) => {
|
|
150
|
+
const parent = node.parent,
|
|
151
|
+
next = node.next;
|
|
152
|
+
node.next = null;
|
|
153
|
+
if (prev) {
|
|
154
|
+
prev.next = next;
|
|
155
|
+
} else {
|
|
156
|
+
parent.child = next;
|
|
157
|
+
}
|
|
158
|
+
node.parent = null;
|
|
159
|
+
};
|
|
160
|
+
const firstChild = node => node.child;
|
|
161
|
+
const nextSib = node => node.next;
|
|
162
|
+
function walkFiber(root) {
|
|
163
|
+
let point = root;
|
|
164
|
+
let shouldSink = true;
|
|
165
|
+
sink: do {
|
|
166
|
+
if (point.type === 'root') {
|
|
167
|
+
point.html = '';
|
|
168
|
+
} else if (point.type === 'anchor') {
|
|
169
|
+
point.html = `<!--${point.props.name}-->`;
|
|
170
|
+
} else if (point.type === 'text') {
|
|
171
|
+
const text = point.props.children;
|
|
172
|
+
if (text != null) {
|
|
173
|
+
point.html = escapeText(point.props.children);
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
point.html = `<${point.type}`;
|
|
177
|
+
const props = point.props;
|
|
178
|
+
let text;
|
|
179
|
+
let classStr = '';
|
|
180
|
+
const dotClasses = [];
|
|
181
|
+
let idValue = null;
|
|
182
|
+
for (const key in props) {
|
|
183
|
+
const value = props[key];
|
|
184
|
+
if (key.startsWith('on') || key === 'ref') continue;
|
|
185
|
+
if (key === 'html') {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (key === 'children') {
|
|
189
|
+
text = value;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (key.startsWith('.')) {
|
|
193
|
+
if (value) dotClasses.push(key.slice(1));
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (key.startsWith('#')) {
|
|
197
|
+
if (value) idValue = key.slice(1);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (key === 'class') {
|
|
201
|
+
if (value == null) continue;
|
|
202
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
203
|
+
classStr = Object.entries(value).filter(([, v]) => !!v).map(([k]) => k).join(' ');
|
|
204
|
+
} else {
|
|
205
|
+
classStr = typeof value === 'boolean' ? value ? 'true' : '' : String(value);
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (key === 'style') {
|
|
210
|
+
if (value == null) continue;
|
|
211
|
+
point.html += ` style="${escapeAttr(String(value))}"`;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (BOOLEAN_ATTRS.has(key)) {
|
|
215
|
+
if (value !== false && value !== null && value !== undefined) {
|
|
216
|
+
point.html += ` ${key}`;
|
|
217
|
+
}
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (key.startsWith('data-') || key.startsWith('aria-')) {
|
|
221
|
+
if (value == null) continue;
|
|
222
|
+
point.html += ` ${key}="${escapeAttr(value)}"`;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (value == null) continue;
|
|
226
|
+
point.html += ` ${key}="${escapeAttr(value)}"`;
|
|
227
|
+
}
|
|
228
|
+
if (dotClasses.length) {
|
|
229
|
+
classStr = (classStr ? classStr + ' ' : '') + dotClasses.join(' ');
|
|
230
|
+
}
|
|
231
|
+
if (classStr) {
|
|
232
|
+
point.html += ` class="${escapeAttr(classStr)}"`;
|
|
233
|
+
}
|
|
234
|
+
if (idValue) {
|
|
235
|
+
point.html += ` id="${escapeAttr(idValue)}"`;
|
|
236
|
+
}
|
|
237
|
+
if (text != null && props.html == null) {
|
|
238
|
+
const content = escapeText(text);
|
|
239
|
+
point.html += `>${content}</${point.type}>`;
|
|
240
|
+
if (point.child) console.warn(`<${point.type}> has text content and child elements — children ignored`);
|
|
241
|
+
shouldSink = false;
|
|
242
|
+
} else {
|
|
243
|
+
if (VOID_TAGS.has(point.type)) {
|
|
244
|
+
point.html += `/>`;
|
|
245
|
+
if (point.child) console.warn(`<${point.type}> can't have children`);
|
|
246
|
+
shouldSink = false;
|
|
247
|
+
} else {
|
|
248
|
+
point.html += `>`;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (point.child && shouldSink) {
|
|
253
|
+
point = point.child;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
do {
|
|
257
|
+
const notRoot = point !== root;
|
|
258
|
+
const notAnchor = point.type !== 'anchor';
|
|
259
|
+
const notText = point.type !== 'text';
|
|
260
|
+
if (shouldSink && notRoot && notAnchor && notText) {
|
|
261
|
+
point.html += `</${point.type}>`;
|
|
262
|
+
}
|
|
263
|
+
if (notRoot) {
|
|
264
|
+
point.parent.html += point.html;
|
|
265
|
+
}
|
|
266
|
+
shouldSink = true;
|
|
267
|
+
if (!notRoot) break sink;
|
|
268
|
+
if (point.next) {
|
|
269
|
+
point = point.next;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
point = point.parent;
|
|
273
|
+
} while (true);
|
|
274
|
+
} while (true);
|
|
275
|
+
}
|
|
276
|
+
const renderHtmlStr = ComponentClass => {
|
|
277
|
+
const root = new SSRFiber('root');
|
|
278
|
+
const render = customRender({
|
|
279
|
+
createNode,
|
|
280
|
+
setProp,
|
|
281
|
+
insertAfter,
|
|
282
|
+
createAnchor,
|
|
283
|
+
remove,
|
|
284
|
+
firstChild,
|
|
285
|
+
nextSib,
|
|
286
|
+
beforeIndent,
|
|
287
|
+
noopEffect: true
|
|
288
|
+
});
|
|
289
|
+
render(ComponentClass, root);
|
|
290
|
+
walkFiber(root);
|
|
291
|
+
return root.html;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export { renderHtmlStr, walkFiber };
|
|
295
|
+
//# sourceMappingURL=ssr-index.esm.js.map
|