@markuplint/astro-parser 4.0.0-dev.20 → 4.0.0-dev.23
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 +51 -0
- package/lib/parser.js +214 -0
- package/package.json +8 -7
- package/lib/attr-tokenizer.d.ts +0 -3
- package/lib/attr-tokenizer.js +0 -57
- package/lib/parse.d.ts +0 -2
- package/lib/parse.js +0 -337
package/LICENSE
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export declare const endTag = "xml";
|
|
1
|
+
export { parser } from './parser.js';
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export const endTag = 'xml';
|
|
1
|
+
export { parser } from './parser.js';
|
package/lib/parser.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Node } from './astro-parser.js';
|
|
2
|
+
import type { MLASTParentNode, MLASTNodeTreeItem } from '@markuplint/ml-ast';
|
|
3
|
+
import type { ChildToken, Token } from '@markuplint/parser-utils';
|
|
4
|
+
import { Parser } from '@markuplint/parser-utils';
|
|
5
|
+
type State = {
|
|
6
|
+
scopeNS: string;
|
|
7
|
+
};
|
|
8
|
+
declare class AstroParser extends Parser<Node, State> {
|
|
9
|
+
#private;
|
|
10
|
+
constructor();
|
|
11
|
+
tokenize(): {
|
|
12
|
+
ast: Node[];
|
|
13
|
+
isFragment: boolean;
|
|
14
|
+
};
|
|
15
|
+
nodeize(originNode: Node, parentNode: MLASTParentNode | null, depth: number): readonly MLASTNodeTreeItem[];
|
|
16
|
+
afterFlattenNodes(nodeList: readonly MLASTNodeTreeItem[]): readonly MLASTNodeTreeItem[];
|
|
17
|
+
visitElement(token: ChildToken, childNodes: readonly Node[]): readonly MLASTNodeTreeItem[];
|
|
18
|
+
visitChildren(children: readonly Node[], parentNode: MLASTParentNode | null): never[];
|
|
19
|
+
visitAttr(token: Token): (import("@markuplint/ml-ast").MLASTSpreadAttr & {
|
|
20
|
+
__rightText?: string | undefined;
|
|
21
|
+
}) | {
|
|
22
|
+
isDynamicValue: true | undefined;
|
|
23
|
+
isDirective: true | undefined;
|
|
24
|
+
potentialName: string | undefined;
|
|
25
|
+
type: "attr";
|
|
26
|
+
nodeName: string;
|
|
27
|
+
spacesBeforeName: import("@markuplint/ml-ast").MLASTToken;
|
|
28
|
+
name: import("@markuplint/ml-ast").MLASTToken;
|
|
29
|
+
spacesBeforeEqual: import("@markuplint/ml-ast").MLASTToken;
|
|
30
|
+
equal: import("@markuplint/ml-ast").MLASTToken;
|
|
31
|
+
spacesAfterEqual: import("@markuplint/ml-ast").MLASTToken;
|
|
32
|
+
startQuote: import("@markuplint/ml-ast").MLASTToken;
|
|
33
|
+
value: import("@markuplint/ml-ast").MLASTToken;
|
|
34
|
+
endQuote: import("@markuplint/ml-ast").MLASTToken;
|
|
35
|
+
potentialValue?: string | undefined;
|
|
36
|
+
valueType?: "string" | "number" | "boolean" | "code" | undefined;
|
|
37
|
+
candidate?: string | undefined;
|
|
38
|
+
isDuplicatable: boolean;
|
|
39
|
+
uuid: string;
|
|
40
|
+
raw: string;
|
|
41
|
+
startOffset: number;
|
|
42
|
+
endOffset: number;
|
|
43
|
+
startLine: number;
|
|
44
|
+
endLine: number;
|
|
45
|
+
startCol: number;
|
|
46
|
+
endCol: number;
|
|
47
|
+
__rightText?: string | undefined;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export declare const parser: AstroParser;
|
|
51
|
+
export {};
|
package/lib/parser.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var _AstroParser_instances, _AstroParser_updateScopeNS;
|
|
7
|
+
import { AttrState, Parser, ParserError } from '@markuplint/parser-utils';
|
|
8
|
+
import { astroParse } from './astro-parser.js';
|
|
9
|
+
class AstroParser extends Parser {
|
|
10
|
+
constructor() {
|
|
11
|
+
super({
|
|
12
|
+
endTagType: 'xml',
|
|
13
|
+
selfCloseType: 'html+xml',
|
|
14
|
+
}, {
|
|
15
|
+
scopeNS: 'http://www.w3.org/1999/xhtml',
|
|
16
|
+
});
|
|
17
|
+
_AstroParser_instances.add(this);
|
|
18
|
+
}
|
|
19
|
+
tokenize() {
|
|
20
|
+
return {
|
|
21
|
+
ast: astroParse(this.rawCode).children,
|
|
22
|
+
isFragment: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
nodeize(
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
27
|
+
originNode, parentNode, depth) {
|
|
28
|
+
if (!originNode.position) {
|
|
29
|
+
throw new TypeError("Node doesn't have position");
|
|
30
|
+
}
|
|
31
|
+
const startOffset = originNode.position.start.offset;
|
|
32
|
+
const endOffset = originNode.position.end?.offset;
|
|
33
|
+
const token = this.sliceFragment(startOffset, endOffset);
|
|
34
|
+
__classPrivateFieldGet(this, _AstroParser_instances, "m", _AstroParser_updateScopeNS).call(this, originNode, parentNode);
|
|
35
|
+
switch (originNode.type) {
|
|
36
|
+
case 'doctype': {
|
|
37
|
+
return this.visitDoctype({
|
|
38
|
+
...token,
|
|
39
|
+
depth,
|
|
40
|
+
parentNode,
|
|
41
|
+
name: originNode.value,
|
|
42
|
+
publicId: '',
|
|
43
|
+
systemId: '',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
case 'text': {
|
|
47
|
+
if (parentNode?.type === 'psblock') {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
return this.visitText({
|
|
51
|
+
...token,
|
|
52
|
+
depth,
|
|
53
|
+
parentNode,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
case 'comment': {
|
|
57
|
+
return this.visitComment({
|
|
58
|
+
...token,
|
|
59
|
+
depth,
|
|
60
|
+
parentNode,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
case 'component':
|
|
64
|
+
case 'custom-element':
|
|
65
|
+
case 'fragment':
|
|
66
|
+
case 'element': {
|
|
67
|
+
const tagToken = token.raw ? token : this.sliceFragment(0);
|
|
68
|
+
return this.visitElement({
|
|
69
|
+
...tagToken,
|
|
70
|
+
depth,
|
|
71
|
+
parentNode,
|
|
72
|
+
}, originNode.children);
|
|
73
|
+
}
|
|
74
|
+
case 'expression': {
|
|
75
|
+
const firstChild = originNode.children.at(0);
|
|
76
|
+
const lastChild = originNode.children.at(-1);
|
|
77
|
+
let startExpressionRaw = token.raw;
|
|
78
|
+
let startExpressionStartLine = token.startLine;
|
|
79
|
+
let startExpressionStartCol = token.startCol;
|
|
80
|
+
const nodes = [];
|
|
81
|
+
if (firstChild && lastChild && firstChild !== lastChild) {
|
|
82
|
+
const startExpressionEndOffset = firstChild.position?.end?.offset ?? endOffset ?? startOffset;
|
|
83
|
+
const startExpressionLocation = this.sliceFragment(startOffset, startExpressionEndOffset);
|
|
84
|
+
startExpressionRaw = startExpressionLocation.raw;
|
|
85
|
+
startExpressionStartLine = startExpressionLocation.startLine;
|
|
86
|
+
startExpressionStartCol = startExpressionLocation.startCol;
|
|
87
|
+
const closeExpressionLocation = this.sliceFragment(lastChild.position?.start.offset ?? startOffset, endOffset);
|
|
88
|
+
nodes.push(...this.visitPsBlock({
|
|
89
|
+
...closeExpressionLocation,
|
|
90
|
+
depth,
|
|
91
|
+
parentNode,
|
|
92
|
+
nodeName: 'MustacheTag',
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
nodes.push(...this.visitPsBlock({
|
|
96
|
+
raw: startExpressionRaw,
|
|
97
|
+
startOffset,
|
|
98
|
+
startLine: startExpressionStartLine,
|
|
99
|
+
startCol: startExpressionStartCol,
|
|
100
|
+
depth,
|
|
101
|
+
parentNode,
|
|
102
|
+
nodeName: 'MustacheTag',
|
|
103
|
+
}, originNode.children));
|
|
104
|
+
return nodes;
|
|
105
|
+
}
|
|
106
|
+
default: {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
afterFlattenNodes(nodeList) {
|
|
112
|
+
return super.afterFlattenNodes(nodeList, {
|
|
113
|
+
exposeInvalidNode: false,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
visitElement(token,
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
118
|
+
childNodes) {
|
|
119
|
+
const parsedNodes = this.parseCodeFragment(token);
|
|
120
|
+
const startTagNode = parsedNodes.at(0);
|
|
121
|
+
if (!startTagNode || startTagNode.type !== 'starttag') {
|
|
122
|
+
throw new ParserError('Not found start tag', startTagNode ?? token);
|
|
123
|
+
}
|
|
124
|
+
return super.visitElement(startTagNode, childNodes, {
|
|
125
|
+
overwriteProps: {
|
|
126
|
+
namespace: this.state.scopeNS,
|
|
127
|
+
},
|
|
128
|
+
createEndTagToken: () => {
|
|
129
|
+
if (startTagNode.selfClosingSolidus?.raw === '/') {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const endTagNode = parsedNodes.at(-1);
|
|
133
|
+
if (endTagNode?.type !== 'endtag') {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return endTagNode ?? null;
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
visitChildren(
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
142
|
+
children, parentNode) {
|
|
143
|
+
const siblings = super.visitChildren(children, parentNode);
|
|
144
|
+
if (siblings.length > 0) {
|
|
145
|
+
throw new ParserError('Discovered child nodes with differing hierarchy levels', siblings[0]);
|
|
146
|
+
}
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
visitAttr(token) {
|
|
150
|
+
const attr = super.visitAttr(token, {
|
|
151
|
+
quoteSet: [
|
|
152
|
+
{ start: '"', end: '"' },
|
|
153
|
+
{ start: "'", end: "'" },
|
|
154
|
+
{ start: '{', end: '}' },
|
|
155
|
+
],
|
|
156
|
+
quoteInValueChars: [
|
|
157
|
+
{ start: '"', end: '"' },
|
|
158
|
+
{ start: "'", end: "'" },
|
|
159
|
+
{ start: '`', end: '`' },
|
|
160
|
+
{ start: '${', end: '}' },
|
|
161
|
+
],
|
|
162
|
+
startState:
|
|
163
|
+
// is shorthand attribute
|
|
164
|
+
token.raw.trim().startsWith('{') ? AttrState.BeforeValue : AttrState.BeforeName,
|
|
165
|
+
});
|
|
166
|
+
if (attr.type === 'spread') {
|
|
167
|
+
return attr;
|
|
168
|
+
}
|
|
169
|
+
const isDynamicValue = attr.startQuote.raw === '{' || undefined;
|
|
170
|
+
let potentialName;
|
|
171
|
+
let isDirective;
|
|
172
|
+
if (isDynamicValue && attr.name.raw === '') {
|
|
173
|
+
potentialName = attr.value.raw;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Detects Template Directive
|
|
177
|
+
*
|
|
178
|
+
* @see https://docs.astro.build/en/reference/directives-reference/
|
|
179
|
+
*/
|
|
180
|
+
const [, directive] = attr.name.raw.match(/^([^:]+):([^:]+)$/) ?? [];
|
|
181
|
+
if (directive) {
|
|
182
|
+
const lowerCaseDirectiveName = directive.toLowerCase();
|
|
183
|
+
switch (lowerCaseDirectiveName) {
|
|
184
|
+
case 'class': {
|
|
185
|
+
potentialName = lowerCaseDirectiveName;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
default: {
|
|
189
|
+
isDirective = true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
...attr,
|
|
195
|
+
isDynamicValue,
|
|
196
|
+
isDirective,
|
|
197
|
+
potentialName,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
_AstroParser_instances = new WeakSet(), _AstroParser_updateScopeNS = function _AstroParser_updateScopeNS(
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
203
|
+
originNode, parentNode) {
|
|
204
|
+
const parentNS = this.state.scopeNS;
|
|
205
|
+
if (parentNS === 'http://www.w3.org/1999/xhtml' &&
|
|
206
|
+
originNode.type === 'element' &&
|
|
207
|
+
originNode.name?.toLowerCase() === 'svg') {
|
|
208
|
+
this.state.scopeNS = 'http://www.w3.org/2000/svg';
|
|
209
|
+
}
|
|
210
|
+
else if (parentNS === 'http://www.w3.org/2000/svg' && parentNode && parentNode.nodeName === 'foreignObject') {
|
|
211
|
+
this.state.scopeNS = 'http://www.w3.org/1999/xhtml';
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
export const parser = new AstroParser();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/astro-parser",
|
|
3
|
-
"version": "4.0.0-dev.
|
|
3
|
+
"version": "4.0.0-dev.23+d6f2aa9bc",
|
|
4
4
|
"description": "astro parser for markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -21,11 +21,12 @@
|
|
|
21
21
|
"clean": "tsc --build --clean"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"@markuplint/
|
|
26
|
-
"
|
|
27
|
-
"@markuplint/parser-utils": "4.0.0-dev.20+6b35da16",
|
|
28
|
-
"astro-eslint-parser": "^0.16.0"
|
|
24
|
+
"@markuplint/ml-ast": "4.0.0-dev.23+d6f2aa9bc",
|
|
25
|
+
"@markuplint/parser-utils": "4.0.0-dev.23+d6f2aa9bc",
|
|
26
|
+
"astro-eslint-parser": "^0.16.2"
|
|
29
27
|
},
|
|
30
|
-
"
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@astrojs/compiler": "^2.5.0"
|
|
30
|
+
},
|
|
31
|
+
"gitHead": "d6f2aa9bc287768466f23b5340e4e0eecfa30d59"
|
|
31
32
|
}
|
package/lib/attr-tokenizer.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { AttributeNode } from './astro-parser.js';
|
|
2
|
-
import type { MLASTHTMLAttr } from '@markuplint/ml-ast';
|
|
3
|
-
export declare function attrTokenizer(attr: AttributeNode, nextAttr: AttributeNode | null, rawHtml: string, startTag: string, startTagEndOffset: number): MLASTHTMLAttr;
|
package/lib/attr-tokenizer.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { defaultValueDelimiters, getSpaceBefore, parseAttr, sliceFragment } from '@markuplint/parser-utils';
|
|
2
|
-
const mustacheTag = {
|
|
3
|
-
start: '{',
|
|
4
|
-
end: '}',
|
|
5
|
-
};
|
|
6
|
-
export function attrTokenizer(
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
8
|
-
attr,
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
10
|
-
nextAttr, rawHtml, startTag, startTagEndOffset) {
|
|
11
|
-
if (!attr.position) {
|
|
12
|
-
throw new TypeError("Attr doesn't have position");
|
|
13
|
-
}
|
|
14
|
-
if (attr.position.end) {
|
|
15
|
-
throw new TypeError('Node may is an attribute because it has end position');
|
|
16
|
-
}
|
|
17
|
-
let nextAttrBeforeSpaceOffset;
|
|
18
|
-
if (nextAttr) {
|
|
19
|
-
if (!nextAttr.position) {
|
|
20
|
-
throw new TypeError("NextAttr doesn't have position");
|
|
21
|
-
}
|
|
22
|
-
if (nextAttr.position.end) {
|
|
23
|
-
throw new TypeError('NextAttr Node may is an attribute because it has end position');
|
|
24
|
-
}
|
|
25
|
-
nextAttrBeforeSpaceOffset = getSpaceBefore(nextAttr.position.start.offset, rawHtml).startOffset;
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
const close = /(\/?>)$/.exec(startTag)?.[1] ?? '';
|
|
29
|
-
nextAttrBeforeSpaceOffset = startTagEndOffset - close.length;
|
|
30
|
-
}
|
|
31
|
-
const { raw, startOffset } = sliceFragment(rawHtml, attr.position.start.offset, nextAttrBeforeSpaceOffset);
|
|
32
|
-
const result = parseAttr(raw, startOffset, rawHtml, {
|
|
33
|
-
valueDelimiters: [...defaultValueDelimiters, mustacheTag],
|
|
34
|
-
});
|
|
35
|
-
if (result.startQuote.raw === mustacheTag.start) {
|
|
36
|
-
result.isDynamicValue = true;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Detects Template Directive
|
|
40
|
-
*
|
|
41
|
-
* @see https://docs.astro.build/en/reference/directives-reference/
|
|
42
|
-
*/
|
|
43
|
-
const [, directive] = result.name.raw.match(/^([^:]+):([^:]+)$/) ?? [];
|
|
44
|
-
if (directive) {
|
|
45
|
-
const lowerCaseDirectiveName = directive.toLowerCase();
|
|
46
|
-
switch (lowerCaseDirectiveName) {
|
|
47
|
-
case 'class': {
|
|
48
|
-
result.potentialName = lowerCaseDirectiveName;
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
default: {
|
|
52
|
-
result.isDirective = true;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return result;
|
|
57
|
-
}
|
package/lib/parse.d.ts
DELETED
package/lib/parse.js
DELETED
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
import { parse as htmlParse } from '@markuplint/html-parser';
|
|
2
|
-
import { flattenNodes, detectElementType, getEndCol, getEndLine, sliceFragment, uuid, tagParser, } from '@markuplint/parser-utils';
|
|
3
|
-
import { astroParse } from './astro-parser.js';
|
|
4
|
-
import { attrTokenizer } from './attr-tokenizer.js';
|
|
5
|
-
export const parse = (rawCode, options = {}) => {
|
|
6
|
-
const ast = astroParse(rawCode);
|
|
7
|
-
const htmlRawNodeList = traverse(ast, null, 'http://www.w3.org/1999/xhtml', rawCode, 0, options);
|
|
8
|
-
const firstOffset = htmlRawNodeList[0]?.startOffset ?? 0;
|
|
9
|
-
if (firstOffset > 0) {
|
|
10
|
-
const head = rawCode.slice(0, firstOffset);
|
|
11
|
-
const ast = htmlParse(head, {
|
|
12
|
-
...options,
|
|
13
|
-
ignoreFrontMatter: true,
|
|
14
|
-
});
|
|
15
|
-
const headNodes = ast.nodeList.filter(node => {
|
|
16
|
-
return !node.isGhost;
|
|
17
|
-
});
|
|
18
|
-
htmlRawNodeList.unshift(...headNodes);
|
|
19
|
-
}
|
|
20
|
-
const nodeList = flattenNodes(htmlRawNodeList, rawCode);
|
|
21
|
-
return {
|
|
22
|
-
nodeList,
|
|
23
|
-
isFragment: true,
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
function traverse(
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
28
|
-
rootNode, parentNode = null, scopeNS, rawHtml, offset, options, inExpression) {
|
|
29
|
-
const nodeList = [];
|
|
30
|
-
if (!('children' in rootNode)) {
|
|
31
|
-
return nodeList;
|
|
32
|
-
}
|
|
33
|
-
if (rootNode.children.length === 0) {
|
|
34
|
-
return nodeList;
|
|
35
|
-
}
|
|
36
|
-
let prevNode = null;
|
|
37
|
-
for (const astNode of rootNode.children) {
|
|
38
|
-
const node = nodeize(astNode, prevNode, parentNode, scopeNS, rawHtml, offset, options, inExpression);
|
|
39
|
-
if (!node) {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
const nodes = Array.isArray(node) ? node : [node];
|
|
43
|
-
for (const node of nodes) {
|
|
44
|
-
if (prevNode) {
|
|
45
|
-
if (node.type !== 'endtag') {
|
|
46
|
-
prevNode.nextNode = node;
|
|
47
|
-
}
|
|
48
|
-
node.prevNode = prevNode;
|
|
49
|
-
}
|
|
50
|
-
prevNode = node;
|
|
51
|
-
nodeList.push(node);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return nodeList;
|
|
55
|
-
}
|
|
56
|
-
function nodeize(
|
|
57
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
58
|
-
originNode,
|
|
59
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
60
|
-
prevNode,
|
|
61
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
62
|
-
parentNode, scopeNS, rawHtml, offset = 0, options, inExpression) {
|
|
63
|
-
if (!originNode.position) {
|
|
64
|
-
throw new TypeError("Node doesn't have position");
|
|
65
|
-
}
|
|
66
|
-
const nextNode = null;
|
|
67
|
-
const startOffset = originNode.position.start.offset + offset;
|
|
68
|
-
const endOffset = (originNode.position.end?.offset ?? originNode.position.start.offset) + offset;
|
|
69
|
-
const { startLine, endLine, startCol, endCol, raw } = sliceFragment(rawHtml, startOffset, endOffset);
|
|
70
|
-
if (scopeNS === 'http://www.w3.org/1999/xhtml' &&
|
|
71
|
-
originNode.type === 'element' &&
|
|
72
|
-
originNode.name?.toLowerCase() === 'svg') {
|
|
73
|
-
scopeNS = 'http://www.w3.org/2000/svg';
|
|
74
|
-
}
|
|
75
|
-
else if (scopeNS === 'http://www.w3.org/2000/svg' && parentNode && parentNode.nodeName === 'foreignObject') {
|
|
76
|
-
scopeNS = 'http://www.w3.org/1999/xhtml';
|
|
77
|
-
}
|
|
78
|
-
switch (originNode.type) {
|
|
79
|
-
case 'text': {
|
|
80
|
-
if (inExpression) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
const node = {
|
|
84
|
-
uuid: uuid(),
|
|
85
|
-
raw,
|
|
86
|
-
startOffset,
|
|
87
|
-
endOffset,
|
|
88
|
-
startLine,
|
|
89
|
-
endLine,
|
|
90
|
-
startCol,
|
|
91
|
-
endCol,
|
|
92
|
-
nodeName: '#text',
|
|
93
|
-
type: 'text',
|
|
94
|
-
parentNode,
|
|
95
|
-
prevNode,
|
|
96
|
-
nextNode,
|
|
97
|
-
isFragment: false,
|
|
98
|
-
isGhost: false,
|
|
99
|
-
};
|
|
100
|
-
return node;
|
|
101
|
-
}
|
|
102
|
-
case 'expression': {
|
|
103
|
-
let _endOffset = endOffset;
|
|
104
|
-
let _startLine = startLine;
|
|
105
|
-
let _endLine = endLine;
|
|
106
|
-
let _startCol = startCol;
|
|
107
|
-
let _endCol = endCol;
|
|
108
|
-
let _raw = raw;
|
|
109
|
-
let closeExpression = null;
|
|
110
|
-
const firstChild = originNode.children[0];
|
|
111
|
-
const lastChild = originNode.children.at(-1);
|
|
112
|
-
if (firstChild && lastChild && firstChild !== lastChild) {
|
|
113
|
-
_endOffset = firstChild.position?.end?.offset ?? endOffset;
|
|
114
|
-
const startLoc = sliceFragment(rawHtml, startOffset, _endOffset);
|
|
115
|
-
_startLine = startLoc.startLine;
|
|
116
|
-
_endLine = startLoc.endLine;
|
|
117
|
-
_startCol = startLoc.startCol;
|
|
118
|
-
_endCol = startLoc.endCol;
|
|
119
|
-
_raw = startLoc.raw;
|
|
120
|
-
const closeStartOffset = lastChild.position?.start.offset ?? startOffset;
|
|
121
|
-
const closeLoc = sliceFragment(rawHtml, closeStartOffset, endOffset);
|
|
122
|
-
closeExpression = {
|
|
123
|
-
uuid: uuid(),
|
|
124
|
-
raw: closeLoc.raw,
|
|
125
|
-
startOffset: closeStartOffset,
|
|
126
|
-
endOffset: closeLoc.endOffset,
|
|
127
|
-
startLine: closeLoc.startLine,
|
|
128
|
-
endLine: closeLoc.endLine,
|
|
129
|
-
startCol: closeLoc.startCol,
|
|
130
|
-
endCol: closeLoc.endCol,
|
|
131
|
-
nodeName: 'MustacheTag',
|
|
132
|
-
type: 'psblock',
|
|
133
|
-
parentNode,
|
|
134
|
-
prevNode,
|
|
135
|
-
nextNode,
|
|
136
|
-
isFragment: false,
|
|
137
|
-
isGhost: false,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
const node = {
|
|
141
|
-
uuid: uuid(),
|
|
142
|
-
raw: _raw,
|
|
143
|
-
startOffset,
|
|
144
|
-
endOffset: _endOffset,
|
|
145
|
-
startLine: _startLine,
|
|
146
|
-
endLine: _endLine,
|
|
147
|
-
startCol: _startCol,
|
|
148
|
-
endCol: _endCol,
|
|
149
|
-
nodeName: 'MustacheTag',
|
|
150
|
-
type: 'psblock',
|
|
151
|
-
parentNode,
|
|
152
|
-
prevNode,
|
|
153
|
-
nextNode,
|
|
154
|
-
isFragment: false,
|
|
155
|
-
isGhost: false,
|
|
156
|
-
};
|
|
157
|
-
if (originNode.children.length > 0) {
|
|
158
|
-
node.childNodes = traverse(originNode, parentNode, scopeNS, rawHtml, offset, options, true);
|
|
159
|
-
}
|
|
160
|
-
if (closeExpression) {
|
|
161
|
-
return [node, closeExpression];
|
|
162
|
-
}
|
|
163
|
-
return node;
|
|
164
|
-
}
|
|
165
|
-
case 'comment': {
|
|
166
|
-
return {
|
|
167
|
-
uuid: uuid(),
|
|
168
|
-
raw,
|
|
169
|
-
startOffset,
|
|
170
|
-
endOffset,
|
|
171
|
-
startLine,
|
|
172
|
-
endLine,
|
|
173
|
-
startCol,
|
|
174
|
-
endCol,
|
|
175
|
-
nodeName: '#comment',
|
|
176
|
-
type: 'comment',
|
|
177
|
-
parentNode,
|
|
178
|
-
prevNode,
|
|
179
|
-
nextNode,
|
|
180
|
-
isFragment: false,
|
|
181
|
-
isGhost: false,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
case 'component':
|
|
185
|
-
case 'custom-element':
|
|
186
|
-
case 'fragment':
|
|
187
|
-
case 'element': {
|
|
188
|
-
if (originNode.name?.toLowerCase() === '!doctype') {
|
|
189
|
-
return {
|
|
190
|
-
uuid: uuid(),
|
|
191
|
-
raw,
|
|
192
|
-
name: originNode.name,
|
|
193
|
-
publicId: '',
|
|
194
|
-
systemId: '',
|
|
195
|
-
startOffset,
|
|
196
|
-
endOffset,
|
|
197
|
-
startLine,
|
|
198
|
-
endLine,
|
|
199
|
-
startCol,
|
|
200
|
-
endCol,
|
|
201
|
-
nodeName: '#doctype',
|
|
202
|
-
type: 'doctype',
|
|
203
|
-
parentNode,
|
|
204
|
-
prevNode,
|
|
205
|
-
nextNode,
|
|
206
|
-
isFragment: false,
|
|
207
|
-
isGhost: false,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
return parseElement(originNode, scopeNS, rawHtml, startLine, startCol, startOffset, parentNode, prevNode, nextNode, offset, options);
|
|
211
|
-
}
|
|
212
|
-
default: {
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
function parseElement(
|
|
218
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
219
|
-
originNode, scopeNS, rawHtml, startLine, startCol, startOffset,
|
|
220
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
221
|
-
parentNode,
|
|
222
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
223
|
-
prevNode,
|
|
224
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
225
|
-
nextNode, offset, options) {
|
|
226
|
-
if (!originNode.position) {
|
|
227
|
-
throw new TypeError("Node doesn't have position");
|
|
228
|
-
}
|
|
229
|
-
let startTagRaw;
|
|
230
|
-
let childrenStart;
|
|
231
|
-
let childrenEnd;
|
|
232
|
-
if (originNode.children[0]) {
|
|
233
|
-
childrenStart = (originNode.children[0].position?.start?.offset ?? 0) + offset;
|
|
234
|
-
childrenEnd = (originNode.children.at(-1)?.position?.end?.offset ?? 0) + offset;
|
|
235
|
-
const startTagStartOffset = originNode.position.start.offset + offset;
|
|
236
|
-
const startTagEndOffset = childrenStart;
|
|
237
|
-
startTagRaw = rawHtml.slice(startTagStartOffset, startTagEndOffset);
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
childrenStart = offset + (originNode.position.end?.offset ?? nextNode?.endOffset ?? rawHtml.length - offset);
|
|
241
|
-
childrenEnd = childrenStart;
|
|
242
|
-
const startTagStartOffset = originNode.position.start.offset + offset;
|
|
243
|
-
let startTagEndOffset = childrenStart;
|
|
244
|
-
startTagRaw = rawHtml.slice(startTagStartOffset, startTagEndOffset);
|
|
245
|
-
const expectedCloseTag = `</${originNode.name}>`;
|
|
246
|
-
if (startTagRaw.includes(expectedCloseTag)) {
|
|
247
|
-
childrenStart -= expectedCloseTag.length;
|
|
248
|
-
childrenEnd = childrenStart;
|
|
249
|
-
startTagRaw = startTagRaw.replace(expectedCloseTag, '');
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
let startTagRawMasked = startTagRaw;
|
|
253
|
-
for (const attr of originNode.attributes) {
|
|
254
|
-
startTagRawMasked = startTagRawMasked.replace(attr.value, ' '.repeat(attr.value.length));
|
|
255
|
-
}
|
|
256
|
-
const closeChars = '>';
|
|
257
|
-
const closeOffset = startTagRawMasked.indexOf(closeChars) + closeChars.length;
|
|
258
|
-
startTagEndOffset = startTagStartOffset + closeOffset;
|
|
259
|
-
startTagRaw = rawHtml.slice(startTagStartOffset, startTagEndOffset);
|
|
260
|
-
}
|
|
261
|
-
// console.log({
|
|
262
|
-
// originNode,
|
|
263
|
-
// attrs: originNode.attributes,
|
|
264
|
-
// startTagRaw,
|
|
265
|
-
// startTagStartOffset,
|
|
266
|
-
// startTagEndOffset,
|
|
267
|
-
// expectedCloseTag,
|
|
268
|
-
// childrenStart,
|
|
269
|
-
// childrenEnd,
|
|
270
|
-
// });
|
|
271
|
-
}
|
|
272
|
-
const tagTokens = tagParser(startTagRaw, startLine, startCol, startOffset);
|
|
273
|
-
const tagName = tagTokens.tagName;
|
|
274
|
-
let endTag = null;
|
|
275
|
-
if (childrenEnd < (originNode.position.end?.offset ?? 0) + offset) {
|
|
276
|
-
const endTagLoc = sliceFragment(rawHtml, childrenEnd, (originNode.position.end?.offset ?? 0) + offset);
|
|
277
|
-
const endTagTokens = tagParser(endTagLoc.raw, endTagLoc.startLine, endTagLoc.startCol, endTagLoc.startOffset);
|
|
278
|
-
const endTagName = endTagTokens.tagName;
|
|
279
|
-
endTag = {
|
|
280
|
-
uuid: uuid(),
|
|
281
|
-
raw: endTagLoc.raw,
|
|
282
|
-
startOffset: endTagLoc.startOffset,
|
|
283
|
-
endOffset: endTagLoc.endOffset,
|
|
284
|
-
startLine: endTagLoc.startLine,
|
|
285
|
-
endLine: endTagLoc.endLine,
|
|
286
|
-
startCol: endTagLoc.startCol,
|
|
287
|
-
endCol: endTagLoc.endCol,
|
|
288
|
-
nodeName: endTagName,
|
|
289
|
-
type: 'endtag',
|
|
290
|
-
namespace: scopeNS,
|
|
291
|
-
attributes: endTagTokens.attrs,
|
|
292
|
-
parentNode,
|
|
293
|
-
prevNode,
|
|
294
|
-
nextNode,
|
|
295
|
-
pearNode: null,
|
|
296
|
-
isFragment: false,
|
|
297
|
-
isGhost: false,
|
|
298
|
-
tagOpenChar: '</',
|
|
299
|
-
tagCloseChar: '>',
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
const startTag = {
|
|
303
|
-
uuid: uuid(),
|
|
304
|
-
raw: startTagRaw,
|
|
305
|
-
startOffset,
|
|
306
|
-
endOffset: startOffset + startTagRaw.length,
|
|
307
|
-
startLine,
|
|
308
|
-
endLine: getEndLine(startTagRaw, startLine),
|
|
309
|
-
startCol,
|
|
310
|
-
endCol: getEndCol(startTagRaw, startCol),
|
|
311
|
-
nodeName: tagName,
|
|
312
|
-
type: 'starttag',
|
|
313
|
-
namespace: scopeNS,
|
|
314
|
-
elementType: detectElementType(tagName, options?.authoredElementName, /^[A-Z]|\./),
|
|
315
|
-
attributes: originNode.attributes.map((
|
|
316
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
317
|
-
attr, i) => attrTokenizer(attr, originNode.attributes[i + 1] ?? null, rawHtml, startTagRaw, startOffset + startTagRaw.length)),
|
|
318
|
-
hasSpreadAttr: false,
|
|
319
|
-
parentNode,
|
|
320
|
-
prevNode,
|
|
321
|
-
nextNode,
|
|
322
|
-
pearNode: endTag,
|
|
323
|
-
selfClosingSolidus: tagTokens.selfClosingSolidus,
|
|
324
|
-
endSpace: tagTokens.afterAttrSpaces,
|
|
325
|
-
isFragment: false,
|
|
326
|
-
isGhost: false,
|
|
327
|
-
tagOpenChar: '<',
|
|
328
|
-
tagCloseChar: '>',
|
|
329
|
-
};
|
|
330
|
-
if (endTag) {
|
|
331
|
-
endTag.pearNode = startTag;
|
|
332
|
-
}
|
|
333
|
-
startTag.childNodes = ['style', 'script'].includes(tagName)
|
|
334
|
-
? undefined
|
|
335
|
-
: traverse(originNode, startTag, scopeNS, rawHtml, offset, options);
|
|
336
|
-
return startTag;
|
|
337
|
-
}
|