astro-eslint-parser 0.2.0 → 0.3.0

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
@@ -17,7 +17,7 @@ You can check it on [Online DEMO](https://ota-meshi.github.io/astro-eslint-parse
17
17
 
18
18
  This parser is in the ***experimental stages*** of development.
19
19
 
20
- At least it works fine with a [fork of the `astro.build` repository](https://github.com/ota-meshi/astro.build/tree/eslint).
20
+ At least it works fine with a [withastro/docs](https://github.com/withastro/docs) repository.
21
21
 
22
22
  [@astrojs/compiler]: https://github.com/withastro/compiler
23
23
 
@@ -33,8 +33,6 @@ This parser allows us to lint the script of `.astro` files.
33
33
 
34
34
  ESLint plugin for Astro component.
35
35
 
36
- [eslint-plugin-astro]: https://ota-meshi.github.io/eslint-plugin-astro/
37
-
38
36
  ## 💿 Installation
39
37
 
40
38
  ```bash
@@ -128,6 +126,48 @@ module.exports = {
128
126
  }
129
127
  ```
130
128
 
129
+ ### parserOptions.astroFeatures
130
+
131
+ You can use `parserOptions.astroFeatures` property to specify how to parse related to Astro component features. For example:
132
+
133
+ ```json
134
+ {
135
+ "parser": "astro-eslint-parser",
136
+ "parserOptions": {
137
+ "astroFeatures": {
138
+ "syntax": "auto",
139
+ }
140
+ }
141
+ }
142
+ ```
143
+
144
+ ### parserOptions.vueFeatures.syntax
145
+
146
+ You can use `parserOptions.vueFeatures.syntax` property to choose whether to parse as Astro Component (`*.astro`) or Astro Markdown Page (`*.md`).
147
+ If `"astro"` is specified, it will be parsed as `*.astro`. If `"markdown"` is specified, it will be parsed as `*.md`. If `"auto"` is specified, it will be automatically selected from the file extensions.
148
+ For example:
149
+
150
+ ```json
151
+ {
152
+ "parser": "astro-eslint-parser",
153
+ "parserOptions": {
154
+ "astroFeatures": {
155
+ "syntax": "auto", // or "astro", or "markdown"
156
+ }
157
+ }
158
+ }
159
+ ```
160
+
161
+ #### Known Limitations on Markdown Pages
162
+
163
+ There are some known limitations when parsing Markdown Pages for ESLint integration.
164
+
165
+ - Incompatible with ESLint's [indent] rule. Turn off the [indent] rule in the markdown file. Otherwise the file syntax will be broken.
166
+ - Incompatible with [eslint-plugin-markdown]. eslint-plugin-markdown separates the contents of markdown by the processor. So using this parser doesn't work because the parser doesn't know the whole markdown.
167
+
168
+ [indent]: https://eslint.org/docs/rules/indent
169
+ [eslint-plugin-markdown]: https://github.com/eslint/eslint-plugin-markdown
170
+
131
171
  ## :computer: Editor Integrations
132
172
 
133
173
  ### Visual Studio Code
@@ -167,11 +207,11 @@ See also [`@typescript-eslint/parser` readme](https://github.com/typescript-esli
167
207
 
168
208
  ## :hammer_and_wrench: Usage for Custom Rules / Plugins
169
209
 
210
+ <!-- - [AST.md](./docs/AST.md) is AST specification. You can check it on the [Online DEMO](https://ota-meshi.github.io/astro-eslint-parser/). -->
211
+
170
212
  - TBA
171
213
  - 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.
172
-
173
- <!-- - [AST.md](./docs/AST.md) is AST specification. You can check it on the [Online DEMO](https://ota-meshi.github.io/astro-eslint-parser/). -->
174
- <!-- - I have already [implemented some rules] in the [`@ota-meshi/eslint-plugin-astro`]. The source code for these rules will be helpful to you. -->
214
+ - I have already [implemented some rules] in the [eslint-plugin-astro]. The source code for these rules will be helpful to you.
175
215
 
176
216
  ## :beers: Contributing
177
217
 
@@ -191,3 +231,5 @@ See the [LICENSE](LICENSE) file for license rights and limitations (MIT).
191
231
 
192
232
  [Astro]: https://astro.build/
193
233
  [ESLint]: https://eslint.org/
234
+ [eslint-plugin-astro]: https://ota-meshi.github.io/eslint-plugin-astro/
235
+ [implemented some rules]: https://ota-meshi.github.io/eslint-plugin-astro/rules/
@@ -2,7 +2,7 @@ import type { JSXAttribute, JSXElement, JSXExpression, JSXExpressionContainer, J
2
2
  import type { TSESTree as ES } from "@typescript-eslint/types";
3
3
  import type { BaseNode } from "./base";
4
4
  export declare type AstroNode = AstroProgram | AstroFragment | AstroHTMLComment | AstroDoctype | AstroShorthandAttribute | AstroTemplateLiteralAttribute | AstroRawText;
5
- export declare type AstroChild = JSXElement | JSXFragment | JSXExpression | JSXText | AstroHTMLComment;
5
+ export declare type AstroChild = JSXElement | JSXFragment | JSXExpression | JSXText | AstroHTMLComment | AstroRawText;
6
6
  export declare type AstroParentNode = JSXElement | JSXFragment | AstroFragment;
7
7
  /** Node of Astro program root */
8
8
  export interface AstroProgram extends Omit<ES.Program, "type" | "body"> {
package/lib/ast/jsx.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import type { TSESTree as ES } from "@typescript-eslint/types";
2
- import type { AstroFragment, AstroHTMLComment, AstroShorthandAttribute, AstroTemplateLiteralAttribute } from "./astro";
2
+ import type { AstroFragment, AstroHTMLComment, AstroRawText, AstroShorthandAttribute, AstroTemplateLiteralAttribute } from "./astro";
3
3
  import type { BaseNode } from "./base";
4
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;
5
+ export declare type JSXChild = JSXElement | JSXFragment | JSXExpression | JSXText | AstroHTMLComment | AstroRawText;
6
6
  export declare type JSXParentNode = JSXElement | JSXFragment | AstroFragment;
7
7
  export interface JSXElement extends BaseNode {
8
8
  type: "JSXElement";
@@ -4,8 +4,10 @@ import type { ESLintExtendedProgram } from "../types";
4
4
  declare class RestoreNodeProcessContext {
5
5
  readonly result: ESLintExtendedProgram;
6
6
  readonly removeTokens: Set<(token: TSESTree.Token) => boolean>;
7
- constructor(result: ESLintExtendedProgram);
7
+ private readonly parentMap;
8
+ constructor(result: ESLintExtendedProgram, parentMap: Map<TSESTree.Node, TSESTree.Node | null>);
8
9
  addRemoveToken(test: (token: TSESTree.Token) => boolean): void;
10
+ getParent(node: TSESTree.Node): TSESTree.Node | null;
9
11
  }
10
12
  export declare class ScriptContext {
11
13
  private readonly ctx;
@@ -16,11 +18,12 @@ export declare class ScriptContext {
16
18
  private readonly tokens;
17
19
  private readonly restoreNodeProcesses;
18
20
  constructor(ctx: Context);
21
+ get originalCode(): string;
19
22
  skipOriginalOffset(offset: number): void;
20
23
  appendOriginal(index: number): void;
21
24
  appendScript(fragment: string): void;
22
25
  addToken(type: TSESTree.Token["type"], range: TSESTree.Range): void;
23
- addRestoreNodeProcess(process: (node: TSESTree.Node, context: RestoreNodeProcessContext, parent: TSESTree.Node) => boolean): void;
26
+ addRestoreNodeProcess(process: (node: TSESTree.Node, context: RestoreNodeProcessContext) => boolean): void;
24
27
  /**
25
28
  * Restore AST nodes
26
29
  */
@@ -3,13 +3,17 @@ 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) {
6
+ constructor(result, parentMap) {
7
7
  this.removeTokens = new Set();
8
8
  this.result = result;
9
+ this.parentMap = parentMap;
9
10
  }
10
11
  addRemoveToken(test) {
11
12
  this.removeTokens.add(test);
12
13
  }
14
+ getParent(node) {
15
+ return this.parentMap.get(node) || null;
16
+ }
13
17
  }
14
18
  class ScriptContext {
15
19
  constructor(ctx) {
@@ -21,6 +25,9 @@ class ScriptContext {
21
25
  this.restoreNodeProcesses = [];
22
26
  this.ctx = ctx;
23
27
  }
28
+ get originalCode() {
29
+ return this.ctx.code;
30
+ }
24
31
  skipOriginalOffset(offset) {
25
32
  this.consumedIndex += offset;
26
33
  }
@@ -41,6 +48,9 @@ class ScriptContext {
41
48
  this.fragments.push({ start, end: this.script.length });
42
49
  }
43
50
  addToken(type, range) {
51
+ if (range[0] >= range[1]) {
52
+ return;
53
+ }
44
54
  this.tokens.push(this.ctx.buildToken(type, range));
45
55
  }
46
56
  addRestoreNodeProcess(process) {
@@ -76,12 +86,12 @@ class ScriptContext {
76
86
  for (const token of result.ast.comments || []) {
77
87
  this.remapLocation(token);
78
88
  }
79
- const context = new RestoreNodeProcessContext(result);
89
+ const context = new RestoreNodeProcessContext(result, traversed);
80
90
  let restoreNodeProcesses = this.restoreNodeProcesses;
81
91
  for (const [node, parent] of traversed) {
82
92
  if (!parent)
83
93
  continue;
84
- restoreNodeProcesses = restoreNodeProcesses.filter((proc) => !proc(node, context, parent));
94
+ restoreNodeProcesses = restoreNodeProcesses.filter((proc) => !proc(node, context));
85
95
  }
86
96
  if (context.removeTokens.size) {
87
97
  const tokens = result.ast.tokens || [];
package/lib/index.d.ts CHANGED
@@ -1,9 +1,13 @@
1
- import { parseForESLint } from "./parser";
1
+ import { parseForESLint as parseAstro } from "./parser";
2
+ import { parseForESLint as parseMarkdown } from "./markdown";
2
3
  import { parseTemplate, ParseTemplateResult } from "./astro-tools";
3
4
  import * as AST from "./ast";
4
5
  import { traverseNodes } from "./traverse";
5
6
  import { ParseError } from "./errors";
6
7
  export { AST, ParseError };
7
- export { parseForESLint };
8
+ /**
9
+ * Parse source code
10
+ */
11
+ export declare function parseForESLint(code: string, options?: any): ReturnType<typeof parseAstro | typeof parseMarkdown>;
8
12
  export declare const VisitorKeys: import("eslint").SourceCode.VisitorKeys;
9
13
  export { traverseNodes, parseTemplate, ParseTemplateResult };
package/lib/index.js CHANGED
@@ -25,7 +25,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.parseTemplate = exports.traverseNodes = exports.VisitorKeys = exports.parseForESLint = exports.ParseError = exports.AST = void 0;
27
27
  const parser_1 = require("./parser");
28
- Object.defineProperty(exports, "parseForESLint", { enumerable: true, get: function () { return parser_1.parseForESLint; } });
28
+ const markdown_1 = require("./markdown");
29
29
  const astro_tools_1 = require("./astro-tools");
30
30
  Object.defineProperty(exports, "parseTemplate", { enumerable: true, get: function () { return astro_tools_1.parseTemplate; } });
31
31
  const AST = __importStar(require("./ast"));
@@ -35,6 +35,18 @@ Object.defineProperty(exports, "traverseNodes", { enumerable: true, get: functio
35
35
  const visitor_keys_1 = require("./visitor-keys");
36
36
  const errors_1 = require("./errors");
37
37
  Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return errors_1.ParseError; } });
38
+ /**
39
+ * Parse source code
40
+ */
41
+ function parseForESLint(code, options) {
42
+ const syntax = options?.astroFeatures?.syntax ?? "auto";
43
+ if (syntax === "markdown" ||
44
+ (syntax === "auto" && options?.filePath?.endsWith(".md"))) {
45
+ return (0, markdown_1.parseForESLint)(code, options);
46
+ }
47
+ return (0, parser_1.parseForESLint)(code, options);
48
+ }
49
+ exports.parseForESLint = parseForESLint;
38
50
  // Keys
39
51
  // eslint-disable-next-line @typescript-eslint/naming-convention -- ignore
40
52
  exports.VisitorKeys = visitor_keys_1.KEYS;
@@ -0,0 +1,9 @@
1
+ export declare type MarkdownContent = {
2
+ range: [number, number];
3
+ value: string;
4
+ };
5
+ /** Parse frontmatter */
6
+ export declare function parseFrontmatter(code: string): {
7
+ frontmatter: MarkdownContent | null;
8
+ content: MarkdownContent;
9
+ };
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseFrontmatter = void 0;
4
+ /** Parse frontmatter */
5
+ function parseFrontmatter(code) {
6
+ if (!code.startsWith("---") || code[3] === "-") {
7
+ return {
8
+ frontmatter: null,
9
+ content: {
10
+ range: [0, code.length],
11
+ value: code,
12
+ },
13
+ };
14
+ }
15
+ let closeIndex = code.indexOf("\n---", 3);
16
+ let contentOpenIndex = closeIndex + 4;
17
+ if (closeIndex === -1) {
18
+ closeIndex = code.length;
19
+ contentOpenIndex = code.length;
20
+ }
21
+ let openIndex = 3;
22
+ if (code[openIndex] === "\r") {
23
+ openIndex++;
24
+ }
25
+ if (code[openIndex] === "\n") {
26
+ openIndex++;
27
+ }
28
+ if (openIndex < closeIndex && code[closeIndex] === "\n") {
29
+ closeIndex--;
30
+ }
31
+ if (openIndex < closeIndex && code[closeIndex] === "\r") {
32
+ closeIndex++;
33
+ }
34
+ if (code[contentOpenIndex] === "\r") {
35
+ contentOpenIndex++;
36
+ }
37
+ if (code[contentOpenIndex] === "\n") {
38
+ contentOpenIndex++;
39
+ }
40
+ const frontmatter = code.slice(openIndex, closeIndex + 1);
41
+ const content = code.slice(contentOpenIndex);
42
+ return {
43
+ frontmatter: {
44
+ range: [openIndex, closeIndex + 1],
45
+ value: frontmatter,
46
+ },
47
+ content: {
48
+ range: [contentOpenIndex, code.length],
49
+ value: content,
50
+ },
51
+ };
52
+ }
53
+ exports.parseFrontmatter = parseFrontmatter;
@@ -0,0 +1,19 @@
1
+ import type { ScopeManager } from "eslint-scope";
2
+ import type { AstroProgram } from "../ast";
3
+ import type { ParseResult } from "@astrojs/compiler/node";
4
+ /**
5
+ * Parse source code
6
+ */
7
+ export declare function parseForESLint(code: string, options?: any): {
8
+ ast: AstroProgram;
9
+ services: Record<string, any> & {
10
+ isAstro: true;
11
+ getAstroAst: () => ParseResult;
12
+ isAstroMarkdown: true;
13
+ getAstroMarkdownFrontmatter: () => any;
14
+ };
15
+ visitorKeys: {
16
+ [type: string]: string[];
17
+ };
18
+ scopeManager: ScopeManager;
19
+ };
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseForESLint = void 0;
4
+ const context_1 = require("../context");
5
+ const mdast_util_from_markdown_service_1 = require("./mdast-util-from-markdown-service");
6
+ const frontmatter_1 = require("./frontmatter");
7
+ const process_markdown_1 = require("./process-markdown");
8
+ const parser_1 = require("../parser");
9
+ const sort_1 = require("../parser/sort");
10
+ const script_1 = require("../context/script");
11
+ const yaml_1 = require("./yaml");
12
+ /**
13
+ * Parse source code
14
+ */
15
+ function parseForESLint(code, options) {
16
+ const { frontmatter, content } = (0, frontmatter_1.parseFrontmatter)(code);
17
+ const ctx = new context_1.Context(code);
18
+ const root = (0, mdast_util_from_markdown_service_1.parseMarkdown)(content.value);
19
+ const scriptContext = new script_1.ScriptContext(ctx);
20
+ let yamlResult;
21
+ if (frontmatter) {
22
+ scriptContext.appendOriginal(frontmatter.range[0]);
23
+ yamlResult = (0, yaml_1.parseYaml)(frontmatter);
24
+ if (yamlResult) {
25
+ scriptContext.skipOriginalOffset(yamlResult.setupValueRange[0] - frontmatter.range[0]);
26
+ scriptContext.appendOriginal(yamlResult.setupValueRange[1]);
27
+ scriptContext.skipOriginalOffset(frontmatter.range[1] - yamlResult.setupValueRange[1]);
28
+ scriptContext.addToken("YAMLToken", [
29
+ frontmatter.range[0],
30
+ yamlResult.before,
31
+ ]);
32
+ scriptContext.addToken("YAMLToken", [
33
+ yamlResult.after,
34
+ frontmatter.range[1],
35
+ ]);
36
+ }
37
+ else {
38
+ scriptContext.skipOriginalOffset(frontmatter.range[1] - frontmatter.range[0]);
39
+ scriptContext.addToken("YAMLToken", [
40
+ frontmatter.range[0],
41
+ frontmatter.range[1],
42
+ ]);
43
+ }
44
+ }
45
+ (0, process_markdown_1.processMarkdown)(scriptContext, root, content.range[0]);
46
+ scriptContext.appendOriginal(code.length);
47
+ const resultAstro = (0, parser_1.parseForESLint)(scriptContext.script, options);
48
+ scriptContext.restore(resultAstro);
49
+ (0, sort_1.sort)(resultAstro.ast.comments);
50
+ (0, sort_1.sort)(resultAstro.ast.tokens);
51
+ (0, parser_1.extractTokens)(resultAstro, ctx);
52
+ resultAstro.services = Object.assign(resultAstro.services || {}, {
53
+ isAstroMarkdown: true,
54
+ getAstroMarkdownFrontmatter() {
55
+ return yamlResult?.getYamlValue();
56
+ },
57
+ });
58
+ return resultAstro;
59
+ }
60
+ exports.parseForESLint = parseForESLint;
@@ -0,0 +1,5 @@
1
+ import type * as mdast from "mdast";
2
+ /**
3
+ * Parse code by `mdast-util-from-markdown`
4
+ */
5
+ export declare function parseMarkdown(code: string): mdast.Root;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseMarkdown = void 0;
4
+ const synckit_1 = require("synckit");
5
+ const parseSync = (0, synckit_1.createSyncFn)(require.resolve("./mdast-util-from-markdown-worker"));
6
+ /**
7
+ * Parse code by `mdast-util-from-markdown`
8
+ */
9
+ function parseMarkdown(code) {
10
+ return parseSync(code);
11
+ }
12
+ exports.parseMarkdown = parseMarkdown;
@@ -0,0 +1,2 @@
1
+ import type * as mdast from "mdast";
2
+ export declare type ParseSyncFunction = (source: string) => mdast.Root;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const synckit_1 = require("synckit");
4
+ const dynamicImport = new Function("m", "return import(m)");
5
+ (0, synckit_1.runAsWorker)(async (source) => {
6
+ const { fromMarkdown } = await dynamicImport("mdast-util-from-markdown");
7
+ return fromMarkdown(source);
8
+ });
@@ -0,0 +1,6 @@
1
+ import type * as mdast from "mdast";
2
+ import type { ScriptContext } from "../context/script";
3
+ /**
4
+ * Process the markdown ast to generate a ScriptContext.
5
+ */
6
+ export declare function processMarkdown(script: ScriptContext, root: mdast.Root, offset: number): void;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processMarkdown = void 0;
4
+ const types_1 = require("@typescript-eslint/types");
5
+ /**
6
+ * Process the markdown ast to generate a ScriptContext.
7
+ */
8
+ function processMarkdown(script, root, offset) {
9
+ walk(root, (node) => {
10
+ const start = node.position.start.offset + offset;
11
+ const end = node.position.end.offset + offset;
12
+ script.appendOriginal(start);
13
+ if (node.type === "code" || node.type === "inlineCode") {
14
+ script.appendScript(`<></>`);
15
+ script.skipOriginalOffset(end - start);
16
+ script.addRestoreNodeProcess((scriptNode, context) => {
17
+ if (scriptNode.range[0] === start &&
18
+ scriptNode.type === types_1.AST_NODE_TYPES.JSXFragment) {
19
+ delete scriptNode.openingFragment;
20
+ delete scriptNode.closingFragment;
21
+ delete scriptNode.expression;
22
+ delete scriptNode.children;
23
+ const text = script.originalCode.slice(start, end);
24
+ const mdNode = scriptNode;
25
+ mdNode.type = "AstroRawText";
26
+ mdNode.value = text;
27
+ mdNode.raw = text;
28
+ let parent = context.getParent(scriptNode);
29
+ while (parent) {
30
+ let update = false;
31
+ if (parent.range[0] > scriptNode.range[0]) {
32
+ parent.range[0] = scriptNode.range[0];
33
+ parent.loc.start = {
34
+ line: scriptNode.loc.start.line,
35
+ column: scriptNode.loc.start.column,
36
+ };
37
+ update = true;
38
+ }
39
+ if (parent.range[1] < scriptNode.range[1]) {
40
+ parent.range[1] = scriptNode.range[1];
41
+ parent.loc.end = {
42
+ line: scriptNode.loc.end.line,
43
+ column: scriptNode.loc.end.column,
44
+ };
45
+ update = true;
46
+ }
47
+ if (!update) {
48
+ break;
49
+ }
50
+ parent = context.getParent(parent);
51
+ }
52
+ return true;
53
+ }
54
+ return false;
55
+ });
56
+ script.addToken(types_1.AST_TOKEN_TYPES.JSXText, [start, end]);
57
+ }
58
+ }, (node) => {
59
+ // const start = node.position!.start.offset! + offset
60
+ const end = node.position.end.offset + offset;
61
+ script.appendOriginal(end);
62
+ });
63
+ script.addRestoreNodeProcess((scriptNode) => {
64
+ if (scriptNode.type === "JSXText") {
65
+ const rawNode = scriptNode;
66
+ rawNode.type = "AstroRawText";
67
+ }
68
+ return false;
69
+ });
70
+ }
71
+ exports.processMarkdown = processMarkdown;
72
+ /** walk nodes */
73
+ function walk(parent, enter, leave, parents = []) {
74
+ const currParents = [parent, ...parents];
75
+ if ("children" in parent) {
76
+ for (const node of parent.children) {
77
+ enter(node, currParents);
78
+ walk(node, enter, leave, currParents);
79
+ leave(node, currParents);
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,9 @@
1
+ import type { MarkdownContent } from "./frontmatter";
2
+ export declare type FrontmatterYAMLResult = {
3
+ before: number;
4
+ setupValueRange: [number, number];
5
+ after: number;
6
+ getYamlValue: () => any;
7
+ };
8
+ /** Parse for yaml */
9
+ export declare function parseYaml(frontmatter: MarkdownContent): FrontmatterYAMLResult | null;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseYaml = void 0;
4
+ const yaml_1 = require("yaml");
5
+ /** Parse for yaml */
6
+ function parseYaml(frontmatter) {
7
+ const doc = (0, yaml_1.parseDocument)(frontmatter.value, { keepSourceTokens: true });
8
+ const range = getSetupRange(doc);
9
+ if (!range) {
10
+ return null;
11
+ }
12
+ let before = range[0];
13
+ let after = range[1];
14
+ while (isSpace(frontmatter.value[before - 1])) {
15
+ before--;
16
+ }
17
+ while (isSpace(frontmatter.value[after])) {
18
+ after++;
19
+ }
20
+ return {
21
+ before: before + frontmatter.range[0],
22
+ setupValueRange: [
23
+ range[0] + frontmatter.range[0],
24
+ range[1] + frontmatter.range[0],
25
+ ],
26
+ after: after + frontmatter.range[0],
27
+ getYamlValue: () => doc.toJS(),
28
+ };
29
+ }
30
+ exports.parseYaml = parseYaml;
31
+ /** Checks whether the given char is spaces, or not */
32
+ function isSpace(c) {
33
+ return c && !c.trim();
34
+ }
35
+ /** Get setup range */
36
+ function getSetupRange(ast) {
37
+ if (!ast.contents) {
38
+ return null;
39
+ }
40
+ if (!(0, yaml_1.isMap)(ast.contents)) {
41
+ return null;
42
+ }
43
+ for (const item of ast.contents.items) {
44
+ if (!(0, yaml_1.isScalar)(item.key) || !(0, yaml_1.isScalar)(item.value)) {
45
+ continue;
46
+ }
47
+ if (item.key.value !== "setup") {
48
+ continue;
49
+ }
50
+ if (item.value.type === "PLAIN") {
51
+ return [item.value.range[0], item.value.range[1]];
52
+ }
53
+ if (item.value.type === "QUOTE_DOUBLE" ||
54
+ item.value.type === "QUOTE_SINGLE") {
55
+ return [item.value.range[0] + 1, item.value.range[1] - 1];
56
+ }
57
+ if (item.value.type === "BLOCK_FOLDED" ||
58
+ item.value.type === "BLOCK_LITERAL") {
59
+ const cst = item.value.srcToken;
60
+ let blockStart = cst.offset;
61
+ for (const token of cst.props) {
62
+ if (isCommentOrSpace(token) ||
63
+ token.type === "block-scalar-header") {
64
+ blockStart = token.offset + token.source.length;
65
+ continue;
66
+ }
67
+ /* istanbul ignore next */
68
+ throw new Error(`Unknown token:${token.type}`);
69
+ }
70
+ return [blockStart, item.value.range[1]];
71
+ }
72
+ break;
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Checks whether the given cst is comments, spaces, or not
78
+ */
79
+ function isCommentOrSpace(node) {
80
+ if (node.type === "space" ||
81
+ node.type === "newline" ||
82
+ node.type === "comment") {
83
+ return true;
84
+ }
85
+ return false;
86
+ }
@@ -52,38 +52,44 @@ function adjustHTML(ast, htmlElement, ctx) {
52
52
  if (htmlEnd == null) {
53
53
  return;
54
54
  }
55
+ const hasTokenAfter = Boolean(ctx.code.slice(htmlEnd + 7).trim());
55
56
  const children = [...htmlElement.children];
56
57
  for (const child of children) {
57
58
  const offset = child.position?.start.offset;
58
- if (offset != null) {
59
+ if (hasTokenAfter && offset != null) {
59
60
  if (htmlEnd <= offset) {
60
61
  htmlElement.children.splice(htmlElement.children.indexOf(child), 1);
61
62
  ast.children.push(child);
62
63
  }
63
64
  }
64
65
  if (child.type === "element" && child.name === "body") {
65
- adjustHTMLBody(ast, htmlElement, htmlEnd, child, ctx);
66
+ adjustHTMLBody(ast, htmlElement, htmlEnd, hasTokenAfter, child, ctx);
66
67
  }
67
68
  }
68
69
  }
69
70
  /**
70
71
  * Adjust <body> element node
71
72
  */
72
- function adjustHTMLBody(ast, htmlElement, htmlEnd, bodyElement, ctx) {
73
+ function adjustHTMLBody(ast, htmlElement, htmlEnd, hasTokenAfterHtmlEnd, bodyElement, ctx) {
73
74
  const bodyEnd = ctx.code.indexOf("</body");
74
75
  if (bodyEnd == null) {
75
76
  return;
76
77
  }
78
+ const hasTokenAfter = Boolean(ctx.code.slice(bodyEnd + 7, htmlEnd).trim());
79
+ if (!hasTokenAfter && !hasTokenAfterHtmlEnd) {
80
+ return;
81
+ }
77
82
  const children = [...bodyElement.children];
78
83
  for (const child of children) {
79
84
  const offset = child.position?.start.offset;
80
85
  if (offset != null) {
81
86
  if (bodyEnd <= offset) {
82
- bodyElement.children.splice(bodyElement.children.indexOf(child), 1);
83
- if (htmlEnd <= offset) {
87
+ if (hasTokenAfterHtmlEnd && htmlEnd <= offset) {
88
+ bodyElement.children.splice(bodyElement.children.indexOf(child), 1);
84
89
  ast.children.push(child);
85
90
  }
86
- else {
91
+ else if (hasTokenAfter) {
92
+ bodyElement.children.splice(bodyElement.children.indexOf(child), 1);
87
93
  htmlElement.children.push(child);
88
94
  }
89
95
  }
@@ -1,6 +1,8 @@
1
+ import type { Context } from "../context";
1
2
  import type { AstroProgram } from "../ast";
2
3
  import type { ScopeManager } from "eslint-scope";
3
4
  import type { ParseResult } from "@astrojs/compiler";
5
+ import type { ESLintExtendedProgram } from "../types";
4
6
  /**
5
7
  * Parse source code
6
8
  */
@@ -15,3 +17,5 @@ export declare function parseForESLint(code: string, options?: any): {
15
17
  };
16
18
  scopeManager: ScopeManager;
17
19
  };
20
+ /** Extract tokens */
21
+ export declare function extractTokens(ast: ESLintExtendedProgram, ctx: Context): void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseForESLint = void 0;
3
+ exports.extractTokens = exports.parseForESLint = void 0;
4
4
  const visitor_keys_1 = require("../visitor-keys");
5
5
  const types_1 = require("@typescript-eslint/types");
6
6
  const script_1 = require("./script");
@@ -69,3 +69,4 @@ function extractTokens(ast, ctx) {
69
69
  return /^[^\w$]$/iu.test(c);
70
70
  }
71
71
  }
72
+ exports.extractTokens = extractTokens;
@@ -328,7 +328,8 @@ function processTemplate(ctx, resultTemplate) {
328
328
  const offset = (0, astro_1.calcContentEndOffset)(node, ctx);
329
329
  script.appendOriginal(offset);
330
330
  script.appendScript(`</${node.name}>`);
331
- script.addRestoreNodeProcess((scriptNode, _result, parent) => {
331
+ script.addRestoreNodeProcess((scriptNode, context) => {
332
+ const parent = context.getParent(scriptNode);
332
333
  if (scriptNode.range[0] === offset &&
333
334
  scriptNode.type ===
334
335
  types_1.AST_NODE_TYPES.JSXClosingElement &&
@@ -11,6 +11,13 @@ const fs_1 = __importDefault(require("fs"));
11
11
  * Apply a patch to parse .astro files as TSX.
12
12
  */
13
13
  function tsPatch(scriptParserOptions) {
14
+ let targetExt = ".astro";
15
+ if (scriptParserOptions.filePath) {
16
+ const ext = path_1.default.extname(scriptParserOptions.filePath);
17
+ if (ext) {
18
+ targetExt = ext;
19
+ }
20
+ }
14
21
  try {
15
22
  // Apply a patch to parse .astro files as TSX.
16
23
  const cwd = process.cwd();
@@ -20,13 +27,13 @@ function tsPatch(scriptParserOptions) {
20
27
  if (typeof ensureScriptKind === "function" &&
21
28
  typeof getScriptKindFromFileName === "function") {
22
29
  ts.ensureScriptKind = function (fileName, ...args) {
23
- if (fileName.endsWith(".astro")) {
30
+ if (fileName.endsWith(targetExt)) {
24
31
  return ts.ScriptKind.TSX;
25
32
  }
26
33
  return ensureScriptKind.call(this, fileName, ...args);
27
34
  };
28
35
  ts.getScriptKindFromFileName = function (fileName, ...args) {
29
- if (fileName.endsWith(".astro")) {
36
+ if (fileName.endsWith(targetExt)) {
30
37
  return ts.ScriptKind.TSX;
31
38
  }
32
39
  return getScriptKindFromFileName.call(this, fileName, ...args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-eslint-parser",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Astro component parser for ESLint",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -42,12 +42,14 @@
42
42
  },
43
43
  "homepage": "https://github.com/ota-meshi/astro-eslint-parser#readme",
44
44
  "dependencies": {
45
- "@astrojs/compiler": "^0.15.0",
45
+ "@astrojs/compiler": "^0.16.1",
46
46
  "@typescript-eslint/types": "^5.25.0",
47
47
  "debug": "^4.3.4",
48
48
  "eslint-visitor-keys": "^3.0.0",
49
49
  "espree": "^9.0.0",
50
- "synckit": "^0.7.1"
50
+ "mdast-util-from-markdown": "^1.2.0",
51
+ "synckit": "^0.7.1",
52
+ "yaml": "^2.1.1"
51
53
  },
52
54
  "devDependencies": {
53
55
  "@ota-meshi/eslint-plugin": "^0.10.0",
@@ -70,10 +72,11 @@
70
72
  "eslint": "^8.15.0",
71
73
  "eslint-config-prettier": "^8.3.0",
72
74
  "eslint-formatter-codeframe": "^7.32.1",
73
- "eslint-plugin-astro": "^0.2.0",
75
+ "eslint-plugin-astro": "^0.12.0",
74
76
  "eslint-plugin-eslint-comments": "^3.2.0",
75
77
  "eslint-plugin-json-schema-validator": "^3.0.0",
76
78
  "eslint-plugin-jsonc": "^2.0.0",
79
+ "eslint-plugin-jsx-a11y": "^6.5.1",
77
80
  "eslint-plugin-node": "^11.1.0",
78
81
  "eslint-plugin-node-dependencies": "^0.8.0",
79
82
  "eslint-plugin-prettier": "^4.0.0",