@markuplint/pug-parser 4.0.0-alpha.1 → 4.0.0-alpha.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/LICENSE +1 -1
- package/lib/index.d.ts +1 -2
- package/lib/index.js +1 -2
- package/lib/parser.d.ts +29 -0
- package/lib/parser.js +162 -0
- package/lib/pug-parser/index.js +5 -6
- package/lib/utils/get-offset-from-line-and-col.js +1 -1
- package/package.json +6 -7
- package/lib/attr-tokenizer.d.ts +0 -3
- package/lib/attr-tokenizer.js +0 -137
- package/lib/parse.d.ts +0 -2
- package/lib/parse.js +0 -248
package/LICENSE
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export declare const endTag = "never";
|
|
1
|
+
export { parser } from './parser.js';
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export const endTag = 'never';
|
|
1
|
+
export { parser } from './parser.js';
|
package/lib/parser.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ASTNode } from './pug-parser/index.js';
|
|
2
|
+
import type { MLASTAttr, MLASTNodeTreeItem, MLASTParentNode } from '@markuplint/ml-ast';
|
|
3
|
+
import type { ChildToken, Token } from '@markuplint/parser-utils';
|
|
4
|
+
import { ParserError, Parser } from '@markuplint/parser-utils';
|
|
5
|
+
declare class PugParser extends Parser<ASTNode> {
|
|
6
|
+
constructor();
|
|
7
|
+
tokenize(): {
|
|
8
|
+
ast: ASTNode[];
|
|
9
|
+
isFragment: boolean;
|
|
10
|
+
};
|
|
11
|
+
parseError(error: any): ParserError;
|
|
12
|
+
nodeize(originNode: ASTNode, parentNode: MLASTParentNode | null, depth: number): readonly MLASTNodeTreeItem[];
|
|
13
|
+
afterFlattenNodes(nodeList: readonly MLASTNodeTreeItem[]): readonly MLASTNodeTreeItem[];
|
|
14
|
+
visitElement(token: ChildToken & {
|
|
15
|
+
readonly nodeName: string;
|
|
16
|
+
readonly namespace: string;
|
|
17
|
+
}, childNodes: readonly ASTNode[], options: {
|
|
18
|
+
readonly overwriteProps: {
|
|
19
|
+
readonly attributes: readonly MLASTAttr[];
|
|
20
|
+
};
|
|
21
|
+
}): MLASTNodeTreeItem[];
|
|
22
|
+
visitAttr(token: Token): (import("@markuplint/ml-ast").MLASTHTMLAttr & {
|
|
23
|
+
__rightText?: string | undefined;
|
|
24
|
+
}) | (import("@markuplint/ml-ast").MLASTSpreadAttr & {
|
|
25
|
+
__rightText?: string | undefined;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export declare const parser: PugParser;
|
|
29
|
+
export {};
|
package/lib/parser.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { getNamespace, parser as htmlParser } from '@markuplint/html-parser';
|
|
2
|
+
import { ParserError, Parser, AttrState, removeQuote, scriptParser } from '@markuplint/parser-utils';
|
|
3
|
+
import { pugParse } from './pug-parser/index.js';
|
|
4
|
+
class PugParser extends Parser {
|
|
5
|
+
constructor() {
|
|
6
|
+
super({
|
|
7
|
+
endTagType: 'never',
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
tokenize() {
|
|
11
|
+
return {
|
|
12
|
+
ast: pugParse(this.rawCode).nodes,
|
|
13
|
+
isFragment: true,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
parseError(error) {
|
|
17
|
+
if (error instanceof Error && 'msg' in error && 'line' in error && 'column' in error && 'src' in error) {
|
|
18
|
+
return new ParserError(error.msg, {
|
|
19
|
+
line: error.line,
|
|
20
|
+
col: error.column,
|
|
21
|
+
raw: error.src,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return super.parseError(error);
|
|
25
|
+
}
|
|
26
|
+
nodeize(
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
28
|
+
originNode, parentNode, depth) {
|
|
29
|
+
const parentNamespace = parentNode && 'namespace' in parentNode ? parentNode.namespace : 'http://www.w3.org/1999/xhtml';
|
|
30
|
+
const token = this.sliceFragment(originNode.offset, originNode.endOffset);
|
|
31
|
+
switch (originNode.type) {
|
|
32
|
+
case 'Doctype': {
|
|
33
|
+
return this.visitDoctype({
|
|
34
|
+
...token,
|
|
35
|
+
depth,
|
|
36
|
+
parentNode,
|
|
37
|
+
name: originNode.val ?? '',
|
|
38
|
+
publicId: '',
|
|
39
|
+
systemId: '',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
case 'Text': {
|
|
43
|
+
if (originNode.raw.trim() === '') {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const htmlDoc = htmlParser.parse(originNode.raw, {
|
|
47
|
+
offsetOffset: originNode.offset,
|
|
48
|
+
offsetLine: originNode.line,
|
|
49
|
+
offsetColumn: originNode.column,
|
|
50
|
+
depth,
|
|
51
|
+
});
|
|
52
|
+
return htmlDoc.nodeList;
|
|
53
|
+
}
|
|
54
|
+
case 'Comment': {
|
|
55
|
+
return this.visitComment({
|
|
56
|
+
...token,
|
|
57
|
+
depth,
|
|
58
|
+
parentNode,
|
|
59
|
+
}, {
|
|
60
|
+
isBogus: false,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
case 'Tag': {
|
|
64
|
+
const namespace = getNamespace(originNode.name, parentNamespace);
|
|
65
|
+
return this.visitElement({
|
|
66
|
+
...token,
|
|
67
|
+
depth,
|
|
68
|
+
parentNode,
|
|
69
|
+
nodeName: originNode.name,
|
|
70
|
+
namespace,
|
|
71
|
+
}, originNode.block.nodes, {
|
|
72
|
+
overwriteProps: {
|
|
73
|
+
attributes: originNode.attrs.map(attr => {
|
|
74
|
+
const token = this.sliceFragment(attr.offset, attr.endOffset);
|
|
75
|
+
return this.visitAttr(token);
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
default: {
|
|
81
|
+
return this.visitPsBlock({
|
|
82
|
+
...token,
|
|
83
|
+
depth,
|
|
84
|
+
parentNode,
|
|
85
|
+
nodeName: originNode.type,
|
|
86
|
+
}, 'block' in originNode && originNode.block ? originNode.block.nodes : []);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
afterFlattenNodes(nodeList) {
|
|
91
|
+
return super.afterFlattenNodes(nodeList, {
|
|
92
|
+
exposeInvalidNode: false,
|
|
93
|
+
exposeWhiteSpace: false,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
visitElement(token,
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
98
|
+
childNodes, options) {
|
|
99
|
+
const startTag = {
|
|
100
|
+
...token,
|
|
101
|
+
...this.createToken(token),
|
|
102
|
+
...options.overwriteProps,
|
|
103
|
+
type: 'starttag',
|
|
104
|
+
elementType: this.detectElementType(token.nodeName),
|
|
105
|
+
childNodes: [],
|
|
106
|
+
pairNode: null,
|
|
107
|
+
tagOpenChar: '',
|
|
108
|
+
tagCloseChar: '',
|
|
109
|
+
isGhost: false,
|
|
110
|
+
};
|
|
111
|
+
const siblings = this.visitChildren(childNodes, startTag);
|
|
112
|
+
return [startTag, ...siblings];
|
|
113
|
+
}
|
|
114
|
+
visitAttr(token) {
|
|
115
|
+
if (token.raw[0] === '#' || token.raw[0] === '.') {
|
|
116
|
+
const attr = super.visitAttr(token, {
|
|
117
|
+
startState: AttrState.BeforeValue,
|
|
118
|
+
quoteSet: [],
|
|
119
|
+
quoteInValueChars: [],
|
|
120
|
+
endOfUnquotedValueChars: [],
|
|
121
|
+
});
|
|
122
|
+
if (attr.type === 'spread') {
|
|
123
|
+
return attr;
|
|
124
|
+
}
|
|
125
|
+
this.updateAttr(attr, {
|
|
126
|
+
potentialName: token.raw[0] === '#' ? 'id' : 'class',
|
|
127
|
+
potentialValue: attr.value.raw.slice(1),
|
|
128
|
+
isDuplicatable: attr.potentialName === 'class',
|
|
129
|
+
});
|
|
130
|
+
return attr;
|
|
131
|
+
}
|
|
132
|
+
const attr = super.visitAttr(token, {
|
|
133
|
+
quoteSet: [],
|
|
134
|
+
quoteInValueChars: [],
|
|
135
|
+
endOfUnquotedValueChars: [],
|
|
136
|
+
});
|
|
137
|
+
if (attr.type === 'spread') {
|
|
138
|
+
return attr;
|
|
139
|
+
}
|
|
140
|
+
if (attr.name.raw.toLowerCase() === 'class') {
|
|
141
|
+
this.updateAttr(attr, { isDuplicatable: true });
|
|
142
|
+
}
|
|
143
|
+
const valueCodeTokens = scriptParser(attr.value.raw.trim());
|
|
144
|
+
if (valueCodeTokens.length === 1) {
|
|
145
|
+
const token = valueCodeTokens[0];
|
|
146
|
+
if (token.type === 'Numeric' || token.type === 'Boolean') {
|
|
147
|
+
this.updateAttr(attr, { potentialValue: token.value });
|
|
148
|
+
}
|
|
149
|
+
else if (token.type === 'String' || token.type === 'Template') {
|
|
150
|
+
this.updateAttr(attr, { potentialValue: removeQuote(token.value) });
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
this.updateAttr(attr, { isDynamicValue: true });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.updateAttr(attr, { isDynamicValue: true });
|
|
158
|
+
}
|
|
159
|
+
return attr;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
export const parser = new PugParser();
|
package/lib/pug-parser/index.js
CHANGED
|
@@ -7,13 +7,11 @@ export function pugParse(pug) {
|
|
|
7
7
|
const lexOrigin = lexer(pug);
|
|
8
8
|
const lex = JSON.parse(JSON.stringify(lexOrigin));
|
|
9
9
|
const originAst = parser(lexOrigin);
|
|
10
|
-
// console.log(lex);
|
|
11
|
-
// console.log(JSON.stringify(originAst, null, 2));
|
|
12
10
|
const ast = optimizeAST(originAst, lex, pug);
|
|
13
11
|
return ast;
|
|
14
12
|
}
|
|
15
13
|
function getOffsetsFromLines(pug) {
|
|
16
|
-
const lines = pug.split(/\n/
|
|
14
|
+
const lines = pug.split(/\n/);
|
|
17
15
|
let chars = 0;
|
|
18
16
|
const result = lines.map(line => {
|
|
19
17
|
chars += line.length + 1;
|
|
@@ -26,7 +24,7 @@ function mergeTextNode(
|
|
|
26
24
|
nodes, pug) {
|
|
27
25
|
const baseNodes = [];
|
|
28
26
|
for (const node of nodes) {
|
|
29
|
-
const prevNode = baseNodes
|
|
27
|
+
const prevNode = baseNodes.at(-1) ?? null;
|
|
30
28
|
if (prevNode && prevNode.type === 'Text' && node.type === 'Text') {
|
|
31
29
|
prevNode.raw = pug.slice(prevNode.offset, node.endOffset);
|
|
32
30
|
prevNode.endColumn = node.endColumn;
|
|
@@ -292,8 +290,9 @@ tokens, pug) {
|
|
|
292
290
|
continue;
|
|
293
291
|
}
|
|
294
292
|
default: {
|
|
293
|
+
throw new Error(`Unsupported syntax: The "${
|
|
295
294
|
// @ts-ignore
|
|
296
|
-
|
|
295
|
+
node.type}" node\n${JSON.stringify(node, null, 2)}`);
|
|
297
296
|
}
|
|
298
297
|
}
|
|
299
298
|
}
|
|
@@ -382,7 +381,7 @@ tokens, pug, offsets, depth) {
|
|
|
382
381
|
function getLocationFromToken(offset, line, column,
|
|
383
382
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
384
383
|
tokens, tokenType) {
|
|
385
|
-
const tokenTypes = typeof tokenType === 'string' ? (tokenType
|
|
384
|
+
const tokenTypes = typeof tokenType === 'string' ? (tokenType === '' ? null : [tokenType]) : tokenType ?? null;
|
|
386
385
|
let tokenOfCurrentNode = null;
|
|
387
386
|
for (const token of tokens) {
|
|
388
387
|
if ((tokenTypes == null || tokenTypes.includes(token.type)) &&
|
|
@@ -2,7 +2,7 @@ export function getOffsetFromLineAndCol(str, line, col) {
|
|
|
2
2
|
const lines = str.split('\n').slice(0, line);
|
|
3
3
|
const lastLine = lines.pop();
|
|
4
4
|
if (lastLine) {
|
|
5
|
-
lines.push(lastLine.
|
|
5
|
+
lines.push([...lastLine].slice(0, col).join(''));
|
|
6
6
|
}
|
|
7
7
|
return lines.join('\n').length;
|
|
8
8
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/pug-parser",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.10",
|
|
4
4
|
"description": "Pug parser for markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -21,12 +21,11 @@
|
|
|
21
21
|
"clean": "tsc --build --clean"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@markuplint/html-parser": "4.0.0-alpha.
|
|
25
|
-
"@markuplint/ml-ast": "4.0.0-alpha.
|
|
26
|
-
"@markuplint/parser-utils": "4.0.0-alpha.
|
|
24
|
+
"@markuplint/html-parser": "4.0.0-alpha.10",
|
|
25
|
+
"@markuplint/ml-ast": "4.0.0-alpha.10",
|
|
26
|
+
"@markuplint/parser-utils": "4.0.0-alpha.10",
|
|
27
27
|
"pug-lexer": "^5.0.1",
|
|
28
|
-
"pug-parser": "^6.0.0"
|
|
29
|
-
"tslib": "^2.6.1"
|
|
28
|
+
"pug-parser": "^6.0.0"
|
|
30
29
|
},
|
|
31
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "b41153ea665aa8f091daf6114a06047f4ccb8350"
|
|
32
31
|
}
|
package/lib/attr-tokenizer.d.ts
DELETED
package/lib/attr-tokenizer.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { tokenizer, uuid } from '@markuplint/parser-utils';
|
|
2
|
-
export default function attrTokenizer(
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
4
|
-
attr) {
|
|
5
|
-
if (attr.raw[0] === '#' || attr.raw[0] === '.') {
|
|
6
|
-
let value = '';
|
|
7
|
-
if (typeof attr.val === 'string') {
|
|
8
|
-
[, , value] = attr.val.match(/(['"`]?)([^\1]+)(\1)/) ?? ['', '', ''];
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
value = `${attr.val}`;
|
|
12
|
-
}
|
|
13
|
-
return {
|
|
14
|
-
type: 'ps-attr',
|
|
15
|
-
uuid: uuid(),
|
|
16
|
-
raw: attr.raw,
|
|
17
|
-
startOffset: attr.offset,
|
|
18
|
-
endOffset: attr.endOffset,
|
|
19
|
-
startLine: attr.line,
|
|
20
|
-
endLine: attr.endLine,
|
|
21
|
-
startCol: attr.column,
|
|
22
|
-
endCol: attr.endColumn,
|
|
23
|
-
potentialName: attr.name,
|
|
24
|
-
potentialValue: value ?? '',
|
|
25
|
-
valueType: 'string',
|
|
26
|
-
isDuplicatable: attr.raw[0] === '.',
|
|
27
|
-
nodeName: '#pug-special-attr',
|
|
28
|
-
parentNode: null,
|
|
29
|
-
nextNode: null,
|
|
30
|
-
prevNode: null,
|
|
31
|
-
isFragment: false,
|
|
32
|
-
isGhost: false,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const spacesBeforeAttrString = '';
|
|
36
|
-
const nameChars = attr.name;
|
|
37
|
-
let spacesBeforeEqualChars;
|
|
38
|
-
let equalChars;
|
|
39
|
-
let spacesAfterEqualChars;
|
|
40
|
-
let quoteChars;
|
|
41
|
-
let valueChars;
|
|
42
|
-
let isDynamicValue = undefined;
|
|
43
|
-
if (attr.val === true) {
|
|
44
|
-
spacesBeforeEqualChars = '';
|
|
45
|
-
equalChars = '';
|
|
46
|
-
spacesAfterEqualChars = '';
|
|
47
|
-
quoteChars = '';
|
|
48
|
-
valueChars = '';
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
const withoutName = attr.raw.slice(attr.name.length);
|
|
52
|
-
const valueOffset = withoutName.indexOf(attr.val);
|
|
53
|
-
const equalAndBeforeSpaceAfterSpace = withoutName.slice(0, valueOffset);
|
|
54
|
-
const [, before, equal, after] = equalAndBeforeSpaceAfterSpace.match(/^(\s*)(=)(\s*)$/) ?? ['', '', '', ''];
|
|
55
|
-
const [, quote, coreValue] = attr.val.match(/(['"`]?)([^\1]+)(\1)/) ?? ['', '', ''];
|
|
56
|
-
spacesBeforeEqualChars = before ?? '';
|
|
57
|
-
equalChars = equal ?? '';
|
|
58
|
-
spacesAfterEqualChars = after ?? '';
|
|
59
|
-
quoteChars = quote ?? '';
|
|
60
|
-
valueChars = coreValue ?? '';
|
|
61
|
-
if (quote === '`' || quote === '') {
|
|
62
|
-
isDynamicValue = true;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
const invalid = !!(valueChars && quoteChars === null && /["'=<>`]/.test(valueChars)) ||
|
|
66
|
-
!!(equalChars && quoteChars === null && valueChars === null);
|
|
67
|
-
if (invalid) {
|
|
68
|
-
throw new Error('Parse error: It has invalid attribute');
|
|
69
|
-
}
|
|
70
|
-
let offset = attr.offset;
|
|
71
|
-
let line = attr.line;
|
|
72
|
-
let col = attr.column;
|
|
73
|
-
const attrToken = tokenizer(attr.raw, line, col, offset);
|
|
74
|
-
const spacesBeforeName = tokenizer(spacesBeforeAttrString, line, col, offset);
|
|
75
|
-
line = spacesBeforeName.endLine;
|
|
76
|
-
col = spacesBeforeName.endCol;
|
|
77
|
-
offset = spacesBeforeName.endOffset;
|
|
78
|
-
const name = tokenizer(nameChars, line, col, offset);
|
|
79
|
-
line = name.endLine;
|
|
80
|
-
col = name.endCol;
|
|
81
|
-
offset = name.endOffset;
|
|
82
|
-
const spacesBeforeEqual = tokenizer(spacesBeforeEqualChars, line, col, offset);
|
|
83
|
-
line = spacesBeforeEqual.endLine;
|
|
84
|
-
col = spacesBeforeEqual.endCol;
|
|
85
|
-
offset = spacesBeforeEqual.endOffset;
|
|
86
|
-
const equal = tokenizer(equalChars, line, col, offset);
|
|
87
|
-
line = equal.endLine;
|
|
88
|
-
col = equal.endCol;
|
|
89
|
-
offset = equal.endOffset;
|
|
90
|
-
const spacesAfterEqual = tokenizer(spacesAfterEqualChars, line, col, offset);
|
|
91
|
-
line = spacesAfterEqual.endLine;
|
|
92
|
-
col = spacesAfterEqual.endCol;
|
|
93
|
-
offset = spacesAfterEqual.endOffset;
|
|
94
|
-
const startQuote = tokenizer(quoteChars, line, col, offset);
|
|
95
|
-
line = startQuote.endLine;
|
|
96
|
-
col = startQuote.endCol;
|
|
97
|
-
offset = startQuote.endOffset;
|
|
98
|
-
const value = tokenizer(valueChars, line, col, offset);
|
|
99
|
-
line = value.endLine;
|
|
100
|
-
col = value.endCol;
|
|
101
|
-
offset = value.endOffset;
|
|
102
|
-
const endQuote = tokenizer(quoteChars, line, col, offset);
|
|
103
|
-
line = endQuote.endLine;
|
|
104
|
-
col = endQuote.endCol;
|
|
105
|
-
offset = endQuote.endOffset;
|
|
106
|
-
let isDuplicatable = false;
|
|
107
|
-
if (name.raw.toLowerCase() === 'class') {
|
|
108
|
-
isDuplicatable = true;
|
|
109
|
-
}
|
|
110
|
-
return {
|
|
111
|
-
type: 'html-attr',
|
|
112
|
-
uuid: uuid(),
|
|
113
|
-
raw: attrToken.raw,
|
|
114
|
-
startOffset: attrToken.startOffset,
|
|
115
|
-
endOffset: attrToken.endOffset,
|
|
116
|
-
startLine: attrToken.startLine,
|
|
117
|
-
endLine: attrToken.endLine,
|
|
118
|
-
startCol: attrToken.startCol,
|
|
119
|
-
endCol: attrToken.endCol,
|
|
120
|
-
spacesBeforeName,
|
|
121
|
-
name,
|
|
122
|
-
spacesBeforeEqual,
|
|
123
|
-
equal,
|
|
124
|
-
spacesAfterEqual,
|
|
125
|
-
startQuote,
|
|
126
|
-
value,
|
|
127
|
-
endQuote,
|
|
128
|
-
isDynamicValue,
|
|
129
|
-
isDuplicatable,
|
|
130
|
-
nodeName: name.raw,
|
|
131
|
-
parentNode: null,
|
|
132
|
-
nextNode: null,
|
|
133
|
-
prevNode: null,
|
|
134
|
-
isFragment: false,
|
|
135
|
-
isGhost: false,
|
|
136
|
-
};
|
|
137
|
-
}
|
package/lib/parse.d.ts
DELETED
package/lib/parse.js
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
var _Parser_ast;
|
|
2
|
-
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
|
-
import { getNamespace, parse as htmlParser, isDocumentFragment } from '@markuplint/html-parser';
|
|
4
|
-
import { detectElementType, ignoreFrontMatter, ParserError, tokenizer, uuid, walk, removeDeprecatedNode, } from '@markuplint/parser-utils';
|
|
5
|
-
import attrTokenizer from './attr-tokenizer.js';
|
|
6
|
-
import { pugParse } from './pug-parser/index.js';
|
|
7
|
-
export const parse = (rawCode, options) => {
|
|
8
|
-
let unknownParseError;
|
|
9
|
-
let nodeList;
|
|
10
|
-
if (options?.ignoreFrontMatter) {
|
|
11
|
-
rawCode = ignoreFrontMatter(rawCode);
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
const parser = new Parser(rawCode);
|
|
15
|
-
nodeList = parser.getNodeList();
|
|
16
|
-
}
|
|
17
|
-
catch (err) {
|
|
18
|
-
nodeList = [];
|
|
19
|
-
if (err instanceof Error && 'msg' in err && 'line' in err && 'column' in err && 'src' in err) {
|
|
20
|
-
throw new ParserError(
|
|
21
|
-
// @ts-ignore
|
|
22
|
-
err.msg, {
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
line: err.line,
|
|
25
|
-
// @ts-ignore
|
|
26
|
-
col: err.column,
|
|
27
|
-
// @ts-ignore
|
|
28
|
-
raw: err.src,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
unknownParseError = err instanceof Error ? err.message : new Error(`${err}`).message;
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
nodeList,
|
|
35
|
-
isFragment: isDocumentFragment(rawCode),
|
|
36
|
-
unknownParseError: unknownParseError,
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
class Parser {
|
|
40
|
-
constructor(raw) {
|
|
41
|
-
_Parser_ast.set(this, void 0);
|
|
42
|
-
__classPrivateFieldSet(this, _Parser_ast, pugParse(raw), "f");
|
|
43
|
-
// console.log(JSON.stringify(this.#ast, null, 2));
|
|
44
|
-
}
|
|
45
|
-
flattenNodes(
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
47
|
-
nodeTree) {
|
|
48
|
-
const nodeOrders = [];
|
|
49
|
-
walk(nodeTree, node => {
|
|
50
|
-
nodeOrders.push(node);
|
|
51
|
-
});
|
|
52
|
-
removeDeprecatedNode(nodeOrders);
|
|
53
|
-
return nodeOrders;
|
|
54
|
-
}
|
|
55
|
-
getNodeList() {
|
|
56
|
-
const nodeTree = this.traverse(__classPrivateFieldGet(this, _Parser_ast, "f").nodes, null);
|
|
57
|
-
return this.flattenNodes(nodeTree);
|
|
58
|
-
}
|
|
59
|
-
nodeize(
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
61
|
-
originNode,
|
|
62
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
63
|
-
prevNode,
|
|
64
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
65
|
-
parentNode) {
|
|
66
|
-
const nextNode = null;
|
|
67
|
-
const startOffset = originNode.offset;
|
|
68
|
-
const endOffset = originNode.endOffset;
|
|
69
|
-
const startLine = originNode.line;
|
|
70
|
-
const endLine = originNode.endLine;
|
|
71
|
-
const startCol = originNode.column;
|
|
72
|
-
const endCol = originNode.endColumn;
|
|
73
|
-
const parentNamespace = parentNode && 'namespace' in parentNode ? parentNode.namespace : 'http://www.w3.org/1999/xhtml';
|
|
74
|
-
switch (originNode.type) {
|
|
75
|
-
case 'Doctype': {
|
|
76
|
-
return {
|
|
77
|
-
uuid: uuid(),
|
|
78
|
-
raw: originNode.raw,
|
|
79
|
-
name: originNode.val ?? '',
|
|
80
|
-
// TODO:
|
|
81
|
-
publicId: '',
|
|
82
|
-
// TODO:
|
|
83
|
-
systemId: '',
|
|
84
|
-
startOffset,
|
|
85
|
-
endOffset,
|
|
86
|
-
startLine,
|
|
87
|
-
endLine,
|
|
88
|
-
startCol,
|
|
89
|
-
endCol,
|
|
90
|
-
nodeName: '#doctype',
|
|
91
|
-
type: 'doctype',
|
|
92
|
-
parentNode,
|
|
93
|
-
prevNode,
|
|
94
|
-
_addPrevNode: 102,
|
|
95
|
-
nextNode,
|
|
96
|
-
isFragment: false,
|
|
97
|
-
isGhost: false,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
case 'Text': {
|
|
101
|
-
if (parentNode && /^script$|^style$/i.test(parentNode.nodeName)) {
|
|
102
|
-
return {
|
|
103
|
-
uuid: uuid(),
|
|
104
|
-
raw: originNode.raw,
|
|
105
|
-
startOffset,
|
|
106
|
-
endOffset,
|
|
107
|
-
startLine,
|
|
108
|
-
endLine,
|
|
109
|
-
startCol,
|
|
110
|
-
endCol,
|
|
111
|
-
nodeName: '#text',
|
|
112
|
-
type: 'text',
|
|
113
|
-
parentNode,
|
|
114
|
-
prevNode,
|
|
115
|
-
nextNode,
|
|
116
|
-
isFragment: false,
|
|
117
|
-
isGhost: false,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
const htmlDoc = htmlParser(originNode.raw, {
|
|
121
|
-
offsetOffset: originNode.offset,
|
|
122
|
-
offsetLine: originNode.line - 1,
|
|
123
|
-
offsetColumn: originNode.column - 1,
|
|
124
|
-
});
|
|
125
|
-
const nodes = htmlDoc.nodeList;
|
|
126
|
-
for (const node of nodes) {
|
|
127
|
-
if (!node.parentNode) {
|
|
128
|
-
node.parentNode = parentNode;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return nodes;
|
|
132
|
-
}
|
|
133
|
-
case 'Comment': {
|
|
134
|
-
return {
|
|
135
|
-
uuid: uuid(),
|
|
136
|
-
raw: originNode.raw,
|
|
137
|
-
startOffset,
|
|
138
|
-
endOffset,
|
|
139
|
-
startLine,
|
|
140
|
-
endLine,
|
|
141
|
-
startCol,
|
|
142
|
-
endCol,
|
|
143
|
-
nodeName: '#comment',
|
|
144
|
-
type: 'comment',
|
|
145
|
-
parentNode,
|
|
146
|
-
prevNode,
|
|
147
|
-
nextNode,
|
|
148
|
-
isFragment: false,
|
|
149
|
-
isGhost: false,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
case 'Tag': {
|
|
153
|
-
const namespace = getNamespace(originNode.name, parentNamespace);
|
|
154
|
-
const tag = {
|
|
155
|
-
uuid: uuid(),
|
|
156
|
-
raw: originNode.raw,
|
|
157
|
-
startOffset,
|
|
158
|
-
endOffset,
|
|
159
|
-
startLine,
|
|
160
|
-
endLine,
|
|
161
|
-
startCol,
|
|
162
|
-
endCol,
|
|
163
|
-
nodeName: originNode.name,
|
|
164
|
-
type: 'starttag',
|
|
165
|
-
namespace,
|
|
166
|
-
elementType: detectElementType(originNode.name),
|
|
167
|
-
attributes: originNode.attrs.map(attr => attrTokenizer(attr)),
|
|
168
|
-
hasSpreadAttr: false,
|
|
169
|
-
parentNode,
|
|
170
|
-
prevNode,
|
|
171
|
-
nextNode,
|
|
172
|
-
pearNode: null,
|
|
173
|
-
selfClosingSolidus: tokenizer('', originNode.line, originNode.column, originNode.offset),
|
|
174
|
-
endSpace: tokenizer('', originNode.line, originNode.column, originNode.offset),
|
|
175
|
-
isFragment: false,
|
|
176
|
-
isGhost: false,
|
|
177
|
-
tagOpenChar: '',
|
|
178
|
-
tagCloseChar: '',
|
|
179
|
-
};
|
|
180
|
-
if (originNode.block.nodes.length > 0) {
|
|
181
|
-
tag.childNodes = this.traverse(originNode.block.nodes, tag);
|
|
182
|
-
}
|
|
183
|
-
return tag;
|
|
184
|
-
}
|
|
185
|
-
default: {
|
|
186
|
-
const tag = {
|
|
187
|
-
uuid: uuid(),
|
|
188
|
-
raw: originNode.raw,
|
|
189
|
-
startOffset,
|
|
190
|
-
endOffset,
|
|
191
|
-
startLine,
|
|
192
|
-
endLine,
|
|
193
|
-
startCol,
|
|
194
|
-
endCol,
|
|
195
|
-
type: 'psblock',
|
|
196
|
-
nodeName: originNode.type,
|
|
197
|
-
parentNode,
|
|
198
|
-
prevNode,
|
|
199
|
-
nextNode,
|
|
200
|
-
isFragment: true,
|
|
201
|
-
isGhost: false,
|
|
202
|
-
};
|
|
203
|
-
if ('block' in originNode && originNode.block && originNode.block.nodes.length > 0) {
|
|
204
|
-
tag.childNodes = this.traverse(originNode.block.nodes, tag);
|
|
205
|
-
}
|
|
206
|
-
return tag;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
traverse(
|
|
211
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
212
|
-
astNodes, parentNode = null) {
|
|
213
|
-
const nodeList = [];
|
|
214
|
-
let prevNode = null;
|
|
215
|
-
for (const astNode of astNodes) {
|
|
216
|
-
const nodes = this.nodeize(astNode, prevNode, parentNode);
|
|
217
|
-
if (!nodes || (Array.isArray(nodes) && nodes.length === 0)) {
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
let node;
|
|
221
|
-
if (Array.isArray(nodes)) {
|
|
222
|
-
const lastNode = nodes[nodes.length - 1];
|
|
223
|
-
if (!lastNode) {
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
node = lastNode;
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
node = nodes;
|
|
230
|
-
}
|
|
231
|
-
if (prevNode) {
|
|
232
|
-
if (node.type !== 'endtag') {
|
|
233
|
-
prevNode.nextNode = node;
|
|
234
|
-
}
|
|
235
|
-
node.prevNode = prevNode;
|
|
236
|
-
}
|
|
237
|
-
prevNode = node;
|
|
238
|
-
if (Array.isArray(nodes)) {
|
|
239
|
-
nodeList.push(...nodes);
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
nodeList.push(nodes);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return nodeList;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
_Parser_ast = new WeakMap();
|