bbcode-compiler 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/generateHtml.d.ts +1 -1
- package/dist/generator/Generator.d.ts +2 -1
- package/dist/generator/transforms/Transform.d.ts +2 -1
- package/dist/generator/transforms/htmlTransforms.d.ts +2 -1
- package/dist/generator/transforms/htmlTransforms.d.ts.map +1 -1
- package/dist/generator/utils/getTagImmediateAttrVal.d.ts +2 -1
- package/dist/generator/utils/getTagImmediateText.d.ts +1 -0
- package/dist/generator/utils/getWidthHeightAttr.d.ts +2 -1
- package/dist/generator/utils/isOrderedList.d.ts +2 -1
- package/dist/index.js +1061 -16
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +1065 -0
- package/dist/index.umd.cjs.map +1 -0
- package/dist/lexer/Lexer.d.ts +2 -1
- package/dist/lexer/Token.d.ts +1 -0
- package/dist/parser/AstNode.d.ts +27 -17
- package/dist/parser/AstNode.d.ts.map +1 -1
- package/dist/parser/Parser.d.ts +2 -1
- package/dist/parser/nodeIsType.d.ts +1 -0
- package/package.json +41 -20
- package/src/generator/transforms/htmlTransforms.ts +7 -0
- package/src/parser/AstNode.ts +162 -89
- package/src/parser/Parser.ts +103 -103
- package/dist/generateHtml.js +0 -13
- package/dist/generateHtml.js.map +0 -1
- package/dist/generator/Generator.js +0 -54
- package/dist/generator/Generator.js.map +0 -1
- package/dist/generator/transforms/Transform.js +0 -2
- package/dist/generator/transforms/Transform.js.map +0 -1
- package/dist/generator/transforms/htmlTransforms.js +0 -198
- package/dist/generator/transforms/htmlTransforms.js.map +0 -1
- package/dist/generator/utils/getTagImmediateAttrVal.js +0 -19
- package/dist/generator/utils/getTagImmediateAttrVal.js.map +0 -1
- package/dist/generator/utils/getTagImmediateText.js +0 -28
- package/dist/generator/utils/getTagImmediateText.js.map +0 -1
- package/dist/generator/utils/getWidthHeightAttr.js +0 -47
- package/dist/generator/utils/getWidthHeightAttr.js.map +0 -1
- package/dist/generator/utils/isDangerousUrl.js +0 -14
- package/dist/generator/utils/isDangerousUrl.js.map +0 -1
- package/dist/generator/utils/isOrderedList.js +0 -26
- package/dist/generator/utils/isOrderedList.js.map +0 -1
- package/dist/lexer/Lexer.js +0 -81
- package/dist/lexer/Lexer.js.map +0 -1
- package/dist/lexer/Token.js +0 -53
- package/dist/lexer/Token.js.map +0 -1
- package/dist/lexer/TokenType.js +0 -41
- package/dist/lexer/TokenType.js.map +0 -1
- package/dist/parser/AstNode.js +0 -263
- package/dist/parser/AstNode.js.map +0 -1
- package/dist/parser/Parser.js +0 -268
- package/dist/parser/Parser.js.map +0 -1
- package/dist/parser/nodeIsType.js +0 -4
- package/dist/parser/nodeIsType.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,16 +1,1061 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
function nodeIsType(node, nodeType) {
|
|
5
|
+
return node.nodeType === nodeType;
|
|
6
|
+
}
|
|
7
|
+
var AstNodeType = /* @__PURE__ */ ((AstNodeType2) => {
|
|
8
|
+
AstNodeType2["RootNode"] = "RootNode";
|
|
9
|
+
AstNodeType2["TextNode"] = "TextNode";
|
|
10
|
+
AstNodeType2["LinebreakNode"] = "LinebreakNode";
|
|
11
|
+
AstNodeType2["TagNode"] = "TagNode";
|
|
12
|
+
AstNodeType2["StartTagNode"] = "StartTagNode";
|
|
13
|
+
AstNodeType2["EndTagNode"] = "EndTagNode";
|
|
14
|
+
AstNodeType2["AttrNode"] = "AttrNode";
|
|
15
|
+
return AstNodeType2;
|
|
16
|
+
})(AstNodeType || {});
|
|
17
|
+
class AstNode {
|
|
18
|
+
constructor(children = []) {
|
|
19
|
+
this.children = children;
|
|
20
|
+
}
|
|
21
|
+
addChild(node) {
|
|
22
|
+
this.children.push(node);
|
|
23
|
+
}
|
|
24
|
+
isValid() {
|
|
25
|
+
for (const child of this.children) {
|
|
26
|
+
if (!child.isValid()) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
toShortString() {
|
|
33
|
+
return this.nodeType;
|
|
34
|
+
}
|
|
35
|
+
// For debugging purposes only
|
|
36
|
+
// Pretty-prints AST
|
|
37
|
+
toString(depth = 0) {
|
|
38
|
+
let s = " ".repeat(depth * 2) + this.toShortString();
|
|
39
|
+
for (const child of this.children) {
|
|
40
|
+
s += "\n" + child.toString(depth + 1);
|
|
41
|
+
}
|
|
42
|
+
return s;
|
|
43
|
+
}
|
|
44
|
+
toJSON() {
|
|
45
|
+
const json = {
|
|
46
|
+
type: this.nodeType
|
|
47
|
+
};
|
|
48
|
+
if (this.children.length > 0) {
|
|
49
|
+
json.children = this.children.map((child) => child.toJSON());
|
|
50
|
+
}
|
|
51
|
+
return json;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
class RootNode extends AstNode {
|
|
55
|
+
constructor() {
|
|
56
|
+
super(...arguments);
|
|
57
|
+
__publicField(this, "nodeType", "RootNode");
|
|
58
|
+
}
|
|
59
|
+
isValid() {
|
|
60
|
+
for (const child of this.children) {
|
|
61
|
+
if (child.nodeType !== "TagNode" && child.nodeType !== "TextNode" && child.nodeType !== "LinebreakNode") {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return super.isValid() && this.children.length > 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
class TextNode extends AstNode {
|
|
69
|
+
constructor(str) {
|
|
70
|
+
super();
|
|
71
|
+
__publicField(this, "nodeType", "TextNode");
|
|
72
|
+
__publicField(this, "str");
|
|
73
|
+
this.str = str;
|
|
74
|
+
}
|
|
75
|
+
isValid() {
|
|
76
|
+
return super.isValid() && this.children.length === 0;
|
|
77
|
+
}
|
|
78
|
+
toShortString() {
|
|
79
|
+
return `${super.toShortString()} "${this.str}"`;
|
|
80
|
+
}
|
|
81
|
+
toJSON() {
|
|
82
|
+
const json = super.toJSON();
|
|
83
|
+
json.data = {
|
|
84
|
+
str: this.str
|
|
85
|
+
};
|
|
86
|
+
return json;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
class LinebreakNode extends AstNode {
|
|
90
|
+
constructor() {
|
|
91
|
+
super(...arguments);
|
|
92
|
+
__publicField(this, "nodeType", "LinebreakNode");
|
|
93
|
+
}
|
|
94
|
+
toShortString() {
|
|
95
|
+
return `${super.toShortString()} "\\n"`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const _AttrNode = class _AttrNode extends AstNode {
|
|
99
|
+
constructor() {
|
|
100
|
+
super(...arguments);
|
|
101
|
+
__publicField(this, "nodeType", "AttrNode");
|
|
102
|
+
}
|
|
103
|
+
get key() {
|
|
104
|
+
switch (this.children.length) {
|
|
105
|
+
case 1: {
|
|
106
|
+
return _AttrNode.DEFAULT_KEY;
|
|
107
|
+
}
|
|
108
|
+
case 2: {
|
|
109
|
+
if (!nodeIsType(
|
|
110
|
+
this.children[0],
|
|
111
|
+
"TextNode"
|
|
112
|
+
/* TextNode */
|
|
113
|
+
)) {
|
|
114
|
+
throw new Error("Invalid TextNode");
|
|
115
|
+
}
|
|
116
|
+
return this.children[0].str.trim();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
throw new Error("Invalid AttrNode");
|
|
120
|
+
}
|
|
121
|
+
get val() {
|
|
122
|
+
switch (this.children.length) {
|
|
123
|
+
case 1: {
|
|
124
|
+
if (!nodeIsType(
|
|
125
|
+
this.children[0],
|
|
126
|
+
"TextNode"
|
|
127
|
+
/* TextNode */
|
|
128
|
+
)) {
|
|
129
|
+
throw new Error("Invalid TextNode");
|
|
130
|
+
}
|
|
131
|
+
return this.children[0].str.trim();
|
|
132
|
+
}
|
|
133
|
+
case 2: {
|
|
134
|
+
if (!nodeIsType(
|
|
135
|
+
this.children[1],
|
|
136
|
+
"TextNode"
|
|
137
|
+
/* TextNode */
|
|
138
|
+
)) {
|
|
139
|
+
throw new Error("Invalid TextNode");
|
|
140
|
+
}
|
|
141
|
+
return this.children[1].str.trim();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
throw new Error("Invalid AttrNode");
|
|
145
|
+
}
|
|
146
|
+
isValid() {
|
|
147
|
+
return super.isValid() && (this.children.length >= 1 && this.children.length <= 2);
|
|
148
|
+
}
|
|
149
|
+
toShortString() {
|
|
150
|
+
let s = super.toShortString();
|
|
151
|
+
switch (this.children.length) {
|
|
152
|
+
case 1: {
|
|
153
|
+
s += ` VAL="${this.val}"`;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case 2: {
|
|
157
|
+
s += ` KEY="${this.key}" VAL="${this.val}"`;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return s;
|
|
162
|
+
}
|
|
163
|
+
toJSON() {
|
|
164
|
+
const json = {
|
|
165
|
+
type: this.nodeType
|
|
166
|
+
};
|
|
167
|
+
switch (this.children.length) {
|
|
168
|
+
case 1: {
|
|
169
|
+
json.data = {
|
|
170
|
+
key: this.key
|
|
171
|
+
};
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
case 2: {
|
|
175
|
+
json.data = {
|
|
176
|
+
key: this.key,
|
|
177
|
+
val: this.val
|
|
178
|
+
};
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return json;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
__publicField(_AttrNode, "DEFAULT_KEY", "default");
|
|
186
|
+
let AttrNode = _AttrNode;
|
|
187
|
+
class StartTagNode extends AstNode {
|
|
188
|
+
constructor(tagName, ogTag, attrNodes = []) {
|
|
189
|
+
super(attrNodes);
|
|
190
|
+
__publicField(this, "nodeType", "StartTagNode");
|
|
191
|
+
__publicField(this, "tagName");
|
|
192
|
+
__publicField(this, "ogTag");
|
|
193
|
+
this.tagName = tagName.toLowerCase();
|
|
194
|
+
this.ogTag = ogTag;
|
|
195
|
+
}
|
|
196
|
+
isValid() {
|
|
197
|
+
for (const child of this.children) {
|
|
198
|
+
if (child.nodeType !== "AttrNode") {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return super.isValid();
|
|
203
|
+
}
|
|
204
|
+
toShortString() {
|
|
205
|
+
return `${super.toShortString()} ${this.ogTag}`;
|
|
206
|
+
}
|
|
207
|
+
toJSON() {
|
|
208
|
+
const json = super.toJSON();
|
|
209
|
+
json.data = {
|
|
210
|
+
tag: this.tagName
|
|
211
|
+
};
|
|
212
|
+
return json;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
class EndTagNode extends AstNode {
|
|
216
|
+
constructor(tagName, ogTag) {
|
|
217
|
+
super();
|
|
218
|
+
__publicField(this, "nodeType", "EndTagNode");
|
|
219
|
+
__publicField(this, "tagName");
|
|
220
|
+
__publicField(this, "ogTag");
|
|
221
|
+
this.tagName = tagName;
|
|
222
|
+
this.ogTag = ogTag;
|
|
223
|
+
}
|
|
224
|
+
isValid() {
|
|
225
|
+
return super.isValid() && this.children.length === 0;
|
|
226
|
+
}
|
|
227
|
+
toShortString() {
|
|
228
|
+
return `${super.toShortString()} ${this.ogTag}`;
|
|
229
|
+
}
|
|
230
|
+
toJSON() {
|
|
231
|
+
const json = super.toJSON();
|
|
232
|
+
json.data = {
|
|
233
|
+
tag: this.tagName
|
|
234
|
+
};
|
|
235
|
+
return json;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
class TagNode extends AstNode {
|
|
239
|
+
constructor(startTag, endTag) {
|
|
240
|
+
super();
|
|
241
|
+
__publicField(this, "nodeType", "TagNode");
|
|
242
|
+
__publicField(this, "_startTag");
|
|
243
|
+
__publicField(this, "_endTag");
|
|
244
|
+
this._startTag = startTag;
|
|
245
|
+
this._endTag = endTag;
|
|
246
|
+
}
|
|
247
|
+
get tagName() {
|
|
248
|
+
return this._startTag.tagName;
|
|
249
|
+
}
|
|
250
|
+
get attributes() {
|
|
251
|
+
return this._startTag.children;
|
|
252
|
+
}
|
|
253
|
+
get ogStartTag() {
|
|
254
|
+
return this._startTag.ogTag;
|
|
255
|
+
}
|
|
256
|
+
get ogEndTag() {
|
|
257
|
+
if (!this._endTag) {
|
|
258
|
+
return "";
|
|
259
|
+
}
|
|
260
|
+
if (nodeIsType(
|
|
261
|
+
this._endTag,
|
|
262
|
+
"LinebreakNode"
|
|
263
|
+
/* LinebreakNode */
|
|
264
|
+
)) {
|
|
265
|
+
return "\n";
|
|
266
|
+
} else {
|
|
267
|
+
return this._endTag.ogTag;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
isValid() {
|
|
271
|
+
var _a;
|
|
272
|
+
if (this._endTag && nodeIsType(
|
|
273
|
+
this._endTag,
|
|
274
|
+
"EndTagNode"
|
|
275
|
+
/* EndTagNode */
|
|
276
|
+
) && this._startTag.tagName !== this._endTag.tagName) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
if (this.children.length === 1 && this.children[0].nodeType !== "RootNode") {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
if (this.children.length > 2) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
return super.isValid() && this._startTag.isValid() && (((_a = this._endTag) == null ? void 0 : _a.isValid()) ?? true);
|
|
286
|
+
}
|
|
287
|
+
toString(depth = 0) {
|
|
288
|
+
let s = " ".repeat(depth * 2) + this.toShortString() + ` [${this.tagName}]`;
|
|
289
|
+
for (const attrNode of this._startTag.children) {
|
|
290
|
+
s += "\n" + attrNode.toString(depth + 1);
|
|
291
|
+
}
|
|
292
|
+
for (const child of this.children) {
|
|
293
|
+
s += "\n" + child.toString(depth + 1);
|
|
294
|
+
}
|
|
295
|
+
return s;
|
|
296
|
+
}
|
|
297
|
+
toJSON() {
|
|
298
|
+
const json = super.toJSON();
|
|
299
|
+
json.data = {
|
|
300
|
+
startTag: this._startTag.toJSON()
|
|
301
|
+
};
|
|
302
|
+
if (this._endTag) {
|
|
303
|
+
json.data.endTag = this._endTag.toJSON();
|
|
304
|
+
}
|
|
305
|
+
return json;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function getTagImmediateAttrVal(tagNode) {
|
|
309
|
+
if (tagNode.attributes.length !== 1) {
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
312
|
+
const attrNode = tagNode.attributes[0];
|
|
313
|
+
return attrNode.val;
|
|
314
|
+
}
|
|
315
|
+
function getTagImmediateText(tagNode) {
|
|
316
|
+
if (tagNode.children.length !== 1) {
|
|
317
|
+
return void 0;
|
|
318
|
+
}
|
|
319
|
+
const child = tagNode.children[0];
|
|
320
|
+
if (!nodeIsType(child, AstNodeType.RootNode)) {
|
|
321
|
+
return void 0;
|
|
322
|
+
}
|
|
323
|
+
if (child.children.length !== 1) {
|
|
324
|
+
return void 0;
|
|
325
|
+
}
|
|
326
|
+
const textNode = child.children[0];
|
|
327
|
+
if (!nodeIsType(textNode, AstNodeType.TextNode)) {
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
return textNode.str;
|
|
331
|
+
}
|
|
332
|
+
function getWidthHeightAttr(tagNode) {
|
|
333
|
+
let width;
|
|
334
|
+
let height;
|
|
335
|
+
for (const child of tagNode.attributes) {
|
|
336
|
+
if (child.key === "width") {
|
|
337
|
+
width = child.val;
|
|
338
|
+
}
|
|
339
|
+
if (child.key === "height") {
|
|
340
|
+
height = child.val;
|
|
341
|
+
}
|
|
342
|
+
const matches = /(\d+)x(\d+)/.exec(child.val);
|
|
343
|
+
if (matches) {
|
|
344
|
+
width = matches[1];
|
|
345
|
+
height = matches[2];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
width,
|
|
350
|
+
height
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const dangerousUriRe = /^(vbscript|javascript|file|data):/;
|
|
354
|
+
const safeDataUriRe = /^data:image\/(gif|png|jpeg|webp);/;
|
|
355
|
+
function isDangerousUrl(url) {
|
|
356
|
+
const normalizedUrl = url.trim().toLowerCase();
|
|
357
|
+
if (!dangerousUriRe.test(normalizedUrl)) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
if (safeDataUriRe.test(normalizedUrl)) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
function isOrderedList(node) {
|
|
366
|
+
for (const child of node.attributes) {
|
|
367
|
+
const val = child.val;
|
|
368
|
+
if (val === "1") {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
const htmlTransforms = [
|
|
375
|
+
{
|
|
376
|
+
name: "b",
|
|
377
|
+
start: () => {
|
|
378
|
+
return "<strong>";
|
|
379
|
+
},
|
|
380
|
+
end: () => {
|
|
381
|
+
return "</strong>";
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: "i",
|
|
386
|
+
start: () => {
|
|
387
|
+
return "<em>";
|
|
388
|
+
},
|
|
389
|
+
end: () => {
|
|
390
|
+
return "</em>";
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: "u",
|
|
395
|
+
start: () => {
|
|
396
|
+
return "<ins>";
|
|
397
|
+
},
|
|
398
|
+
end: () => {
|
|
399
|
+
return "</ins>";
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
name: "s",
|
|
404
|
+
start: () => {
|
|
405
|
+
return "<del>";
|
|
406
|
+
},
|
|
407
|
+
end: () => {
|
|
408
|
+
return "</del>";
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: "style",
|
|
413
|
+
start: (tagNode) => {
|
|
414
|
+
let style = "";
|
|
415
|
+
for (const child of tagNode.attributes) {
|
|
416
|
+
switch (child.key) {
|
|
417
|
+
case "color": {
|
|
418
|
+
style += `color:${child.val};`;
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
case "size": {
|
|
422
|
+
if (/^\d+$/.test(child.val)) {
|
|
423
|
+
style += `font-size:${child.val}%;`;
|
|
424
|
+
} else {
|
|
425
|
+
style += `font-size:${child.val};`;
|
|
426
|
+
}
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return `<span style="${style}">`;
|
|
432
|
+
},
|
|
433
|
+
end: () => {
|
|
434
|
+
return "</span>";
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
name: "color",
|
|
439
|
+
start: (tagNode) => {
|
|
440
|
+
const color = getTagImmediateAttrVal(tagNode);
|
|
441
|
+
return `<span style="color:${color};">`;
|
|
442
|
+
},
|
|
443
|
+
end: () => {
|
|
444
|
+
return "</span>";
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: "hr",
|
|
449
|
+
isStandalone: true,
|
|
450
|
+
start: () => {
|
|
451
|
+
return "<hr />";
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
name: "br",
|
|
456
|
+
isStandalone: true,
|
|
457
|
+
start: () => {
|
|
458
|
+
return "<br />";
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
name: "list",
|
|
463
|
+
start: (tagNode) => {
|
|
464
|
+
return isOrderedList(tagNode) ? "<ol>" : "<ul>";
|
|
465
|
+
},
|
|
466
|
+
end: (tagNode) => {
|
|
467
|
+
return isOrderedList(tagNode) ? "</ol>" : "</ul>";
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
name: "*",
|
|
472
|
+
isLinebreakTerminated: true,
|
|
473
|
+
start: () => {
|
|
474
|
+
return "<li>";
|
|
475
|
+
},
|
|
476
|
+
end: () => {
|
|
477
|
+
return "</li>";
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
name: "img",
|
|
482
|
+
skipChildren: true,
|
|
483
|
+
start: (tagNode) => {
|
|
484
|
+
const src = getTagImmediateText(tagNode);
|
|
485
|
+
if (!src) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
if (isDangerousUrl(src)) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
const { width, height } = getWidthHeightAttr(tagNode);
|
|
492
|
+
let str = `<img src="${src}"`;
|
|
493
|
+
if (width) {
|
|
494
|
+
str += ` width="${width}"`;
|
|
495
|
+
}
|
|
496
|
+
if (height) {
|
|
497
|
+
str += ` height="${height}"`;
|
|
498
|
+
}
|
|
499
|
+
str += ">";
|
|
500
|
+
return str;
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: "url",
|
|
505
|
+
start: (tagNode) => {
|
|
506
|
+
const href = getTagImmediateAttrVal(tagNode) ?? getTagImmediateText(tagNode);
|
|
507
|
+
if (!href) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
if (isDangerousUrl(href)) {
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
return `<a href="${href}">`;
|
|
514
|
+
},
|
|
515
|
+
end: () => {
|
|
516
|
+
return "</a>";
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
name: "quote",
|
|
521
|
+
start: (tagNode) => {
|
|
522
|
+
const author = getTagImmediateAttrVal(tagNode);
|
|
523
|
+
return author ? `<blockquote><strong>${author}</strong>` : "<blockquote>";
|
|
524
|
+
},
|
|
525
|
+
end: () => {
|
|
526
|
+
return "</blockquote>";
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
name: "table",
|
|
531
|
+
start: () => {
|
|
532
|
+
return "<table>";
|
|
533
|
+
},
|
|
534
|
+
end: () => {
|
|
535
|
+
return "</table>";
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
name: "tr",
|
|
540
|
+
start: () => {
|
|
541
|
+
return "<tr>";
|
|
542
|
+
},
|
|
543
|
+
end: () => {
|
|
544
|
+
return "</tr>";
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: "td",
|
|
549
|
+
start: () => {
|
|
550
|
+
return "<td>";
|
|
551
|
+
},
|
|
552
|
+
end: () => {
|
|
553
|
+
return "</td>";
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
name: "code",
|
|
558
|
+
start: () => {
|
|
559
|
+
return "<code>";
|
|
560
|
+
},
|
|
561
|
+
end: () => {
|
|
562
|
+
return "</code>";
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
];
|
|
566
|
+
class Generator {
|
|
567
|
+
constructor(transforms = htmlTransforms) {
|
|
568
|
+
__publicField(this, "transforms");
|
|
569
|
+
this.transforms = new Map(transforms.map((transform) => [transform.name, transform]));
|
|
570
|
+
}
|
|
571
|
+
generate(root) {
|
|
572
|
+
const stringify = (node) => {
|
|
573
|
+
var _a;
|
|
574
|
+
let output = "";
|
|
575
|
+
if (nodeIsType(node, AstNodeType.TagNode)) {
|
|
576
|
+
const tagName = node.tagName;
|
|
577
|
+
const transform = this.transforms.get(tagName);
|
|
578
|
+
if (!transform) {
|
|
579
|
+
throw new Error(`Unrecognized bbcode ${node.tagName}`);
|
|
580
|
+
}
|
|
581
|
+
const renderedStartTag = transform.start(node);
|
|
582
|
+
const renderedEndTag = ((_a = transform.end) == null ? void 0 : _a.call(transform, node)) ?? "";
|
|
583
|
+
const isInvalidTag = renderedStartTag === false;
|
|
584
|
+
if (isInvalidTag) {
|
|
585
|
+
output += node.ogStartTag;
|
|
586
|
+
} else {
|
|
587
|
+
output += renderedStartTag;
|
|
588
|
+
}
|
|
589
|
+
if (!transform.skipChildren || isInvalidTag) {
|
|
590
|
+
for (const child of node.children) {
|
|
591
|
+
output += stringify(child);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (isInvalidTag) {
|
|
595
|
+
output += node.ogEndTag;
|
|
596
|
+
} else {
|
|
597
|
+
output += renderedEndTag;
|
|
598
|
+
}
|
|
599
|
+
} else if (nodeIsType(node, AstNodeType.TextNode)) {
|
|
600
|
+
output += node.str;
|
|
601
|
+
} else if (nodeIsType(node, AstNodeType.LinebreakNode)) {
|
|
602
|
+
output += "\n";
|
|
603
|
+
} else {
|
|
604
|
+
for (const child of node.children) {
|
|
605
|
+
output += stringify(child);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return output;
|
|
609
|
+
};
|
|
610
|
+
return stringify(root);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
var TokenType = /* @__PURE__ */ ((TokenType2) => {
|
|
614
|
+
TokenType2[TokenType2["STR"] = 0] = "STR";
|
|
615
|
+
TokenType2[TokenType2["LINEBREAK"] = 1] = "LINEBREAK";
|
|
616
|
+
TokenType2[TokenType2["L_BRACKET"] = 2] = "L_BRACKET";
|
|
617
|
+
TokenType2[TokenType2["R_BRACKET"] = 3] = "R_BRACKET";
|
|
618
|
+
TokenType2[TokenType2["BACKSLASH"] = 4] = "BACKSLASH";
|
|
619
|
+
TokenType2[TokenType2["EQUALS"] = 5] = "EQUALS";
|
|
620
|
+
TokenType2[TokenType2["XSS_AMP"] = 6] = "XSS_AMP";
|
|
621
|
+
TokenType2[TokenType2["XSS_LT"] = 7] = "XSS_LT";
|
|
622
|
+
TokenType2[TokenType2["XSS_GT"] = 8] = "XSS_GT";
|
|
623
|
+
TokenType2[TokenType2["XSS_D_QUOTE"] = 9] = "XSS_D_QUOTE";
|
|
624
|
+
TokenType2[TokenType2["XSS_S_QUOTE"] = 10] = "XSS_S_QUOTE";
|
|
625
|
+
return TokenType2;
|
|
626
|
+
})(TokenType || {});
|
|
627
|
+
function tokenTypeToString(tokenType) {
|
|
628
|
+
switch (tokenType) {
|
|
629
|
+
case 0:
|
|
630
|
+
return "STR";
|
|
631
|
+
case 1:
|
|
632
|
+
return "LINEBREAK";
|
|
633
|
+
case 2:
|
|
634
|
+
return "L_BRACKET";
|
|
635
|
+
case 3:
|
|
636
|
+
return "R_BRACKET";
|
|
637
|
+
case 4:
|
|
638
|
+
return "BACKSLASH";
|
|
639
|
+
case 5:
|
|
640
|
+
return "EQUALS";
|
|
641
|
+
case 6:
|
|
642
|
+
return "XSS_AMP";
|
|
643
|
+
case 7:
|
|
644
|
+
return "XSS_LT";
|
|
645
|
+
case 8:
|
|
646
|
+
return "XSS_GT";
|
|
647
|
+
case 9:
|
|
648
|
+
return "XSS_D_QUOTE";
|
|
649
|
+
case 10:
|
|
650
|
+
return "XSS_S_QUOTE";
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function isStringToken(tokenType) {
|
|
654
|
+
switch (tokenType) {
|
|
655
|
+
case 6:
|
|
656
|
+
case 7:
|
|
657
|
+
case 8:
|
|
658
|
+
case 9:
|
|
659
|
+
case 10:
|
|
660
|
+
case 0: {
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
const symbolTable = {
|
|
667
|
+
"\n": 1,
|
|
668
|
+
"[": 2,
|
|
669
|
+
"]": 3,
|
|
670
|
+
"/": 4,
|
|
671
|
+
"=": 5,
|
|
672
|
+
"&": 6,
|
|
673
|
+
"<": 7,
|
|
674
|
+
">": 8,
|
|
675
|
+
'"': 9,
|
|
676
|
+
"'": 10
|
|
677
|
+
/* XSS_S_QUOTE */
|
|
678
|
+
};
|
|
679
|
+
class Lexer {
|
|
680
|
+
tokenize(input) {
|
|
681
|
+
const tokens = new Array();
|
|
682
|
+
const re = /\n|\[\/|\[(\w+|\*)|\]|=|&|<|>|'|"/g;
|
|
683
|
+
let offset = 0;
|
|
684
|
+
while (true) {
|
|
685
|
+
const match = re.exec(input);
|
|
686
|
+
if (!match) {
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
const length2 = match.index - offset;
|
|
690
|
+
if (length2 > 0) {
|
|
691
|
+
tokens.push({
|
|
692
|
+
type: TokenType.STR,
|
|
693
|
+
offset,
|
|
694
|
+
length: length2
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
offset = match.index;
|
|
698
|
+
if (match[0] === "[/") {
|
|
699
|
+
tokens.push({
|
|
700
|
+
type: TokenType.L_BRACKET,
|
|
701
|
+
offset,
|
|
702
|
+
length: 1
|
|
703
|
+
});
|
|
704
|
+
offset += 1;
|
|
705
|
+
tokens.push({
|
|
706
|
+
type: TokenType.BACKSLASH,
|
|
707
|
+
offset,
|
|
708
|
+
length: 1
|
|
709
|
+
});
|
|
710
|
+
offset += 1;
|
|
711
|
+
} else if (match[0].startsWith("[")) {
|
|
712
|
+
tokens.push({
|
|
713
|
+
type: TokenType.L_BRACKET,
|
|
714
|
+
offset,
|
|
715
|
+
length: 1
|
|
716
|
+
});
|
|
717
|
+
offset += 1;
|
|
718
|
+
const length3 = match[0].length - 1;
|
|
719
|
+
tokens.push({
|
|
720
|
+
type: TokenType.STR,
|
|
721
|
+
offset,
|
|
722
|
+
length: length3
|
|
723
|
+
});
|
|
724
|
+
offset += length3;
|
|
725
|
+
} else {
|
|
726
|
+
tokens.push({
|
|
727
|
+
type: symbolTable[match[0]] ?? TokenType.STR,
|
|
728
|
+
offset,
|
|
729
|
+
length: 1
|
|
730
|
+
});
|
|
731
|
+
offset += 1;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
const length = input.length - offset;
|
|
735
|
+
if (length > 0) {
|
|
736
|
+
tokens.push({
|
|
737
|
+
type: TokenType.STR,
|
|
738
|
+
offset,
|
|
739
|
+
length
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
return tokens;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function stringifyTokens(ogText, tokens) {
|
|
746
|
+
let s = "";
|
|
747
|
+
for (const token of tokens) {
|
|
748
|
+
switch (token.type) {
|
|
749
|
+
case TokenType.STR: {
|
|
750
|
+
s += ogText.substring(token.offset, token.offset + token.length);
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
case TokenType.LINEBREAK: {
|
|
754
|
+
s += "\n";
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
757
|
+
case TokenType.L_BRACKET: {
|
|
758
|
+
s += "[";
|
|
759
|
+
break;
|
|
760
|
+
}
|
|
761
|
+
case TokenType.R_BRACKET: {
|
|
762
|
+
s += "]";
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
case TokenType.BACKSLASH: {
|
|
766
|
+
s += "/";
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
case TokenType.EQUALS: {
|
|
770
|
+
s += "=";
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
case TokenType.XSS_AMP: {
|
|
774
|
+
s += "&";
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
case TokenType.XSS_LT: {
|
|
778
|
+
s += "<";
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
case TokenType.XSS_GT: {
|
|
782
|
+
s += ">";
|
|
783
|
+
break;
|
|
784
|
+
}
|
|
785
|
+
case TokenType.XSS_D_QUOTE: {
|
|
786
|
+
s += """;
|
|
787
|
+
break;
|
|
788
|
+
}
|
|
789
|
+
case TokenType.XSS_S_QUOTE: {
|
|
790
|
+
s += "'";
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return s;
|
|
796
|
+
}
|
|
797
|
+
class Parser {
|
|
798
|
+
constructor(transforms = htmlTransforms) {
|
|
799
|
+
__publicField(this, "tags");
|
|
800
|
+
__publicField(this, "linebreakTerminatedTags");
|
|
801
|
+
__publicField(this, "standaloneTags");
|
|
802
|
+
this.tags = new Set(transforms.map((transform) => transform.name));
|
|
803
|
+
this.linebreakTerminatedTags = new Set(transforms.filter((transform) => transform.isLinebreakTerminated).map((transform) => transform.name.toLowerCase()));
|
|
804
|
+
this.standaloneTags = new Set(transforms.filter((transform) => transform.isStandalone).map((transform) => transform.name.toLowerCase()));
|
|
805
|
+
}
|
|
806
|
+
parse(ogText, tokens) {
|
|
807
|
+
let idx = 0;
|
|
808
|
+
const parseLabel = () => {
|
|
809
|
+
const slice = tokens.slice(idx, idx + 1);
|
|
810
|
+
const label = stringifyTokens(ogText, slice);
|
|
811
|
+
idx += 1;
|
|
812
|
+
return label.toLowerCase();
|
|
813
|
+
};
|
|
814
|
+
const parseText = (endOnQuotes = false, endOnSpace = false) => {
|
|
815
|
+
const startIdx = idx;
|
|
816
|
+
while (idx < tokens.length) {
|
|
817
|
+
if (!isStringToken(tokens[idx].type)) {
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
820
|
+
if (endOnQuotes && (tokens[idx].type === TokenType.XSS_S_QUOTE || tokens[idx].type === TokenType.XSS_D_QUOTE)) {
|
|
821
|
+
break;
|
|
822
|
+
}
|
|
823
|
+
if (endOnSpace && !endOnQuotes) {
|
|
824
|
+
const origStr = stringifyTokens(ogText, [tokens[idx]]);
|
|
825
|
+
const spaceIdx = origStr.indexOf(" ");
|
|
826
|
+
if (spaceIdx >= 0) {
|
|
827
|
+
const oldToken = {
|
|
828
|
+
type: TokenType.STR,
|
|
829
|
+
offset: tokens[idx].offset,
|
|
830
|
+
length: spaceIdx
|
|
831
|
+
};
|
|
832
|
+
const newToken = {
|
|
833
|
+
type: TokenType.STR,
|
|
834
|
+
offset: tokens[idx].offset + spaceIdx,
|
|
835
|
+
length: tokens[idx].length - spaceIdx
|
|
836
|
+
};
|
|
837
|
+
tokens.splice(idx + 0, 1, oldToken);
|
|
838
|
+
tokens.splice(idx + 1, 0, newToken);
|
|
839
|
+
idx += 1;
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
idx += 1;
|
|
844
|
+
}
|
|
845
|
+
const slice = tokens.slice(startIdx, idx);
|
|
846
|
+
const str = stringifyTokens(ogText, slice);
|
|
847
|
+
return new TextNode(str);
|
|
848
|
+
};
|
|
849
|
+
const parseAttr = () => {
|
|
850
|
+
if (idx + 1 >= tokens.length) {
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
const attrNode = new AttrNode();
|
|
854
|
+
if (tokens[idx].type === TokenType.EQUALS && isStringToken(tokens[idx + 1].type)) {
|
|
855
|
+
idx += 1;
|
|
856
|
+
const openedWithQuotes = tokens[idx].type === TokenType.XSS_S_QUOTE || tokens[idx].type === TokenType.XSS_D_QUOTE;
|
|
857
|
+
if (openedWithQuotes) {
|
|
858
|
+
idx += 1;
|
|
859
|
+
}
|
|
860
|
+
const valNode = parseText(openedWithQuotes, true);
|
|
861
|
+
attrNode.addChild(valNode);
|
|
862
|
+
if (openedWithQuotes) {
|
|
863
|
+
if (tokens[idx].type !== TokenType.XSS_S_QUOTE && tokens[idx].type !== TokenType.XSS_D_QUOTE) {
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
idx += 1;
|
|
867
|
+
}
|
|
868
|
+
} else if (isStringToken(tokens[idx].type) && tokens[idx + 1].type === TokenType.EQUALS && (idx + 2 < tokens.length && isStringToken(tokens[idx + 2].type))) {
|
|
869
|
+
const keyNode = parseText();
|
|
870
|
+
attrNode.addChild(keyNode);
|
|
871
|
+
idx += 1;
|
|
872
|
+
const openedWithQuotes = tokens[idx].type === TokenType.XSS_S_QUOTE || tokens[idx].type === TokenType.XSS_D_QUOTE;
|
|
873
|
+
if (openedWithQuotes) {
|
|
874
|
+
idx += 1;
|
|
875
|
+
}
|
|
876
|
+
const valNode = parseText(openedWithQuotes, true);
|
|
877
|
+
if (openedWithQuotes) {
|
|
878
|
+
if (tokens[idx].type !== TokenType.XSS_S_QUOTE && tokens[idx].type !== TokenType.XSS_D_QUOTE) {
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
idx += 1;
|
|
882
|
+
}
|
|
883
|
+
attrNode.addChild(valNode);
|
|
884
|
+
} else if (isStringToken(tokens[idx].type) && tokens[idx + 1].type !== TokenType.EQUALS) {
|
|
885
|
+
const valNode = parseText();
|
|
886
|
+
attrNode.addChild(valNode);
|
|
887
|
+
} else {
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
return attrNode;
|
|
891
|
+
};
|
|
892
|
+
const parseTag = () => {
|
|
893
|
+
if (idx + 1 >= tokens.length) {
|
|
894
|
+
return null;
|
|
895
|
+
}
|
|
896
|
+
if (tokens[idx].type !== TokenType.L_BRACKET) {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
if (isStringToken(tokens[idx + 1].type)) {
|
|
900
|
+
const startIdx = idx;
|
|
901
|
+
idx += 1;
|
|
902
|
+
const labelText = parseLabel();
|
|
903
|
+
if (!this.tags.has(labelText)) {
|
|
904
|
+
return null;
|
|
905
|
+
}
|
|
906
|
+
const attrNodes = new Array();
|
|
907
|
+
while (true) {
|
|
908
|
+
const attrNode = parseAttr();
|
|
909
|
+
if (attrNode === null) {
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
attrNodes.push(attrNode);
|
|
913
|
+
}
|
|
914
|
+
if (tokens[idx].type !== TokenType.R_BRACKET) {
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
idx += 1;
|
|
918
|
+
const slice = tokens.slice(startIdx, idx);
|
|
919
|
+
const ogTag = stringifyTokens(ogText, slice);
|
|
920
|
+
const startTagNode = new StartTagNode(labelText, ogTag, attrNodes);
|
|
921
|
+
return startTagNode;
|
|
922
|
+
}
|
|
923
|
+
if (tokens[idx + 1].type === TokenType.BACKSLASH) {
|
|
924
|
+
const startIdx = idx;
|
|
925
|
+
idx += 1;
|
|
926
|
+
idx += 1;
|
|
927
|
+
const labelText = parseLabel();
|
|
928
|
+
if (!this.tags.has(labelText)) {
|
|
929
|
+
return null;
|
|
930
|
+
}
|
|
931
|
+
if (tokens[idx].type !== TokenType.R_BRACKET) {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
idx += 1;
|
|
935
|
+
const slice = tokens.slice(startIdx, idx);
|
|
936
|
+
const ogTag = stringifyTokens(ogText, slice);
|
|
937
|
+
const endTagNode = new EndTagNode(labelText, ogTag);
|
|
938
|
+
return endTagNode;
|
|
939
|
+
}
|
|
940
|
+
return null;
|
|
941
|
+
};
|
|
942
|
+
const parseRoot = () => {
|
|
943
|
+
const root2 = new RootNode();
|
|
944
|
+
while (idx < tokens.length) {
|
|
945
|
+
if (tokens[idx].type === TokenType.L_BRACKET) {
|
|
946
|
+
const startIdx = idx;
|
|
947
|
+
const tagNode = parseTag();
|
|
948
|
+
if (tagNode !== null) {
|
|
949
|
+
root2.addChild(tagNode);
|
|
950
|
+
} else {
|
|
951
|
+
const invalidTokens = tokens.slice(startIdx, idx);
|
|
952
|
+
const str = stringifyTokens(ogText, invalidTokens);
|
|
953
|
+
const textNode = new TextNode(str);
|
|
954
|
+
root2.addChild(textNode);
|
|
955
|
+
}
|
|
956
|
+
} else if (tokens[idx].type === TokenType.LINEBREAK) {
|
|
957
|
+
idx += 1;
|
|
958
|
+
root2.addChild(new LinebreakNode());
|
|
959
|
+
} else {
|
|
960
|
+
const startIdx = idx;
|
|
961
|
+
while (idx < tokens.length && tokens[idx].type !== TokenType.L_BRACKET && tokens[idx].type !== TokenType.LINEBREAK) {
|
|
962
|
+
idx += 1;
|
|
963
|
+
}
|
|
964
|
+
const slice = tokens.slice(startIdx, idx);
|
|
965
|
+
const str = stringifyTokens(ogText, slice);
|
|
966
|
+
root2.addChild(new TextNode(str));
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return root2;
|
|
970
|
+
};
|
|
971
|
+
let root = parseRoot();
|
|
972
|
+
root = this.matchTagNodes(root);
|
|
973
|
+
return root;
|
|
974
|
+
}
|
|
975
|
+
// ------------------------------------------------------------------------
|
|
976
|
+
// Post Parsing Transforms
|
|
977
|
+
// ------------------------------------------------------------------------
|
|
978
|
+
matchTagNodes(rootNode) {
|
|
979
|
+
const transformedRoot = new RootNode();
|
|
980
|
+
for (let i = 0; i < rootNode.children.length; i++) {
|
|
981
|
+
const child = rootNode.children[i];
|
|
982
|
+
if (nodeIsType(child, AstNodeType.StartTagNode)) {
|
|
983
|
+
const endTag = this.findMatchingEndTag(rootNode.children, i, child.tagName);
|
|
984
|
+
const isStandalone = this.standaloneTags.has(child.tagName);
|
|
985
|
+
if (endTag || isStandalone) {
|
|
986
|
+
const tagNode = new TagNode(child, endTag == null ? void 0 : endTag.node);
|
|
987
|
+
transformedRoot.addChild(tagNode);
|
|
988
|
+
if (endTag) {
|
|
989
|
+
const subRoot = new RootNode(rootNode.children.slice(i + 1, endTag.idx));
|
|
990
|
+
i = endTag.idx;
|
|
991
|
+
const transformedSubRoot = this.matchTagNodes(subRoot);
|
|
992
|
+
tagNode.addChild(transformedSubRoot);
|
|
993
|
+
}
|
|
994
|
+
} else {
|
|
995
|
+
transformedRoot.addChild(new TextNode(child.ogTag));
|
|
996
|
+
}
|
|
997
|
+
} else if (nodeIsType(child, AstNodeType.EndTagNode)) {
|
|
998
|
+
transformedRoot.addChild(new TextNode(child.ogTag));
|
|
999
|
+
} else if (nodeIsType(child, AstNodeType.TextNode)) {
|
|
1000
|
+
transformedRoot.addChild(child);
|
|
1001
|
+
} else if (nodeIsType(child, AstNodeType.LinebreakNode)) {
|
|
1002
|
+
transformedRoot.addChild(child);
|
|
1003
|
+
} else {
|
|
1004
|
+
throw new Error("Unexpected child of RootNode");
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return transformedRoot;
|
|
1008
|
+
}
|
|
1009
|
+
findMatchingEndTag(siblings, startIdx, tagName) {
|
|
1010
|
+
if (this.standaloneTags.has(tagName)) {
|
|
1011
|
+
return null;
|
|
1012
|
+
}
|
|
1013
|
+
for (let i = startIdx; i < siblings.length; i++) {
|
|
1014
|
+
const sibling = siblings[i];
|
|
1015
|
+
const isEndTag = nodeIsType(sibling, AstNodeType.LinebreakNode) && this.linebreakTerminatedTags.has(tagName) || nodeIsType(sibling, AstNodeType.EndTagNode) && sibling.tagName === tagName;
|
|
1016
|
+
if (isEndTag) {
|
|
1017
|
+
return {
|
|
1018
|
+
idx: i,
|
|
1019
|
+
node: sibling
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
return null;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
function generateHtml(input, transforms = htmlTransforms) {
|
|
1027
|
+
const lexer = new Lexer();
|
|
1028
|
+
const tokens = lexer.tokenize(input);
|
|
1029
|
+
const parser = new Parser(transforms);
|
|
1030
|
+
const root = parser.parse(input, tokens);
|
|
1031
|
+
const generator = new Generator(transforms);
|
|
1032
|
+
return generator.generate(root);
|
|
1033
|
+
}
|
|
1034
|
+
export {
|
|
1035
|
+
AstNode,
|
|
1036
|
+
AstNodeType,
|
|
1037
|
+
AttrNode,
|
|
1038
|
+
EndTagNode,
|
|
1039
|
+
Generator,
|
|
1040
|
+
Lexer,
|
|
1041
|
+
LinebreakNode,
|
|
1042
|
+
Parser,
|
|
1043
|
+
RootNode,
|
|
1044
|
+
StartTagNode,
|
|
1045
|
+
TagNode,
|
|
1046
|
+
TextNode,
|
|
1047
|
+
TokenType,
|
|
1048
|
+
generateHtml,
|
|
1049
|
+
getTagImmediateAttrVal,
|
|
1050
|
+
getTagImmediateText,
|
|
1051
|
+
getWidthHeightAttr,
|
|
1052
|
+
htmlTransforms,
|
|
1053
|
+
isDangerousUrl,
|
|
1054
|
+
isOrderedList,
|
|
1055
|
+
isStringToken,
|
|
1056
|
+
nodeIsType,
|
|
1057
|
+
stringifyTokens,
|
|
1058
|
+
symbolTable,
|
|
1059
|
+
tokenTypeToString
|
|
1060
|
+
};
|
|
1061
|
+
//# sourceMappingURL=index.js.map
|