@markuplint/vue-parser 4.0.0-dev.28 → 4.0.0-rc.0
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 +28 -0
- package/lib/parser.js +238 -0
- package/lib/vue-parser/index.d.ts +2 -1
- package/lib/vue-parser/index.js +1 -1
- package/package.json +6 -6
- package/lib/attr.d.ts +0 -2
- package/lib/attr.js +0 -110
- package/lib/parse.d.ts +0 -2
- package/lib/parse.js +0 -208
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,28 @@
|
|
|
1
|
+
/// <reference types="vue-eslint-parser" />
|
|
2
|
+
import type { ASTNode, ASTComment } from './vue-parser/index.js';
|
|
3
|
+
import type { MLASTParentNode, MLASTNodeTreeItem } from '@markuplint/ml-ast';
|
|
4
|
+
import type { Token } from '@markuplint/parser-utils';
|
|
5
|
+
import { ParserError, Parser } from '@markuplint/parser-utils';
|
|
6
|
+
type State = {
|
|
7
|
+
comments: readonly ASTComment[];
|
|
8
|
+
};
|
|
9
|
+
declare class VueParser extends Parser<ASTNode, State> {
|
|
10
|
+
readonly duplicatableAttrs: Set<string>;
|
|
11
|
+
constructor();
|
|
12
|
+
tokenize(): {
|
|
13
|
+
ast: (import("vue-eslint-parser/ast/nodes").VElement | import("vue-eslint-parser/ast/nodes").VText | import("vue-eslint-parser/ast/nodes").VExpressionContainer)[];
|
|
14
|
+
isFragment: boolean;
|
|
15
|
+
};
|
|
16
|
+
parseError(error: any): ParserError;
|
|
17
|
+
nodeize(originNode: ASTNode, parentNode: MLASTParentNode | null, depth: number): readonly MLASTNodeTreeItem[];
|
|
18
|
+
flattenNodes(nodeTree: readonly MLASTNodeTreeItem[]): MLASTNodeTreeItem[];
|
|
19
|
+
afterFlattenNodes(nodeList: readonly MLASTNodeTreeItem[]): MLASTNodeTreeItem[];
|
|
20
|
+
visitAttr(token: Token): (import("@markuplint/ml-ast").MLASTHTMLAttr & {
|
|
21
|
+
__rightText?: string | undefined;
|
|
22
|
+
}) | (import("@markuplint/ml-ast").MLASTSpreadAttr & {
|
|
23
|
+
__rightText?: string | undefined;
|
|
24
|
+
});
|
|
25
|
+
detectElementType(nodeName: string): import("@markuplint/ml-ast").ElementType;
|
|
26
|
+
}
|
|
27
|
+
export declare const parser: VueParser;
|
|
28
|
+
export {};
|
package/lib/parser.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { ParserError, Parser } from '@markuplint/parser-utils';
|
|
2
|
+
import { vueParse } from './vue-parser/index.js';
|
|
3
|
+
class VueParser extends Parser {
|
|
4
|
+
constructor() {
|
|
5
|
+
super({
|
|
6
|
+
endTagType: 'xml',
|
|
7
|
+
}, {
|
|
8
|
+
comments: [],
|
|
9
|
+
});
|
|
10
|
+
this.duplicatableAttrs = new Set(['class', 'style']);
|
|
11
|
+
}
|
|
12
|
+
tokenize() {
|
|
13
|
+
const ast = vueParse(this.rawCode);
|
|
14
|
+
if (ast.templateBody?.comments) {
|
|
15
|
+
this.state.comments = ast.templateBody.comments;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
ast: ast.templateBody?.children ?? [],
|
|
19
|
+
isFragment: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
parseError(error) {
|
|
23
|
+
if (error instanceof SyntaxError && 'lineNumber' in error && 'column' in error) {
|
|
24
|
+
throw new ParserError(error.message, {
|
|
25
|
+
line: error.lineNumber,
|
|
26
|
+
col: error.column,
|
|
27
|
+
raw: '',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return super.parseError(error);
|
|
31
|
+
}
|
|
32
|
+
nodeize(
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
34
|
+
originNode, parentNode, depth) {
|
|
35
|
+
const token = this.sliceFragment(originNode.range[0], originNode.range[1]);
|
|
36
|
+
switch (originNode.type) {
|
|
37
|
+
case 'VText': {
|
|
38
|
+
return this.visitText({
|
|
39
|
+
...token,
|
|
40
|
+
depth,
|
|
41
|
+
parentNode,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
case 'VExpressionContainer': {
|
|
45
|
+
return this.visitPsBlock({
|
|
46
|
+
...token,
|
|
47
|
+
depth,
|
|
48
|
+
parentNode,
|
|
49
|
+
nodeName: 'vue-expression-container',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
default: {
|
|
53
|
+
const token = this.sliceFragment(originNode.startTag.range[0], originNode.startTag.range[1]);
|
|
54
|
+
return this.visitElement({
|
|
55
|
+
...token,
|
|
56
|
+
depth,
|
|
57
|
+
parentNode,
|
|
58
|
+
nodeName: originNode.name,
|
|
59
|
+
namespace: originNode.namespace,
|
|
60
|
+
}, originNode.children, {
|
|
61
|
+
createEndTagToken: () => {
|
|
62
|
+
if (!originNode.endTag) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const token = this.sliceFragment(originNode.endTag.range[0], originNode.endTag.range[1]);
|
|
66
|
+
return {
|
|
67
|
+
...token,
|
|
68
|
+
depth,
|
|
69
|
+
parentNode,
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
flattenNodes(nodeTree) {
|
|
77
|
+
const nodeList = super.flattenNodes(nodeTree);
|
|
78
|
+
const newNodeList = [];
|
|
79
|
+
let prevNode = null;
|
|
80
|
+
for (const node of nodeList) {
|
|
81
|
+
const lastOffset = prevNode?.endOffset ?? node.parentNode?.endOffset ?? 0;
|
|
82
|
+
const betweenComment = this.state.comments.find(comment => {
|
|
83
|
+
return lastOffset <= comment.range[0] && comment.range[1] <= node.startOffset;
|
|
84
|
+
});
|
|
85
|
+
if (betweenComment) {
|
|
86
|
+
const token = this.sliceFragment(betweenComment.range[0], betweenComment.range[1]);
|
|
87
|
+
const comment = this.visitComment({
|
|
88
|
+
...token,
|
|
89
|
+
depth: node.depth,
|
|
90
|
+
parentNode: node.parentNode,
|
|
91
|
+
}, {
|
|
92
|
+
isBogus: betweenComment.type === 'HTMLBogusComment',
|
|
93
|
+
})[0];
|
|
94
|
+
if (comment && comment.type === 'comment') {
|
|
95
|
+
newNodeList.push(comment);
|
|
96
|
+
if (node.parentNode) {
|
|
97
|
+
this.appendChild(node.parentNode, comment);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
newNodeList.push(node);
|
|
102
|
+
prevNode = node;
|
|
103
|
+
}
|
|
104
|
+
return newNodeList;
|
|
105
|
+
}
|
|
106
|
+
afterFlattenNodes(nodeList) {
|
|
107
|
+
const optimizedNodeList = super.afterFlattenNodes(nodeList);
|
|
108
|
+
const newNodeList = [];
|
|
109
|
+
let openTemplateTag = false;
|
|
110
|
+
let closeTemplateTag = false;
|
|
111
|
+
for (const node of optimizedNodeList) {
|
|
112
|
+
if (!openTemplateTag && node.type === 'text') {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (node.type === 'starttag' && node.nodeName === 'template' && node.depth === 0) {
|
|
116
|
+
openTemplateTag = true;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (/^<\/template>$/i.test(node.raw) && node.depth === 0) {
|
|
120
|
+
closeTemplateTag = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (closeTemplateTag && node.type === 'text') {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
newNodeList.push(node);
|
|
127
|
+
}
|
|
128
|
+
return newNodeList;
|
|
129
|
+
}
|
|
130
|
+
visitAttr(token) {
|
|
131
|
+
const attr = super.visitAttr(token);
|
|
132
|
+
if (attr.type === 'spread') {
|
|
133
|
+
return attr;
|
|
134
|
+
}
|
|
135
|
+
{
|
|
136
|
+
/**
|
|
137
|
+
* `v-on`
|
|
138
|
+
*/
|
|
139
|
+
const [, directive, potentialName] = attr.name.raw.match(/^(v-on:|@)([^.]+)(?:\.([^.]+))?$/i) ?? [];
|
|
140
|
+
if (directive && potentialName) {
|
|
141
|
+
return {
|
|
142
|
+
...attr,
|
|
143
|
+
potentialName: `on${potentialName.toLowerCase()}`,
|
|
144
|
+
isDynamicValue: true,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
{
|
|
149
|
+
/**
|
|
150
|
+
* `v-bind`
|
|
151
|
+
*/
|
|
152
|
+
const [, directive, potentialName, modifier] = attr.name.raw.match(/^(v-bind:|:)([^.]+)(?:\.([^.]+))?$/i) ?? [];
|
|
153
|
+
if (directive && potentialName) {
|
|
154
|
+
if (this.duplicatableAttrs.has(potentialName.toLowerCase())) {
|
|
155
|
+
this.updateAttr(attr, { isDuplicatable: true });
|
|
156
|
+
}
|
|
157
|
+
if (!modifier) {
|
|
158
|
+
return {
|
|
159
|
+
...attr,
|
|
160
|
+
potentialName,
|
|
161
|
+
isDynamicValue: true,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
switch (modifier) {
|
|
165
|
+
case '.attr': {
|
|
166
|
+
return {
|
|
167
|
+
...attr,
|
|
168
|
+
potentialName,
|
|
169
|
+
isDynamicValue: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/* eslint-disable unicorn/no-useless-switch-case */
|
|
173
|
+
case '.prop':
|
|
174
|
+
case '.camel':
|
|
175
|
+
default: {
|
|
176
|
+
const name = `v-bind:${potentialName}${modifier ?? ''}`;
|
|
177
|
+
return {
|
|
178
|
+
...attr,
|
|
179
|
+
potentialName: attr.name.raw === name ? undefined : name,
|
|
180
|
+
isDirective: true,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/* eslint-enable unicorn/no-useless-switch-case */
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
{
|
|
188
|
+
/**
|
|
189
|
+
* `v-model`
|
|
190
|
+
*/
|
|
191
|
+
const [, directive] = attr.name.raw.match(/^(v-model)(?:\.([^.]+))?$/i) ?? [];
|
|
192
|
+
if (directive) {
|
|
193
|
+
return {
|
|
194
|
+
...attr,
|
|
195
|
+
isDirective: true,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
{
|
|
200
|
+
/**
|
|
201
|
+
* `v-slot`
|
|
202
|
+
*/
|
|
203
|
+
const slotName = (attr.name.raw.match(/^(v-slot:|#)(.+)$/i) ?? [])[2];
|
|
204
|
+
const name = `v-slot:${slotName}`;
|
|
205
|
+
if (slotName) {
|
|
206
|
+
return {
|
|
207
|
+
...attr,
|
|
208
|
+
potentialName: attr.name.raw === name ? undefined : name,
|
|
209
|
+
isDirective: true,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* If directives
|
|
215
|
+
*/
|
|
216
|
+
if (attr.name.raw.startsWith('v-')) {
|
|
217
|
+
return {
|
|
218
|
+
...attr,
|
|
219
|
+
isDirective: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return attr;
|
|
223
|
+
}
|
|
224
|
+
detectElementType(nodeName) {
|
|
225
|
+
return super.detectElementType(nodeName, [
|
|
226
|
+
'component',
|
|
227
|
+
'slot',
|
|
228
|
+
'Transition',
|
|
229
|
+
'TransitionGroup',
|
|
230
|
+
'KeepAlive',
|
|
231
|
+
'Teleport',
|
|
232
|
+
'Suspense',
|
|
233
|
+
// Backward compatibility
|
|
234
|
+
/^[A-Z]/,
|
|
235
|
+
]);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
export const parser = new VueParser();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as VueESLintParser from 'vue-eslint-parser';
|
|
2
2
|
export type VueTokens = VueESLintParser.AST.ESLintProgram;
|
|
3
|
-
export
|
|
3
|
+
export declare function vueParse(vueTemplate: string): VueTokens;
|
|
4
4
|
export type ASTNode = VueESLintParser.AST.VElement | VueESLintParser.AST.VText | VueESLintParser.AST.VExpressionContainer;
|
|
5
|
+
export type ASTComment = VueESLintParser.AST.Token & VueESLintParser.AST.HasLocation;
|
package/lib/vue-parser/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/vue-parser",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-rc.0",
|
|
4
4
|
"description": "Vue parser for markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"clean": "tsc --build --clean"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@markuplint/html-parser": "4.0.0-
|
|
25
|
-
"@markuplint/ml-ast": "4.0.0-
|
|
26
|
-
"@markuplint/parser-utils": "4.0.0-
|
|
27
|
-
"vue-eslint-parser": "^9.
|
|
24
|
+
"@markuplint/html-parser": "4.0.0-rc.0",
|
|
25
|
+
"@markuplint/ml-ast": "4.0.0-rc.0",
|
|
26
|
+
"@markuplint/parser-utils": "4.0.0-rc.0",
|
|
27
|
+
"vue-eslint-parser": "^9.4.2"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "3fdeb45cb69ed52b3a215a7520cea1181601443f"
|
|
30
30
|
}
|
package/lib/attr.d.ts
DELETED
package/lib/attr.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
const duplicatableAttrs = new Set(['class', 'style']);
|
|
2
|
-
export function attr(
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
4
|
-
attr) {
|
|
5
|
-
if (attr.type !== 'html-attr') {
|
|
6
|
-
const name = attr.potentialName;
|
|
7
|
-
if (duplicatableAttrs.has(name)) {
|
|
8
|
-
attr.isDuplicatable = true;
|
|
9
|
-
}
|
|
10
|
-
return attr;
|
|
11
|
-
}
|
|
12
|
-
{
|
|
13
|
-
/**
|
|
14
|
-
* `v-on`
|
|
15
|
-
*/
|
|
16
|
-
const [, directive, potentialName, modifier] = attr.name.raw.match(/^(v-on:|@)([^.]+)(?:\.([^.]+))?$/i) ?? [];
|
|
17
|
-
if (directive && potentialName) {
|
|
18
|
-
return {
|
|
19
|
-
...attr,
|
|
20
|
-
potentialName: `on${potentialName.toLowerCase()}`,
|
|
21
|
-
isDynamicValue: true,
|
|
22
|
-
// @ts-ignore
|
|
23
|
-
_modifier: modifier,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
{
|
|
28
|
-
/**
|
|
29
|
-
* `v-bind`
|
|
30
|
-
*/
|
|
31
|
-
const [, directive, potentialName, modifier] = attr.name.raw.match(/^(v-bind:|:)([^.]+)(?:\.([^.]+))?$/i) ?? [];
|
|
32
|
-
if (directive && potentialName) {
|
|
33
|
-
if (duplicatableAttrs.has(potentialName.toLowerCase())) {
|
|
34
|
-
attr.isDuplicatable = true;
|
|
35
|
-
}
|
|
36
|
-
if (!modifier) {
|
|
37
|
-
return {
|
|
38
|
-
...attr,
|
|
39
|
-
potentialName,
|
|
40
|
-
isDynamicValue: true,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
switch (modifier) {
|
|
44
|
-
case '.attr': {
|
|
45
|
-
return {
|
|
46
|
-
...attr,
|
|
47
|
-
potentialName,
|
|
48
|
-
isDynamicValue: true,
|
|
49
|
-
// @ts-ignore
|
|
50
|
-
_modifier: modifier,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/* eslint-disable unicorn/no-useless-switch-case */
|
|
54
|
-
// TODO: Supporting for `prop` and `camel` https://github.com/markuplint/markuplint/pull/681
|
|
55
|
-
case '.prop':
|
|
56
|
-
case '.camel':
|
|
57
|
-
default: {
|
|
58
|
-
const name = `v-bind:${potentialName}${modifier ?? ''}`;
|
|
59
|
-
return {
|
|
60
|
-
...attr,
|
|
61
|
-
potentialName: attr.name.raw === name ? undefined : name,
|
|
62
|
-
isDirective: true,
|
|
63
|
-
// @ts-ignore
|
|
64
|
-
_modifier: modifier,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
/* eslint-enable unicorn/no-useless-switch-case */
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
{
|
|
72
|
-
/**
|
|
73
|
-
* `v-model`
|
|
74
|
-
*/
|
|
75
|
-
const [, directive, modifier] = attr.name.raw.match(/^(v-model)(?:\.([^.]+))?$/i) ?? [];
|
|
76
|
-
if (directive) {
|
|
77
|
-
// TODO: Supporting for `v-model` https://github.com/markuplint/markuplint/pull/681
|
|
78
|
-
return {
|
|
79
|
-
...attr,
|
|
80
|
-
isDirective: true,
|
|
81
|
-
// @ts-ignore
|
|
82
|
-
_modifier: modifier,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
{
|
|
87
|
-
/**
|
|
88
|
-
* `v-slot`
|
|
89
|
-
*/
|
|
90
|
-
const slotName = (attr.name.raw.match(/^(v-slot:|#)(.+)$/i) ?? [])[2];
|
|
91
|
-
const name = `v-slot:${slotName}`;
|
|
92
|
-
if (slotName) {
|
|
93
|
-
return {
|
|
94
|
-
...attr,
|
|
95
|
-
potentialName: attr.name.raw === name ? undefined : name,
|
|
96
|
-
isDirective: true,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* If directives
|
|
102
|
-
*/
|
|
103
|
-
if (attr.name.raw.startsWith('v-')) {
|
|
104
|
-
return {
|
|
105
|
-
...attr,
|
|
106
|
-
isDirective: true,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return attr;
|
|
110
|
-
}
|
package/lib/parse.d.ts
DELETED
package/lib/parse.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import { parseRawTag } from '@markuplint/html-parser';
|
|
2
|
-
import { flattenNodes, getEndCol, getEndLine, uuid, ParserError, detectElementType } from '@markuplint/parser-utils';
|
|
3
|
-
import { attr } from './attr.js';
|
|
4
|
-
import vueParse from './vue-parser/index.js';
|
|
5
|
-
export const parse = (rawCode, options) => {
|
|
6
|
-
let ast;
|
|
7
|
-
try {
|
|
8
|
-
ast = vueParse(rawCode);
|
|
9
|
-
}
|
|
10
|
-
catch (error) {
|
|
11
|
-
if (error instanceof SyntaxError && 'lineNumber' in error && 'column' in error) {
|
|
12
|
-
throw new ParserError(
|
|
13
|
-
// @ts-ignore
|
|
14
|
-
error.message, {
|
|
15
|
-
// @ts-ignore
|
|
16
|
-
line: error.lineNumber,
|
|
17
|
-
// @ts-ignore
|
|
18
|
-
col: error.column,
|
|
19
|
-
raw: '',
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
nodeList: [],
|
|
24
|
-
isFragment: true,
|
|
25
|
-
parseError: error instanceof Error ? error.message : new Error(`${error}`).message,
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
const nodeList = ast.templateBody
|
|
29
|
-
? flattenNodes(traverse(ast.templateBody, null, rawCode, options), rawCode)
|
|
30
|
-
: [];
|
|
31
|
-
// Remove `</template>`
|
|
32
|
-
const templateEndTagIndex = nodeList.findIndex(node => /\s*<\/\s*template\s*>\s*/i.test(node.raw));
|
|
33
|
-
if (templateEndTagIndex !== -1) {
|
|
34
|
-
const templateEndTag = nodeList[templateEndTagIndex];
|
|
35
|
-
for (const node of nodeList) {
|
|
36
|
-
if (node.nextNode && node.nextNode.uuid === templateEndTag?.uuid) {
|
|
37
|
-
node.nextNode = null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
nodeList.splice(templateEndTagIndex, 1);
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
nodeList,
|
|
44
|
-
isFragment: true,
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
function traverse(
|
|
48
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
49
|
-
rootNode, parentNode = null, rawHtml, options) {
|
|
50
|
-
const nodeList = [];
|
|
51
|
-
if (rootNode.type !== 'VElement') {
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
let prevNode = null;
|
|
55
|
-
for (const vNode of rootNode.children) {
|
|
56
|
-
const node = nodeize(vNode, prevNode, parentNode, rawHtml, options);
|
|
57
|
-
if (!node) {
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (prevNode) {
|
|
61
|
-
if (node.type !== 'endtag') {
|
|
62
|
-
prevNode.nextNode = node;
|
|
63
|
-
}
|
|
64
|
-
node.prevNode = prevNode;
|
|
65
|
-
}
|
|
66
|
-
prevNode = node;
|
|
67
|
-
nodeList.push(node);
|
|
68
|
-
}
|
|
69
|
-
return nodeList;
|
|
70
|
-
}
|
|
71
|
-
function nodeize(
|
|
72
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
73
|
-
originNode,
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
75
|
-
prevNode,
|
|
76
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
77
|
-
parentNode, rawHtml, options) {
|
|
78
|
-
const nextNode = null;
|
|
79
|
-
const startOffset = originNode.range[0];
|
|
80
|
-
const endOffset = originNode.range[1];
|
|
81
|
-
const startLine = originNode.loc.start.line;
|
|
82
|
-
const endLine = originNode.loc.end.line;
|
|
83
|
-
const startCol = originNode.loc.start.column + 1;
|
|
84
|
-
const endCol = originNode.loc.end.column + 1;
|
|
85
|
-
const raw = rawHtml.slice(startOffset, endOffset ?? startOffset);
|
|
86
|
-
switch (originNode.type) {
|
|
87
|
-
case 'VText': {
|
|
88
|
-
const node = {
|
|
89
|
-
uuid: uuid(),
|
|
90
|
-
raw,
|
|
91
|
-
startOffset,
|
|
92
|
-
endOffset,
|
|
93
|
-
startLine,
|
|
94
|
-
endLine,
|
|
95
|
-
startCol,
|
|
96
|
-
endCol,
|
|
97
|
-
nodeName: '#text',
|
|
98
|
-
type: 'text',
|
|
99
|
-
parentNode,
|
|
100
|
-
prevNode,
|
|
101
|
-
nextNode,
|
|
102
|
-
isFragment: false,
|
|
103
|
-
isGhost: false,
|
|
104
|
-
};
|
|
105
|
-
return node;
|
|
106
|
-
}
|
|
107
|
-
case 'VExpressionContainer': {
|
|
108
|
-
return {
|
|
109
|
-
uuid: uuid(),
|
|
110
|
-
raw,
|
|
111
|
-
startOffset,
|
|
112
|
-
endOffset,
|
|
113
|
-
startLine,
|
|
114
|
-
endLine,
|
|
115
|
-
startCol,
|
|
116
|
-
endCol,
|
|
117
|
-
nodeName: '#vue-expression-container',
|
|
118
|
-
type: 'psblock',
|
|
119
|
-
parentNode,
|
|
120
|
-
prevNode,
|
|
121
|
-
nextNode,
|
|
122
|
-
isFragment: false,
|
|
123
|
-
isGhost: false,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
default: {
|
|
127
|
-
const tagLoc = originNode.startTag;
|
|
128
|
-
const startTagRaw = rawHtml.slice(tagLoc.range[0], tagLoc.range[1]);
|
|
129
|
-
const tagTokens = parseRawTag(startTagRaw, startLine, startCol, startOffset);
|
|
130
|
-
const tagName = tagTokens.tagName;
|
|
131
|
-
let endTag = null;
|
|
132
|
-
const endTagLoc = originNode.endTag;
|
|
133
|
-
if (endTagLoc) {
|
|
134
|
-
const endTagRaw = rawHtml.slice(endTagLoc.range[0], endTagLoc.range[1]);
|
|
135
|
-
const endTagTokens = parseRawTag(endTagRaw, endTagLoc.loc.start.line, endTagLoc.loc.start.column, endTagLoc.range[0]);
|
|
136
|
-
const endTagName = endTagTokens.tagName;
|
|
137
|
-
endTag = {
|
|
138
|
-
uuid: uuid(),
|
|
139
|
-
raw: endTagRaw,
|
|
140
|
-
startOffset: endTagLoc.range[0],
|
|
141
|
-
endOffset: endTagLoc.range[1],
|
|
142
|
-
startLine: endTagLoc.loc.start.line,
|
|
143
|
-
endLine: endTagLoc.loc.end.line,
|
|
144
|
-
startCol: endTagLoc.loc.start.column + 1,
|
|
145
|
-
endCol: endTagLoc.loc.end.column + 1,
|
|
146
|
-
nodeName: endTagName,
|
|
147
|
-
type: 'endtag',
|
|
148
|
-
namespace: originNode.namespace,
|
|
149
|
-
attributes: endTagTokens.attrs,
|
|
150
|
-
parentNode,
|
|
151
|
-
prevNode,
|
|
152
|
-
nextNode,
|
|
153
|
-
pearNode: null,
|
|
154
|
-
isFragment: false,
|
|
155
|
-
isGhost: false,
|
|
156
|
-
tagOpenChar: '</',
|
|
157
|
-
tagCloseChar: '>',
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
const startTag = {
|
|
161
|
-
uuid: uuid(),
|
|
162
|
-
raw: startTagRaw,
|
|
163
|
-
startOffset,
|
|
164
|
-
endOffset: startOffset + startTagRaw.length,
|
|
165
|
-
startLine,
|
|
166
|
-
endLine: getEndLine(startTagRaw, startLine),
|
|
167
|
-
startCol,
|
|
168
|
-
endCol: getEndCol(startTagRaw, startCol),
|
|
169
|
-
nodeName: tagName,
|
|
170
|
-
type: 'starttag',
|
|
171
|
-
namespace: originNode.namespace,
|
|
172
|
-
elementType: detectElementType(tagName, options?.authoredElementName,
|
|
173
|
-
/**
|
|
174
|
-
* @see https://vuejs.org/api/built-in-components.html
|
|
175
|
-
* @see https://vuejs.org/api/built-in-special-elements.html
|
|
176
|
-
*/
|
|
177
|
-
[
|
|
178
|
-
'component',
|
|
179
|
-
'slot',
|
|
180
|
-
'Transition',
|
|
181
|
-
'TransitionGroup',
|
|
182
|
-
'KeepAlive',
|
|
183
|
-
'Teleport',
|
|
184
|
-
'Suspense',
|
|
185
|
-
// Backward compatibility
|
|
186
|
-
/^[A-Z]/,
|
|
187
|
-
]),
|
|
188
|
-
attributes: tagTokens.attrs.map(attr),
|
|
189
|
-
hasSpreadAttr: false,
|
|
190
|
-
parentNode,
|
|
191
|
-
prevNode,
|
|
192
|
-
nextNode,
|
|
193
|
-
pearNode: endTag,
|
|
194
|
-
selfClosingSolidus: tagTokens.selfClosingSolidus,
|
|
195
|
-
endSpace: tagTokens.endSpace,
|
|
196
|
-
isFragment: false,
|
|
197
|
-
isGhost: false,
|
|
198
|
-
tagOpenChar: '<',
|
|
199
|
-
tagCloseChar: '>',
|
|
200
|
-
};
|
|
201
|
-
if (endTag) {
|
|
202
|
-
endTag.pearNode = startTag;
|
|
203
|
-
}
|
|
204
|
-
startTag.childNodes = traverse(originNode, startTag, rawHtml, options);
|
|
205
|
-
return startTag;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|