astro-eslint-parser 0.0.15 → 0.0.18
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/astro.d.ts +49 -0
- package/lib/{ast.js → ast/astro.js} +0 -0
- package/lib/ast/base.d.ts +6 -0
- package/lib/ast/base.js +2 -0
- package/lib/ast/index.d.ts +2 -0
- package/lib/ast/index.js +18 -0
- package/lib/ast/jsx.d.ts +91 -0
- package/lib/ast/jsx.js +2 -0
- package/lib/astro/index.d.ts +12 -16
- package/lib/astro/index.js +88 -102
- package/lib/context/script.d.ts +8 -1
- package/lib/context/script.js +43 -18
- package/lib/parser/astro-parser/parse.js +18 -5
- package/lib/parser/process-template.js +82 -76
- package/lib/visitor-keys.js +1 -2
- package/package.json +11 -9
- package/lib/ast.d.ts +0 -53
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
|
+
## :handshake: 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_and_wrench: 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).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { JSXAttribute, JSXElement, JSXExpression, JSXExpressionContainer, JSXFragment, JSXText } from "./jsx";
|
|
2
|
+
import type { TSESTree as ES } from "@typescript-eslint/types";
|
|
3
|
+
import type { BaseNode } from "./base";
|
|
4
|
+
export declare type AstroNode = AstroProgram | AstroFragment | AstroHTMLComment | AstroDoctype | AstroShorthandAttribute | AstroTemplateLiteralAttribute | AstroRawText;
|
|
5
|
+
export declare type AstroChild = JSXElement | JSXFragment | JSXExpression | JSXText | AstroHTMLComment;
|
|
6
|
+
export declare type AstroParentNode = JSXElement | JSXFragment | AstroFragment;
|
|
7
|
+
/** Node of Astro program root */
|
|
8
|
+
export interface AstroProgram extends Omit<ES.Program, "type" | "body"> {
|
|
9
|
+
type: "Program";
|
|
10
|
+
body: (ES.Program["body"][number] | AstroFragment)[];
|
|
11
|
+
sourceType: "script" | "module";
|
|
12
|
+
comments: ES.Comment[];
|
|
13
|
+
tokens: ES.Token[];
|
|
14
|
+
parent?: undefined;
|
|
15
|
+
}
|
|
16
|
+
/** Node of Astro fragment */
|
|
17
|
+
export interface AstroFragment extends BaseNode {
|
|
18
|
+
type: "AstroFragment";
|
|
19
|
+
children: AstroChild[];
|
|
20
|
+
parent?: AstroParentNode;
|
|
21
|
+
}
|
|
22
|
+
/** Node of Astro html comment */
|
|
23
|
+
export interface AstroHTMLComment extends BaseNode {
|
|
24
|
+
type: "AstroHTMLComment";
|
|
25
|
+
value: string;
|
|
26
|
+
parent?: AstroParentNode;
|
|
27
|
+
}
|
|
28
|
+
/** Node of Astro doctype */
|
|
29
|
+
export interface AstroDoctype extends BaseNode {
|
|
30
|
+
type: "AstroDoctype";
|
|
31
|
+
parent?: AstroFragment;
|
|
32
|
+
}
|
|
33
|
+
/** Node of Astro shorthand attribute */
|
|
34
|
+
export interface AstroShorthandAttribute extends Omit<JSXAttribute, "type"> {
|
|
35
|
+
type: "AstroShorthandAttribute";
|
|
36
|
+
value: JSXExpressionContainer;
|
|
37
|
+
}
|
|
38
|
+
/** Node of Astro template-literal attribute */
|
|
39
|
+
export interface AstroTemplateLiteralAttribute extends Omit<JSXAttribute, "type"> {
|
|
40
|
+
type: "AstroTemplateLiteralAttribute";
|
|
41
|
+
value: JSXExpressionContainer & {
|
|
42
|
+
expression: ES.TemplateLiteral;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Node of Astro raw text */
|
|
46
|
+
export interface AstroRawText extends Omit<JSXText, "type"> {
|
|
47
|
+
type: "AstroRawText";
|
|
48
|
+
parent?: JSXElement;
|
|
49
|
+
}
|
|
File without changes
|
package/lib/ast/base.js
ADDED
package/lib/ast/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./astro"), exports);
|
|
18
|
+
__exportStar(require("./jsx"), exports);
|
package/lib/ast/jsx.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { TSESTree as ES, AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
2
|
+
import type { AstroFragment, AstroHTMLComment, AstroShorthandAttribute, AstroTemplateLiteralAttribute } from "./astro";
|
|
3
|
+
import type { BaseNode } from "./base";
|
|
4
|
+
export declare type JSXNode = JSXAttribute | JSXClosingElement | JSXClosingFragment | JSXElement | JSXEmptyExpression | JSXExpressionContainer | JSXFragment | JSXIdentifier | JSXMemberExpression | JSXNamespacedName | JSXOpeningElement | JSXOpeningFragment | JSXSpreadAttribute | JSXSpreadChild | JSXText;
|
|
5
|
+
export declare type JSXChild = JSXElement | JSXFragment | JSXExpression | JSXText | AstroHTMLComment;
|
|
6
|
+
export declare type JSXParentNode = JSXElement | JSXFragment | AstroFragment;
|
|
7
|
+
export interface JSXElement extends BaseNode {
|
|
8
|
+
type: AST_NODE_TYPES.JSXElement;
|
|
9
|
+
openingElement: JSXOpeningElement;
|
|
10
|
+
closingElement: JSXClosingElement | null;
|
|
11
|
+
children: JSXChild[];
|
|
12
|
+
parent?: JSXParentNode;
|
|
13
|
+
}
|
|
14
|
+
export interface JSXFragment extends BaseNode {
|
|
15
|
+
type: AST_NODE_TYPES.JSXFragment;
|
|
16
|
+
openingFragment: JSXOpeningFragment;
|
|
17
|
+
closingFragment: JSXClosingFragment;
|
|
18
|
+
children: JSXChild[];
|
|
19
|
+
parent?: JSXParentNode;
|
|
20
|
+
}
|
|
21
|
+
export interface JSXOpeningElement extends BaseNode {
|
|
22
|
+
type: AST_NODE_TYPES.JSXOpeningElement;
|
|
23
|
+
typeParameters?: ES.TSTypeParameterInstantiation;
|
|
24
|
+
selfClosing: boolean;
|
|
25
|
+
name: JSXTagNameExpression;
|
|
26
|
+
attributes: (JSXAttribute | JSXSpreadAttribute | AstroShorthandAttribute | AstroTemplateLiteralAttribute)[];
|
|
27
|
+
parent?: JSXElement;
|
|
28
|
+
}
|
|
29
|
+
export interface JSXClosingElement extends BaseNode {
|
|
30
|
+
type: AST_NODE_TYPES.JSXClosingElement;
|
|
31
|
+
name: JSXTagNameExpression;
|
|
32
|
+
parent?: JSXElement;
|
|
33
|
+
}
|
|
34
|
+
export interface JSXClosingFragment extends BaseNode {
|
|
35
|
+
type: AST_NODE_TYPES.JSXClosingFragment;
|
|
36
|
+
parent?: JSXFragment;
|
|
37
|
+
}
|
|
38
|
+
export interface JSXOpeningFragment extends BaseNode {
|
|
39
|
+
type: AST_NODE_TYPES.JSXOpeningFragment;
|
|
40
|
+
parent?: JSXFragment;
|
|
41
|
+
}
|
|
42
|
+
export interface JSXAttribute extends BaseNode {
|
|
43
|
+
type: AST_NODE_TYPES.JSXAttribute;
|
|
44
|
+
name: JSXIdentifier | JSXNamespacedName;
|
|
45
|
+
value: JSXExpression | ES.Literal | null;
|
|
46
|
+
parent?: JSXOpeningElement;
|
|
47
|
+
}
|
|
48
|
+
export interface JSXSpreadAttribute extends BaseNode {
|
|
49
|
+
type: AST_NODE_TYPES.JSXSpreadAttribute;
|
|
50
|
+
argument: ES.Expression;
|
|
51
|
+
parent?: JSXOpeningElement;
|
|
52
|
+
}
|
|
53
|
+
export declare type JSXTagNameExpression = JSXIdentifier | JSXMemberExpression | JSXNamespacedName;
|
|
54
|
+
export interface JSXIdentifier extends BaseNode {
|
|
55
|
+
type: AST_NODE_TYPES.JSXIdentifier;
|
|
56
|
+
name: string;
|
|
57
|
+
parent?: JSXAttribute | AstroShorthandAttribute | AstroTemplateLiteralAttribute | JSXMemberExpression | JSXNamespacedName | JSXOpeningElement | JSXClosingElement;
|
|
58
|
+
}
|
|
59
|
+
export interface JSXMemberExpression extends BaseNode {
|
|
60
|
+
type: AST_NODE_TYPES.JSXMemberExpression;
|
|
61
|
+
object: JSXTagNameExpression;
|
|
62
|
+
property: JSXIdentifier;
|
|
63
|
+
parent?: JSXMemberExpression | JSXOpeningElement | JSXClosingElement;
|
|
64
|
+
}
|
|
65
|
+
export interface JSXNamespacedName extends BaseNode {
|
|
66
|
+
type: AST_NODE_TYPES.JSXNamespacedName;
|
|
67
|
+
namespace: JSXIdentifier;
|
|
68
|
+
name: JSXIdentifier;
|
|
69
|
+
parent?: JSXAttribute | AstroShorthandAttribute | AstroTemplateLiteralAttribute | JSXMemberExpression | JSXOpeningElement | JSXClosingElement;
|
|
70
|
+
}
|
|
71
|
+
export declare type JSXExpression = JSXExpressionContainer | JSXSpreadChild;
|
|
72
|
+
export interface JSXExpressionContainer extends BaseNode {
|
|
73
|
+
type: AST_NODE_TYPES.JSXExpressionContainer;
|
|
74
|
+
expression: ES.Expression | JSXEmptyExpression;
|
|
75
|
+
parent?: JSXAttribute | AstroShorthandAttribute | AstroTemplateLiteralAttribute | JSXParentNode;
|
|
76
|
+
}
|
|
77
|
+
export interface JSXSpreadChild extends BaseNode {
|
|
78
|
+
type: AST_NODE_TYPES.JSXSpreadChild;
|
|
79
|
+
expression: ES.Expression;
|
|
80
|
+
parent?: JSXAttribute | JSXParentNode;
|
|
81
|
+
}
|
|
82
|
+
export interface JSXEmptyExpression extends BaseNode {
|
|
83
|
+
type: AST_NODE_TYPES.JSXEmptyExpression;
|
|
84
|
+
parent?: JSXExpressionContainer;
|
|
85
|
+
}
|
|
86
|
+
export interface JSXText extends BaseNode {
|
|
87
|
+
type: AST_NODE_TYPES.JSXText;
|
|
88
|
+
value: string;
|
|
89
|
+
raw: string;
|
|
90
|
+
parent?: JSXParentNode;
|
|
91
|
+
}
|
package/lib/ast/jsx.js
ADDED
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,35 +15,27 @@ 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, ctx: Context): number;
|
|
23
|
-
/**
|
|
24
|
-
* Get end offset of Expression
|
|
25
|
-
*/
|
|
26
|
-
export declare function getExpressionEndOffset(node: ExpressionNode, 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
|
};
|
|
@@ -54,6 +46,10 @@ 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, 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;
|
|
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,22 +111,46 @@ 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
|
}
|
|
@@ -181,7 +162,7 @@ function getContentEndOffset(parent, ctx) {
|
|
|
181
162
|
return end;
|
|
182
163
|
}
|
|
183
164
|
else if (parent.type === "expression") {
|
|
184
|
-
const end =
|
|
165
|
+
const end = getEndOffset(parent, ctx);
|
|
185
166
|
return code.lastIndexOf("}", end);
|
|
186
167
|
}
|
|
187
168
|
else if (parent.type === "root") {
|
|
@@ -189,33 +170,28 @@ function getContentEndOffset(parent, ctx) {
|
|
|
189
170
|
}
|
|
190
171
|
throw new Error(`unknown type: ${parent.type}`);
|
|
191
172
|
}
|
|
192
|
-
exports.
|
|
173
|
+
exports.calcContentEndOffset = calcContentEndOffset;
|
|
193
174
|
/**
|
|
194
175
|
* If the given tag is a self-close tag, get the self-closing tag.
|
|
195
176
|
*/
|
|
196
|
-
function getSelfClosingTag(node,
|
|
177
|
+
function getSelfClosingTag(node, ctx) {
|
|
197
178
|
if (node.children.length > 0) {
|
|
198
179
|
return null;
|
|
199
180
|
}
|
|
200
181
|
const code = ctx.code;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
else {
|
|
208
|
-
const next = parent.children[childIndex + 1];
|
|
209
|
-
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
|
+
};
|
|
210
188
|
}
|
|
211
|
-
|
|
212
|
-
if (code.slice(endOffset, nextElementIndex).trim()) {
|
|
213
|
-
// has end tag
|
|
189
|
+
if (code.startsWith(`</${node.name}`, startTagEndOffset)) {
|
|
214
190
|
return null;
|
|
215
191
|
}
|
|
216
192
|
return {
|
|
217
|
-
offset:
|
|
218
|
-
end:
|
|
193
|
+
offset: startTagEndOffset,
|
|
194
|
+
end: ">",
|
|
219
195
|
};
|
|
220
196
|
}
|
|
221
197
|
exports.getSelfClosingTag = getSelfClosingTag;
|
|
@@ -229,7 +205,7 @@ function getEndTag(node, ctx) {
|
|
|
229
205
|
beforeIndex = getEndOffset(lastChild, ctx);
|
|
230
206
|
}
|
|
231
207
|
else {
|
|
232
|
-
beforeIndex =
|
|
208
|
+
beforeIndex = calcStartTagEndOffset(node, ctx);
|
|
233
209
|
}
|
|
234
210
|
beforeIndex = skipSpaces(ctx.code, beforeIndex);
|
|
235
211
|
if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) {
|
|
@@ -245,36 +221,46 @@ function getEndTag(node, ctx) {
|
|
|
245
221
|
return null;
|
|
246
222
|
}
|
|
247
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;
|
|
248
232
|
/**
|
|
249
233
|
* Get end offset of tag
|
|
250
234
|
*/
|
|
251
|
-
function
|
|
252
|
-
|
|
253
|
-
if (
|
|
254
|
-
|
|
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;
|
|
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);
|
|
265
240
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return ctx.code.indexOf(">", start) + 1;
|
|
241
|
+
else {
|
|
242
|
+
beforeIndex = calcStartTagEndOffset(node, ctx);
|
|
269
243
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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;
|
|
273
249
|
}
|
|
274
|
-
|
|
275
|
-
|
|
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;
|
|
276
261
|
}
|
|
277
|
-
|
|
262
|
+
const info = getTokenInfo(ctx, ["{", "}"], node.position.start.offset);
|
|
263
|
+
return info.index + info.match.length;
|
|
278
264
|
}
|
|
279
265
|
/**
|
|
280
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,7 +230,7 @@ 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
235
|
}
|
|
223
236
|
else if (node.type === "expression") {
|
|
@@ -227,7 +240,7 @@ function fixLocations(node, ctx) {
|
|
|
227
240
|
node.type === "element" ||
|
|
228
241
|
node.type === "component" ||
|
|
229
242
|
node.type === "custom-element") {
|
|
230
|
-
if (!(0, astro_1.getSelfClosingTag)(node,
|
|
243
|
+
if (!(0, astro_1.getSelfClosingTag)(node, ctx)) {
|
|
231
244
|
const closeTagStart = tokenIndexSafe(ctx.code, `</${node.name}`, start);
|
|
232
245
|
if (closeTagStart != null) {
|
|
233
246
|
start = closeTagStart + 2 + node.name.length;
|
|
@@ -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
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
|
}
|
|
@@ -81,6 +98,11 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
81
98
|
}
|
|
82
99
|
}
|
|
83
100
|
}
|
|
101
|
+
const start = node.position.start.offset;
|
|
102
|
+
script.appendOriginal(start);
|
|
103
|
+
if (!fragmentOpened) {
|
|
104
|
+
openRootFragment(start);
|
|
105
|
+
}
|
|
84
106
|
// Process for attributes
|
|
85
107
|
for (const attr of node.attributes) {
|
|
86
108
|
if ((node.type === "component" ||
|
|
@@ -107,7 +129,7 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
107
129
|
start + colonIndex + 1,
|
|
108
130
|
start + attr.name.length,
|
|
109
131
|
]);
|
|
110
|
-
script.addRestoreNodeProcess((scriptNode,
|
|
132
|
+
script.addRestoreNodeProcess((scriptNode, context) => {
|
|
111
133
|
if (scriptNode.type ===
|
|
112
134
|
types_1.AST_NODE_TYPES.JSXAttribute &&
|
|
113
135
|
scriptNode.range[0] === start) {
|
|
@@ -119,17 +141,10 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
119
141
|
scriptNode.name = nsn;
|
|
120
142
|
nsn.namespace.parent = nsn;
|
|
121
143
|
nsn.name.parent = nsn;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
baseNameNode.range[0] &&
|
|
127
|
-
token.range[1] ===
|
|
128
|
-
baseNameNode.range[1]) {
|
|
129
|
-
tokens.splice(index, 1);
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
144
|
+
context.addRemoveToken((token) => token.range[0] ===
|
|
145
|
+
baseNameNode.range[0] &&
|
|
146
|
+
token.range[1] ===
|
|
147
|
+
baseNameNode.range[1]);
|
|
133
148
|
return true;
|
|
134
149
|
}
|
|
135
150
|
return false;
|
|
@@ -162,8 +177,8 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
162
177
|
}
|
|
163
178
|
else if (attr.kind === "template-literal") {
|
|
164
179
|
const attrStart = attr.position.start.offset;
|
|
165
|
-
const start = (0, astro_1.
|
|
166
|
-
const end = (0, astro_1.
|
|
180
|
+
const start = (0, astro_1.calcAttributeValueStartOffset)(attr, ctx);
|
|
181
|
+
const end = (0, astro_1.calcAttributeEndOffset)(attr, ctx);
|
|
167
182
|
script.appendOriginal(start);
|
|
168
183
|
script.appendScript("{");
|
|
169
184
|
script.appendOriginal(end);
|
|
@@ -181,7 +196,7 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
181
196
|
}
|
|
182
197
|
}
|
|
183
198
|
// Process for start tag close
|
|
184
|
-
const closing = (0, astro_1.getSelfClosingTag)(node,
|
|
199
|
+
const closing = (0, astro_1.getSelfClosingTag)(node, ctx);
|
|
185
200
|
if (closing && closing.end === ">") {
|
|
186
201
|
script.appendOriginal(closing.offset - 1);
|
|
187
202
|
script.appendScript("/");
|
|
@@ -214,23 +229,19 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
214
229
|
}
|
|
215
230
|
else if (node.type === "comment") {
|
|
216
231
|
const start = node.position.start.offset;
|
|
217
|
-
const
|
|
232
|
+
const end = (0, astro_1.getEndOffset)(node, ctx);
|
|
233
|
+
const length = end - start;
|
|
218
234
|
script.appendOriginal(start);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
script.appendOriginal(start + 1);
|
|
222
|
-
script.appendScript(`></`);
|
|
223
|
-
script.skipOriginalOffset(length - 2);
|
|
224
|
-
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
235
|
+
if (!fragmentOpened) {
|
|
236
|
+
openRootFragment(start);
|
|
225
237
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
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) => {
|
|
232
243
|
if (scriptNode.range[0] === start &&
|
|
233
|
-
scriptNode.type ===
|
|
244
|
+
scriptNode.type === types_1.AST_NODE_TYPES.JSXFragment) {
|
|
234
245
|
delete scriptNode.children;
|
|
235
246
|
delete scriptNode.openingFragment;
|
|
236
247
|
delete scriptNode.closingFragment;
|
|
@@ -238,27 +249,10 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
238
249
|
const commentNode = scriptNode;
|
|
239
250
|
commentNode.type = "AstroHTMLComment";
|
|
240
251
|
commentNode.value = node.value;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
}
|
|
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]);
|
|
262
256
|
return true;
|
|
263
257
|
}
|
|
264
258
|
return false;
|
|
@@ -270,40 +264,49 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
270
264
|
}
|
|
271
265
|
else if (node.type === "doctype") {
|
|
272
266
|
const start = node.position.start.offset;
|
|
273
|
-
const end = node
|
|
267
|
+
const end = (0, astro_1.getEndOffset)(node, ctx);
|
|
268
|
+
const length = end - start;
|
|
274
269
|
script.appendOriginal(start);
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
script.appendScript(`<></>`);
|
|
278
|
-
targetType = types_1.AST_NODE_TYPES.JSXFragment;
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
script.appendScript(`0;`);
|
|
282
|
-
targetType = types_1.AST_NODE_TYPES.ExpressionStatement;
|
|
270
|
+
if (!fragmentOpened) {
|
|
271
|
+
openRootFragment(start);
|
|
283
272
|
}
|
|
284
|
-
script.
|
|
285
|
-
script.
|
|
273
|
+
script.appendOriginal(start + 1);
|
|
274
|
+
script.appendScript(`></`);
|
|
275
|
+
script.skipOriginalOffset(length - 2);
|
|
276
|
+
script.appendOriginal(end);
|
|
277
|
+
script.addRestoreNodeProcess((scriptNode, context) => {
|
|
286
278
|
if (scriptNode.range[0] === start &&
|
|
287
|
-
scriptNode.type ===
|
|
279
|
+
scriptNode.type === types_1.AST_NODE_TYPES.JSXFragment) {
|
|
288
280
|
delete scriptNode.children;
|
|
289
281
|
delete scriptNode.openingFragment;
|
|
290
282
|
delete scriptNode.closingFragment;
|
|
291
283
|
delete scriptNode.expression;
|
|
292
284
|
const doctypeNode = scriptNode;
|
|
293
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]);
|
|
294
290
|
return true;
|
|
295
291
|
}
|
|
296
292
|
return false;
|
|
297
293
|
});
|
|
298
294
|
script.addToken("HTMLDocType", [start, end]);
|
|
299
295
|
}
|
|
296
|
+
else {
|
|
297
|
+
const start = node.position.start.offset;
|
|
298
|
+
script.appendOriginal(start);
|
|
299
|
+
if (!fragmentOpened) {
|
|
300
|
+
openRootFragment(start);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
300
303
|
}, (node, [parent]) => {
|
|
301
304
|
if ((0, astro_1.isTag)(node)) {
|
|
302
|
-
const closing = (0, astro_1.getSelfClosingTag)(node,
|
|
305
|
+
const closing = (0, astro_1.getSelfClosingTag)(node, ctx);
|
|
303
306
|
if (!closing) {
|
|
304
307
|
const end = (0, astro_1.getEndTag)(node, ctx);
|
|
305
308
|
if (!end) {
|
|
306
|
-
const offset = (0, astro_1.
|
|
309
|
+
const offset = (0, astro_1.calcContentEndOffset)(node, ctx);
|
|
307
310
|
script.appendOriginal(offset);
|
|
308
311
|
script.appendScript(`</${node.name}>`);
|
|
309
312
|
script.addRestoreNodeProcess((scriptNode, _result, parent) => {
|
|
@@ -328,17 +331,20 @@ function processTemplate(ctx, resultTemplate) {
|
|
|
328
331
|
const before = parent.children[index - 1];
|
|
329
332
|
if (before &&
|
|
330
333
|
((0, astro_1.isTag)(before) || before.type === "comment")) {
|
|
331
|
-
const end = (0, astro_1.
|
|
332
|
-
? (0, astro_1.getTagEndOffset)(node, ctx)
|
|
333
|
-
: (0, astro_1.getCommentEndOffset)(node, ctx);
|
|
334
|
+
const end = (0, astro_1.getEndOffset)(node, ctx);
|
|
334
335
|
script.appendOriginal(end);
|
|
335
336
|
script.appendScript("</>");
|
|
336
337
|
}
|
|
337
338
|
}
|
|
338
339
|
}
|
|
339
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
|
+
}
|
|
340
347
|
script.appendOriginal(ctx.code.length);
|
|
341
|
-
script.appendScript("</>");
|
|
342
348
|
return script;
|
|
343
349
|
/**
|
|
344
350
|
* Generate unique id
|
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.18",
|
|
4
|
+
"description": "Astro component parser for ESLint",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"lib"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"prebuild": "npm run -s clean",
|
|
14
14
|
"build": "tsc --project ./tsconfig.build.json",
|
|
15
15
|
"clean": "rimraf .nyc_output lib coverage",
|
|
16
|
-
"lint": "eslint . --ext .js,.ts,.json",
|
|
16
|
+
"lint": "eslint . --ext .js,.ts,.json,.astro,.svelte",
|
|
17
17
|
"eslint-fix": "npm run lint -- --fix",
|
|
18
18
|
"test": "mocha --require ts-node/register \"tests/src/**/*.ts\" --reporter dot --timeout 60000",
|
|
19
19
|
"cover": "nyc --reporter=lcov npm run test",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@ota-meshi/eslint-plugin": "^0.10.0",
|
|
52
|
+
"@ota-meshi/eslint-plugin-svelte": "^0.34.0",
|
|
52
53
|
"@types/benchmark": "^2.1.1",
|
|
53
54
|
"@types/chai": "^4.3.0",
|
|
54
55
|
"@types/debug": "^4.1.7",
|
|
@@ -60,21 +61,21 @@
|
|
|
60
61
|
"@types/semver": "^7.3.9",
|
|
61
62
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
|
62
63
|
"@typescript-eslint/parser": "^5.4.0",
|
|
64
|
+
"astro-eslint-parser": ">=0.0.15",
|
|
63
65
|
"benchmark": "^2.1.4",
|
|
64
66
|
"chai": "^4.3.4",
|
|
65
67
|
"code-red": "^0.2.3",
|
|
66
|
-
"eslint": "^8.
|
|
68
|
+
"eslint": "^8.15.0",
|
|
67
69
|
"eslint-config-prettier": "^8.3.0",
|
|
68
70
|
"eslint-formatter-codeframe": "^7.32.1",
|
|
69
71
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
70
|
-
"eslint-plugin-json-schema-validator": "^
|
|
72
|
+
"eslint-plugin-json-schema-validator": "^3.0.0",
|
|
71
73
|
"eslint-plugin-jsonc": "^2.0.0",
|
|
72
74
|
"eslint-plugin-node": "^11.1.0",
|
|
73
75
|
"eslint-plugin-node-dependencies": "^0.8.0",
|
|
74
76
|
"eslint-plugin-prettier": "^4.0.0",
|
|
75
77
|
"eslint-plugin-react": "^7.29.4",
|
|
76
78
|
"eslint-plugin-regexp": "^1.5.0",
|
|
77
|
-
"eslint-plugin-vue": "^8.0.3",
|
|
78
79
|
"estree-walker": "^3.0.0",
|
|
79
80
|
"locate-character": "^2.0.5",
|
|
80
81
|
"magic-string": "^0.26.0",
|
|
@@ -82,11 +83,12 @@
|
|
|
82
83
|
"mocha-chai-jest-snapshot": "^1.1.3",
|
|
83
84
|
"nyc": "^15.1.0",
|
|
84
85
|
"prettier": "^2.0.5",
|
|
85
|
-
"prettier-plugin-astro": "^0.0
|
|
86
|
+
"prettier-plugin-astro": "^0.1.0-0",
|
|
87
|
+
"prettier-plugin-svelte": "^2.7.0",
|
|
86
88
|
"semver": "^7.3.5",
|
|
87
89
|
"string-replace-loader": "^3.0.3",
|
|
90
|
+
"svelte": "^3.48.0",
|
|
88
91
|
"ts-node": "^10.4.0",
|
|
89
|
-
"typescript": "~4.6.0"
|
|
90
|
-
"vue-eslint-parser": "^8.0.1"
|
|
92
|
+
"typescript": "~4.6.0"
|
|
91
93
|
}
|
|
92
94
|
}
|
package/lib/ast.d.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { TSESTree } from "@typescript-eslint/types";
|
|
2
|
-
export declare type AstroNode = AstroProgram | AstroRootFragment | AstroHTMLComment | AstroDoctype | AstroShorthandAttribute | AstroTemplateLiteralAttribute | AstroRawText | AstroFragment;
|
|
3
|
-
/** Node of Astro program root */
|
|
4
|
-
export interface AstroProgram extends Omit<TSESTree.Program, "type" | "body"> {
|
|
5
|
-
type: "Program";
|
|
6
|
-
body: (TSESTree.Program["body"][number] | AstroRootFragment | AstroHTMLComment)[];
|
|
7
|
-
sourceType: "script" | "module";
|
|
8
|
-
comments: TSESTree.Comment[];
|
|
9
|
-
tokens: TSESTree.Token[];
|
|
10
|
-
parent: undefined;
|
|
11
|
-
}
|
|
12
|
-
/** Node of Astro fragment root */
|
|
13
|
-
export interface AstroRootFragment extends Omit<TSESTree.BaseNode, "type" | "parent"> {
|
|
14
|
-
type: "AstroRootFragment";
|
|
15
|
-
children: TSESTree.JSXFragment["children"];
|
|
16
|
-
parent: AstroProgram;
|
|
17
|
-
}
|
|
18
|
-
/** Node of Astro html comment */
|
|
19
|
-
export interface AstroHTMLComment extends Omit<TSESTree.BaseNode, "type" | "parent"> {
|
|
20
|
-
type: "AstroHTMLComment";
|
|
21
|
-
value: string;
|
|
22
|
-
parent: AstroRootFragment | TSESTree.JSXElement | TSESTree.JSXFragment;
|
|
23
|
-
}
|
|
24
|
-
/** Node of Astro doctype */
|
|
25
|
-
export interface AstroDoctype extends Omit<TSESTree.BaseNode, "type" | "parent"> {
|
|
26
|
-
type: "AstroDoctype";
|
|
27
|
-
parent: AstroRootFragment;
|
|
28
|
-
}
|
|
29
|
-
/** Node of Astro shorthand attribute */
|
|
30
|
-
export interface AstroShorthandAttribute extends Omit<TSESTree.JSXAttribute, "type" | "parent"> {
|
|
31
|
-
type: "AstroShorthandAttribute";
|
|
32
|
-
value: TSESTree.JSXExpressionContainer;
|
|
33
|
-
parent: TSESTree.JSXElement | TSESTree.JSXFragment;
|
|
34
|
-
}
|
|
35
|
-
/** Node of Astro template-literal attribute */
|
|
36
|
-
export interface AstroTemplateLiteralAttribute extends Omit<TSESTree.JSXAttribute, "type" | "parent"> {
|
|
37
|
-
type: "AstroTemplateLiteralAttribute";
|
|
38
|
-
value: TSESTree.JSXExpressionContainer & {
|
|
39
|
-
expression: TSESTree.TemplateLiteral;
|
|
40
|
-
};
|
|
41
|
-
parent: TSESTree.JSXElement | TSESTree.JSXFragment;
|
|
42
|
-
}
|
|
43
|
-
/** Node of Astro raw text */
|
|
44
|
-
export interface AstroRawText extends Omit<TSESTree.JSXText, "type" | "parent"> {
|
|
45
|
-
type: "AstroRawText";
|
|
46
|
-
parent: AstroRootFragment | TSESTree.JSXElement | TSESTree.JSXFragment;
|
|
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
|
-
}
|