astro-eslint-parser 0.0.14 → 0.0.17
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/README.md +33 -21
- package/lib/ast.d.ts +11 -16
- package/lib/astro/index.d.ts +13 -17
- package/lib/astro/index.js +94 -109
- package/lib/context/script.d.ts +8 -1
- package/lib/context/script.js +43 -18
- package/lib/parser/astro-parser/parse.js +24 -10
- package/lib/parser/process-template.js +85 -81
- package/lib/parser/script.js +3 -1
- package/lib/visitor-keys.js +1 -2
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# astro-eslint-parser
|
|
2
2
|
|
|
3
|
-
[Astro] parser for [ESLint].
|
|
3
|
+
[Astro] component parser for [ESLint].
|
|
4
4
|
You can check it on [Online DEMO](https://ota-meshi.github.io/astro-eslint-parser/playground).
|
|
5
5
|
|
|
6
|
+
[](https://github.com/sponsors/ota-meshi)
|
|
7
|
+
|
|
6
8
|
[](https://www.npmjs.com/package/astro-eslint-parser)
|
|
7
9
|
[](https://www.npmjs.com/package/astro-eslint-parser)
|
|
8
10
|
[](http://www.npmtrends.com/astro-eslint-parser)
|
|
@@ -15,6 +17,8 @@ You can check it on [Online DEMO](https://ota-meshi.github.io/astro-eslint-parse
|
|
|
15
17
|
|
|
16
18
|
This parser is in the ***experimental stages*** of development.
|
|
17
19
|
|
|
20
|
+
At least it works fine with a [fork of the `astro.build` repository](https://github.com/ota-meshi/astro.build/tree/eslint).
|
|
21
|
+
|
|
18
22
|
⚠ Currently this parser relies heavily on the internal API of [@astrojs/compiler]. It may stop working in a future update of [@astrojs/compiler]. ⚠
|
|
19
23
|
|
|
20
24
|
[@astrojs/compiler]: https://github.com/withastro/compiler
|
|
@@ -37,25 +41,28 @@ npm install --save-dev eslint astro-eslint-parser
|
|
|
37
41
|
## 📖 Usage
|
|
38
42
|
|
|
39
43
|
1. Write `overrides.parser` option into your `.eslintrc.*` file.
|
|
40
|
-
2. Use glob patterns or `--ext .astro` CLI option.
|
|
41
44
|
|
|
42
|
-
```json
|
|
43
|
-
{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
```
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"extends": "eslint:recommended",
|
|
48
|
+
"overrides": [
|
|
49
|
+
{
|
|
50
|
+
"files": ["*.astro"],
|
|
51
|
+
"parser": "astro-eslint-parser"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
$ eslint src
|
|
58
|
-
|
|
57
|
+
2. If you have specified the extension in the CLI, add `.astro` as well.
|
|
58
|
+
|
|
59
|
+
```console
|
|
60
|
+
$ eslint "src/**/*.{js,astro}"
|
|
61
|
+
# or
|
|
62
|
+
$ eslint src --ext .js,.astro
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The commit diff [here](https://github.com/ota-meshi/astro.build/commit/7f291ac15e6d97cc20a64b8f97dcbd85379759b5) is an example of introducing this parser to the `astro.build` repository.
|
|
59
66
|
|
|
60
67
|
## 🔧 Options
|
|
61
68
|
|
|
@@ -138,17 +145,16 @@ Example **.vscode/settings.json**:
|
|
|
138
145
|
}
|
|
139
146
|
```
|
|
140
147
|
|
|
141
|
-
## Compatibility With Existing ESLint Rules
|
|
148
|
+
## :two_hearts: Compatibility With Existing ESLint Rules
|
|
142
149
|
|
|
143
150
|
Most of the rules in the ESLint core work for the script part, but some rules are incompatible.
|
|
144
151
|
This parser will generate a JSX compatible AST for most of the HTML part of the Astro component. Therefore, some rules of [eslint-plugin-react] may work.
|
|
145
152
|
For example, the [react/jsx-no-target-blank] rule works fine.
|
|
146
153
|
|
|
147
|
-
[semi]: https://eslint.org/docs/rules/semi
|
|
148
154
|
[eslint-plugin-react]: https://github.com/jsx-eslint/eslint-plugin-react/
|
|
149
155
|
[react/jsx-no-target-blank]: https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md
|
|
150
156
|
|
|
151
|
-
## Usage for Custom Rules / Plugins
|
|
157
|
+
## :hammer: Usage for Custom Rules / Plugins
|
|
152
158
|
|
|
153
159
|
- TBA
|
|
154
160
|
- You can check the AST in the [Online DEMO](https://ota-meshi.github.io/astro-eslint-parser/). However, AST is subject to major changes in the future.
|
|
@@ -162,6 +168,12 @@ Welcome contributing!
|
|
|
162
168
|
|
|
163
169
|
Please use GitHub's Issues/PRs.
|
|
164
170
|
|
|
171
|
+
## :heart: Supporting
|
|
172
|
+
|
|
173
|
+
If you are willing to see that this package continues to be maintained, please consider sponsoring me.
|
|
174
|
+
|
|
175
|
+
[](https://github.com/sponsors/ota-meshi)
|
|
176
|
+
|
|
165
177
|
## :lock: License
|
|
166
178
|
|
|
167
179
|
See the [LICENSE](LICENSE) file for license rights and limitations (MIT).
|
package/lib/ast.d.ts
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
import type { TSESTree } from "@typescript-eslint/types";
|
|
2
|
-
export declare type AstroNode = AstroProgram |
|
|
2
|
+
export declare type AstroNode = AstroProgram | AstroFragment | AstroHTMLComment | AstroDoctype | AstroShorthandAttribute | AstroTemplateLiteralAttribute | AstroRawText;
|
|
3
|
+
export declare type AstroChild = TSESTree.JSXFragment["children"][number] | AstroHTMLComment;
|
|
3
4
|
/** Node of Astro program root */
|
|
4
5
|
export interface AstroProgram extends Omit<TSESTree.Program, "type" | "body"> {
|
|
5
6
|
type: "Program";
|
|
6
|
-
body: (TSESTree.Program["body"][number] |
|
|
7
|
+
body: (TSESTree.Program["body"][number] | AstroFragment)[];
|
|
7
8
|
sourceType: "script" | "module";
|
|
8
9
|
comments: TSESTree.Comment[];
|
|
9
10
|
tokens: TSESTree.Token[];
|
|
10
11
|
parent: undefined;
|
|
11
12
|
}
|
|
12
|
-
/** Node of Astro fragment
|
|
13
|
-
export interface
|
|
14
|
-
type: "
|
|
15
|
-
children:
|
|
16
|
-
parent:
|
|
13
|
+
/** Node of Astro fragment */
|
|
14
|
+
export interface AstroFragment extends Omit<TSESTree.BaseNode, "type" | "parent"> {
|
|
15
|
+
type: "AstroFragment";
|
|
16
|
+
children: AstroChild[];
|
|
17
|
+
parent: TSESTree.JSXFragment["parent"];
|
|
17
18
|
}
|
|
18
19
|
/** Node of Astro html comment */
|
|
19
20
|
export interface AstroHTMLComment extends Omit<TSESTree.BaseNode, "type" | "parent"> {
|
|
20
21
|
type: "AstroHTMLComment";
|
|
21
22
|
value: string;
|
|
22
|
-
parent:
|
|
23
|
+
parent: AstroFragment | AstroFragment | TSESTree.JSXElement | TSESTree.JSXFragment;
|
|
23
24
|
}
|
|
24
25
|
/** Node of Astro doctype */
|
|
25
26
|
export interface AstroDoctype extends Omit<TSESTree.BaseNode, "type" | "parent"> {
|
|
26
27
|
type: "AstroDoctype";
|
|
27
|
-
parent:
|
|
28
|
+
parent: AstroFragment | AstroFragment;
|
|
28
29
|
}
|
|
29
30
|
/** Node of Astro shorthand attribute */
|
|
30
31
|
export interface AstroShorthandAttribute extends Omit<TSESTree.JSXAttribute, "type" | "parent"> {
|
|
@@ -43,11 +44,5 @@ export interface AstroTemplateLiteralAttribute extends Omit<TSESTree.JSXAttribut
|
|
|
43
44
|
/** Node of Astro raw text */
|
|
44
45
|
export interface AstroRawText extends Omit<TSESTree.JSXText, "type" | "parent"> {
|
|
45
46
|
type: "AstroRawText";
|
|
46
|
-
parent:
|
|
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"];
|
|
47
|
+
parent: AstroFragment | TSESTree.JSXElement | TSESTree.JSXFragment;
|
|
53
48
|
}
|
package/lib/astro/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AttributeNode, CommentNode,
|
|
1
|
+
import type { AttributeNode, CommentNode, 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
|
|
@@ -15,45 +15,41 @@ export declare function walk(parent: ParentNode, code: string, enter: (n: Node |
|
|
|
15
15
|
/**
|
|
16
16
|
* Get end offset of start tag
|
|
17
17
|
*/
|
|
18
|
-
export declare function
|
|
19
|
-
/**
|
|
20
|
-
* Get end offset of tag
|
|
21
|
-
*/
|
|
22
|
-
export declare function getTagEndOffset(node: TagLikeNode, parents: ParentNode[], ctx: Context): number;
|
|
23
|
-
/**
|
|
24
|
-
* Get end offset of Expression
|
|
25
|
-
*/
|
|
26
|
-
export declare function getExpressionEndOffset(node: ExpressionNode, parents: ParentNode[], ctx: Context): number;
|
|
18
|
+
export declare function calcStartTagEndOffset(node: TagLikeNode, ctx: Context): number;
|
|
27
19
|
/**
|
|
28
20
|
* Get end offset of attribute
|
|
29
21
|
*/
|
|
30
|
-
export declare function
|
|
22
|
+
export declare function calcAttributeEndOffset(node: AttributeNode, ctx: Context): number;
|
|
31
23
|
/**
|
|
32
24
|
* Get start offset of attribute value
|
|
33
25
|
*/
|
|
34
|
-
export declare function
|
|
26
|
+
export declare function calcAttributeValueStartOffset(node: AttributeNode, ctx: Context): number;
|
|
35
27
|
/**
|
|
36
|
-
* Get end offset of
|
|
28
|
+
* Get end offset of tag
|
|
37
29
|
*/
|
|
38
|
-
export declare function
|
|
30
|
+
export declare function getEndOffset(node: Node, ctx: Context): number;
|
|
39
31
|
/**
|
|
40
32
|
* Get content end offset
|
|
41
33
|
*/
|
|
42
|
-
export declare function
|
|
34
|
+
export declare function calcContentEndOffset(parent: ParentNode, ctx: Context): number;
|
|
43
35
|
/**
|
|
44
36
|
* If the given tag is a self-close tag, get the self-closing tag.
|
|
45
37
|
*/
|
|
46
|
-
export declare function getSelfClosingTag(node: TagLikeNode,
|
|
38
|
+
export declare function getSelfClosingTag(node: TagLikeNode, ctx: Context): null | {
|
|
47
39
|
offset: number;
|
|
48
40
|
end: "/>" | ">";
|
|
49
41
|
};
|
|
50
42
|
/**
|
|
51
43
|
* If the given tag has a end tag, get the end tag.
|
|
52
44
|
*/
|
|
53
|
-
export declare function getEndTag(node: TagLikeNode,
|
|
45
|
+
export declare function getEndTag(node: TagLikeNode, ctx: Context): null | {
|
|
54
46
|
offset: number;
|
|
55
47
|
tag: string;
|
|
56
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* Get end offset of comment
|
|
51
|
+
*/
|
|
52
|
+
export declare function calcCommentEndOffset(node: CommentNode, ctx: Context): number;
|
|
57
53
|
/**
|
|
58
54
|
* Skip spaces
|
|
59
55
|
*/
|
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.
|
|
3
|
+
exports.skipSpaces = exports.calcCommentEndOffset = exports.getEndTag = exports.getSelfClosingTag = exports.calcContentEndOffset = exports.getEndOffset = exports.calcAttributeValueStartOffset = exports.calcAttributeEndOffset = exports.calcStartTagEndOffset = 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
|
|
@@ -49,11 +49,11 @@ exports.walk = walk;
|
|
|
49
49
|
/**
|
|
50
50
|
* Get end offset of start tag
|
|
51
51
|
*/
|
|
52
|
-
function
|
|
52
|
+
function calcStartTagEndOffset(node, ctx) {
|
|
53
53
|
const lastAttr = node.attributes[node.attributes.length - 1];
|
|
54
54
|
let beforeCloseIndex;
|
|
55
55
|
if (lastAttr) {
|
|
56
|
-
beforeCloseIndex =
|
|
56
|
+
beforeCloseIndex = calcAttributeEndOffset(lastAttr, ctx);
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
59
|
const info = getTokenInfo(ctx, [`<${node.name}`], node.position.start.offset);
|
|
@@ -62,63 +62,20 @@ function getStartTagEndOffset(node, ctx) {
|
|
|
62
62
|
const info = getTokenInfo(ctx, [[">", "/>"]], beforeCloseIndex);
|
|
63
63
|
return info.index + info.match.length;
|
|
64
64
|
}
|
|
65
|
-
exports.
|
|
66
|
-
/**
|
|
67
|
-
* Get end offset of tag
|
|
68
|
-
*/
|
|
69
|
-
function getTagEndOffset(node, parents, 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, [node, ...parents], 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, parents, 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, [node, ...parents], 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;
|
|
65
|
+
exports.calcStartTagEndOffset = calcStartTagEndOffset;
|
|
109
66
|
/**
|
|
110
67
|
* Get end offset of attribute
|
|
111
68
|
*/
|
|
112
|
-
function
|
|
69
|
+
function calcAttributeEndOffset(node, ctx) {
|
|
113
70
|
let info;
|
|
114
71
|
if (node.kind === "empty") {
|
|
115
72
|
info = getTokenInfo(ctx, [node.name], node.position.start.offset);
|
|
116
73
|
}
|
|
117
74
|
else if (node.kind === "quoted") {
|
|
118
|
-
info = getTokenInfo(ctx, [[`"${node.value}"`, `'${node.value}'`, node.value]],
|
|
75
|
+
info = getTokenInfo(ctx, [[`"${node.value}"`, `'${node.value}'`, node.value]], calcAttributeValueStartOffset(node, ctx));
|
|
119
76
|
}
|
|
120
77
|
else if (node.kind === "expression") {
|
|
121
|
-
info = getTokenInfo(ctx, ["{", node.value, "}"],
|
|
78
|
+
info = getTokenInfo(ctx, ["{", node.value, "}"], calcAttributeValueStartOffset(node, ctx));
|
|
122
79
|
}
|
|
123
80
|
else if (node.kind === "shorthand") {
|
|
124
81
|
info = getTokenInfo(ctx, ["{", node.name, "}"], node.position.start.offset);
|
|
@@ -127,18 +84,18 @@ function getAttributeEndOffset(node, ctx) {
|
|
|
127
84
|
info = getTokenInfo(ctx, ["{", "...", node.name, "}"], node.position.start.offset);
|
|
128
85
|
}
|
|
129
86
|
else if (node.kind === "template-literal") {
|
|
130
|
-
info = getTokenInfo(ctx, [`\`${node.value}\``],
|
|
87
|
+
info = getTokenInfo(ctx, [`\`${node.value}\``], calcAttributeValueStartOffset(node, ctx));
|
|
131
88
|
}
|
|
132
89
|
else {
|
|
133
90
|
throw new errors_1.ParseError(`Unknown attr kind: ${node.kind}`, node.position.start.offset, ctx);
|
|
134
91
|
}
|
|
135
92
|
return info.index + info.match.length;
|
|
136
93
|
}
|
|
137
|
-
exports.
|
|
94
|
+
exports.calcAttributeEndOffset = calcAttributeEndOffset;
|
|
138
95
|
/**
|
|
139
96
|
* Get start offset of attribute value
|
|
140
97
|
*/
|
|
141
|
-
function
|
|
98
|
+
function calcAttributeValueStartOffset(node, ctx) {
|
|
142
99
|
let info;
|
|
143
100
|
if (node.kind === "quoted") {
|
|
144
101
|
info = getTokenInfo(ctx, [node.name, "=", [`"`, `'`, node.value]], node.position.start.offset);
|
|
@@ -154,33 +111,58 @@ function getAttributeValueStartOffset(node, ctx) {
|
|
|
154
111
|
}
|
|
155
112
|
return info.index;
|
|
156
113
|
}
|
|
157
|
-
exports.
|
|
114
|
+
exports.calcAttributeValueStartOffset = calcAttributeValueStartOffset;
|
|
158
115
|
/**
|
|
159
|
-
* Get end offset of
|
|
116
|
+
* Get end offset of tag
|
|
160
117
|
*/
|
|
161
|
-
function
|
|
162
|
-
|
|
163
|
-
|
|
118
|
+
function getEndOffset(node, ctx) {
|
|
119
|
+
var _a;
|
|
120
|
+
if (((_a = node.position.end) === null || _a === void 0 ? void 0 : _a.offset) != null) {
|
|
121
|
+
return node.position.end.offset;
|
|
122
|
+
}
|
|
123
|
+
if (isTag(node))
|
|
124
|
+
return calcTagEndOffset(node, ctx);
|
|
125
|
+
if (node.type === "expression")
|
|
126
|
+
return calcExpressionEndOffset(node, ctx);
|
|
127
|
+
if (node.type === "comment")
|
|
128
|
+
return calcCommentEndOffset(node, ctx);
|
|
129
|
+
if (node.type === "frontmatter") {
|
|
130
|
+
const start = node.position.start.offset;
|
|
131
|
+
return ctx.code.indexOf("---", start + 3) + 3;
|
|
132
|
+
}
|
|
133
|
+
if (node.type === "doctype") {
|
|
134
|
+
const start = node.position.start.offset;
|
|
135
|
+
return ctx.code.indexOf(">", start) + 1;
|
|
136
|
+
}
|
|
137
|
+
if (node.type === "text") {
|
|
138
|
+
const start = node.position.start.offset;
|
|
139
|
+
return start + node.value.length;
|
|
140
|
+
}
|
|
141
|
+
if (node.type === "root") {
|
|
142
|
+
return ctx.code.length;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(`unknown type: ${node.type}`);
|
|
164
145
|
}
|
|
165
|
-
exports.
|
|
146
|
+
exports.getEndOffset = getEndOffset;
|
|
166
147
|
/**
|
|
167
148
|
* Get content end offset
|
|
168
149
|
*/
|
|
169
|
-
function
|
|
150
|
+
function calcContentEndOffset(parent, ctx) {
|
|
170
151
|
const code = ctx.code;
|
|
171
152
|
if (isTag(parent)) {
|
|
172
|
-
const end =
|
|
153
|
+
const end = getEndOffset(parent, ctx);
|
|
173
154
|
if (code[end - 1] !== ">") {
|
|
174
155
|
return end;
|
|
175
156
|
}
|
|
176
|
-
const index = code.lastIndexOf("</", end);
|
|
177
|
-
if (index >= 0 &&
|
|
157
|
+
const index = code.lastIndexOf("</", end - 1);
|
|
158
|
+
if (index >= 0 &&
|
|
159
|
+
code.slice(index + 2, end - 1).trim() === parent.name) {
|
|
178
160
|
return index;
|
|
179
161
|
}
|
|
180
162
|
return end;
|
|
181
163
|
}
|
|
182
164
|
else if (parent.type === "expression") {
|
|
183
|
-
const end =
|
|
165
|
+
const end = getEndOffset(parent, ctx);
|
|
184
166
|
return code.lastIndexOf("}", end);
|
|
185
167
|
}
|
|
186
168
|
else if (parent.type === "root") {
|
|
@@ -188,49 +170,42 @@ function getContentEndOffset(parent, parents, ctx) {
|
|
|
188
170
|
}
|
|
189
171
|
throw new Error(`unknown type: ${parent.type}`);
|
|
190
172
|
}
|
|
191
|
-
exports.
|
|
173
|
+
exports.calcContentEndOffset = calcContentEndOffset;
|
|
192
174
|
/**
|
|
193
175
|
* If the given tag is a self-close tag, get the self-closing tag.
|
|
194
176
|
*/
|
|
195
|
-
function getSelfClosingTag(node,
|
|
196
|
-
|
|
197
|
-
if (children.length > 0) {
|
|
177
|
+
function getSelfClosingTag(node, ctx) {
|
|
178
|
+
if (node.children.length > 0) {
|
|
198
179
|
return null;
|
|
199
180
|
}
|
|
200
|
-
const parent = parents[0];
|
|
201
181
|
const code = ctx.code;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
else {
|
|
209
|
-
const next = parent.children[childIndex + 1];
|
|
210
|
-
nextElementIndex = next.position.start.offset;
|
|
182
|
+
const startTagEndOffset = calcStartTagEndOffset(node, ctx);
|
|
183
|
+
if (code.startsWith("/>", startTagEndOffset - 2)) {
|
|
184
|
+
return {
|
|
185
|
+
offset: startTagEndOffset,
|
|
186
|
+
end: "/>",
|
|
187
|
+
};
|
|
211
188
|
}
|
|
212
|
-
|
|
213
|
-
if (code.slice(endOffset, nextElementIndex).trim()) {
|
|
214
|
-
// has end tag
|
|
189
|
+
if (code.startsWith(`</${node.name}`, startTagEndOffset)) {
|
|
215
190
|
return null;
|
|
216
191
|
}
|
|
217
192
|
return {
|
|
218
|
-
offset:
|
|
219
|
-
end:
|
|
193
|
+
offset: startTagEndOffset,
|
|
194
|
+
end: ">",
|
|
220
195
|
};
|
|
221
196
|
}
|
|
222
197
|
exports.getSelfClosingTag = getSelfClosingTag;
|
|
223
198
|
/**
|
|
224
199
|
* If the given tag has a end tag, get the end tag.
|
|
225
200
|
*/
|
|
226
|
-
function getEndTag(node,
|
|
201
|
+
function getEndTag(node, ctx) {
|
|
227
202
|
let beforeIndex;
|
|
228
203
|
if (node.children.length) {
|
|
229
204
|
const lastChild = node.children[node.children.length - 1];
|
|
230
|
-
beforeIndex = getEndOffset(lastChild,
|
|
205
|
+
beforeIndex = getEndOffset(lastChild, ctx);
|
|
231
206
|
}
|
|
232
207
|
else {
|
|
233
|
-
beforeIndex =
|
|
208
|
+
beforeIndex = calcStartTagEndOffset(node, ctx);
|
|
234
209
|
}
|
|
235
210
|
beforeIndex = skipSpaces(ctx.code, beforeIndex);
|
|
236
211
|
if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) {
|
|
@@ -246,36 +221,46 @@ function getEndTag(node, parents, ctx) {
|
|
|
246
221
|
return null;
|
|
247
222
|
}
|
|
248
223
|
exports.getEndTag = getEndTag;
|
|
224
|
+
/**
|
|
225
|
+
* Get end offset of comment
|
|
226
|
+
*/
|
|
227
|
+
function calcCommentEndOffset(node, ctx) {
|
|
228
|
+
const info = getTokenInfo(ctx, ["<!--", node.value, "-->"], node.position.start.offset);
|
|
229
|
+
return info.index + info.match.length;
|
|
230
|
+
}
|
|
231
|
+
exports.calcCommentEndOffset = calcCommentEndOffset;
|
|
249
232
|
/**
|
|
250
233
|
* Get end offset of tag
|
|
251
234
|
*/
|
|
252
|
-
function
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (isTag(node))
|
|
258
|
-
return getTagEndOffset(node, parents, ctx);
|
|
259
|
-
if (node.type === "expression")
|
|
260
|
-
return getExpressionEndOffset(node, parents, ctx);
|
|
261
|
-
if (node.type === "comment")
|
|
262
|
-
return getCommentEndOffset(node, ctx);
|
|
263
|
-
if (node.type === "frontmatter") {
|
|
264
|
-
const start = node.position.start.offset;
|
|
265
|
-
return ctx.code.indexOf("---", start + 3) + 3;
|
|
235
|
+
function calcTagEndOffset(node, ctx) {
|
|
236
|
+
let beforeIndex;
|
|
237
|
+
if (node.children.length) {
|
|
238
|
+
const lastChild = node.children[node.children.length - 1];
|
|
239
|
+
beforeIndex = getEndOffset(lastChild, ctx);
|
|
266
240
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return ctx.code.indexOf(">", start) + 1;
|
|
241
|
+
else {
|
|
242
|
+
beforeIndex = calcStartTagEndOffset(node, ctx);
|
|
270
243
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
244
|
+
beforeIndex = skipSpaces(ctx.code, beforeIndex);
|
|
245
|
+
if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) {
|
|
246
|
+
beforeIndex = beforeIndex + 2 + node.name.length;
|
|
247
|
+
const info = getTokenInfo(ctx, [">"], beforeIndex);
|
|
248
|
+
return info.index + info.match.length;
|
|
274
249
|
}
|
|
275
|
-
|
|
276
|
-
|
|
250
|
+
return beforeIndex;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get end offset of Expression
|
|
254
|
+
*/
|
|
255
|
+
function calcExpressionEndOffset(node, ctx) {
|
|
256
|
+
if (node.children.length) {
|
|
257
|
+
const lastChild = node.children[node.children.length - 1];
|
|
258
|
+
const beforeIndex = getEndOffset(lastChild, ctx);
|
|
259
|
+
const info = getTokenInfo(ctx, ["}"], beforeIndex);
|
|
260
|
+
return info.index + info.match.length;
|
|
277
261
|
}
|
|
278
|
-
|
|
262
|
+
const info = getTokenInfo(ctx, ["{", "}"], node.position.start.offset);
|
|
263
|
+
return info.index + info.match.length;
|
|
279
264
|
}
|
|
280
265
|
/**
|
|
281
266
|
* Get token info
|
package/lib/context/script.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { Context } from ".";
|
|
2
2
|
import type { ESLintExtendedProgram } from "../parser";
|
|
3
3
|
import type { TSESTree } from "@typescript-eslint/types";
|
|
4
|
+
declare class RestoreNodeProcessContext {
|
|
5
|
+
readonly result: ESLintExtendedProgram;
|
|
6
|
+
readonly removeTokens: Set<(token: TSESTree.Token) => boolean>;
|
|
7
|
+
constructor(result: ESLintExtendedProgram);
|
|
8
|
+
addRemoveToken(test: (token: TSESTree.Token) => boolean): void;
|
|
9
|
+
}
|
|
4
10
|
export declare class ScriptContext {
|
|
5
11
|
private readonly ctx;
|
|
6
12
|
script: string;
|
|
@@ -14,7 +20,7 @@ export declare class ScriptContext {
|
|
|
14
20
|
appendOriginal(index: number): void;
|
|
15
21
|
appendScript(fragment: string): void;
|
|
16
22
|
addToken(type: TSESTree.Token["type"], range: TSESTree.Range): void;
|
|
17
|
-
addRestoreNodeProcess(process: (node: TSESTree.Node,
|
|
23
|
+
addRestoreNodeProcess(process: (node: TSESTree.Node, context: RestoreNodeProcessContext, parent: TSESTree.Node) => boolean): void;
|
|
18
24
|
/**
|
|
19
25
|
* Restore AST nodes
|
|
20
26
|
*/
|
|
@@ -22,3 +28,4 @@ export declare class ScriptContext {
|
|
|
22
28
|
private remapLocation;
|
|
23
29
|
private getRemapRange;
|
|
24
30
|
}
|
|
31
|
+
export {};
|
package/lib/context/script.js
CHANGED
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ScriptContext = void 0;
|
|
4
4
|
const traverse_1 = require("../traverse");
|
|
5
|
-
|
|
5
|
+
class RestoreNodeProcessContext {
|
|
6
|
+
constructor(result) {
|
|
7
|
+
this.removeTokens = new Set();
|
|
8
|
+
this.result = result;
|
|
9
|
+
}
|
|
10
|
+
addRemoveToken(test) {
|
|
11
|
+
this.removeTokens.add(test);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
6
14
|
class ScriptContext {
|
|
7
15
|
constructor(ctx) {
|
|
8
16
|
this.script = "";
|
|
@@ -17,6 +25,9 @@ class ScriptContext {
|
|
|
17
25
|
this.consumedIndex += offset;
|
|
18
26
|
}
|
|
19
27
|
appendOriginal(index) {
|
|
28
|
+
if (this.consumedIndex >= index) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
20
31
|
this.offsets.push({
|
|
21
32
|
original: this.consumedIndex,
|
|
22
33
|
script: this.script.length,
|
|
@@ -39,18 +50,7 @@ class ScriptContext {
|
|
|
39
50
|
* Restore AST nodes
|
|
40
51
|
*/
|
|
41
52
|
restore(result) {
|
|
42
|
-
|
|
43
|
-
if (last.type !== "ExpressionStatement") {
|
|
44
|
-
throw new errors_1.ParseError("Unknown state error: Expected ExpressionStatement", last.range[0], this.ctx);
|
|
45
|
-
}
|
|
46
|
-
if (last.expression.type !== "JSXFragment") {
|
|
47
|
-
throw new errors_1.ParseError("Unknown state error: Expected JSXFragment", last.expression.range[0], this.ctx);
|
|
48
|
-
}
|
|
49
|
-
// Process for Astro
|
|
50
|
-
const rootFragment = (result.ast.body[result.ast.body.length - 1] = last.expression);
|
|
51
|
-
delete rootFragment.closingFragment;
|
|
52
|
-
delete rootFragment.openingFragment;
|
|
53
|
-
rootFragment.type = "AstroRootFragment";
|
|
53
|
+
var _a, _b;
|
|
54
54
|
// remap locations
|
|
55
55
|
const traversed = new Map();
|
|
56
56
|
(0, traverse_1.traverseNodes)(result.ast, {
|
|
@@ -77,17 +77,39 @@ class ScriptContext {
|
|
|
77
77
|
for (const token of result.ast.comments || []) {
|
|
78
78
|
this.remapLocation(token);
|
|
79
79
|
}
|
|
80
|
+
const context = new RestoreNodeProcessContext(result);
|
|
80
81
|
let restoreNodeProcesses = this.restoreNodeProcesses;
|
|
81
82
|
for (const [node, parent] of traversed) {
|
|
82
83
|
if (!parent)
|
|
83
84
|
continue;
|
|
84
|
-
restoreNodeProcesses = restoreNodeProcesses.filter((proc) => !proc(node,
|
|
85
|
+
restoreNodeProcesses = restoreNodeProcesses.filter((proc) => !proc(node, context, parent));
|
|
86
|
+
}
|
|
87
|
+
if (context.removeTokens.size) {
|
|
88
|
+
const tokens = result.ast.tokens || [];
|
|
89
|
+
for (let index = tokens.length - 1; index >= 0; index--) {
|
|
90
|
+
const token = tokens[index];
|
|
91
|
+
for (const rt of context.removeTokens) {
|
|
92
|
+
if (rt(token)) {
|
|
93
|
+
tokens.splice(index, 1);
|
|
94
|
+
context.removeTokens.delete(rt);
|
|
95
|
+
if (!context.removeTokens.size) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
85
101
|
}
|
|
86
102
|
// Adjust program node location
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
result.ast.
|
|
90
|
-
|
|
103
|
+
const firstOffset = Math.min(...[
|
|
104
|
+
result.ast.body[0],
|
|
105
|
+
(_a = result.ast.tokens) === null || _a === void 0 ? void 0 : _a[0],
|
|
106
|
+
(_b = result.ast.comments) === null || _b === void 0 ? void 0 : _b[0],
|
|
107
|
+
]
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.map((t) => t.range[0]));
|
|
110
|
+
if (firstOffset < result.ast.range[0]) {
|
|
111
|
+
result.ast.range[0] = firstOffset;
|
|
112
|
+
result.ast.loc.start = this.ctx.getLocFromIndex(firstOffset);
|
|
91
113
|
}
|
|
92
114
|
}
|
|
93
115
|
remapLocation(node) {
|
|
@@ -116,6 +138,9 @@ class ScriptContext {
|
|
|
116
138
|
}
|
|
117
139
|
}
|
|
118
140
|
getRemapRange(start, end) {
|
|
141
|
+
if (!this.offsets.length) {
|
|
142
|
+
return [start, end];
|
|
143
|
+
}
|
|
119
144
|
let lastStart = this.offsets[0];
|
|
120
145
|
let lastEnd = this.offsets[0];
|
|
121
146
|
for (const offset of this.offsets) {
|
|
@@ -32,6 +32,10 @@ const errors_1 = require("../../errors");
|
|
|
32
32
|
*/
|
|
33
33
|
function parse(code, ctx) {
|
|
34
34
|
const ast = parseByService(code, ctx).ast;
|
|
35
|
+
if (!ast.children) {
|
|
36
|
+
// If the source code is empty, the children property may not be available.
|
|
37
|
+
ast.children = [];
|
|
38
|
+
}
|
|
35
39
|
const htmlElement = ast.children.find((n) => n.type === "element" && n.name === "html");
|
|
36
40
|
if (htmlElement) {
|
|
37
41
|
adjustHTML(ast, htmlElement, ctx);
|
|
@@ -140,16 +144,22 @@ function fixLocations(node, ctx) {
|
|
|
140
144
|
start += 1;
|
|
141
145
|
start += node.name.length;
|
|
142
146
|
if (!node.attributes.length) {
|
|
143
|
-
start = (0, astro_1.
|
|
147
|
+
start = (0, astro_1.calcStartTagEndOffset)(node, ctx);
|
|
144
148
|
}
|
|
145
149
|
}
|
|
146
150
|
else if (node.type === "attribute") {
|
|
147
151
|
fixLocationForAttr(node, ctx, start);
|
|
148
|
-
start = (0, astro_1.
|
|
152
|
+
start = (0, astro_1.calcAttributeEndOffset)(node, ctx);
|
|
153
|
+
if (node.position.end) {
|
|
154
|
+
node.position.end.offset = start;
|
|
155
|
+
}
|
|
149
156
|
}
|
|
150
157
|
else if (node.type === "comment") {
|
|
151
158
|
node.position.start.offset = tokenIndex(ctx, "<!--", start);
|
|
152
|
-
start = (0, astro_1.
|
|
159
|
+
start = (0, astro_1.calcCommentEndOffset)(node, ctx);
|
|
160
|
+
if (node.position.end) {
|
|
161
|
+
node.position.end.offset = start;
|
|
162
|
+
}
|
|
153
163
|
}
|
|
154
164
|
else if (node.type === "text") {
|
|
155
165
|
if (parent.type === "element" &&
|
|
@@ -193,6 +203,9 @@ function fixLocations(node, ctx) {
|
|
|
193
203
|
node.value = ctx.code.slice(node.position.start.offset, start);
|
|
194
204
|
}
|
|
195
205
|
}
|
|
206
|
+
if (node.position.end) {
|
|
207
|
+
node.position.end.offset = start;
|
|
208
|
+
}
|
|
196
209
|
}
|
|
197
210
|
else if (node.type === "expression") {
|
|
198
211
|
start = node.position.start.offset = tokenIndex(ctx, "{", start);
|
|
@@ -217,21 +230,22 @@ function fixLocations(node, ctx) {
|
|
|
217
230
|
if (node.type === "attribute") {
|
|
218
231
|
const attributes = parent.attributes;
|
|
219
232
|
if (attributes[attributes.length - 1] === node) {
|
|
220
|
-
start = (0, astro_1.
|
|
233
|
+
start = (0, astro_1.calcStartTagEndOffset)(parent, ctx);
|
|
221
234
|
}
|
|
222
|
-
return;
|
|
223
235
|
}
|
|
224
|
-
if (node.type === "expression") {
|
|
236
|
+
else if (node.type === "expression") {
|
|
225
237
|
start = tokenIndex(ctx, "}", start) + 1;
|
|
226
238
|
}
|
|
227
239
|
else if (node.type === "fragment" ||
|
|
228
240
|
node.type === "element" ||
|
|
229
241
|
node.type === "component" ||
|
|
230
242
|
node.type === "custom-element") {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
243
|
+
if (!(0, astro_1.getSelfClosingTag)(node, ctx)) {
|
|
244
|
+
const closeTagStart = tokenIndexSafe(ctx.code, `</${node.name}`, start);
|
|
245
|
+
if (closeTagStart != null) {
|
|
246
|
+
start = closeTagStart + 2 + node.name.length;
|
|
247
|
+
start = tokenIndex(ctx, ">", start) + 1;
|
|
248
|
+
}
|
|
235
249
|
}
|
|
236
250
|
}
|
|
237
251
|
else {
|
|
@@ -11,30 +11,47 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
11
11
|
let uniqueIdSeq = 0;
|
|
12
12
|
const usedUniqueIds = new Set();
|
|
13
13
|
const script = new script_1.ScriptContext(ctx);
|
|
14
|
-
const frontmatter = resultTemplate.ast.children.find((n) => n.type === "frontmatter");
|
|
15
14
|
let fragmentOpened = false;
|
|
16
|
-
|
|
15
|
+
/** Open astro root fragment */
|
|
16
|
+
function openRootFragment(startOffset) {
|
|
17
17
|
script.appendScript("<>");
|
|
18
18
|
fragmentOpened = true;
|
|
19
|
+
script.addRestoreNodeProcess((scriptNode, { result }) => {
|
|
20
|
+
if (scriptNode.type === types_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
21
|
+
scriptNode.expression.type === types_1.AST_NODE_TYPES.JSXFragment &&
|
|
22
|
+
scriptNode.range[0] === startOffset &&
|
|
23
|
+
result.ast.body.includes(scriptNode)) {
|
|
24
|
+
const index = result.ast.body.indexOf(scriptNode);
|
|
25
|
+
const rootFragment = (result.ast.body[index] =
|
|
26
|
+
scriptNode.expression);
|
|
27
|
+
delete rootFragment.closingFragment;
|
|
28
|
+
delete rootFragment.openingFragment;
|
|
29
|
+
rootFragment.type = "AstroFragment";
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
});
|
|
19
34
|
}
|
|
20
35
|
(0, astro_1.walkElements)(resultTemplate.ast, ctx.code,
|
|
21
36
|
// eslint-disable-next-line complexity -- X(
|
|
22
|
-
(node,
|
|
37
|
+
(node, [parent]) => {
|
|
23
38
|
if (node.type === "frontmatter") {
|
|
24
39
|
const start = node.position.start.offset;
|
|
40
|
+
if (fragmentOpened) {
|
|
41
|
+
script.appendScript("</>;");
|
|
42
|
+
fragmentOpened = false;
|
|
43
|
+
}
|
|
25
44
|
script.appendOriginal(start);
|
|
26
45
|
script.skipOriginalOffset(3);
|
|
27
|
-
const end = node
|
|
46
|
+
const end = (0, astro_1.getEndOffset)(node, ctx);
|
|
28
47
|
script.appendOriginal(end - 3);
|
|
29
|
-
script.appendScript("
|
|
30
|
-
fragmentOpened = true;
|
|
48
|
+
script.appendScript(";");
|
|
31
49
|
script.skipOriginalOffset(3);
|
|
32
|
-
script.addRestoreNodeProcess((_scriptNode, result) => {
|
|
50
|
+
script.addRestoreNodeProcess((_scriptNode, { result }) => {
|
|
33
51
|
for (let index = 0; index < result.ast.body.length; index++) {
|
|
34
52
|
const st = result.ast.body[index];
|
|
35
53
|
if (st.type === types_1.AST_NODE_TYPES.EmptyStatement) {
|
|
36
|
-
if (st.range[0] === end - 3 &&
|
|
37
|
-
st.range[1] === end) {
|
|
54
|
+
if (st.range[0] === end - 3 && st.range[1] <= end) {
|
|
38
55
|
result.ast.body.splice(index, 1);
|
|
39
56
|
break;
|
|
40
57
|
}
|
|
@@ -50,7 +67,6 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
50
67
|
}
|
|
51
68
|
else if ((0, astro_1.isTag)(node)) {
|
|
52
69
|
// Process for multiple tag
|
|
53
|
-
const parent = parents[0];
|
|
54
70
|
if (parent.type === "expression") {
|
|
55
71
|
const index = parent.children.indexOf(node);
|
|
56
72
|
const before = parent.children[index - 1];
|
|
@@ -82,6 +98,11 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
82
98
|
}
|
|
83
99
|
}
|
|
84
100
|
}
|
|
101
|
+
const start = node.position.start.offset;
|
|
102
|
+
script.appendOriginal(start);
|
|
103
|
+
if (!fragmentOpened) {
|
|
104
|
+
openRootFragment(start);
|
|
105
|
+
}
|
|
85
106
|
// Process for attributes
|
|
86
107
|
for (const attr of node.attributes) {
|
|
87
108
|
if ((node.type === "component" ||
|
|
@@ -108,7 +129,7 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
108
129
|
start + colonIndex + 1,
|
|
109
130
|
start + attr.name.length,
|
|
110
131
|
]);
|
|
111
|
-
script.addRestoreNodeProcess((scriptNode,
|
|
132
|
+
script.addRestoreNodeProcess((scriptNode, context) => {
|
|
112
133
|
if (scriptNode.type ===
|
|
113
134
|
types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
114
135
|
scriptNode.range[0] === start) {
|
|
@@ -120,17 +141,10 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
120
141
|
scriptNode.name = nsn;
|
|
121
142
|
nsn.namespace.parent = nsn;
|
|
122
143
|
nsn.name.parent = nsn;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
baseNameNode.range[0] &&
|
|
128
|
-
token.range[1] ===
|
|
129
|
-
baseNameNode.range[1]) {
|
|
130
|
-
tokens.splice(index, 1);
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
144
|
+
context.addRemoveToken((token) => token.range[0] ===
|
|
145
|
+
baseNameNode.range[0] &&
|
|
146
|
+
token.range[1] ===
|
|
147
|
+
baseNameNode.range[1]);
|
|
134
148
|
return true;
|
|
135
149
|
}
|
|
136
150
|
return false;
|
|
@@ -163,8 +177,8 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
163
177
|
}
|
|
164
178
|
else if (attr.kind === "template-literal") {
|
|
165
179
|
const attrStart = attr.position.start.offset;
|
|
166
|
-
const start = (0, astro_1.
|
|
167
|
-
const end = (0, astro_1.
|
|
180
|
+
const start = (0, astro_1.calcAttributeValueStartOffset)(attr, ctx);
|
|
181
|
+
const end = (0, astro_1.calcAttributeEndOffset)(attr, ctx);
|
|
168
182
|
script.appendOriginal(start);
|
|
169
183
|
script.appendScript("{");
|
|
170
184
|
script.appendOriginal(end);
|
|
@@ -182,7 +196,7 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
182
196
|
}
|
|
183
197
|
}
|
|
184
198
|
// Process for start tag close
|
|
185
|
-
const closing = (0, astro_1.getSelfClosingTag)(node,
|
|
199
|
+
const closing = (0, astro_1.getSelfClosingTag)(node, ctx);
|
|
186
200
|
if (closing && closing.end === ">") {
|
|
187
201
|
script.appendOriginal(closing.offset - 1);
|
|
188
202
|
script.appendScript("/");
|
|
@@ -215,23 +229,19 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
215
229
|
}
|
|
216
230
|
else if (node.type === "comment") {
|
|
217
231
|
const start = node.position.start.offset;
|
|
218
|
-
const
|
|
232
|
+
const end = (0, astro_1.getEndOffset)(node, ctx);
|
|
233
|
+
const length = end - start;
|
|
219
234
|
script.appendOriginal(start);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
script.appendOriginal(start + 1);
|
|
223
|
-
script.appendScript(`></`);
|
|
224
|
-
script.skipOriginalOffset(length - 2);
|
|
225
|
-
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
235
|
+
if (!fragmentOpened) {
|
|
236
|
+
openRootFragment(start);
|
|
226
237
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
script.addRestoreNodeProcess((scriptNode, result) => {
|
|
238
|
+
script.appendOriginal(start + 1);
|
|
239
|
+
script.appendScript(`></`);
|
|
240
|
+
script.skipOriginalOffset(length - 2);
|
|
241
|
+
script.appendOriginal(end);
|
|
242
|
+
script.addRestoreNodeProcess((scriptNode, context) => {
|
|
233
243
|
if (scriptNode.range[0] === start &&
|
|
234
|
-
scriptNode.type ===
|
|
244
|
+
scriptNode.type === types_1.AST_NODE_TYPES.JSXFragment) {
|
|
235
245
|
delete scriptNode.children;
|
|
236
246
|
delete scriptNode.openingFragment;
|
|
237
247
|
delete scriptNode.closingFragment;
|
|
@@ -239,27 +249,10 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
239
249
|
const commentNode = scriptNode;
|
|
240
250
|
commentNode.type = "AstroHTMLComment";
|
|
241
251
|
commentNode.value = node.value;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
(token) => token.value === ">" &&
|
|
247
|
-
token.range[1] === scriptNode.range[1],
|
|
248
|
-
]);
|
|
249
|
-
const tokens = result.ast.tokens || [];
|
|
250
|
-
for (let index = tokens.length - 1; index >= 0; index--) {
|
|
251
|
-
const token = tokens[index];
|
|
252
|
-
for (const rt of removeTokenSet) {
|
|
253
|
-
if (rt(token)) {
|
|
254
|
-
tokens.splice(index, 1);
|
|
255
|
-
removeTokenSet.delete(rt);
|
|
256
|
-
if (!removeTokenSet.size) {
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
252
|
+
context.addRemoveToken((token) => token.value === "<" &&
|
|
253
|
+
token.range[0] === scriptNode.range[0]);
|
|
254
|
+
context.addRemoveToken((token) => token.value === ">" &&
|
|
255
|
+
token.range[1] === scriptNode.range[1]);
|
|
263
256
|
return true;
|
|
264
257
|
}
|
|
265
258
|
return false;
|
|
@@ -271,40 +264,49 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
271
264
|
}
|
|
272
265
|
else if (node.type === "doctype") {
|
|
273
266
|
const start = node.position.start.offset;
|
|
274
|
-
const end = node
|
|
267
|
+
const end = (0, astro_1.getEndOffset)(node, ctx);
|
|
268
|
+
const length = end - start;
|
|
275
269
|
script.appendOriginal(start);
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
script.appendScript(`<></>`);
|
|
279
|
-
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
script.appendScript(`0;`);
|
|
283
|
-
targetType = types_1.AST_NODE_TYPES.ExpressionStatement;
|
|
270
|
+
if (!fragmentOpened) {
|
|
271
|
+
openRootFragment(start);
|
|
284
272
|
}
|
|
285
|
-
script.
|
|
286
|
-
script.
|
|
273
|
+
script.appendOriginal(start + 1);
|
|
274
|
+
script.appendScript(`></`);
|
|
275
|
+
script.skipOriginalOffset(length - 2);
|
|
276
|
+
script.appendOriginal(end);
|
|
277
|
+
script.addRestoreNodeProcess((scriptNode, context) => {
|
|
287
278
|
if (scriptNode.range[0] === start &&
|
|
288
|
-
scriptNode.type ===
|
|
279
|
+
scriptNode.type === types_1.AST_NODE_TYPES.JSXFragment) {
|
|
289
280
|
delete scriptNode.children;
|
|
290
281
|
delete scriptNode.openingFragment;
|
|
291
282
|
delete scriptNode.closingFragment;
|
|
292
283
|
delete scriptNode.expression;
|
|
293
284
|
const doctypeNode = scriptNode;
|
|
294
285
|
doctypeNode.type = "AstroDoctype";
|
|
286
|
+
context.addRemoveToken((token) => token.value === "<" &&
|
|
287
|
+
token.range[0] === scriptNode.range[0]);
|
|
288
|
+
context.addRemoveToken((token) => token.value === ">" &&
|
|
289
|
+
token.range[1] === scriptNode.range[1]);
|
|
295
290
|
return true;
|
|
296
291
|
}
|
|
297
292
|
return false;
|
|
298
293
|
});
|
|
299
294
|
script.addToken("HTMLDocType", [start, end]);
|
|
300
295
|
}
|
|
301
|
-
|
|
296
|
+
else {
|
|
297
|
+
const start = node.position.start.offset;
|
|
298
|
+
script.appendOriginal(start);
|
|
299
|
+
if (!fragmentOpened) {
|
|
300
|
+
openRootFragment(start);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}, (node, [parent]) => {
|
|
302
304
|
if ((0, astro_1.isTag)(node)) {
|
|
303
|
-
const closing = (0, astro_1.getSelfClosingTag)(node,
|
|
305
|
+
const closing = (0, astro_1.getSelfClosingTag)(node, ctx);
|
|
304
306
|
if (!closing) {
|
|
305
|
-
const end = (0, astro_1.getEndTag)(node,
|
|
307
|
+
const end = (0, astro_1.getEndTag)(node, ctx);
|
|
306
308
|
if (!end) {
|
|
307
|
-
const offset = (0, astro_1.
|
|
309
|
+
const offset = (0, astro_1.calcContentEndOffset)(node, ctx);
|
|
308
310
|
script.appendOriginal(offset);
|
|
309
311
|
script.appendScript(`</${node.name}>`);
|
|
310
312
|
script.addRestoreNodeProcess((scriptNode, _result, parent) => {
|
|
@@ -321,7 +323,6 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
321
323
|
}
|
|
322
324
|
}
|
|
323
325
|
// Process for multiple tag
|
|
324
|
-
const parent = parents[0];
|
|
325
326
|
if (((0, astro_1.isTag)(node) || node.type === "comment") &&
|
|
326
327
|
parent.type === "expression") {
|
|
327
328
|
const index = parent.children.indexOf(node);
|
|
@@ -330,17 +331,20 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
330
331
|
const before = parent.children[index - 1];
|
|
331
332
|
if (before &&
|
|
332
333
|
((0, astro_1.isTag)(before) || before.type === "comment")) {
|
|
333
|
-
const end = (0, astro_1.
|
|
334
|
-
? (0, astro_1.getTagEndOffset)(node, parents, ctx)
|
|
335
|
-
: (0, astro_1.getCommentEndOffset)(node, ctx);
|
|
334
|
+
const end = (0, astro_1.getEndOffset)(node, ctx);
|
|
336
335
|
script.appendOriginal(end);
|
|
337
336
|
script.appendScript("</>");
|
|
338
337
|
}
|
|
339
338
|
}
|
|
340
339
|
}
|
|
341
340
|
});
|
|
341
|
+
if (fragmentOpened) {
|
|
342
|
+
const last = resultTemplate.ast.children[resultTemplate.ast.children.length - 1];
|
|
343
|
+
const end = (0, astro_1.getEndOffset)(last, ctx);
|
|
344
|
+
script.appendOriginal(end);
|
|
345
|
+
script.appendScript("</>");
|
|
346
|
+
}
|
|
342
347
|
script.appendOriginal(ctx.code.length);
|
|
343
|
-
script.appendScript("</>");
|
|
344
348
|
return script;
|
|
345
349
|
/**
|
|
346
350
|
* Generate unique id
|
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
|
@@ -4,12 +4,11 @@ exports.KEYS = void 0;
|
|
|
4
4
|
const eslint_visitor_keys_1 = require("eslint-visitor-keys");
|
|
5
5
|
const astroKeys = {
|
|
6
6
|
Program: ["body"],
|
|
7
|
-
|
|
7
|
+
AstroFragment: ["children"],
|
|
8
8
|
AstroHTMLComment: [],
|
|
9
9
|
AstroDoctype: [],
|
|
10
10
|
AstroShorthandAttribute: ["name", "value"],
|
|
11
11
|
AstroTemplateLiteralAttribute: ["name", "value"],
|
|
12
12
|
AstroRawText: [],
|
|
13
|
-
AstroFragment: ["children"],
|
|
14
13
|
};
|
|
15
14
|
exports.KEYS = (0, eslint_visitor_keys_1.unionWith)(astroKeys);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-eslint-parser",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Astro parser for ESLint",
|
|
3
|
+
"version": "0.0.17",
|
|
4
|
+
"description": "Astro component parser for ESLint",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"lib"
|
|
@@ -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"
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"@types/semver": "^7.3.9",
|
|
61
61
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
|
62
62
|
"@typescript-eslint/parser": "^5.4.0",
|
|
63
|
+
"astro-eslint-parser": ">=0.0.15",
|
|
63
64
|
"benchmark": "^2.1.4",
|
|
64
65
|
"chai": "^4.3.4",
|
|
65
66
|
"code-red": "^0.2.3",
|
|
@@ -87,6 +88,6 @@
|
|
|
87
88
|
"string-replace-loader": "^3.0.3",
|
|
88
89
|
"ts-node": "^10.4.0",
|
|
89
90
|
"typescript": "~4.6.0",
|
|
90
|
-
"vue-eslint-parser": "^
|
|
91
|
+
"vue-eslint-parser": "^9.0.0"
|
|
91
92
|
}
|
|
92
93
|
}
|