astro-eslint-parser 0.0.12 → 0.0.15
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/lib/ast.d.ts +7 -1
- package/lib/astro/index.d.ts +29 -3
- package/lib/astro/index.js +168 -14
- package/lib/context/index.d.ts +3 -1
- package/lib/context/index.js +6 -0
- package/lib/context/script.d.ts +1 -1
- package/lib/context/script.js +7 -5
- package/lib/errors.d.ts +1 -0
- package/lib/errors.js +1 -0
- package/lib/parser/astro-parser/parse.js +27 -21
- package/lib/parser/process-template.js +117 -46
- package/lib/parser/script.js +3 -1
- package/lib/visitor-keys.js +1 -0
- package/package.json +2 -2
package/lib/ast.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TSESTree } from "@typescript-eslint/types";
|
|
2
|
-
export declare type AstroNode = AstroProgram | AstroRootFragment | AstroHTMLComment | AstroDoctype | AstroShorthandAttribute | AstroTemplateLiteralAttribute | AstroRawText;
|
|
2
|
+
export declare type AstroNode = AstroProgram | AstroRootFragment | AstroHTMLComment | AstroDoctype | AstroShorthandAttribute | AstroTemplateLiteralAttribute | AstroRawText | AstroFragment;
|
|
3
3
|
/** Node of Astro program root */
|
|
4
4
|
export interface AstroProgram extends Omit<TSESTree.Program, "type" | "body"> {
|
|
5
5
|
type: "Program";
|
|
@@ -45,3 +45,9 @@ export interface AstroRawText extends Omit<TSESTree.JSXText, "type" | "parent">
|
|
|
45
45
|
type: "AstroRawText";
|
|
46
46
|
parent: AstroRootFragment | TSESTree.JSXElement | TSESTree.JSXFragment;
|
|
47
47
|
}
|
|
48
|
+
/** Node of Astro fragment expression */
|
|
49
|
+
export interface AstroFragment extends Omit<TSESTree.BaseNode, "type" | "parent"> {
|
|
50
|
+
type: "AstroFragment";
|
|
51
|
+
children: TSESTree.JSXFragment["children"];
|
|
52
|
+
parent: TSESTree.JSXFragment["parent"];
|
|
53
|
+
}
|
package/lib/astro/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AttributeNode, CommentNode, Node, ParentNode, TagLikeNode } from "@astrojs/compiler/types";
|
|
1
|
+
import type { AttributeNode, CommentNode, ExpressionNode, Node, ParentNode, TagLikeNode } from "@astrojs/compiler/types";
|
|
2
2
|
import type { Context } from "../context";
|
|
3
3
|
/**
|
|
4
4
|
* Checks if the given node is TagLikeNode
|
|
@@ -9,13 +9,21 @@ export declare function isTag(node: Node): node is Node & TagLikeNode;
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function isParent(node: Node): node is ParentNode;
|
|
11
11
|
/** walk element nodes */
|
|
12
|
-
export declare function walkElements(parent: ParentNode, code: string,
|
|
12
|
+
export declare function walkElements(parent: ParentNode, code: string, enter: (n: Node, parents: ParentNode[]) => void, leave: (n: Node, parents: ParentNode[]) => void, parents?: ParentNode[]): void;
|
|
13
13
|
/** walk nodes */
|
|
14
|
-
export declare function walk(parent: ParentNode, code: string, enter: (n: Node | AttributeNode,
|
|
14
|
+
export declare function walk(parent: ParentNode, code: string, enter: (n: Node | AttributeNode, parents: ParentNode[]) => void, leave: (n: Node | AttributeNode, parents: ParentNode[]) => void): void;
|
|
15
15
|
/**
|
|
16
16
|
* Get end offset of start tag
|
|
17
17
|
*/
|
|
18
18
|
export declare function getStartTagEndOffset(node: TagLikeNode, ctx: Context): number;
|
|
19
|
+
/**
|
|
20
|
+
* Get end offset of tag
|
|
21
|
+
*/
|
|
22
|
+
export declare function getTagEndOffset(node: TagLikeNode, ctx: Context): number;
|
|
23
|
+
/**
|
|
24
|
+
* Get end offset of Expression
|
|
25
|
+
*/
|
|
26
|
+
export declare function getExpressionEndOffset(node: ExpressionNode, ctx: Context): number;
|
|
19
27
|
/**
|
|
20
28
|
* Get end offset of attribute
|
|
21
29
|
*/
|
|
@@ -28,6 +36,24 @@ export declare function getAttributeValueStartOffset(node: AttributeNode, ctx: C
|
|
|
28
36
|
* Get end offset of comment
|
|
29
37
|
*/
|
|
30
38
|
export declare function getCommentEndOffset(node: CommentNode, ctx: Context): number;
|
|
39
|
+
/**
|
|
40
|
+
* Get content end offset
|
|
41
|
+
*/
|
|
42
|
+
export declare function getContentEndOffset(parent: ParentNode, ctx: Context): number;
|
|
43
|
+
/**
|
|
44
|
+
* If the given tag is a self-close tag, get the self-closing tag.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getSelfClosingTag(node: TagLikeNode, parent: ParentNode, ctx: Context): null | {
|
|
47
|
+
offset: number;
|
|
48
|
+
end: "/>" | ">";
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* If the given tag has a end tag, get the end tag.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getEndTag(node: TagLikeNode, ctx: Context): null | {
|
|
54
|
+
offset: number;
|
|
55
|
+
tag: string;
|
|
56
|
+
};
|
|
31
57
|
/**
|
|
32
58
|
* Skip spaces
|
|
33
59
|
*/
|
package/lib/astro/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.skipSpaces = exports.getCommentEndOffset = exports.getAttributeValueStartOffset = exports.getAttributeEndOffset = exports.getStartTagEndOffset = exports.walk = exports.walkElements = exports.isParent = exports.isTag = void 0;
|
|
3
|
+
exports.skipSpaces = exports.getEndTag = exports.getSelfClosingTag = exports.getContentEndOffset = exports.getCommentEndOffset = exports.getAttributeValueStartOffset = exports.getAttributeEndOffset = exports.getExpressionEndOffset = exports.getTagEndOffset = exports.getStartTagEndOffset = exports.walk = exports.walkElements = exports.isParent = exports.isTag = void 0;
|
|
4
4
|
const errors_1 = require("../errors");
|
|
5
5
|
/**
|
|
6
6
|
* Checks if the given node is TagLikeNode
|
|
@@ -20,32 +20,30 @@ function isParent(node) {
|
|
|
20
20
|
}
|
|
21
21
|
exports.isParent = isParent;
|
|
22
22
|
/** walk element nodes */
|
|
23
|
-
function walkElements(parent, code,
|
|
23
|
+
function walkElements(parent, code, enter, leave, parents = []) {
|
|
24
24
|
const children = getSortedChildren(parent, code);
|
|
25
|
+
const currParents = [parent, ...parents];
|
|
25
26
|
for (const node of children) {
|
|
26
|
-
|
|
27
|
+
enter(node, currParents);
|
|
27
28
|
if (isParent(node)) {
|
|
28
|
-
walkElements(node, code,
|
|
29
|
+
walkElements(node, code, enter, leave, currParents);
|
|
29
30
|
}
|
|
31
|
+
leave(node, currParents);
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
exports.walkElements = walkElements;
|
|
33
35
|
/** walk nodes */
|
|
34
36
|
function walk(parent, code, enter, leave) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
enter(node, parent);
|
|
37
|
+
walkElements(parent, code, (node, parents) => {
|
|
38
|
+
enter(node, parents);
|
|
38
39
|
if (isTag(node)) {
|
|
40
|
+
const attrParents = [node, ...parents];
|
|
39
41
|
for (const attr of node.attributes) {
|
|
40
|
-
enter(attr,
|
|
41
|
-
leave
|
|
42
|
+
enter(attr, attrParents);
|
|
43
|
+
leave(attr, attrParents);
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
|
-
|
|
45
|
-
walk(node, code, enter, leave);
|
|
46
|
-
}
|
|
47
|
-
leave === null || leave === void 0 ? void 0 : leave(node, parent);
|
|
48
|
-
}
|
|
46
|
+
}, leave);
|
|
49
47
|
}
|
|
50
48
|
exports.walk = walk;
|
|
51
49
|
/**
|
|
@@ -65,6 +63,49 @@ function getStartTagEndOffset(node, ctx) {
|
|
|
65
63
|
return info.index + info.match.length;
|
|
66
64
|
}
|
|
67
65
|
exports.getStartTagEndOffset = getStartTagEndOffset;
|
|
66
|
+
/**
|
|
67
|
+
* Get end offset of tag
|
|
68
|
+
*/
|
|
69
|
+
function getTagEndOffset(node, ctx) {
|
|
70
|
+
var _a;
|
|
71
|
+
if (((_a = node.position.end) === null || _a === void 0 ? void 0 : _a.offset) != null) {
|
|
72
|
+
return node.position.end.offset;
|
|
73
|
+
}
|
|
74
|
+
let beforeIndex;
|
|
75
|
+
if (node.children.length) {
|
|
76
|
+
const lastChild = node.children[node.children.length - 1];
|
|
77
|
+
beforeIndex = getEndOffset(lastChild, ctx);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
beforeIndex = getStartTagEndOffset(node, ctx);
|
|
81
|
+
}
|
|
82
|
+
beforeIndex = skipSpaces(ctx.code, beforeIndex);
|
|
83
|
+
if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) {
|
|
84
|
+
beforeIndex = beforeIndex + 2 + node.name.length;
|
|
85
|
+
const info = getTokenInfo(ctx, [">"], beforeIndex);
|
|
86
|
+
return info.index + info.match.length;
|
|
87
|
+
}
|
|
88
|
+
return beforeIndex;
|
|
89
|
+
}
|
|
90
|
+
exports.getTagEndOffset = getTagEndOffset;
|
|
91
|
+
/**
|
|
92
|
+
* Get end offset of Expression
|
|
93
|
+
*/
|
|
94
|
+
function getExpressionEndOffset(node, ctx) {
|
|
95
|
+
var _a;
|
|
96
|
+
if (((_a = node.position.end) === null || _a === void 0 ? void 0 : _a.offset) != null) {
|
|
97
|
+
return node.position.end.offset;
|
|
98
|
+
}
|
|
99
|
+
if (node.children.length) {
|
|
100
|
+
const lastChild = node.children[node.children.length - 1];
|
|
101
|
+
const beforeIndex = getEndOffset(lastChild, ctx);
|
|
102
|
+
const info = getTokenInfo(ctx, ["}"], beforeIndex);
|
|
103
|
+
return info.index + info.match.length;
|
|
104
|
+
}
|
|
105
|
+
const info = getTokenInfo(ctx, ["{", "}"], node.position.start.offset);
|
|
106
|
+
return info.index + info.match.length;
|
|
107
|
+
}
|
|
108
|
+
exports.getExpressionEndOffset = getExpressionEndOffset;
|
|
68
109
|
/**
|
|
69
110
|
* Get end offset of attribute
|
|
70
111
|
*/
|
|
@@ -122,6 +163,119 @@ function getCommentEndOffset(node, ctx) {
|
|
|
122
163
|
return info.index + info.match.length;
|
|
123
164
|
}
|
|
124
165
|
exports.getCommentEndOffset = getCommentEndOffset;
|
|
166
|
+
/**
|
|
167
|
+
* Get content end offset
|
|
168
|
+
*/
|
|
169
|
+
function getContentEndOffset(parent, ctx) {
|
|
170
|
+
const code = ctx.code;
|
|
171
|
+
if (isTag(parent)) {
|
|
172
|
+
const end = getTagEndOffset(parent, ctx);
|
|
173
|
+
if (code[end - 1] !== ">") {
|
|
174
|
+
return end;
|
|
175
|
+
}
|
|
176
|
+
const index = code.lastIndexOf("</", end - 1);
|
|
177
|
+
if (index >= 0 &&
|
|
178
|
+
code.slice(index + 2, end - 1).trim() === parent.name) {
|
|
179
|
+
return index;
|
|
180
|
+
}
|
|
181
|
+
return end;
|
|
182
|
+
}
|
|
183
|
+
else if (parent.type === "expression") {
|
|
184
|
+
const end = getExpressionEndOffset(parent, ctx);
|
|
185
|
+
return code.lastIndexOf("}", end);
|
|
186
|
+
}
|
|
187
|
+
else if (parent.type === "root") {
|
|
188
|
+
return code.length;
|
|
189
|
+
}
|
|
190
|
+
throw new Error(`unknown type: ${parent.type}`);
|
|
191
|
+
}
|
|
192
|
+
exports.getContentEndOffset = getContentEndOffset;
|
|
193
|
+
/**
|
|
194
|
+
* If the given tag is a self-close tag, get the self-closing tag.
|
|
195
|
+
*/
|
|
196
|
+
function getSelfClosingTag(node, parent, ctx) {
|
|
197
|
+
if (node.children.length > 0) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const code = ctx.code;
|
|
201
|
+
let nextElementIndex = code.length;
|
|
202
|
+
const childIndex = parent.children.indexOf(node);
|
|
203
|
+
if (childIndex === parent.children.length - 1) {
|
|
204
|
+
// last
|
|
205
|
+
nextElementIndex = getContentEndOffset(parent, ctx);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
const next = parent.children[childIndex + 1];
|
|
209
|
+
nextElementIndex = next.position.start.offset;
|
|
210
|
+
}
|
|
211
|
+
const endOffset = getStartTagEndOffset(node, ctx);
|
|
212
|
+
if (code.slice(endOffset, nextElementIndex).trim()) {
|
|
213
|
+
// has end tag
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
offset: endOffset,
|
|
218
|
+
end: code.slice(endOffset - 2, endOffset) === "/>" ? "/>" : ">",
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
exports.getSelfClosingTag = getSelfClosingTag;
|
|
222
|
+
/**
|
|
223
|
+
* If the given tag has a end tag, get the end tag.
|
|
224
|
+
*/
|
|
225
|
+
function getEndTag(node, ctx) {
|
|
226
|
+
let beforeIndex;
|
|
227
|
+
if (node.children.length) {
|
|
228
|
+
const lastChild = node.children[node.children.length - 1];
|
|
229
|
+
beforeIndex = getEndOffset(lastChild, ctx);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
beforeIndex = getStartTagEndOffset(node, ctx);
|
|
233
|
+
}
|
|
234
|
+
beforeIndex = skipSpaces(ctx.code, beforeIndex);
|
|
235
|
+
if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) {
|
|
236
|
+
const offset = beforeIndex;
|
|
237
|
+
beforeIndex = beforeIndex + 2 + node.name.length;
|
|
238
|
+
const info = getTokenInfo(ctx, [">"], beforeIndex);
|
|
239
|
+
const end = info.index + info.match.length;
|
|
240
|
+
return {
|
|
241
|
+
offset,
|
|
242
|
+
tag: ctx.code.slice(offset, end),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
exports.getEndTag = getEndTag;
|
|
248
|
+
/**
|
|
249
|
+
* Get end offset of tag
|
|
250
|
+
*/
|
|
251
|
+
function getEndOffset(node, ctx) {
|
|
252
|
+
var _a;
|
|
253
|
+
if (((_a = node.position.end) === null || _a === void 0 ? void 0 : _a.offset) != null) {
|
|
254
|
+
return node.position.end.offset;
|
|
255
|
+
}
|
|
256
|
+
if (isTag(node))
|
|
257
|
+
return getTagEndOffset(node, ctx);
|
|
258
|
+
if (node.type === "expression")
|
|
259
|
+
return getExpressionEndOffset(node, ctx);
|
|
260
|
+
if (node.type === "comment")
|
|
261
|
+
return getCommentEndOffset(node, ctx);
|
|
262
|
+
if (node.type === "frontmatter") {
|
|
263
|
+
const start = node.position.start.offset;
|
|
264
|
+
return ctx.code.indexOf("---", start + 3) + 3;
|
|
265
|
+
}
|
|
266
|
+
if (node.type === "doctype") {
|
|
267
|
+
const start = node.position.start.offset;
|
|
268
|
+
return ctx.code.indexOf(">", start) + 1;
|
|
269
|
+
}
|
|
270
|
+
if (node.type === "text") {
|
|
271
|
+
const start = node.position.start.offset;
|
|
272
|
+
return start + node.value.length;
|
|
273
|
+
}
|
|
274
|
+
if (node.type === "root") {
|
|
275
|
+
return ctx.code.length;
|
|
276
|
+
}
|
|
277
|
+
throw new Error(`unknown type: ${node.type}`);
|
|
278
|
+
}
|
|
125
279
|
/**
|
|
126
280
|
* Get token info
|
|
127
281
|
*/
|
package/lib/context/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare class Context {
|
|
|
9
9
|
readonly parserOptions: any;
|
|
10
10
|
readonly locs: LinesAndColumns;
|
|
11
11
|
private readonly locsMap;
|
|
12
|
-
private state;
|
|
12
|
+
private readonly state;
|
|
13
13
|
constructor(code: string, parserOptions: any);
|
|
14
14
|
getLocFromIndex(index: number): {
|
|
15
15
|
line: number;
|
|
@@ -29,6 +29,8 @@ export declare class Context {
|
|
|
29
29
|
getText(range: TSESTree.Range): string;
|
|
30
30
|
isTypeScript(): boolean;
|
|
31
31
|
remapCR({ ast, visitorKeys }: ESLintExtendedProgram): void;
|
|
32
|
+
get originalAST(): any;
|
|
33
|
+
set originalAST(originalAST: any);
|
|
32
34
|
}
|
|
33
35
|
export declare class LinesAndColumns {
|
|
34
36
|
readonly code: string;
|
package/lib/context/index.js
CHANGED
|
@@ -129,6 +129,12 @@ class Context {
|
|
|
129
129
|
comment.range = remapRange(comment.range);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
get originalAST() {
|
|
133
|
+
return this.state.originalAST;
|
|
134
|
+
}
|
|
135
|
+
set originalAST(originalAST) {
|
|
136
|
+
this.state.originalAST = originalAST;
|
|
137
|
+
}
|
|
132
138
|
}
|
|
133
139
|
exports.Context = Context;
|
|
134
140
|
class LinesAndColumns {
|
package/lib/context/script.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare class ScriptContext {
|
|
|
14
14
|
appendOriginal(index: number): void;
|
|
15
15
|
appendScript(fragment: string): void;
|
|
16
16
|
addToken(type: TSESTree.Token["type"], range: TSESTree.Range): void;
|
|
17
|
-
addRestoreNodeProcess(process: (node: TSESTree.Node, result: ESLintExtendedProgram) => boolean): void;
|
|
17
|
+
addRestoreNodeProcess(process: (node: TSESTree.Node, result: ESLintExtendedProgram, parent: TSESTree.Node) => boolean): void;
|
|
18
18
|
/**
|
|
19
19
|
* Restore AST nodes
|
|
20
20
|
*/
|
package/lib/context/script.js
CHANGED
|
@@ -52,12 +52,12 @@ class ScriptContext {
|
|
|
52
52
|
delete rootFragment.openingFragment;
|
|
53
53
|
rootFragment.type = "AstroRootFragment";
|
|
54
54
|
// remap locations
|
|
55
|
-
const traversed = new
|
|
55
|
+
const traversed = new Map();
|
|
56
56
|
(0, traverse_1.traverseNodes)(result.ast, {
|
|
57
57
|
visitorKeys: result.visitorKeys,
|
|
58
|
-
enterNode: (node) => {
|
|
58
|
+
enterNode: (node, p) => {
|
|
59
59
|
if (!traversed.has(node)) {
|
|
60
|
-
traversed.
|
|
60
|
+
traversed.set(node, p);
|
|
61
61
|
this.remapLocation(node);
|
|
62
62
|
}
|
|
63
63
|
},
|
|
@@ -78,8 +78,10 @@ class ScriptContext {
|
|
|
78
78
|
this.remapLocation(token);
|
|
79
79
|
}
|
|
80
80
|
let restoreNodeProcesses = this.restoreNodeProcesses;
|
|
81
|
-
for (const node of traversed) {
|
|
82
|
-
|
|
81
|
+
for (const [node, parent] of traversed) {
|
|
82
|
+
if (!parent)
|
|
83
|
+
continue;
|
|
84
|
+
restoreNodeProcesses = restoreNodeProcesses.filter((proc) => !proc(node, result, parent));
|
|
83
85
|
}
|
|
84
86
|
// Adjust program node location
|
|
85
87
|
const first = result.ast.body[0];
|
package/lib/errors.d.ts
CHANGED
package/lib/errors.js
CHANGED
|
@@ -31,7 +31,7 @@ const errors_1 = require("../../errors");
|
|
|
31
31
|
* Parse code by `@astrojs/compiler`
|
|
32
32
|
*/
|
|
33
33
|
function parse(code, ctx) {
|
|
34
|
-
const ast = parseByService(code).ast;
|
|
34
|
+
const ast = parseByService(code, ctx).ast;
|
|
35
35
|
const htmlElement = ast.children.find((n) => n.type === "element" && n.name === "html");
|
|
36
36
|
if (htmlElement) {
|
|
37
37
|
adjustHTML(ast, htmlElement, ctx);
|
|
@@ -43,13 +43,16 @@ exports.parse = parse;
|
|
|
43
43
|
/**
|
|
44
44
|
* Parse code by `@astrojs/compiler`
|
|
45
45
|
*/
|
|
46
|
-
function parseByService(code) {
|
|
46
|
+
function parseByService(code, ctx) {
|
|
47
47
|
const jsonAst = service.parse(code, { position: true }).ast;
|
|
48
|
+
ctx.originalAST = jsonAst;
|
|
48
49
|
try {
|
|
49
50
|
const ast = JSON.parse(jsonAst);
|
|
51
|
+
ctx.originalAST = ast;
|
|
50
52
|
return { ast };
|
|
51
53
|
}
|
|
52
54
|
catch (_a) {
|
|
55
|
+
// FIXME: Workaround for escape bugs
|
|
53
56
|
// Adjust because may get the wrong escape as JSON.
|
|
54
57
|
const ast = JSON.parse(jsonAst.replace(/\\./gu, (m) => {
|
|
55
58
|
try {
|
|
@@ -60,6 +63,7 @@ function parseByService(code) {
|
|
|
60
63
|
return `\\${m}`;
|
|
61
64
|
}
|
|
62
65
|
}));
|
|
66
|
+
ctx.originalAST = ast;
|
|
63
67
|
return { ast };
|
|
64
68
|
}
|
|
65
69
|
}
|
|
@@ -119,7 +123,7 @@ function fixLocations(node, ctx) {
|
|
|
119
123
|
let start = 0;
|
|
120
124
|
(0, astro_1.walk)(node, ctx.code,
|
|
121
125
|
// eslint-disable-next-line complexity -- X(
|
|
122
|
-
(node, parent) => {
|
|
126
|
+
(node, [parent]) => {
|
|
123
127
|
if (node.type === "frontmatter") {
|
|
124
128
|
start = node.position.start.offset = tokenIndex(ctx, "---", start);
|
|
125
129
|
start = node.position.end.offset =
|
|
@@ -155,7 +159,7 @@ function fixLocations(node, ctx) {
|
|
|
155
159
|
if (start < 0) {
|
|
156
160
|
start = ctx.code.length;
|
|
157
161
|
}
|
|
158
|
-
// Workaround for escape bugs
|
|
162
|
+
// FIXME: Workaround for escape bugs
|
|
159
163
|
node.value = ctx.code.slice(node.position.start.offset, start);
|
|
160
164
|
}
|
|
161
165
|
else {
|
|
@@ -165,24 +169,25 @@ function fixLocations(node, ctx) {
|
|
|
165
169
|
start += node.value.length;
|
|
166
170
|
}
|
|
167
171
|
else {
|
|
168
|
-
// Workaround for escape bugs
|
|
172
|
+
// FIXME: Workaround for escape bugs
|
|
169
173
|
node.position.start.offset = start;
|
|
170
|
-
|
|
171
|
-
|
|
174
|
+
const value = node.value.replace(/\s+/gu, "");
|
|
175
|
+
for (let charIndex = 0; charIndex < value.length; charIndex++) {
|
|
176
|
+
const char = value[charIndex];
|
|
177
|
+
const index = tokenIndexSafe(ctx.code, char, start);
|
|
172
178
|
if (index != null) {
|
|
173
|
-
start = index +
|
|
179
|
+
start = index + 1;
|
|
174
180
|
continue;
|
|
175
181
|
}
|
|
176
182
|
start = (0, astro_1.skipSpaces)(ctx.code, start);
|
|
177
|
-
let t = token;
|
|
178
183
|
if (ctx.code.startsWith("\\", start)) {
|
|
179
|
-
const
|
|
180
|
-
if (char.trim()) {
|
|
181
|
-
t = t.slice(1);
|
|
182
|
-
}
|
|
184
|
+
const codeChar = JSON.parse(`"\\${ctx.code[start + 1]}"`);
|
|
183
185
|
start += 2;
|
|
186
|
+
if (codeChar === char) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
184
189
|
}
|
|
185
|
-
start = tokenIndex(ctx,
|
|
190
|
+
start = tokenIndex(ctx, char, start) + 1;
|
|
186
191
|
}
|
|
187
192
|
start = (0, astro_1.skipSpaces)(ctx.code, start);
|
|
188
193
|
node.value = ctx.code.slice(node.position.start.offset, start);
|
|
@@ -208,25 +213,26 @@ function fixLocations(node, ctx) {
|
|
|
208
213
|
else if (node.type === "root") {
|
|
209
214
|
// noop
|
|
210
215
|
}
|
|
211
|
-
}, (node, parent) => {
|
|
216
|
+
}, (node, [parent]) => {
|
|
212
217
|
if (node.type === "attribute") {
|
|
213
218
|
const attributes = parent.attributes;
|
|
214
219
|
if (attributes[attributes.length - 1] === node) {
|
|
215
220
|
start = (0, astro_1.getStartTagEndOffset)(parent, ctx);
|
|
216
221
|
}
|
|
217
|
-
return;
|
|
218
222
|
}
|
|
219
|
-
if (node.type === "expression") {
|
|
223
|
+
else if (node.type === "expression") {
|
|
220
224
|
start = tokenIndex(ctx, "}", start) + 1;
|
|
221
225
|
}
|
|
222
226
|
else if (node.type === "fragment" ||
|
|
223
227
|
node.type === "element" ||
|
|
224
228
|
node.type === "component" ||
|
|
225
229
|
node.type === "custom-element") {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
+
if (!(0, astro_1.getSelfClosingTag)(node, parent, ctx)) {
|
|
231
|
+
const closeTagStart = tokenIndexSafe(ctx.code, `</${node.name}`, start);
|
|
232
|
+
if (closeTagStart != null) {
|
|
233
|
+
start = closeTagStart + 2 + node.name.length;
|
|
234
|
+
start = tokenIndex(ctx, ">", start) + 1;
|
|
235
|
+
}
|
|
230
236
|
}
|
|
231
237
|
}
|
|
232
238
|
else {
|
|
@@ -17,8 +17,9 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
17
17
|
script.appendScript("<>");
|
|
18
18
|
fragmentOpened = true;
|
|
19
19
|
}
|
|
20
|
+
(0, astro_1.walkElements)(resultTemplate.ast, ctx.code,
|
|
20
21
|
// eslint-disable-next-line complexity -- X(
|
|
21
|
-
(
|
|
22
|
+
(node, [parent]) => {
|
|
22
23
|
if (node.type === "frontmatter") {
|
|
23
24
|
const start = node.position.start.offset;
|
|
24
25
|
script.appendOriginal(start);
|
|
@@ -32,7 +33,8 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
32
33
|
for (let index = 0; index < result.ast.body.length; index++) {
|
|
33
34
|
const st = result.ast.body[index];
|
|
34
35
|
if (st.type === types_1.AST_NODE_TYPES.EmptyStatement) {
|
|
35
|
-
if (st.range[0] === end - 3 &&
|
|
36
|
+
if (st.range[0] === end - 3 &&
|
|
37
|
+
st.range[1] === end) {
|
|
36
38
|
result.ast.body.splice(index, 1);
|
|
37
39
|
break;
|
|
38
40
|
}
|
|
@@ -47,8 +49,42 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
47
49
|
script.addToken(types_1.AST_TOKEN_TYPES.Punctuator, [end - 3, end]);
|
|
48
50
|
}
|
|
49
51
|
else if ((0, astro_1.isTag)(node)) {
|
|
52
|
+
// Process for multiple tag
|
|
53
|
+
if (parent.type === "expression") {
|
|
54
|
+
const index = parent.children.indexOf(node);
|
|
55
|
+
const before = parent.children[index - 1];
|
|
56
|
+
if (!before || !(0, astro_1.isTag)(before)) {
|
|
57
|
+
const after = parent.children[index + 1];
|
|
58
|
+
if (after &&
|
|
59
|
+
((0, astro_1.isTag)(after) || after.type === "comment")) {
|
|
60
|
+
const start = node.position.start.offset;
|
|
61
|
+
script.appendOriginal(start);
|
|
62
|
+
script.appendScript("<>");
|
|
63
|
+
script.addRestoreNodeProcess((scriptNode) => {
|
|
64
|
+
if (scriptNode.range[0] === start &&
|
|
65
|
+
scriptNode.type ===
|
|
66
|
+
types_1.AST_NODE_TYPES.JSXFragment) {
|
|
67
|
+
delete scriptNode.openingFragment;
|
|
68
|
+
delete scriptNode.closingFragment;
|
|
69
|
+
const fragmentNode = scriptNode;
|
|
70
|
+
fragmentNode.type = "AstroFragment";
|
|
71
|
+
const last = fragmentNode.children[fragmentNode.children.length - 1];
|
|
72
|
+
if (fragmentNode.range[1] < last.range[1]) {
|
|
73
|
+
fragmentNode.range[1] = last.range[1];
|
|
74
|
+
fragmentNode.loc.end =
|
|
75
|
+
ctx.getLocFromIndex(fragmentNode.range[1]);
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Process for attributes
|
|
50
85
|
for (const attr of node.attributes) {
|
|
51
|
-
if ((node.type === "component" ||
|
|
86
|
+
if ((node.type === "component" ||
|
|
87
|
+
node.type === "fragment") &&
|
|
52
88
|
(attr.kind === "quoted" ||
|
|
53
89
|
attr.kind === "empty" ||
|
|
54
90
|
attr.kind === "expression" ||
|
|
@@ -76,7 +112,8 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
76
112
|
types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
77
113
|
scriptNode.range[0] === start) {
|
|
78
114
|
const baseNameNode = scriptNode.name;
|
|
79
|
-
const nsn = Object.assign(Object.assign({}, baseNameNode), { type: types_1.AST_NODE_TYPES.JSXNamespacedName, namespace: Object.assign({ type: types_1.AST_NODE_TYPES.JSXIdentifier, name: attr.name.slice(0, colonIndex) }, ctx.getLocations(baseNameNode.range[0], baseNameNode.range[0] +
|
|
115
|
+
const nsn = Object.assign(Object.assign({}, baseNameNode), { type: types_1.AST_NODE_TYPES.JSXNamespacedName, namespace: Object.assign({ type: types_1.AST_NODE_TYPES.JSXIdentifier, name: attr.name.slice(0, colonIndex) }, ctx.getLocations(baseNameNode.range[0], baseNameNode.range[0] +
|
|
116
|
+
colonIndex)), name: Object.assign({ type: types_1.AST_NODE_TYPES.JSXIdentifier, name: attr.name.slice(colonIndex + 1) }, ctx.getLocations(baseNameNode.range[0] +
|
|
80
117
|
colonIndex +
|
|
81
118
|
1, baseNameNode.range[1])) });
|
|
82
119
|
scriptNode.name = nsn;
|
|
@@ -87,7 +124,8 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
87
124
|
const token = tokens[index];
|
|
88
125
|
if (token.range[0] ===
|
|
89
126
|
baseNameNode.range[0] &&
|
|
90
|
-
token.range[1] ===
|
|
127
|
+
token.range[1] ===
|
|
128
|
+
baseNameNode.range[1]) {
|
|
91
129
|
tokens.splice(index, 1);
|
|
92
130
|
break;
|
|
93
131
|
}
|
|
@@ -106,7 +144,8 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
106
144
|
: attr.name;
|
|
107
145
|
script.appendScript(`${jsxName}=`);
|
|
108
146
|
script.addRestoreNodeProcess((scriptNode) => {
|
|
109
|
-
if (scriptNode.type ===
|
|
147
|
+
if (scriptNode.type ===
|
|
148
|
+
types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
110
149
|
scriptNode.range[0] === start) {
|
|
111
150
|
const attrNode = scriptNode;
|
|
112
151
|
attrNode.type = "AstroShorthandAttribute";
|
|
@@ -130,7 +169,8 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
130
169
|
script.appendOriginal(end);
|
|
131
170
|
script.appendScript("}");
|
|
132
171
|
script.addRestoreNodeProcess((scriptNode) => {
|
|
133
|
-
if (scriptNode.type ===
|
|
172
|
+
if (scriptNode.type ===
|
|
173
|
+
types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
134
174
|
scriptNode.range[0] === attrStart) {
|
|
135
175
|
const attrNode = scriptNode;
|
|
136
176
|
attrNode.type = "AstroTemplateLiteralAttribute";
|
|
@@ -140,11 +180,13 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
140
180
|
});
|
|
141
181
|
}
|
|
142
182
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
183
|
+
// Process for start tag close
|
|
184
|
+
const closing = (0, astro_1.getSelfClosingTag)(node, parent, ctx);
|
|
185
|
+
if (closing && closing.end === ">") {
|
|
186
|
+
script.appendOriginal(closing.offset - 1);
|
|
146
187
|
script.appendScript("/");
|
|
147
188
|
}
|
|
189
|
+
// Process for raw text
|
|
148
190
|
if (node.name === "script" || node.name === "style") {
|
|
149
191
|
const text = node.children[0];
|
|
150
192
|
if (text && text.type === "text") {
|
|
@@ -176,15 +218,17 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
176
218
|
script.appendOriginal(start);
|
|
177
219
|
let targetType;
|
|
178
220
|
if (fragmentOpened) {
|
|
179
|
-
script.
|
|
221
|
+
script.appendOriginal(start + 1);
|
|
222
|
+
script.appendScript(`></`);
|
|
223
|
+
script.skipOriginalOffset(length - 2);
|
|
180
224
|
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
181
225
|
}
|
|
182
226
|
else {
|
|
183
227
|
script.appendScript(`0;`);
|
|
184
228
|
targetType = types_1.AST_NODE_TYPES.ExpressionStatement;
|
|
229
|
+
script.skipOriginalOffset(length);
|
|
185
230
|
}
|
|
186
|
-
script.
|
|
187
|
-
script.addRestoreNodeProcess((scriptNode) => {
|
|
231
|
+
script.addRestoreNodeProcess((scriptNode, result) => {
|
|
188
232
|
if (scriptNode.range[0] === start &&
|
|
189
233
|
scriptNode.type === targetType) {
|
|
190
234
|
delete scriptNode.children;
|
|
@@ -194,6 +238,27 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
194
238
|
const commentNode = scriptNode;
|
|
195
239
|
commentNode.type = "AstroHTMLComment";
|
|
196
240
|
commentNode.value = node.value;
|
|
241
|
+
if (fragmentOpened) {
|
|
242
|
+
const removeTokenSet = new Set([
|
|
243
|
+
(token) => token.value === "<" &&
|
|
244
|
+
token.range[0] === scriptNode.range[0],
|
|
245
|
+
(token) => token.value === ">" &&
|
|
246
|
+
token.range[1] === scriptNode.range[1],
|
|
247
|
+
]);
|
|
248
|
+
const tokens = result.ast.tokens || [];
|
|
249
|
+
for (let index = tokens.length - 1; index >= 0; index--) {
|
|
250
|
+
const token = tokens[index];
|
|
251
|
+
for (const rt of removeTokenSet) {
|
|
252
|
+
if (rt(token)) {
|
|
253
|
+
tokens.splice(index, 1);
|
|
254
|
+
removeTokenSet.delete(rt);
|
|
255
|
+
if (!removeTokenSet.size) {
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
197
262
|
return true;
|
|
198
263
|
}
|
|
199
264
|
return false;
|
|
@@ -232,6 +297,45 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
232
297
|
});
|
|
233
298
|
script.addToken("HTMLDocType", [start, end]);
|
|
234
299
|
}
|
|
300
|
+
}, (node, [parent]) => {
|
|
301
|
+
if ((0, astro_1.isTag)(node)) {
|
|
302
|
+
const closing = (0, astro_1.getSelfClosingTag)(node, parent, ctx);
|
|
303
|
+
if (!closing) {
|
|
304
|
+
const end = (0, astro_1.getEndTag)(node, ctx);
|
|
305
|
+
if (!end) {
|
|
306
|
+
const offset = (0, astro_1.getContentEndOffset)(node, ctx);
|
|
307
|
+
script.appendOriginal(offset);
|
|
308
|
+
script.appendScript(`</${node.name}>`);
|
|
309
|
+
script.addRestoreNodeProcess((scriptNode, _result, parent) => {
|
|
310
|
+
if (scriptNode.range[0] === offset &&
|
|
311
|
+
scriptNode.type ===
|
|
312
|
+
types_1.AST_NODE_TYPES.JSXClosingElement &&
|
|
313
|
+
parent.type === types_1.AST_NODE_TYPES.JSXElement) {
|
|
314
|
+
parent.closingElement = null;
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
return false;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Process for multiple tag
|
|
323
|
+
if (((0, astro_1.isTag)(node) || node.type === "comment") &&
|
|
324
|
+
parent.type === "expression") {
|
|
325
|
+
const index = parent.children.indexOf(node);
|
|
326
|
+
const after = parent.children[index + 1];
|
|
327
|
+
if (!after || (!(0, astro_1.isTag)(after) && after.type !== "comment")) {
|
|
328
|
+
const before = parent.children[index - 1];
|
|
329
|
+
if (before &&
|
|
330
|
+
((0, astro_1.isTag)(before) || before.type === "comment")) {
|
|
331
|
+
const end = (0, astro_1.isTag)(node)
|
|
332
|
+
? (0, astro_1.getTagEndOffset)(node, ctx)
|
|
333
|
+
: (0, astro_1.getCommentEndOffset)(node, ctx);
|
|
334
|
+
script.appendOriginal(end);
|
|
335
|
+
script.appendScript("</>");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
235
339
|
});
|
|
236
340
|
script.appendOriginal(ctx.code.length);
|
|
237
341
|
script.appendScript("</>");
|
|
@@ -249,36 +353,3 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
249
353
|
}
|
|
250
354
|
}
|
|
251
355
|
exports.processTemplate = processTemplate;
|
|
252
|
-
/**
|
|
253
|
-
* If the given tag is a void tag, get the self-closing tag.
|
|
254
|
-
*/
|
|
255
|
-
function getVoidSelfClosingTag(node, parent, ctx) {
|
|
256
|
-
var _a;
|
|
257
|
-
const children = node.children.filter((c) => c.type !== "text" || c.value.trim());
|
|
258
|
-
if (children.length > 0) {
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
const code = ctx.code;
|
|
262
|
-
let nextElementIndex = code.length;
|
|
263
|
-
const childIndex = parent.children.indexOf(node);
|
|
264
|
-
if (childIndex === parent.children.length - 1) {
|
|
265
|
-
// last
|
|
266
|
-
if ((_a = parent.position) === null || _a === void 0 ? void 0 : _a.end) {
|
|
267
|
-
nextElementIndex = parent.position.end.offset;
|
|
268
|
-
nextElementIndex = code.lastIndexOf("</", nextElementIndex);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
const next = parent.children[childIndex + 1];
|
|
273
|
-
nextElementIndex = next.position.start.offset;
|
|
274
|
-
}
|
|
275
|
-
const endOffset = (0, astro_1.getStartTagEndOffset)(node, ctx);
|
|
276
|
-
if (code.slice(endOffset, nextElementIndex).trim()) {
|
|
277
|
-
// has end tag
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
return {
|
|
281
|
-
offset: endOffset,
|
|
282
|
-
end: code.slice(endOffset - 2, endOffset) === "/>" ? "/>" : ">",
|
|
283
|
-
};
|
|
284
|
-
}
|
package/lib/parser/script.js
CHANGED
|
@@ -30,7 +30,9 @@ function parseScript(code, ctx) {
|
|
|
30
30
|
return { ast: result };
|
|
31
31
|
}
|
|
32
32
|
catch (e) {
|
|
33
|
-
(0, debug_1.debug)("[script] parsing error:", e.message, `@ ${JSON.stringify(code)}
|
|
33
|
+
(0, debug_1.debug)("[script] parsing error:", e.message, `@ ${JSON.stringify(code)}
|
|
34
|
+
|
|
35
|
+
${code}`);
|
|
34
36
|
throw e;
|
|
35
37
|
}
|
|
36
38
|
finally {
|
package/lib/visitor-keys.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-eslint-parser",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Astro parser for ESLint",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"cover": "nyc --reporter=lcov npm run test",
|
|
20
20
|
"debug": "mocha --require ts-node/register/transpile-only \"tests/src/**/*.ts\" --reporter dot --timeout 60000",
|
|
21
21
|
"preversion": "npm run lint && npm test",
|
|
22
|
-
"update-fixtures": "ts-node --transpile-only ./tools/update-fixtures.ts",
|
|
22
|
+
"update-fixtures": "DEBUG='astro-eslint-parser' ts-node --transpile-only ./tools/update-fixtures.ts",
|
|
23
23
|
"debug-parser": "ts-node --transpile-only ./tools/parser-test.ts",
|
|
24
24
|
"eslint-playground": "eslint tests/fixtures --ext .astro --config .eslintrc-for-playground.js --format codeframe",
|
|
25
25
|
"benchmark": "ts-node --transpile-only benchmark/index.ts"
|