astro-eslint-parser 0.0.0 → 0.0.3
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 +13 -1
- package/lib/context/script.d.ts +0 -5
- package/lib/context/script.js +6 -212
- package/lib/parser/astro-parser/parse.js +21 -18
- package/lib/parser/index.js +2 -2
- package/lib/parser/process-template.d.ts +7 -0
- package/lib/parser/process-template.js +263 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ You can check it on [Online DEMO](https://ota-meshi.github.io/astro-eslint-parse
|
|
|
11
11
|
[](http://www.npmtrends.com/astro-eslint-parser)
|
|
12
12
|
[](http://www.npmtrends.com/astro-eslint-parser)
|
|
13
13
|
[](https://github.com/ota-meshi/astro-eslint-parser/actions?query=workflow%3ACI)
|
|
14
|
+
[](https://coveralls.io/github/ota-meshi/astro-eslint-parser?branch=main)
|
|
14
15
|
|
|
15
16
|
This parser is in the ***experimental stages*** of development.
|
|
16
17
|
|
|
@@ -133,9 +134,20 @@ Example **.vscode/settings.json**:
|
|
|
133
134
|
}
|
|
134
135
|
```
|
|
135
136
|
|
|
137
|
+
## Compatibility With Existing ESLint Rules
|
|
138
|
+
|
|
139
|
+
Most of the rules in the ESLint core work for the script part, but some rules are incompatible.
|
|
140
|
+
For example, the [semi] rule doesn't work.
|
|
141
|
+
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.
|
|
142
|
+
For example, the [react/jsx-no-target-blank] rule works fine.
|
|
143
|
+
|
|
144
|
+
[semi]: https://eslint.org/docs/rules/semi
|
|
145
|
+
[eslint-plugin-react]: https://github.com/jsx-eslint/eslint-plugin-react/
|
|
146
|
+
[react/jsx-no-target-blank]: https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md
|
|
147
|
+
|
|
136
148
|
## Usage for Custom Rules / Plugins
|
|
137
149
|
|
|
138
|
-
-
|
|
150
|
+
- TBA
|
|
139
151
|
|
|
140
152
|
<!-- - [AST.md](./docs/AST.md) is AST specification. You can check it on the [Online DEMO](https://ota-meshi.github.io/astro-eslint-parser/). -->
|
|
141
153
|
<!-- - I have already [implemented some rules] in the [`@ota-meshi/eslint-plugin-astro`]. The source code for these rules will be helpful to you. -->
|
package/lib/context/script.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import type { ParseResult } from "@astrojs/compiler";
|
|
2
1
|
import type { Context } from ".";
|
|
3
2
|
import type { ESLintExtendedProgram } from "../parser";
|
|
4
3
|
import type { TSESTree } from "@typescript-eslint/types";
|
|
5
|
-
/**
|
|
6
|
-
* Process the template to generate a ScriptContext.
|
|
7
|
-
*/
|
|
8
|
-
export declare function processTemplate(ctx: Context, resultTemplate: ParseResult): ScriptContext;
|
|
9
4
|
export declare class ScriptContext {
|
|
10
5
|
private readonly ctx;
|
|
11
6
|
script: string;
|
package/lib/context/script.js
CHANGED
|
@@ -1,214 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ScriptContext =
|
|
3
|
+
exports.ScriptContext = void 0;
|
|
4
4
|
const traverse_1 = require("../traverse");
|
|
5
|
-
const types_1 = require("@typescript-eslint/types");
|
|
6
5
|
const errors_1 = require("../errors");
|
|
7
|
-
const astro_1 = require("../astro");
|
|
8
|
-
/**
|
|
9
|
-
* Process the template to generate a ScriptContext.
|
|
10
|
-
*/
|
|
11
|
-
function processTemplate(ctx, resultTemplate) {
|
|
12
|
-
const script = new ScriptContext(ctx);
|
|
13
|
-
const frontmatter = resultTemplate.ast.children.find((n) => n.type === "frontmatter");
|
|
14
|
-
let fragmentOpened = false;
|
|
15
|
-
if (!frontmatter) {
|
|
16
|
-
script.appendScript("<>");
|
|
17
|
-
fragmentOpened = true;
|
|
18
|
-
}
|
|
19
|
-
(0, astro_1.walkElements)(resultTemplate.ast, (node, parent) => {
|
|
20
|
-
if (node.type === "frontmatter") {
|
|
21
|
-
const start = node.position.start.offset;
|
|
22
|
-
script.appendOriginal(start);
|
|
23
|
-
script.skipOriginalOffset(3);
|
|
24
|
-
const end = node.position.end.offset;
|
|
25
|
-
script.appendOriginal(end - 3);
|
|
26
|
-
script.appendScript(";<>");
|
|
27
|
-
fragmentOpened = true;
|
|
28
|
-
script.skipOriginalOffset(3);
|
|
29
|
-
script.addRestoreNodeProcess((_scriptNode, result) => {
|
|
30
|
-
for (let index = 0; index < result.ast.body.length; index++) {
|
|
31
|
-
const st = result.ast.body[index];
|
|
32
|
-
if (st.type === types_1.AST_NODE_TYPES.EmptyStatement) {
|
|
33
|
-
if (st.range[0] === end - 3 && st.range[1] === end) {
|
|
34
|
-
result.ast.body.splice(index, 1);
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return true;
|
|
40
|
-
});
|
|
41
|
-
script.addToken(types_1.AST_TOKEN_TYPES.Punctuator, [
|
|
42
|
-
node.position.start.offset,
|
|
43
|
-
node.position.start.offset + 3,
|
|
44
|
-
]);
|
|
45
|
-
script.addToken(types_1.AST_TOKEN_TYPES.Punctuator, [end - 3, end]);
|
|
46
|
-
}
|
|
47
|
-
else if ((0, astro_1.isTag)(node)) {
|
|
48
|
-
for (const attr of node.attributes) {
|
|
49
|
-
if (attr.kind === "shorthand") {
|
|
50
|
-
const start = attr.position.start.offset;
|
|
51
|
-
script.appendOriginal(start);
|
|
52
|
-
script.appendScript(`${attr.name}=`);
|
|
53
|
-
script.addRestoreNodeProcess((scriptNode) => {
|
|
54
|
-
if (scriptNode.type === types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
55
|
-
scriptNode.range[0] === start) {
|
|
56
|
-
const attrNode = scriptNode;
|
|
57
|
-
attrNode.type = "AstroShorthandAttribute";
|
|
58
|
-
const locs = ctx.getLocations(...attrNode.value.expression.range);
|
|
59
|
-
attrNode.name.range = locs.range;
|
|
60
|
-
attrNode.name.loc = locs.loc;
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
else if (attr.kind === "template-literal") {
|
|
67
|
-
const start = (0, astro_1.getAttributeValueStartOffset)(attr, ctx.code);
|
|
68
|
-
const end = (0, astro_1.getAttributeEndOffset)(attr, ctx.code);
|
|
69
|
-
script.appendOriginal(start);
|
|
70
|
-
script.appendScript("{");
|
|
71
|
-
script.appendOriginal(end);
|
|
72
|
-
script.appendScript("}");
|
|
73
|
-
script.addRestoreNodeProcess((scriptNode) => {
|
|
74
|
-
if (scriptNode.type === types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
75
|
-
scriptNode.range[0] === start) {
|
|
76
|
-
const attrNode = scriptNode;
|
|
77
|
-
attrNode.type = "AstroTemplateLiteralAttribute";
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
return false;
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
const end = getVoidSelfClosingTag(node, parent, ctx);
|
|
85
|
-
if (end && end.end === ">") {
|
|
86
|
-
script.appendOriginal(end.offset - 1);
|
|
87
|
-
script.appendScript("/");
|
|
88
|
-
}
|
|
89
|
-
if (node.name === "script" || node.name === "style") {
|
|
90
|
-
const text = node.children[0];
|
|
91
|
-
if (text && text.type === "text") {
|
|
92
|
-
const styleNodeStart = node.position.start.offset;
|
|
93
|
-
const start = text.position.start.offset;
|
|
94
|
-
script.appendOriginal(start);
|
|
95
|
-
script.skipOriginalOffset(text.value.length);
|
|
96
|
-
script.addRestoreNodeProcess((scriptNode) => {
|
|
97
|
-
if (scriptNode.type === types_1.AST_NODE_TYPES.JSXElement &&
|
|
98
|
-
scriptNode.range[0] === styleNodeStart) {
|
|
99
|
-
const textNode = Object.assign({ type: types_1.AST_NODE_TYPES.JSXText, value: text.value, raw: text.value, parent: scriptNode }, ctx.getLocations(start, start + text.value.length));
|
|
100
|
-
scriptNode.children = [textNode];
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
return false;
|
|
104
|
-
});
|
|
105
|
-
script.addToken(types_1.AST_TOKEN_TYPES.JSXText, [
|
|
106
|
-
start,
|
|
107
|
-
start + text.value.length,
|
|
108
|
-
]);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
else if (node.type === "comment") {
|
|
113
|
-
const start = node.position.start.offset;
|
|
114
|
-
const length = 4 + node.value.length + 3;
|
|
115
|
-
script.appendOriginal(start);
|
|
116
|
-
let targetType;
|
|
117
|
-
if (fragmentOpened) {
|
|
118
|
-
script.appendScript(`<></>`);
|
|
119
|
-
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
script.appendScript(`0;`);
|
|
123
|
-
targetType = types_1.AST_NODE_TYPES.ExpressionStatement;
|
|
124
|
-
}
|
|
125
|
-
script.skipOriginalOffset(length);
|
|
126
|
-
script.addRestoreNodeProcess((scriptNode) => {
|
|
127
|
-
if (scriptNode.range[0] === start &&
|
|
128
|
-
scriptNode.type === targetType) {
|
|
129
|
-
delete scriptNode.children;
|
|
130
|
-
delete scriptNode.openingFragment;
|
|
131
|
-
delete scriptNode.closingFragment;
|
|
132
|
-
delete scriptNode.expression;
|
|
133
|
-
const commentNode = scriptNode;
|
|
134
|
-
commentNode.type = "AstroHTMLComment";
|
|
135
|
-
commentNode.value = node.value;
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
return false;
|
|
139
|
-
});
|
|
140
|
-
script.addToken("HTMLComment", [
|
|
141
|
-
start,
|
|
142
|
-
start + length,
|
|
143
|
-
]);
|
|
144
|
-
}
|
|
145
|
-
else if (node.type === "doctype") {
|
|
146
|
-
const start = node.position.start.offset;
|
|
147
|
-
const end = node.position.end.offset;
|
|
148
|
-
script.appendOriginal(start);
|
|
149
|
-
let targetType;
|
|
150
|
-
if (fragmentOpened) {
|
|
151
|
-
script.appendScript(`<></>`);
|
|
152
|
-
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
script.appendScript(`0;`);
|
|
156
|
-
targetType = types_1.AST_NODE_TYPES.ExpressionStatement;
|
|
157
|
-
}
|
|
158
|
-
script.skipOriginalOffset(end - start);
|
|
159
|
-
script.addRestoreNodeProcess((scriptNode) => {
|
|
160
|
-
if (scriptNode.range[0] === start &&
|
|
161
|
-
scriptNode.type === targetType) {
|
|
162
|
-
delete scriptNode.children;
|
|
163
|
-
delete scriptNode.openingFragment;
|
|
164
|
-
delete scriptNode.closingFragment;
|
|
165
|
-
delete scriptNode.expression;
|
|
166
|
-
const doctypeNode = scriptNode;
|
|
167
|
-
doctypeNode.type = "AstroDoctype";
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
return false;
|
|
171
|
-
});
|
|
172
|
-
script.addToken("HTMLDocType", [start, end]);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
script.appendOriginal(ctx.code.length);
|
|
176
|
-
script.appendScript("</>");
|
|
177
|
-
return script;
|
|
178
|
-
}
|
|
179
|
-
exports.processTemplate = processTemplate;
|
|
180
|
-
/**
|
|
181
|
-
* If the given tag is a void tag, get the self-closing tag.
|
|
182
|
-
*/
|
|
183
|
-
function getVoidSelfClosingTag(node, parent, ctx) {
|
|
184
|
-
if (node.type === "fragment") {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
if (node.children.length > 0) {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
const code = ctx.code;
|
|
191
|
-
let nextElementIndex = code.length;
|
|
192
|
-
const childIndex = parent.children.indexOf(node);
|
|
193
|
-
if (childIndex === parent.children.length - 1) {
|
|
194
|
-
// last
|
|
195
|
-
nextElementIndex = parent.position.end.offset;
|
|
196
|
-
nextElementIndex = code.lastIndexOf("</", nextElementIndex);
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
const next = parent.children[childIndex + 1];
|
|
200
|
-
nextElementIndex = next.position.start.offset;
|
|
201
|
-
}
|
|
202
|
-
const endOffset = (0, astro_1.getStartTagEndOffset)(node, code);
|
|
203
|
-
if (code.slice(endOffset, nextElementIndex).trim()) {
|
|
204
|
-
// has end tag
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
return {
|
|
208
|
-
offset: endOffset,
|
|
209
|
-
end: code.slice(endOffset - 2, endOffset) === "/>" ? "/>" : ">",
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
6
|
class ScriptContext {
|
|
213
7
|
constructor(ctx) {
|
|
214
8
|
this.script = "";
|
|
@@ -252,6 +46,11 @@ class ScriptContext {
|
|
|
252
46
|
if (last.expression.type !== "JSXFragment") {
|
|
253
47
|
throw new errors_1.ParseError("Unknown state error: Expected JSXFragment", last.expression.range[0], this.ctx);
|
|
254
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";
|
|
255
54
|
// remap locations
|
|
256
55
|
const traversed = new Set();
|
|
257
56
|
(0, traverse_1.traverseNodes)(result.ast, {
|
|
@@ -278,11 +77,6 @@ class ScriptContext {
|
|
|
278
77
|
for (const token of result.ast.comments || []) {
|
|
279
78
|
this.remapLocation(token);
|
|
280
79
|
}
|
|
281
|
-
// Process for Astro
|
|
282
|
-
delete last.expression.closingFragment;
|
|
283
|
-
delete last.expression.openingFragment;
|
|
284
|
-
last.expression.type =
|
|
285
|
-
"AstroRootFragment";
|
|
286
80
|
let restoreNodeProcesses = this.restoreNodeProcesses;
|
|
287
81
|
for (const node of traversed) {
|
|
288
82
|
restoreNodeProcesses = restoreNodeProcesses.filter((proc) => !proc(node, result));
|
|
@@ -41,9 +41,7 @@ exports.parse = parse;
|
|
|
41
41
|
function fixLocations(node, code) {
|
|
42
42
|
// FIXME: Adjust because the parser does not return the correct location.
|
|
43
43
|
let start = 0;
|
|
44
|
-
(0, astro_1.walk)(node,
|
|
45
|
-
// eslint-disable-next-line complexity -- ignore
|
|
46
|
-
(node) => {
|
|
44
|
+
(0, astro_1.walk)(node, (node) => {
|
|
47
45
|
if (node.type === "frontmatter") {
|
|
48
46
|
start = node.position.start.offset = tokenIndex(code, "---", start);
|
|
49
47
|
start = node.position.end.offset =
|
|
@@ -58,11 +56,7 @@ function fixLocations(node, code) {
|
|
|
58
56
|
}
|
|
59
57
|
start = node.position.start.offset = tokenIndex(code, "<", start);
|
|
60
58
|
start += 1;
|
|
61
|
-
|
|
62
|
-
node.type === "component" ||
|
|
63
|
-
node.type === "custom-element") {
|
|
64
|
-
start += node.name.length;
|
|
65
|
-
}
|
|
59
|
+
start += node.name.length;
|
|
66
60
|
if (!node.attributes.length) {
|
|
67
61
|
start = (0, astro_1.getStartTagEndOffset)(node, code);
|
|
68
62
|
}
|
|
@@ -106,20 +100,18 @@ function fixLocations(node, code) {
|
|
|
106
100
|
if (node.type === "expression") {
|
|
107
101
|
start = tokenIndex(code, "}", start) + 1;
|
|
108
102
|
}
|
|
109
|
-
else if (node.type === "fragment"
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
else if (node.type === "element" ||
|
|
103
|
+
else if (node.type === "fragment" ||
|
|
104
|
+
node.type === "element" ||
|
|
113
105
|
node.type === "component" ||
|
|
114
106
|
node.type === "custom-element") {
|
|
115
107
|
if (!node.position.end) {
|
|
116
108
|
return;
|
|
117
109
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
110
|
+
const closeTagStart = tokenIndexSafe(code, `</${node.name}`, start);
|
|
111
|
+
if (closeTagStart != null) {
|
|
112
|
+
start = closeTagStart + 2 + node.name.length;
|
|
113
|
+
start = tokenIndex(code, ">", start) + 1;
|
|
114
|
+
}
|
|
123
115
|
}
|
|
124
116
|
else {
|
|
125
117
|
return;
|
|
@@ -159,9 +151,20 @@ function fixLocationForAttr(node, code, start) {
|
|
|
159
151
|
* Get token index
|
|
160
152
|
*/
|
|
161
153
|
function tokenIndex(string, token, position) {
|
|
154
|
+
const index = tokenIndexSafe(string, token, position);
|
|
155
|
+
if (index == null) {
|
|
156
|
+
const start = token.trim() === token ? (0, astro_1.skipSpaces)(string, position) : position;
|
|
157
|
+
throw new Error(`Unknown token at ${start}, expected: ${JSON.stringify(token)}, actual: ${JSON.stringify(string.slice(start, start + 10))}`);
|
|
158
|
+
}
|
|
159
|
+
return index;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get token index
|
|
163
|
+
*/
|
|
164
|
+
function tokenIndexSafe(string, token, position) {
|
|
162
165
|
const index = token.trim() === token ? (0, astro_1.skipSpaces)(string, position) : position;
|
|
163
166
|
if (string.startsWith(token, index)) {
|
|
164
167
|
return index;
|
|
165
168
|
}
|
|
166
|
-
|
|
169
|
+
return null;
|
|
167
170
|
}
|
package/lib/parser/index.js
CHANGED
|
@@ -8,7 +8,7 @@ const script_1 = require("./script");
|
|
|
8
8
|
const sort_1 = require("./sort");
|
|
9
9
|
const errors_1 = require("../errors");
|
|
10
10
|
const parse_1 = require("./astro-parser/parse");
|
|
11
|
-
const
|
|
11
|
+
const process_template_1 = require("./process-template");
|
|
12
12
|
/**
|
|
13
13
|
* Parse source code
|
|
14
14
|
*/
|
|
@@ -21,7 +21,7 @@ function parseForESLint(code, options) {
|
|
|
21
21
|
}
|
|
22
22
|
const ctx = new context_1.Context(code, parserOptions);
|
|
23
23
|
const resultTemplate = parseTemplate(ctx.code, ctx);
|
|
24
|
-
const scriptContext = (0,
|
|
24
|
+
const scriptContext = (0, process_template_1.processTemplate)(ctx, resultTemplate);
|
|
25
25
|
const resultScript = (0, script_1.parseScript)(scriptContext.script, ctx);
|
|
26
26
|
scriptContext.restore(resultScript);
|
|
27
27
|
(0, sort_1.sort)(resultScript.ast.comments);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ParseResult } from "@astrojs/compiler";
|
|
2
|
+
import type { Context } from "../context";
|
|
3
|
+
import { ScriptContext } from "../context/script";
|
|
4
|
+
/**
|
|
5
|
+
* Process the template to generate a ScriptContext.
|
|
6
|
+
*/
|
|
7
|
+
export declare function processTemplate(ctx: Context, resultTemplate: ParseResult): ScriptContext;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processTemplate = void 0;
|
|
4
|
+
const types_1 = require("@typescript-eslint/types");
|
|
5
|
+
const astro_1 = require("../astro");
|
|
6
|
+
const script_1 = require("../context/script");
|
|
7
|
+
/**
|
|
8
|
+
* Process the template to generate a ScriptContext.
|
|
9
|
+
*/
|
|
10
|
+
function processTemplate(ctx, resultTemplate) {
|
|
11
|
+
const script = new script_1.ScriptContext(ctx);
|
|
12
|
+
const frontmatter = resultTemplate.ast.children.find((n) => n.type === "frontmatter");
|
|
13
|
+
let fragmentOpened = false;
|
|
14
|
+
if (!frontmatter) {
|
|
15
|
+
script.appendScript("<>");
|
|
16
|
+
fragmentOpened = true;
|
|
17
|
+
}
|
|
18
|
+
// eslint-disable-next-line complexity -- X(
|
|
19
|
+
(0, astro_1.walkElements)(resultTemplate.ast, (node, parent) => {
|
|
20
|
+
if (node.type === "frontmatter") {
|
|
21
|
+
const start = node.position.start.offset;
|
|
22
|
+
script.appendOriginal(start);
|
|
23
|
+
script.skipOriginalOffset(3);
|
|
24
|
+
const end = node.position.end.offset;
|
|
25
|
+
script.appendOriginal(end - 3);
|
|
26
|
+
script.appendScript(";<>");
|
|
27
|
+
fragmentOpened = true;
|
|
28
|
+
script.skipOriginalOffset(3);
|
|
29
|
+
script.addRestoreNodeProcess((_scriptNode, result) => {
|
|
30
|
+
for (let index = 0; index < result.ast.body.length; index++) {
|
|
31
|
+
const st = result.ast.body[index];
|
|
32
|
+
if (st.type === types_1.AST_NODE_TYPES.EmptyStatement) {
|
|
33
|
+
if (st.range[0] === end - 3 && st.range[1] === end) {
|
|
34
|
+
result.ast.body.splice(index, 1);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
41
|
+
script.addToken(types_1.AST_TOKEN_TYPES.Punctuator, [
|
|
42
|
+
node.position.start.offset,
|
|
43
|
+
node.position.start.offset + 3,
|
|
44
|
+
]);
|
|
45
|
+
script.addToken(types_1.AST_TOKEN_TYPES.Punctuator, [end - 3, end]);
|
|
46
|
+
}
|
|
47
|
+
else if ((0, astro_1.isTag)(node)) {
|
|
48
|
+
for (const attr of node.attributes) {
|
|
49
|
+
if ((node.type === "component" || node.type === "fragment") &&
|
|
50
|
+
(attr.kind === "quoted" ||
|
|
51
|
+
attr.kind === "empty" ||
|
|
52
|
+
attr.kind === "expression" ||
|
|
53
|
+
attr.kind === "template-literal")) {
|
|
54
|
+
const colonIndex = attr.name.indexOf(":");
|
|
55
|
+
if (colonIndex >= 0) {
|
|
56
|
+
const start = attr.position.start.offset;
|
|
57
|
+
script.appendOriginal(start + colonIndex);
|
|
58
|
+
script.skipOriginalOffset(1);
|
|
59
|
+
script.appendScript(`_`);
|
|
60
|
+
script.addToken(types_1.AST_TOKEN_TYPES.JSXIdentifier, [
|
|
61
|
+
start,
|
|
62
|
+
start + colonIndex,
|
|
63
|
+
]);
|
|
64
|
+
script.addToken(types_1.AST_TOKEN_TYPES.Punctuator, [
|
|
65
|
+
start + colonIndex,
|
|
66
|
+
start + colonIndex + 1,
|
|
67
|
+
]);
|
|
68
|
+
script.addToken(types_1.AST_TOKEN_TYPES.JSXIdentifier, [
|
|
69
|
+
start + colonIndex + 1,
|
|
70
|
+
start + attr.name.length,
|
|
71
|
+
]);
|
|
72
|
+
script.addRestoreNodeProcess((scriptNode, result) => {
|
|
73
|
+
if (scriptNode.type ===
|
|
74
|
+
types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
75
|
+
scriptNode.range[0] === start) {
|
|
76
|
+
const baseNameNode = scriptNode.name;
|
|
77
|
+
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] + colonIndex)), name: Object.assign({ type: types_1.AST_NODE_TYPES.JSXIdentifier, name: attr.name.slice(colonIndex + 1) }, ctx.getLocations(baseNameNode.range[0] +
|
|
78
|
+
colonIndex +
|
|
79
|
+
1, baseNameNode.range[1])) });
|
|
80
|
+
scriptNode.name = nsn;
|
|
81
|
+
nsn.namespace.parent = nsn;
|
|
82
|
+
nsn.name.parent = nsn;
|
|
83
|
+
const tokens = result.ast.tokens || [];
|
|
84
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
85
|
+
const token = tokens[index];
|
|
86
|
+
if (token.range[0] ===
|
|
87
|
+
baseNameNode.range[0] &&
|
|
88
|
+
token.range[1] === baseNameNode.range[1]) {
|
|
89
|
+
tokens.splice(index, 1);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (attr.kind === "shorthand") {
|
|
100
|
+
const start = attr.position.start.offset;
|
|
101
|
+
script.appendOriginal(start);
|
|
102
|
+
script.appendScript(`${attr.name}=`);
|
|
103
|
+
script.addRestoreNodeProcess((scriptNode) => {
|
|
104
|
+
if (scriptNode.type === types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
105
|
+
scriptNode.range[0] === start) {
|
|
106
|
+
const attrNode = scriptNode;
|
|
107
|
+
attrNode.type = "AstroShorthandAttribute";
|
|
108
|
+
const locs = ctx.getLocations(...attrNode.value.expression.range);
|
|
109
|
+
attrNode.name.range = locs.range;
|
|
110
|
+
attrNode.name.loc = locs.loc;
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else if (attr.kind === "template-literal") {
|
|
117
|
+
const attrStart = attr.position.start.offset;
|
|
118
|
+
const start = (0, astro_1.getAttributeValueStartOffset)(attr, ctx.code);
|
|
119
|
+
const end = (0, astro_1.getAttributeEndOffset)(attr, ctx.code);
|
|
120
|
+
script.appendOriginal(start);
|
|
121
|
+
script.appendScript("{");
|
|
122
|
+
script.appendOriginal(end);
|
|
123
|
+
script.appendScript("}");
|
|
124
|
+
script.addRestoreNodeProcess((scriptNode) => {
|
|
125
|
+
if (scriptNode.type === types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
126
|
+
scriptNode.range[0] === attrStart) {
|
|
127
|
+
const attrNode = scriptNode;
|
|
128
|
+
attrNode.type = "AstroTemplateLiteralAttribute";
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const end = getVoidSelfClosingTag(node, parent, ctx);
|
|
136
|
+
if (end && end.end === ">") {
|
|
137
|
+
script.appendOriginal(end.offset - 1);
|
|
138
|
+
script.appendScript("/");
|
|
139
|
+
}
|
|
140
|
+
if (node.name === "script" || node.name === "style") {
|
|
141
|
+
const text = node.children[0];
|
|
142
|
+
if (text && text.type === "text") {
|
|
143
|
+
const styleNodeStart = node.position.start.offset;
|
|
144
|
+
const start = text.position.start.offset;
|
|
145
|
+
script.appendOriginal(start);
|
|
146
|
+
script.skipOriginalOffset(text.value.length);
|
|
147
|
+
script.addRestoreNodeProcess((scriptNode) => {
|
|
148
|
+
if (scriptNode.type === types_1.AST_NODE_TYPES.JSXElement &&
|
|
149
|
+
scriptNode.range[0] === styleNodeStart) {
|
|
150
|
+
const textNode = Object.assign({ type: types_1.AST_NODE_TYPES.JSXText, value: text.value, raw: text.value, parent: scriptNode }, ctx.getLocations(start, start + text.value.length));
|
|
151
|
+
scriptNode.children = [textNode];
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
});
|
|
156
|
+
script.addToken(types_1.AST_TOKEN_TYPES.JSXText, [
|
|
157
|
+
start,
|
|
158
|
+
start + text.value.length,
|
|
159
|
+
]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else if (node.type === "comment") {
|
|
164
|
+
const start = node.position.start.offset;
|
|
165
|
+
const length = 4 + node.value.length + 3;
|
|
166
|
+
script.appendOriginal(start);
|
|
167
|
+
let targetType;
|
|
168
|
+
if (fragmentOpened) {
|
|
169
|
+
script.appendScript(`<></>`);
|
|
170
|
+
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
script.appendScript(`0;`);
|
|
174
|
+
targetType = types_1.AST_NODE_TYPES.ExpressionStatement;
|
|
175
|
+
}
|
|
176
|
+
script.skipOriginalOffset(length);
|
|
177
|
+
script.addRestoreNodeProcess((scriptNode) => {
|
|
178
|
+
if (scriptNode.range[0] === start &&
|
|
179
|
+
scriptNode.type === targetType) {
|
|
180
|
+
delete scriptNode.children;
|
|
181
|
+
delete scriptNode.openingFragment;
|
|
182
|
+
delete scriptNode.closingFragment;
|
|
183
|
+
delete scriptNode.expression;
|
|
184
|
+
const commentNode = scriptNode;
|
|
185
|
+
commentNode.type = "AstroHTMLComment";
|
|
186
|
+
commentNode.value = node.value;
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
});
|
|
191
|
+
script.addToken("HTMLComment", [
|
|
192
|
+
start,
|
|
193
|
+
start + length,
|
|
194
|
+
]);
|
|
195
|
+
}
|
|
196
|
+
else if (node.type === "doctype") {
|
|
197
|
+
const start = node.position.start.offset;
|
|
198
|
+
const end = node.position.end.offset;
|
|
199
|
+
script.appendOriginal(start);
|
|
200
|
+
let targetType;
|
|
201
|
+
if (fragmentOpened) {
|
|
202
|
+
script.appendScript(`<></>`);
|
|
203
|
+
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
script.appendScript(`0;`);
|
|
207
|
+
targetType = types_1.AST_NODE_TYPES.ExpressionStatement;
|
|
208
|
+
}
|
|
209
|
+
script.skipOriginalOffset(end - start);
|
|
210
|
+
script.addRestoreNodeProcess((scriptNode) => {
|
|
211
|
+
if (scriptNode.range[0] === start &&
|
|
212
|
+
scriptNode.type === targetType) {
|
|
213
|
+
delete scriptNode.children;
|
|
214
|
+
delete scriptNode.openingFragment;
|
|
215
|
+
delete scriptNode.closingFragment;
|
|
216
|
+
delete scriptNode.expression;
|
|
217
|
+
const doctypeNode = scriptNode;
|
|
218
|
+
doctypeNode.type = "AstroDoctype";
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
});
|
|
223
|
+
script.addToken("HTMLDocType", [start, end]);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
script.appendOriginal(ctx.code.length);
|
|
227
|
+
script.appendScript("</>");
|
|
228
|
+
return script;
|
|
229
|
+
}
|
|
230
|
+
exports.processTemplate = processTemplate;
|
|
231
|
+
/**
|
|
232
|
+
* If the given tag is a void tag, get the self-closing tag.
|
|
233
|
+
*/
|
|
234
|
+
function getVoidSelfClosingTag(node, parent, ctx) {
|
|
235
|
+
var _a;
|
|
236
|
+
const children = node.children.filter((c) => c.type !== "text" || c.value.trim());
|
|
237
|
+
if (children.length > 0) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
const code = ctx.code;
|
|
241
|
+
let nextElementIndex = code.length;
|
|
242
|
+
const childIndex = parent.children.indexOf(node);
|
|
243
|
+
if (childIndex === parent.children.length - 1) {
|
|
244
|
+
// last
|
|
245
|
+
if ((_a = parent.position) === null || _a === void 0 ? void 0 : _a.end) {
|
|
246
|
+
nextElementIndex = parent.position.end.offset;
|
|
247
|
+
nextElementIndex = code.lastIndexOf("</", nextElementIndex);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const next = parent.children[childIndex + 1];
|
|
252
|
+
nextElementIndex = next.position.start.offset;
|
|
253
|
+
}
|
|
254
|
+
const endOffset = (0, astro_1.getStartTagEndOffset)(node, code);
|
|
255
|
+
if (code.slice(endOffset, nextElementIndex).trim()) {
|
|
256
|
+
// has end tag
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
offset: endOffset,
|
|
261
|
+
end: code.slice(endOffset - 2, endOffset) === "/>" ? "/>" : ">",
|
|
262
|
+
};
|
|
263
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-eslint-parser",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Astro parser for ESLint",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"lib"
|
|
8
8
|
],
|
|
9
9
|
"engines": {
|
|
10
|
-
"node": "^
|
|
10
|
+
"node": "^14.17.0 || >=16.0.0"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"prebuild": "npm run -s clean",
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@astrojs/compiler": "^0.14.2",
|
|
45
45
|
"debug": "^4.3.4",
|
|
46
|
-
"eslint-scope": "^7.0.0",
|
|
47
46
|
"eslint-visitor-keys": "^3.0.0",
|
|
48
47
|
"espree": "^9.0.0"
|
|
49
48
|
},
|
|
@@ -63,7 +62,7 @@
|
|
|
63
62
|
"benchmark": "^2.1.4",
|
|
64
63
|
"chai": "^4.3.4",
|
|
65
64
|
"code-red": "^0.2.3",
|
|
66
|
-
"eslint": "^8.
|
|
65
|
+
"eslint": "^8.14.0",
|
|
67
66
|
"eslint-config-prettier": "^8.3.0",
|
|
68
67
|
"eslint-formatter-codeframe": "^7.32.1",
|
|
69
68
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
@@ -72,12 +71,13 @@
|
|
|
72
71
|
"eslint-plugin-node": "^11.1.0",
|
|
73
72
|
"eslint-plugin-node-dependencies": "^0.8.0",
|
|
74
73
|
"eslint-plugin-prettier": "^4.0.0",
|
|
74
|
+
"eslint-plugin-react": "^7.29.4",
|
|
75
75
|
"eslint-plugin-regexp": "^1.5.0",
|
|
76
76
|
"eslint-plugin-vue": "^8.0.3",
|
|
77
77
|
"estree-walker": "^3.0.0",
|
|
78
78
|
"locate-character": "^2.0.5",
|
|
79
79
|
"magic-string": "^0.26.0",
|
|
80
|
-
"mocha": "^
|
|
80
|
+
"mocha": "^10.0.0",
|
|
81
81
|
"mocha-chai-jest-snapshot": "^1.1.3",
|
|
82
82
|
"nyc": "^15.1.0",
|
|
83
83
|
"prettier": "^2.0.5",
|