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 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
+ [![sponsors](https://img.shields.io/badge/-Sponsor-fafbfc?logo=GitHub%20Sponsors)](https://github.com/sponsors/ota-meshi)
7
+
6
8
  [![NPM license](https://img.shields.io/npm/l/astro-eslint-parser.svg)](https://www.npmjs.com/package/astro-eslint-parser)
7
9
  [![NPM version](https://img.shields.io/npm/v/astro-eslint-parser.svg)](https://www.npmjs.com/package/astro-eslint-parser)
8
10
  [![NPM downloads](https://img.shields.io/badge/dynamic/json.svg?label=downloads&colorB=green&suffix=/day&query=$.downloads&uri=https://api.npmjs.org//downloads/point/last-day/astro-eslint-parser&maxAge=3600)](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
- "extends": "eslint:recommended",
45
- "overrides": [
46
- {
47
- "files": ["*.astro"],
48
- "parser": "astro-eslint-parser"
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
- ```console
55
- $ eslint "src/**/*.{js,astro}"
56
- # or
57
- $ eslint src --ext .astro
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
+ [![sponsors](https://img.shields.io/badge/-Sponsor-fafbfc?logo=GitHub%20Sponsors)](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
@@ -0,0 +1,6 @@
1
+ import type { TSESTree } from "@typescript-eslint/types";
2
+ export interface BaseNode {
3
+ loc: TSESTree.SourceLocation;
4
+ range: TSESTree.Range;
5
+ type: string;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export * from "./astro";
2
+ export * from "./jsx";
@@ -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);
@@ -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
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,4 +1,4 @@
1
- import type { AttributeNode, CommentNode, ExpressionNode, Node, ParentNode, TagLikeNode } from "@astrojs/compiler/types";
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 getStartTagEndOffset(node: TagLikeNode, ctx: Context): number;
19
- /**
20
- * Get end offset of tag
21
- */
22
- export declare function getTagEndOffset(node: TagLikeNode, ctx: Context): number;
23
- /**
24
- * Get end offset of Expression
25
- */
26
- export declare function getExpressionEndOffset(node: ExpressionNode, ctx: Context): number;
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 getAttributeEndOffset(node: AttributeNode, ctx: Context): number;
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 getAttributeValueStartOffset(node: AttributeNode, ctx: Context): number;
26
+ export declare function calcAttributeValueStartOffset(node: AttributeNode, ctx: Context): number;
35
27
  /**
36
- * Get end offset of comment
28
+ * Get end offset of tag
37
29
  */
38
- export declare function getCommentEndOffset(node: CommentNode, ctx: Context): number;
30
+ export declare function getEndOffset(node: Node, ctx: Context): number;
39
31
  /**
40
32
  * Get content end offset
41
33
  */
42
- export declare function getContentEndOffset(parent: ParentNode, ctx: Context): number;
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, parent: ParentNode, ctx: Context): null | {
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
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.skipSpaces = exports.getEndTag = exports.getSelfClosingTag = exports.getContentEndOffset = exports.getCommentEndOffset = exports.getAttributeValueStartOffset = exports.getAttributeEndOffset = exports.getExpressionEndOffset = exports.getTagEndOffset = exports.getStartTagEndOffset = exports.walk = exports.walkElements = exports.isParent = exports.isTag = void 0;
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 getStartTagEndOffset(node, ctx) {
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 = getAttributeEndOffset(lastAttr, ctx);
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.getStartTagEndOffset = getStartTagEndOffset;
66
- /**
67
- * Get end offset of tag
68
- */
69
- function getTagEndOffset(node, ctx) {
70
- var _a;
71
- if (((_a = node.position.end) === null || _a === void 0 ? void 0 : _a.offset) != null) {
72
- return node.position.end.offset;
73
- }
74
- let beforeIndex;
75
- if (node.children.length) {
76
- const lastChild = node.children[node.children.length - 1];
77
- beforeIndex = getEndOffset(lastChild, ctx);
78
- }
79
- else {
80
- beforeIndex = getStartTagEndOffset(node, ctx);
81
- }
82
- beforeIndex = skipSpaces(ctx.code, beforeIndex);
83
- if (ctx.code.startsWith(`</${node.name}`, beforeIndex)) {
84
- beforeIndex = beforeIndex + 2 + node.name.length;
85
- const info = getTokenInfo(ctx, [">"], beforeIndex);
86
- return info.index + info.match.length;
87
- }
88
- return beforeIndex;
89
- }
90
- exports.getTagEndOffset = getTagEndOffset;
91
- /**
92
- * Get end offset of Expression
93
- */
94
- function getExpressionEndOffset(node, ctx) {
95
- var _a;
96
- if (((_a = node.position.end) === null || _a === void 0 ? void 0 : _a.offset) != null) {
97
- return node.position.end.offset;
98
- }
99
- if (node.children.length) {
100
- const lastChild = node.children[node.children.length - 1];
101
- const beforeIndex = getEndOffset(lastChild, ctx);
102
- const info = getTokenInfo(ctx, ["}"], beforeIndex);
103
- return info.index + info.match.length;
104
- }
105
- const info = getTokenInfo(ctx, ["{", "}"], node.position.start.offset);
106
- return info.index + info.match.length;
107
- }
108
- exports.getExpressionEndOffset = getExpressionEndOffset;
65
+ exports.calcStartTagEndOffset = calcStartTagEndOffset;
109
66
  /**
110
67
  * Get end offset of attribute
111
68
  */
112
- function getAttributeEndOffset(node, ctx) {
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]], getAttributeValueStartOffset(node, ctx));
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, "}"], getAttributeValueStartOffset(node, ctx));
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}\``], getAttributeValueStartOffset(node, ctx));
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.getAttributeEndOffset = getAttributeEndOffset;
94
+ exports.calcAttributeEndOffset = calcAttributeEndOffset;
138
95
  /**
139
96
  * Get start offset of attribute value
140
97
  */
141
- function getAttributeValueStartOffset(node, ctx) {
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.getAttributeValueStartOffset = getAttributeValueStartOffset;
114
+ exports.calcAttributeValueStartOffset = calcAttributeValueStartOffset;
158
115
  /**
159
- * Get end offset of comment
116
+ * Get end offset of tag
160
117
  */
161
- function getCommentEndOffset(node, ctx) {
162
- const info = getTokenInfo(ctx, ["<!--", node.value, "-->"], node.position.start.offset);
163
- return info.index + info.match.length;
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.getCommentEndOffset = getCommentEndOffset;
146
+ exports.getEndOffset = getEndOffset;
166
147
  /**
167
148
  * Get content end offset
168
149
  */
169
- function getContentEndOffset(parent, ctx) {
150
+ function calcContentEndOffset(parent, ctx) {
170
151
  const code = ctx.code;
171
152
  if (isTag(parent)) {
172
- const end = getTagEndOffset(parent, ctx);
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 = getExpressionEndOffset(parent, ctx);
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.getContentEndOffset = getContentEndOffset;
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, parent, ctx) {
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
- let nextElementIndex = code.length;
202
- const childIndex = parent.children.indexOf(node);
203
- if (childIndex === parent.children.length - 1) {
204
- // last
205
- nextElementIndex = getContentEndOffset(parent, ctx);
206
- }
207
- else {
208
- const next = parent.children[childIndex + 1];
209
- nextElementIndex = next.position.start.offset;
182
+ const startTagEndOffset = calcStartTagEndOffset(node, ctx);
183
+ if (code.startsWith("/>", startTagEndOffset - 2)) {
184
+ return {
185
+ offset: startTagEndOffset,
186
+ end: "/>",
187
+ };
210
188
  }
211
- const endOffset = getStartTagEndOffset(node, ctx);
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: endOffset,
218
- end: code.slice(endOffset - 2, endOffset) === "/>" ? "/>" : ">",
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 = getStartTagEndOffset(node, ctx);
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 getEndOffset(node, ctx) {
252
- var _a;
253
- if (((_a = node.position.end) === null || _a === void 0 ? void 0 : _a.offset) != null) {
254
- return node.position.end.offset;
255
- }
256
- if (isTag(node))
257
- return getTagEndOffset(node, ctx);
258
- if (node.type === "expression")
259
- return getExpressionEndOffset(node, ctx);
260
- if (node.type === "comment")
261
- return getCommentEndOffset(node, ctx);
262
- if (node.type === "frontmatter") {
263
- const start = node.position.start.offset;
264
- return ctx.code.indexOf("---", start + 3) + 3;
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
- if (node.type === "doctype") {
267
- const start = node.position.start.offset;
268
- return ctx.code.indexOf(">", start) + 1;
241
+ else {
242
+ beforeIndex = calcStartTagEndOffset(node, ctx);
269
243
  }
270
- if (node.type === "text") {
271
- const start = node.position.start.offset;
272
- return start + node.value.length;
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
- if (node.type === "root") {
275
- return ctx.code.length;
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
- throw new Error(`unknown type: ${node.type}`);
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
@@ -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, result: ESLintExtendedProgram, parent: TSESTree.Node) => boolean): void;
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 {};
@@ -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
- const errors_1 = require("../errors");
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
- const last = result.ast.body[result.ast.body.length - 1];
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, result, parent));
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 first = result.ast.body[0];
88
- if (first.range[0] < result.ast.range[0]) {
89
- result.ast.range[0] = first.range[0];
90
- result.ast.loc.start = this.ctx.getLocFromIndex(result.ast.range[0]);
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.getStartTagEndOffset)(node, ctx);
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.getAttributeEndOffset)(node, ctx);
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.getCommentEndOffset)(node, ctx);
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.getStartTagEndOffset)(parent, ctx);
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, parent, ctx)) {
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
- if (!frontmatter) {
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.position.end.offset;
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, result) => {
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
- const tokens = result.ast.tokens || [];
123
- for (let index = 0; index < tokens.length; index++) {
124
- const token = tokens[index];
125
- if (token.range[0] ===
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.getAttributeValueStartOffset)(attr, ctx);
166
- const end = (0, astro_1.getAttributeEndOffset)(attr, ctx);
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, parent, ctx);
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 length = 4 + node.value.length + 3;
232
+ const end = (0, astro_1.getEndOffset)(node, ctx);
233
+ const length = end - start;
218
234
  script.appendOriginal(start);
219
- let targetType;
220
- if (fragmentOpened) {
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
- else {
227
- script.appendScript(`0;`);
228
- targetType = types_1.AST_NODE_TYPES.ExpressionStatement;
229
- script.skipOriginalOffset(length);
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 === targetType) {
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
- if (fragmentOpened) {
242
- const removeTokenSet = new Set([
243
- (token) => token.value === "<" &&
244
- token.range[0] === scriptNode.range[0],
245
- (token) => token.value === ">" &&
246
- token.range[1] === scriptNode.range[1],
247
- ]);
248
- const tokens = result.ast.tokens || [];
249
- for (let index = tokens.length - 1; index >= 0; index--) {
250
- const token = tokens[index];
251
- for (const rt of removeTokenSet) {
252
- if (rt(token)) {
253
- tokens.splice(index, 1);
254
- removeTokenSet.delete(rt);
255
- if (!removeTokenSet.size) {
256
- break;
257
- }
258
- }
259
- }
260
- }
261
- }
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.position.end.offset;
267
+ const end = (0, astro_1.getEndOffset)(node, ctx);
268
+ const length = end - start;
274
269
  script.appendOriginal(start);
275
- let targetType;
276
- if (fragmentOpened) {
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.skipOriginalOffset(end - start);
285
- script.addRestoreNodeProcess((scriptNode) => {
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 === targetType) {
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, parent, ctx);
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.getContentEndOffset)(node, ctx);
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.isTag)(node)
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
@@ -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
- AstroRootFragment: ["children"],
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.15",
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.14.0",
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": "^2.1.6",
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.12",
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
- }