bbcode-compiler 0.1.8 → 0.1.10
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/generator/Generator.d.ts +0 -1
- package/dist/generator/Generator.d.ts.map +1 -1
- package/dist/generator/transforms/Transform.d.ts +0 -1
- package/dist/generator/transforms/htmlTransforms.d.ts +0 -1
- package/dist/generator/utils/getTagImmediateAttrVal.d.ts +0 -1
- package/dist/generator/utils/getTagImmediateText.d.ts +0 -1
- package/dist/generator/utils/getTagImmediateText.d.ts.map +1 -1
- package/dist/generator/utils/getWidthHeightAttr.d.ts +0 -1
- package/dist/generator/utils/isOrderedList.d.ts +0 -1
- package/dist/index.js +104 -165
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +107 -168
- package/dist/index.umd.cjs.map +1 -1
- package/dist/lexer/Lexer.d.ts +0 -1
- package/dist/lexer/Token.d.ts +0 -1
- package/dist/lexer/Token.d.ts.map +1 -1
- package/dist/lexer/TokenType.d.ts +1 -13
- package/dist/lexer/TokenType.d.ts.map +1 -1
- package/dist/parser/AstNode.d.ts +10 -18
- package/dist/parser/AstNode.d.ts.map +1 -1
- package/dist/parser/Parser.d.ts +0 -1
- package/dist/parser/Parser.d.ts.map +1 -1
- package/dist/parser/nodeIsType.d.ts +8 -9
- package/dist/parser/nodeIsType.d.ts.map +1 -1
- package/package.json +83 -83
- package/src/generator/Generator.ts +4 -4
- package/src/generator/utils/getTagImmediateText.ts +3 -3
- package/src/lexer/Lexer.ts +8 -8
- package/src/lexer/Token.ts +12 -12
- package/src/lexer/TokenType.ts +39 -40
- package/src/parser/AstNode.ts +29 -28
- package/src/parser/Parser.ts +26 -26
- package/src/parser/nodeIsType.ts +8 -8
package/package.json
CHANGED
|
@@ -1,85 +1,85 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"files": [
|
|
17
|
-
"README.md",
|
|
18
|
-
"dist/*",
|
|
19
|
-
"src/*"
|
|
20
|
-
],
|
|
21
|
-
"homepage": "https://trinovantes.github.io/bbcode-compiler/",
|
|
22
|
-
"repository": {
|
|
23
|
-
"type": "git",
|
|
24
|
-
"url": "https://github.com/Trinovantes/bbcode-compiler"
|
|
25
|
-
},
|
|
26
|
-
"author": {
|
|
27
|
-
"name": "Stephen",
|
|
28
|
-
"email": "hello@stephenli.ca",
|
|
29
|
-
"url": "https://www.stephenli.ca"
|
|
30
|
-
},
|
|
31
|
-
"scripts": {
|
|
32
|
-
"demoDev": " vite --config vite.config.demo.ts",
|
|
33
|
-
"demoBuild": " vite build --config vite.config.demo.ts",
|
|
34
|
-
"demoPreview": "vite preview --config vite.config.demo.ts",
|
|
35
|
-
"clean": "rm -rf ./dist ./demo/dist",
|
|
36
|
-
"build": "yarn clean && vite build",
|
|
37
|
-
"prepublishOnly": "yarn build",
|
|
38
|
-
"postinstall": "patch-package",
|
|
39
|
-
"lint": "vue-tsc --noEmit && eslint",
|
|
40
|
-
"test": "vitest",
|
|
41
|
-
"benchmark": " node --loader ts-node/esm --experimental-specifier-resolution=node tests/benchmarks/benchmark.ts",
|
|
42
|
-
"profile": " node --loader ts-node/esm --experimental-specifier-resolution=node --prof --no-logfile-per-isolate tests/benchmarks/profile.ts && node --prof-process v8.log > v8.txt"
|
|
43
|
-
},
|
|
44
|
-
"dependencies": {
|
|
45
|
-
"patch-package": "^8.0.0"
|
|
46
|
-
},
|
|
47
|
-
"devDependencies": {
|
|
48
|
-
"@bbob/html": "^3.0.0",
|
|
49
|
-
"@bbob/preset-html5": "^3.0.0",
|
|
50
|
-
"@eslint/compat": "^1.1.0",
|
|
51
|
-
"@eslint/js": "^9.4.0",
|
|
52
|
-
"@quasar/extras": "^1.16.11",
|
|
53
|
-
"@quasar/vite-plugin": "^1.7.0",
|
|
54
|
-
"@stylistic/eslint-plugin": "^2.1.0",
|
|
55
|
-
"@thoughtsunificator/bbcode-parser-template": "^1.0.9",
|
|
56
|
-
"@types/benchmark": "^2.1.1",
|
|
57
|
-
"@types/lodash.debounce": "^4.0.9",
|
|
58
|
-
"@types/markdown-it": "^14.1.1",
|
|
59
|
-
"@types/node": "^20.7.0",
|
|
60
|
-
"@vitejs/plugin-vue": "^5.0.5",
|
|
61
|
-
"bbcode": "^0.1.5",
|
|
62
|
-
"bbcode-parser": "^1.0.10",
|
|
63
|
-
"bbcodejs": "^0.0.4",
|
|
64
|
-
"benchmark": "^2.1.4",
|
|
65
|
-
"eslint": "^9.4.0",
|
|
66
|
-
"eslint-plugin-n": "^17.7.0",
|
|
67
|
-
"eslint-plugin-vue": "^9.9.0",
|
|
68
|
-
"lodash.debounce": "^4.0.8",
|
|
69
|
-
"lz-string": "^1.5.0",
|
|
70
|
-
"markdown-it": "^14.0.0",
|
|
71
|
-
"mitt": "^3.0.1",
|
|
72
|
-
"monaco-editor": "^0.50.0",
|
|
73
|
-
"quasar": "^2.16.4",
|
|
74
|
-
"sass": "^1.77.6",
|
|
75
|
-
"shiki": "^1.9.0",
|
|
76
|
-
"ts-bbcode-parser": "^1.0.4",
|
|
77
|
-
"ts-node": "^10.8.1",
|
|
78
|
-
"typescript": "^5.0.2",
|
|
79
|
-
"typescript-eslint": "^8.0.0-alpha.24",
|
|
80
|
-
"vite-plugin-dts": "^3.9.1",
|
|
81
|
-
"vitest": "^1.2.1",
|
|
82
|
-
"vue": "^3.4.30",
|
|
83
|
-
"ya-bbcode": "^4.0.0"
|
|
2
|
+
"name": "bbcode-compiler",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.10",
|
|
5
|
+
"description": "Parses BBCode and generates HTML ",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"private": false,
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.umd.cjs"
|
|
84
14
|
}
|
|
85
|
-
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"README.md",
|
|
18
|
+
"dist/*",
|
|
19
|
+
"src/*"
|
|
20
|
+
],
|
|
21
|
+
"homepage": "https://trinovantes.github.io/bbcode-compiler/",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/Trinovantes/bbcode-compiler"
|
|
25
|
+
},
|
|
26
|
+
"author": {
|
|
27
|
+
"name": "Stephen",
|
|
28
|
+
"email": "hello@stephenli.ca",
|
|
29
|
+
"url": "https://www.stephenli.ca"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@bbob/html": "^4.2.0",
|
|
33
|
+
"@bbob/preset-html5": "^4.2.0",
|
|
34
|
+
"@eslint/compat": "^1.1.0",
|
|
35
|
+
"@eslint/js": "^9.4.0",
|
|
36
|
+
"@quasar/extras": "^1.16.11",
|
|
37
|
+
"@quasar/vite-plugin": "^1.7.0",
|
|
38
|
+
"@stylistic/eslint-plugin": "^5.1.0",
|
|
39
|
+
"@thoughtsunificator/bbcode-parser-template": "^1.0.9",
|
|
40
|
+
"@types/benchmark": "^2.1.1",
|
|
41
|
+
"@types/lodash.debounce": "^4.0.9",
|
|
42
|
+
"@types/markdown-it": "^14.1.1",
|
|
43
|
+
"@types/node": "^22.10.0",
|
|
44
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
45
|
+
"bbcode": "^0.1.5",
|
|
46
|
+
"bbcode-parser": "^1.0.10",
|
|
47
|
+
"bbcodejs": "^0.0.4",
|
|
48
|
+
"benchmark": "^2.1.4",
|
|
49
|
+
"eslint": "^9.4.0",
|
|
50
|
+
"eslint-plugin-n": "^17.7.0",
|
|
51
|
+
"eslint-plugin-vue": "^10.0.0",
|
|
52
|
+
"globals": "^16.4.0",
|
|
53
|
+
"lodash.debounce": "^4.0.8",
|
|
54
|
+
"lz-string": "^1.5.0",
|
|
55
|
+
"markdown-it": "^14.0.0",
|
|
56
|
+
"mitt": "^3.0.1",
|
|
57
|
+
"monaco-editor": "^0.53.0",
|
|
58
|
+
"quasar": "^2.16.4",
|
|
59
|
+
"sass": "^1.77.6",
|
|
60
|
+
"shiki": "^3.1.0",
|
|
61
|
+
"ts-bbcode-parser": "^1.0.4",
|
|
62
|
+
"ts-node": "^10.8.1",
|
|
63
|
+
"tsx": "^4.15.7",
|
|
64
|
+
"typescript": "^5.8.1-rc",
|
|
65
|
+
"typescript-eslint": "^8.0.0-alpha.24",
|
|
66
|
+
"vite": "^7.1.5",
|
|
67
|
+
"vite-plugin-dts": "^4.2.2",
|
|
68
|
+
"vitest": "^3.0.7",
|
|
69
|
+
"vue": "^3.4.30",
|
|
70
|
+
"vue-eslint-parser": "^10.1.3",
|
|
71
|
+
"vue-tsc": "^3.0.6",
|
|
72
|
+
"ya-bbcode": "^4.0.0"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"demoDev": " vite --config vite.config.demo.ts",
|
|
76
|
+
"demoBuild": " vite build --config vite.config.demo.ts",
|
|
77
|
+
"demoPreview": "vite preview --config vite.config.demo.ts",
|
|
78
|
+
"clean": "rm -rf ./dist ./demo/dist",
|
|
79
|
+
"build": "vite build",
|
|
80
|
+
"lint": "vue-tsc && eslint",
|
|
81
|
+
"test": "vitest",
|
|
82
|
+
"benchmark": "tsx tests/benchmarks/benchmark.ts",
|
|
83
|
+
"profile": "tsx --prof --no-logfile-per-isolate tests/benchmarks/profile.ts && node --prof-process v8.log > v8.txt"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AstNode,
|
|
1
|
+
import { AstNode, RootNode } from '../parser/AstNode.js'
|
|
2
2
|
import { nodeIsType } from '../parser/nodeIsType.js'
|
|
3
3
|
import { htmlTransforms } from './transforms/htmlTransforms.js'
|
|
4
4
|
import type { Transform } from './transforms/Transform.js'
|
|
@@ -14,7 +14,7 @@ export class Generator {
|
|
|
14
14
|
const stringify = (node: AstNode): string => {
|
|
15
15
|
let output = ''
|
|
16
16
|
|
|
17
|
-
if (nodeIsType(node,
|
|
17
|
+
if (nodeIsType(node, 'TagNode')) {
|
|
18
18
|
const tagName = node.tagName
|
|
19
19
|
const transform = this.transforms.get(tagName)
|
|
20
20
|
if (!transform) {
|
|
@@ -42,9 +42,9 @@ export class Generator {
|
|
|
42
42
|
} else {
|
|
43
43
|
output += renderedEndTag
|
|
44
44
|
}
|
|
45
|
-
} else if (nodeIsType(node,
|
|
45
|
+
} else if (nodeIsType(node, 'TextNode')) {
|
|
46
46
|
output += node.str
|
|
47
|
-
} else if (nodeIsType(node,
|
|
47
|
+
} else if (nodeIsType(node, 'LinebreakNode')) {
|
|
48
48
|
output += '\n'
|
|
49
49
|
} else {
|
|
50
50
|
for (const child of node.children) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TagNode } from '../../parser/AstNode.js'
|
|
2
2
|
import { nodeIsType } from '../../parser/nodeIsType.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -16,7 +16,7 @@ export function getTagImmediateText(tagNode: TagNode): string | undefined {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const child = tagNode.children[0]
|
|
19
|
-
if (!nodeIsType(child,
|
|
19
|
+
if (!nodeIsType(child, 'RootNode')) {
|
|
20
20
|
return undefined
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -25,7 +25,7 @@ export function getTagImmediateText(tagNode: TagNode): string | undefined {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const textNode = child.children[0]
|
|
28
|
-
if (!nodeIsType(textNode,
|
|
28
|
+
if (!nodeIsType(textNode, 'TextNode')) {
|
|
29
29
|
return undefined
|
|
30
30
|
}
|
|
31
31
|
|
package/src/lexer/Lexer.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { symbolTable
|
|
1
|
+
import { symbolTable } from './TokenType.js'
|
|
2
2
|
import type { Token } from './Token.js'
|
|
3
3
|
|
|
4
4
|
export class Lexer {
|
|
@@ -25,7 +25,7 @@ export class Lexer {
|
|
|
25
25
|
const length = match.index - offset
|
|
26
26
|
if (length > 0) {
|
|
27
27
|
tokens.push({
|
|
28
|
-
type:
|
|
28
|
+
type: 'STR',
|
|
29
29
|
offset,
|
|
30
30
|
length,
|
|
31
31
|
})
|
|
@@ -37,21 +37,21 @@ export class Lexer {
|
|
|
37
37
|
// In the regex '[/' takes precedence over '['
|
|
38
38
|
if (match[0] === '[/') {
|
|
39
39
|
tokens.push({
|
|
40
|
-
type:
|
|
40
|
+
type: 'L_BRACKET',
|
|
41
41
|
offset,
|
|
42
42
|
length: 1,
|
|
43
43
|
})
|
|
44
44
|
offset += 1
|
|
45
45
|
|
|
46
46
|
tokens.push({
|
|
47
|
-
type:
|
|
47
|
+
type: 'BACKSLASH',
|
|
48
48
|
offset,
|
|
49
49
|
length: 1,
|
|
50
50
|
})
|
|
51
51
|
offset += 1
|
|
52
52
|
} else if (match[0].startsWith('[')) {
|
|
53
53
|
tokens.push({
|
|
54
|
-
type:
|
|
54
|
+
type: 'L_BRACKET',
|
|
55
55
|
offset,
|
|
56
56
|
length: 1,
|
|
57
57
|
})
|
|
@@ -59,14 +59,14 @@ export class Lexer {
|
|
|
59
59
|
|
|
60
60
|
const length = match[0].length - 1
|
|
61
61
|
tokens.push({
|
|
62
|
-
type:
|
|
62
|
+
type: 'STR',
|
|
63
63
|
offset,
|
|
64
64
|
length,
|
|
65
65
|
})
|
|
66
66
|
offset += length
|
|
67
67
|
} else {
|
|
68
68
|
tokens.push({
|
|
69
|
-
type: symbolTable[match[0]] ??
|
|
69
|
+
type: symbolTable[match[0]] ?? 'STR',
|
|
70
70
|
offset,
|
|
71
71
|
length: 1,
|
|
72
72
|
})
|
|
@@ -78,7 +78,7 @@ export class Lexer {
|
|
|
78
78
|
const length = input.length - offset
|
|
79
79
|
if (length > 0) {
|
|
80
80
|
tokens.push({
|
|
81
|
-
type:
|
|
81
|
+
type: 'STR',
|
|
82
82
|
offset,
|
|
83
83
|
length,
|
|
84
84
|
})
|
package/src/lexer/Token.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TokenType } from './TokenType.js'
|
|
1
|
+
import type { TokenType } from './TokenType.js'
|
|
2
2
|
|
|
3
3
|
export type Token = {
|
|
4
4
|
type: TokenType
|
|
@@ -11,49 +11,49 @@ export function stringifyTokens(ogText: string, tokens: ReadonlyArray<Token>): s
|
|
|
11
11
|
|
|
12
12
|
for (const token of tokens) {
|
|
13
13
|
switch (token.type) {
|
|
14
|
-
case
|
|
14
|
+
case 'STR': {
|
|
15
15
|
s += ogText.substring(token.offset, token.offset + token.length)
|
|
16
16
|
break
|
|
17
17
|
}
|
|
18
|
-
case
|
|
18
|
+
case 'LINEBREAK': {
|
|
19
19
|
s += '\n'
|
|
20
20
|
break
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
case
|
|
23
|
+
case 'L_BRACKET': {
|
|
24
24
|
s += '['
|
|
25
25
|
break
|
|
26
26
|
}
|
|
27
|
-
case
|
|
27
|
+
case 'R_BRACKET': {
|
|
28
28
|
s += ']'
|
|
29
29
|
break
|
|
30
30
|
}
|
|
31
|
-
case
|
|
31
|
+
case 'BACKSLASH': {
|
|
32
32
|
s += '/'
|
|
33
33
|
break
|
|
34
34
|
}
|
|
35
|
-
case
|
|
35
|
+
case 'EQUALS': {
|
|
36
36
|
s += '='
|
|
37
37
|
break
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
case
|
|
40
|
+
case 'XSS_AMP': {
|
|
41
41
|
s += '&'
|
|
42
42
|
break
|
|
43
43
|
}
|
|
44
|
-
case
|
|
44
|
+
case 'XSS_LT': {
|
|
45
45
|
s += '<'
|
|
46
46
|
break
|
|
47
47
|
}
|
|
48
|
-
case
|
|
48
|
+
case 'XSS_GT': {
|
|
49
49
|
s += '>'
|
|
50
50
|
break
|
|
51
51
|
}
|
|
52
|
-
case
|
|
52
|
+
case 'XSS_D_QUOTE': {
|
|
53
53
|
s += '"'
|
|
54
54
|
break
|
|
55
55
|
}
|
|
56
|
-
case
|
|
56
|
+
case 'XSS_S_QUOTE': {
|
|
57
57
|
s += '''
|
|
58
58
|
break
|
|
59
59
|
}
|
package/src/lexer/TokenType.ts
CHANGED
|
@@ -1,47 +1,46 @@
|
|
|
1
|
-
export
|
|
2
|
-
STR
|
|
3
|
-
LINEBREAK
|
|
1
|
+
export type TokenType =
|
|
2
|
+
| 'STR'
|
|
3
|
+
| 'LINEBREAK'
|
|
4
4
|
|
|
5
5
|
// BBCode symbols
|
|
6
|
-
L_BRACKET
|
|
7
|
-
R_BRACKET
|
|
8
|
-
BACKSLASH
|
|
9
|
-
EQUALS
|
|
6
|
+
| 'L_BRACKET'
|
|
7
|
+
| 'R_BRACKET'
|
|
8
|
+
| 'BACKSLASH'
|
|
9
|
+
| 'EQUALS'
|
|
10
10
|
|
|
11
11
|
// XSS symbols
|
|
12
|
-
XSS_AMP
|
|
13
|
-
XSS_LT
|
|
14
|
-
XSS_GT
|
|
15
|
-
XSS_D_QUOTE
|
|
16
|
-
XSS_S_QUOTE
|
|
17
|
-
}
|
|
12
|
+
| 'XSS_AMP'
|
|
13
|
+
| 'XSS_LT'
|
|
14
|
+
| 'XSS_GT'
|
|
15
|
+
| 'XSS_D_QUOTE'
|
|
16
|
+
| 'XSS_S_QUOTE'
|
|
18
17
|
|
|
19
18
|
export function tokenTypeToString(tokenType: TokenType): string {
|
|
20
19
|
switch (tokenType) {
|
|
21
|
-
case
|
|
22
|
-
case
|
|
20
|
+
case 'STR': return 'STR'
|
|
21
|
+
case 'LINEBREAK': return 'LINEBREAK'
|
|
23
22
|
|
|
24
|
-
case
|
|
25
|
-
case
|
|
26
|
-
case
|
|
27
|
-
case
|
|
23
|
+
case 'L_BRACKET': return 'L_BRACKET'
|
|
24
|
+
case 'R_BRACKET': return 'R_BRACKET'
|
|
25
|
+
case 'BACKSLASH': return 'BACKSLASH'
|
|
26
|
+
case 'EQUALS': return 'EQUALS'
|
|
28
27
|
|
|
29
|
-
case
|
|
30
|
-
case
|
|
31
|
-
case
|
|
32
|
-
case
|
|
33
|
-
case
|
|
28
|
+
case 'XSS_AMP': return 'XSS_AMP'
|
|
29
|
+
case 'XSS_LT': return 'XSS_LT'
|
|
30
|
+
case 'XSS_GT': return 'XSS_GT'
|
|
31
|
+
case 'XSS_D_QUOTE': return 'XSS_D_QUOTE'
|
|
32
|
+
case 'XSS_S_QUOTE': return 'XSS_S_QUOTE'
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
export function isStringToken(tokenType: TokenType): boolean {
|
|
38
37
|
switch (tokenType) {
|
|
39
|
-
case
|
|
40
|
-
case
|
|
41
|
-
case
|
|
42
|
-
case
|
|
43
|
-
case
|
|
44
|
-
case
|
|
38
|
+
case 'XSS_AMP':
|
|
39
|
+
case 'XSS_LT':
|
|
40
|
+
case 'XSS_GT':
|
|
41
|
+
case 'XSS_D_QUOTE':
|
|
42
|
+
case 'XSS_S_QUOTE':
|
|
43
|
+
case 'STR': {
|
|
45
44
|
return true
|
|
46
45
|
}
|
|
47
46
|
}
|
|
@@ -50,16 +49,16 @@ export function isStringToken(tokenType: TokenType): boolean {
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
export const symbolTable: Record<string, TokenType | undefined> = {
|
|
53
|
-
'\n':
|
|
52
|
+
'\n': 'LINEBREAK',
|
|
54
53
|
|
|
55
|
-
'[':
|
|
56
|
-
']':
|
|
57
|
-
'/':
|
|
58
|
-
'=':
|
|
54
|
+
'[': 'L_BRACKET',
|
|
55
|
+
']': 'R_BRACKET',
|
|
56
|
+
'/': 'BACKSLASH',
|
|
57
|
+
'=': 'EQUALS',
|
|
59
58
|
|
|
60
|
-
'&':
|
|
61
|
-
'<':
|
|
62
|
-
'>':
|
|
63
|
-
'"':
|
|
64
|
-
"'":
|
|
59
|
+
'&': 'XSS_AMP',
|
|
60
|
+
'<': 'XSS_LT',
|
|
61
|
+
'>': 'XSS_GT',
|
|
62
|
+
'"': 'XSS_D_QUOTE',
|
|
63
|
+
"'": 'XSS_S_QUOTE',
|
|
65
64
|
}
|
package/src/parser/AstNode.ts
CHANGED
|
@@ -39,15 +39,14 @@ import { nodeIsType } from './nodeIsType.js'
|
|
|
39
39
|
// MARK: AstNode
|
|
40
40
|
// ----------------------------------------------------------------------------
|
|
41
41
|
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
42
|
+
export type AstNodeType =
|
|
43
|
+
| 'RootNode'
|
|
44
|
+
| 'TextNode'
|
|
45
|
+
| 'LinebreakNode'
|
|
46
|
+
| 'TagNode'
|
|
47
|
+
| 'StartTagNode'
|
|
48
|
+
| 'EndTagNode'
|
|
49
|
+
| 'AttrNode'
|
|
51
50
|
|
|
52
51
|
export type AstNodeJson = {
|
|
53
52
|
type: AstNodeType
|
|
@@ -58,10 +57,12 @@ export type AstNodeJson = {
|
|
|
58
57
|
export abstract class AstNode {
|
|
59
58
|
readonly abstract nodeType: AstNodeType
|
|
60
59
|
|
|
60
|
+
readonly children: Array<AstNode>
|
|
61
|
+
|
|
61
62
|
constructor(
|
|
62
|
-
|
|
63
|
+
children = new Array<AstNode>(),
|
|
63
64
|
) {
|
|
64
|
-
|
|
65
|
+
this.children = children
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
addChild(node: AstNode): void {
|
|
@@ -112,13 +113,13 @@ export abstract class AstNode {
|
|
|
112
113
|
// ----------------------------------------------------------------------------
|
|
113
114
|
|
|
114
115
|
export class RootNode extends AstNode {
|
|
115
|
-
readonly nodeType =
|
|
116
|
+
readonly nodeType = 'RootNode'
|
|
116
117
|
|
|
117
118
|
override isValid(): boolean {
|
|
118
119
|
for (const child of this.children) {
|
|
119
|
-
if (child.nodeType !==
|
|
120
|
-
child.nodeType !==
|
|
121
|
-
child.nodeType !==
|
|
120
|
+
if (child.nodeType !== 'TagNode' &&
|
|
121
|
+
child.nodeType !== 'TextNode' &&
|
|
122
|
+
child.nodeType !== 'LinebreakNode') {
|
|
122
123
|
return false
|
|
123
124
|
}
|
|
124
125
|
}
|
|
@@ -132,7 +133,7 @@ export class RootNode extends AstNode {
|
|
|
132
133
|
// ----------------------------------------------------------------------------
|
|
133
134
|
|
|
134
135
|
export class TextNode extends AstNode {
|
|
135
|
-
readonly nodeType =
|
|
136
|
+
readonly nodeType = 'TextNode'
|
|
136
137
|
readonly str: string
|
|
137
138
|
|
|
138
139
|
constructor(str: string) {
|
|
@@ -160,7 +161,7 @@ export class TextNode extends AstNode {
|
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
export class LinebreakNode extends AstNode {
|
|
163
|
-
readonly nodeType =
|
|
164
|
+
readonly nodeType = 'LinebreakNode'
|
|
164
165
|
|
|
165
166
|
override toShortString(): string {
|
|
166
167
|
return `${super.toShortString()} "\\n"`
|
|
@@ -172,7 +173,7 @@ export class LinebreakNode extends AstNode {
|
|
|
172
173
|
// ----------------------------------------------------------------------------
|
|
173
174
|
|
|
174
175
|
export class AttrNode extends AstNode {
|
|
175
|
-
readonly nodeType =
|
|
176
|
+
readonly nodeType = 'AttrNode'
|
|
176
177
|
|
|
177
178
|
static readonly DEFAULT_KEY = 'default'
|
|
178
179
|
|
|
@@ -182,7 +183,7 @@ export class AttrNode extends AstNode {
|
|
|
182
183
|
return AttrNode.DEFAULT_KEY
|
|
183
184
|
}
|
|
184
185
|
case 2: {
|
|
185
|
-
if (!nodeIsType(this.children[0],
|
|
186
|
+
if (!nodeIsType(this.children[0], 'TextNode')) {
|
|
186
187
|
throw new Error('Invalid TextNode')
|
|
187
188
|
}
|
|
188
189
|
|
|
@@ -196,14 +197,14 @@ export class AttrNode extends AstNode {
|
|
|
196
197
|
get val(): string {
|
|
197
198
|
switch (this.children.length) {
|
|
198
199
|
case 1: {
|
|
199
|
-
if (!nodeIsType(this.children[0],
|
|
200
|
+
if (!nodeIsType(this.children[0], 'TextNode')) {
|
|
200
201
|
throw new Error('Invalid TextNode')
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
return this.children[0].str.trim()
|
|
204
205
|
}
|
|
205
206
|
case 2: {
|
|
206
|
-
if (!nodeIsType(this.children[1],
|
|
207
|
+
if (!nodeIsType(this.children[1], 'TextNode')) {
|
|
207
208
|
throw new Error('Invalid TextNode')
|
|
208
209
|
}
|
|
209
210
|
|
|
@@ -265,7 +266,7 @@ export class AttrNode extends AstNode {
|
|
|
265
266
|
// ----------------------------------------------------------------------------
|
|
266
267
|
|
|
267
268
|
export class StartTagNode extends AstNode {
|
|
268
|
-
readonly nodeType =
|
|
269
|
+
readonly nodeType = 'StartTagNode'
|
|
269
270
|
readonly tagName: string
|
|
270
271
|
readonly ogTag: string
|
|
271
272
|
|
|
@@ -277,7 +278,7 @@ export class StartTagNode extends AstNode {
|
|
|
277
278
|
|
|
278
279
|
override isValid(): boolean {
|
|
279
280
|
for (const child of this.children) {
|
|
280
|
-
if (child.nodeType !==
|
|
281
|
+
if (child.nodeType !== 'AttrNode') {
|
|
281
282
|
return false
|
|
282
283
|
}
|
|
283
284
|
}
|
|
@@ -301,7 +302,7 @@ export class StartTagNode extends AstNode {
|
|
|
301
302
|
}
|
|
302
303
|
|
|
303
304
|
export class EndTagNode extends AstNode {
|
|
304
|
-
readonly nodeType =
|
|
305
|
+
readonly nodeType = 'EndTagNode'
|
|
305
306
|
readonly tagName: string
|
|
306
307
|
readonly ogTag: string
|
|
307
308
|
|
|
@@ -331,7 +332,7 @@ export class EndTagNode extends AstNode {
|
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
export class TagNode extends AstNode {
|
|
334
|
-
readonly nodeType =
|
|
335
|
+
readonly nodeType = 'TagNode'
|
|
335
336
|
private readonly _startTag: StartTagNode
|
|
336
337
|
private readonly _endTag?: EndTagNode | LinebreakNode
|
|
337
338
|
|
|
@@ -358,7 +359,7 @@ export class TagNode extends AstNode {
|
|
|
358
359
|
return ''
|
|
359
360
|
}
|
|
360
361
|
|
|
361
|
-
if (nodeIsType(this._endTag,
|
|
362
|
+
if (nodeIsType(this._endTag, 'LinebreakNode')) {
|
|
362
363
|
return '\n'
|
|
363
364
|
} else {
|
|
364
365
|
return this._endTag.ogTag
|
|
@@ -366,11 +367,11 @@ export class TagNode extends AstNode {
|
|
|
366
367
|
}
|
|
367
368
|
|
|
368
369
|
override isValid(): boolean {
|
|
369
|
-
if (this._endTag && nodeIsType(this._endTag,
|
|
370
|
+
if (this._endTag && nodeIsType(this._endTag, 'EndTagNode') && this._startTag.tagName !== this._endTag.tagName) {
|
|
370
371
|
return false
|
|
371
372
|
}
|
|
372
373
|
|
|
373
|
-
if (this.children.length === 1 && this.children[0].nodeType !==
|
|
374
|
+
if (this.children.length === 1 && this.children[0].nodeType !== 'RootNode') {
|
|
374
375
|
return false
|
|
375
376
|
}
|
|
376
377
|
|