bbcode-compiler 0.1.4 → 0.1.6

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