Skip to content

Commit fc8ba03

Browse files
authored
feat: add missing mdast types to MarkdownRuleVisitor (#334)
* feat: add missing types to `MarkdownRuleVisitor` * wip: create `Toml` type * wip: create more tests * wip: update `types.ts` * wip: update `types.test.ts` * wip: address review comments
1 parent e3d4765 commit fc8ba03

File tree

3 files changed

+203
-40
lines changed

3 files changed

+203
-40
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
".": {
1515
"types": "./dist/esm/index.d.ts",
1616
"default": "./dist/esm/index.js"
17+
},
18+
"./types": {
19+
"types": "./dist/esm/types.d.ts"
1720
}
1821
},
1922
"files": [

src/types.ts

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,47 @@
33
//------------------------------------------------------------------------------
44

55
import type {
6+
// Nodes (abstract)
7+
Node,
8+
Data,
9+
Literal,
10+
Parent,
11+
// Nodes
12+
Blockquote,
13+
Break,
614
Code,
15+
Definition,
16+
Emphasis,
717
Heading,
818
Html,
19+
Image,
20+
ImageReference,
21+
InlineCode,
922
Link,
10-
Node,
11-
Parent,
23+
LinkReference,
24+
List,
25+
ListItem,
26+
Paragraph,
1227
Root,
28+
Strong,
1329
Text,
30+
ThematicBreak,
31+
// Extensions (GFM)
32+
Delete,
33+
FootnoteDefinition,
34+
FootnoteReference,
35+
Table,
36+
TableCell,
37+
TableRow,
38+
// Extensions (front matter)
39+
Yaml,
1440
} from "mdast";
1541
import type { Linter } from "eslint";
1642
import type {
1743
LanguageOptions,
1844
LanguageContext,
1945
RuleDefinition,
2046
RuleVisitor,
21-
SourceLocation,
22-
TextSourceCode,
2347
} from "@eslint/core";
2448
import type { MarkdownSourceCode } from "./index.js";
2549

@@ -58,6 +82,25 @@ export type Message = Linter.LintMessage;
5882

5983
export type RuleType = "problem" | "suggestion" | "layout";
6084

85+
/**
86+
* Markdown TOML.
87+
*/
88+
export interface Toml extends Literal {
89+
/**
90+
* Node type of mdast TOML.
91+
*/
92+
type: "toml";
93+
/**
94+
* Data associated with the mdast TOML.
95+
*/
96+
data?: TomlData | undefined;
97+
}
98+
99+
/**
100+
* Info associated with mdast TOML nodes by the ecosystem.
101+
*/
102+
export interface TomlData extends Data {}
103+
61104
/**
62105
* Language options provided for Markdown files.
63106
*/
@@ -85,14 +128,42 @@ export interface SourceCodeBaseTypeOptions {
85128

86129
export interface MarkdownRuleVisitor
87130
extends RuleVisitor,
88-
WithExit<{
89-
root?(node: Root): void;
90-
code?(node: Code, parent?: Parent): void;
91-
heading?(node: Heading, parent?: Parent): void;
92-
html?(node: Html, parent?: Parent): void;
93-
link?(node: Link, parent?: Parent): void;
94-
text?(node: Text, parent?: Parent): void;
95-
}> {}
131+
WithExit<
132+
{
133+
root?(node: Root): void;
134+
} & {
135+
[NodeType in
136+
| Blockquote // Nodes
137+
| Break
138+
| Code
139+
| Definition
140+
| Emphasis
141+
| Heading
142+
| Html
143+
| Image
144+
| ImageReference
145+
| InlineCode
146+
| Link
147+
| LinkReference
148+
| List
149+
| ListItem
150+
| Paragraph
151+
| Strong
152+
| Text
153+
| ThematicBreak
154+
| Delete // Extensions (GFM)
155+
| FootnoteDefinition
156+
| FootnoteReference
157+
| Table
158+
| TableCell
159+
| TableRow
160+
| Yaml // Extensions (front matter)
161+
| Toml as NodeType["type"]]?: (
162+
node: NodeType,
163+
parent?: Parent,
164+
) => void;
165+
}
166+
> {}
96167

97168
export type MarkdownRuleDefinitionTypeOptions = {
98169
RuleOptions: unknown[];

tests/types/types.test.ts

Lines changed: 117 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,46 @@ import markdown, {
33
MarkdownNode,
44
MarkdownRuleDefinition,
55
MarkdownRuleVisitor,
6-
ParentNode,
7-
RootNode,
86
SourceLocation,
9-
TextNode,
7+
SourceRange,
108
type RuleModule,
119
} from "@eslint/markdown";
10+
import { Toml } from "@eslint/markdown/types";
1211
import { ESLint, Linter } from "eslint";
12+
import type {
13+
// Nodes (abstract)
14+
Node,
15+
Parent,
16+
// Nodes
17+
Blockquote,
18+
Break,
19+
Code,
20+
Definition,
21+
Emphasis,
22+
Heading,
23+
Html,
24+
Image,
25+
ImageReference,
26+
InlineCode,
27+
Link,
28+
LinkReference,
29+
List,
30+
ListItem,
31+
Paragraph,
32+
Root,
33+
Strong,
34+
Text,
35+
ThematicBreak,
36+
// Extensions (GFM)
37+
Delete,
38+
FootnoteDefinition,
39+
FootnoteReference,
40+
Table,
41+
TableCell,
42+
TableRow,
43+
// Extensions (front matter)
44+
Yaml,
45+
} from "mdast";
1346

1447
markdown satisfies ESLint.Plugin;
1548
markdown.meta.name satisfies string;
@@ -49,37 +82,93 @@ typeof processorPlugins satisfies {};
4982
(): RuleModule => ({
5083
create({ sourceCode }): MarkdownRuleVisitor {
5184
sourceCode satisfies MarkdownSourceCode;
52-
53-
sourceCode.ast satisfies RootNode;
85+
sourceCode.ast satisfies Root;
5486
sourceCode.lines satisfies string[];
87+
sourceCode.text satisfies string;
88+
89+
function testVisitor<NodeType extends Node>(
90+
node: NodeType,
91+
parent?: Parent | undefined,
92+
) {
93+
sourceCode.getLoc(node) satisfies SourceLocation;
94+
sourceCode.getRange(node) satisfies SourceRange;
95+
sourceCode.getParent(node) satisfies Node | undefined;
96+
// @ts-expect-error It should be fixed in https://github.com/eslint/markdown/issues/341
97+
sourceCode.getAncestors(node) satisfies Node[];
98+
sourceCode.getText(node) satisfies string;
99+
}
55100

56101
return {
57-
// Root selector
58-
root(node) {
59-
node satisfies RootNode;
60-
},
61-
62-
// Known node selector, sourceCode methods used in visitor
63-
text(node) {
64-
node satisfies TextNode;
65-
sourceCode.getText(node) satisfies string;
66-
sourceCode.getLoc(node) satisfies SourceLocation;
67-
},
68-
69-
// Known node selector with parent
70-
link(node, parent) {
71-
node satisfies MarkdownNode;
72-
parent satisfies ParentNode | undefined;
73-
},
74-
75-
// Known node selector with ":exit"
76-
"html:exit"(node, parent) {
77-
node satisfies MarkdownNode;
78-
parent satisfies ParentNode | undefined;
79-
},
102+
// Nodes
103+
blockquote: (...args) => testVisitor<Blockquote>(...args),
104+
"blockquote:exit": (...args) => testVisitor<Blockquote>(...args),
105+
break: (...args) => testVisitor<Break>(...args),
106+
"break:exit": (...args) => testVisitor<Break>(...args),
107+
code: (...args) => testVisitor<Code>(...args),
108+
"code:exit": (...args) => testVisitor<Code>(...args),
109+
definition: (...args) => testVisitor<Definition>(...args),
110+
"definition:exit": (...args) => testVisitor<Definition>(...args),
111+
emphasis: (...args) => testVisitor<Emphasis>(...args),
112+
"emphasis:exit": (...args) => testVisitor<Emphasis>(...args),
113+
heading: (...args) => testVisitor<Heading>(...args),
114+
"heading:exit": (...args) => testVisitor<Heading>(...args),
115+
html: (...args) => testVisitor<Html>(...args),
116+
"html:exit": (...args) => testVisitor<Html>(...args),
117+
image: (...args) => testVisitor<Image>(...args),
118+
"image:exit": (...args) => testVisitor<Image>(...args),
119+
imageReference: (...args) => testVisitor<ImageReference>(...args),
120+
"imageReference:exit": (...args) =>
121+
testVisitor<ImageReference>(...args),
122+
inlineCode: (...args) => testVisitor<InlineCode>(...args),
123+
"inlineCode:exit": (...args) => testVisitor<InlineCode>(...args),
124+
link: (...args) => testVisitor<Link>(...args),
125+
"link:exit": (...args) => testVisitor<Link>(...args),
126+
linkReference: (...args) => testVisitor<LinkReference>(...args),
127+
"linkReference:exit": (...args) =>
128+
testVisitor<LinkReference>(...args),
129+
list: (...args) => testVisitor<List>(...args),
130+
"list:exit": (...args) => testVisitor<List>(...args),
131+
listItem: (...args) => testVisitor<ListItem>(...args),
132+
"listItem:exit": (...args) => testVisitor<ListItem>(...args),
133+
paragraph: (...args) => testVisitor<Paragraph>(...args),
134+
"paragraph:exit": (...args) => testVisitor<Paragraph>(...args),
135+
root: (...args) => testVisitor<Root>(...args),
136+
"root:exit": (...arg) => testVisitor<Root>(...arg),
137+
strong: (...args) => testVisitor<Strong>(...args),
138+
"strong:exit": (...args) => testVisitor<Strong>(...args),
139+
text: (...args) => testVisitor<Text>(...args),
140+
"text:exit": (...args) => testVisitor<Text>(...args),
141+
thematicBreak: (...args) => testVisitor<ThematicBreak>(...args),
142+
"thematicBreak:exit": (...args) =>
143+
testVisitor<ThematicBreak>(...args),
144+
145+
// Extensions (GFM)
146+
delete: (...args) => testVisitor<Delete>(...args),
147+
"delete:exit": (...args) => testVisitor<Delete>(...args),
148+
footnoteDefinition: (...args) =>
149+
testVisitor<FootnoteDefinition>(...args),
150+
"footnoteDefinition:exit": (...args) =>
151+
testVisitor<FootnoteDefinition>(...args),
152+
footnoteReference: (...args) =>
153+
testVisitor<FootnoteReference>(...args),
154+
"footnoteReference:exit": (...args) =>
155+
testVisitor<FootnoteReference>(...args),
156+
table: (...args) => testVisitor<Table>(...args),
157+
"table:exit": (...args) => testVisitor<Table>(...args),
158+
tableCell: (...args) => testVisitor<TableCell>(...args),
159+
"tableCell:exit": (...args) => testVisitor<TableCell>(...args),
160+
tableRow: (...args) => testVisitor<TableRow>(...args),
161+
"tableRow:exit": (...args) => testVisitor<TableRow>(...args),
162+
163+
// Extensions (front matter)
164+
yaml: (...args) => testVisitor<Yaml>(...args),
165+
"yaml:exit": (...args) => testVisitor<Yaml>(...args),
166+
toml: (...args) => testVisitor<Toml>(...args),
167+
"toml:exit": (...args) => testVisitor<Toml>(...args),
80168

81169
// Unknown selectors allowed
82170
"heading[depth=1]"(node: MarkdownNode, parent?: ParentNode) {},
171+
"randomSelector:exit"(node: MarkdownNode, parent?: ParentNode) {},
83172
};
84173
},
85174
});

0 commit comments

Comments
 (0)