Skip to content

Commit 57992a3

Browse files
committed
Refactor code-style
1 parent c191c56 commit 57992a3

File tree

5 files changed

+462
-451
lines changed

5 files changed

+462
-451
lines changed

index.test-d.ts

Lines changed: 180 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,201 +1,228 @@
1-
/* eslint-disable @typescript-eslint/no-empty-function */
2-
3-
import {expectError, expectType} from 'tsd'
4-
import type {Node, Parent, Literal} from 'unist'
5-
import {is} from 'unist-util-is'
6-
import {visit, SKIP, EXIT, CONTINUE} from './index.js'
1+
import {expectAssignable, expectNotType, expectType} from 'tsd'
2+
import type {
3+
Blockquote,
4+
Content,
5+
Definition,
6+
Delete,
7+
Emphasis,
8+
Footnote,
9+
FootnoteDefinition,
10+
Heading,
11+
Link,
12+
LinkReference,
13+
ListItem,
14+
PhrasingContent,
15+
Root,
16+
Strong,
17+
TableCell,
18+
TableRow
19+
} from 'mdast'
20+
import type {Node, Parent} from 'unist'
21+
import {CONTINUE, EXIT, SKIP, visit} from './index.js'
22+
23+
// To do: use `mdast` when released.
24+
type Nodes = Root | Content
25+
26+
// To do: use `mdast` when released.
27+
type Parents = Extract<Nodes, Parent>
728

829
/* Setup */
9-
const sampleTree: Root = {
30+
const implicitTree = {
1031
type: 'root',
1132
children: [{type: 'heading', depth: 1, children: []}]
1233
}
1334

14-
const complexTree: Root = {
35+
const sampleTree: Root = {
1536
type: 'root',
16-
children: [
17-
{
18-
type: 'blockquote',
19-
children: [{type: 'paragraph', children: [{type: 'text', value: 'a'}]}]
20-
},
21-
{
22-
type: 'paragraph',
23-
children: [
24-
{
25-
type: 'emphasis',
26-
children: [{type: 'emphasis', children: [{type: 'text', value: 'b'}]}]
27-
},
28-
{type: 'text', value: 'c'}
29-
]
30-
}
31-
]
32-
}
33-
34-
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
35-
interface Element extends Parent {
36-
type: 'element'
37-
tagName: string
38-
properties: Record<string, unknown>
39-
content: Node
40-
children: Array<Node>
41-
}
42-
43-
type Content = Flow | Phrasing
44-
45-
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
46-
interface Root extends Parent {
47-
type: 'root'
48-
children: Array<Flow>
49-
}
50-
51-
type Flow = Blockquote | Heading | Paragraph
52-
53-
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
54-
interface Blockquote extends Parent {
55-
type: 'blockquote'
56-
children: Array<Flow>
57-
}
58-
59-
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
60-
interface Heading extends Parent {
61-
type: 'heading'
62-
depth: number
63-
children: Array<Phrasing>
64-
}
65-
66-
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
67-
interface Paragraph extends Parent {
68-
type: 'paragraph'
69-
children: Array<Phrasing>
70-
}
71-
72-
type Phrasing = Text | Emphasis
73-
74-
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
75-
interface Emphasis extends Parent {
76-
type: 'emphasis'
77-
children: Array<Phrasing>
37+
children: [{type: 'heading', depth: 1, children: []}]
7838
}
7939

80-
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
81-
interface Text extends Literal {
82-
type: 'text'
83-
value: string
84-
}
40+
// ## Missing parameters
41+
// @ts-expect-error: check that `node` is passed.
42+
visit()
43+
// @ts-expect-error: check that `visitor` is passed.
44+
visit(sampleTree)
45+
46+
// ## No test
47+
visit(sampleTree, function (node, index, parent) {
48+
expectType<Nodes>(node)
49+
expectType<number | null>(index)
50+
expectType<Parents | null>(parent)
51+
})
8552

86-
const isNode = (node: unknown): node is Node =>
87-
typeof node === 'object' && node !== null && 'type' in node
88-
const headingTest = (node: unknown): node is Heading =>
89-
isNode(node) && node.type === 'heading'
90-
const elementTest = (node: unknown): node is Element =>
91-
isNode(node) && node.type === 'element'
53+
visit(implicitTree, function (node, index, parent) {
54+
// Objects are too loose.
55+
expectAssignable<Node>(node)
56+
expectNotType<Node>(node)
57+
expectType<number | null>(index)
58+
expectType<never>(parent)
59+
})
9260

93-
/* Missing params. */
94-
expectError(visit())
95-
expectError(visit(sampleTree))
61+
// ## String test
9662

97-
/* Visit without test. */
98-
visit(sampleTree, (node, _, parent) => {
99-
expectType<Root | Content>(node)
100-
expectType<Extract<Root | Content, Parent> | null>(parent)
63+
// Knows it’s a heading and its parents.
64+
visit(sampleTree, 'heading', function (node, index, parent) {
65+
expectType<Heading>(node)
66+
expectType<number | null>(index)
67+
expectType<Blockquote | FootnoteDefinition | ListItem | Root | null>(parent)
10168
})
10269

103-
/* Visit with type test. */
104-
visit(sampleTree, 'heading', (node, _, parent) => {
105-
expectType<Heading>(node)
106-
expectType<Root | Blockquote | null>(parent)
70+
// Not in tree.
71+
visit(sampleTree, 'element', function (node, index, parent) {
72+
expectType<never>(node)
73+
expectType<never>(index)
74+
expectType<never>(parent)
10775
})
108-
visit(sampleTree, 'element', (node, index, parent) => {
109-
// Not in tree.
76+
77+
// Implicit nodes are too loose.
78+
visit(implicitTree, 'heading', function (node, index, parent) {
11079
expectType<never>(node)
11180
expectType<never>(index)
11281
expectType<never>(parent)
11382
})
114-
expectError(visit(sampleTree, 'heading', (_: Element) => {}))
11583

116-
/* Visit with object test. */
117-
visit(sampleTree, {depth: 1}, (node) => {
118-
expectType<Heading>(node)
84+
visit(sampleTree, 'tableCell', function (node, index, parent) {
85+
expectType<TableCell>(node)
86+
expectType<number | null>(index)
87+
expectType<Root | TableRow | null>(parent)
11988
})
120-
visit(sampleTree, {random: 'property'}, (node) => {
121-
expectType<never>(node)
89+
90+
// ## Props test
91+
92+
// Knows that headings have depth, but TS doesn’t infer the depth normally.
93+
visit(sampleTree, {depth: 1}, function (node) {
94+
expectType<Heading>(node)
95+
expectType<1 | 2 | 3 | 4 | 5 | 6>(node.depth)
12296
})
123-
visit(sampleTree, {type: 'heading', depth: '2'}, (node) => {
124-
// Not in tree.
125-
expectType<never>(node)
97+
98+
// This goes fine.
99+
visit(sampleTree, {type: 'heading'} as const, function (node) {
100+
expectType<Heading>(node)
101+
expectType<1 | 2 | 3 | 4 | 5 | 6>(node.depth)
126102
})
127-
visit(sampleTree, {tagName: 'section'}, (node) => {
128-
// Not in tree.
103+
104+
// For some reason the const goes wrong.
105+
visit(sampleTree, {depth: 1} as const, function (node) {
106+
// Note: something going wrong here, to do: investigate.
129107
expectType<never>(node)
130108
})
131-
visit(sampleTree, {type: 'element', tagName: 'section'}, (node) => {
132-
// Not in tree.
109+
110+
// For some reason the const goes wrong.
111+
visit(sampleTree, {type: 'heading', depth: 1} as const, function (node) {
112+
// Note: something going wrong here, to do: investigate.
133113
expectType<never>(node)
134114
})
135115

136-
/* Visit with function test. */
137-
visit(sampleTree, headingTest, (node) => {
116+
// Function test (implicit assertion).
117+
visit(sampleTree, isHeadingLoose, function (node) {
118+
expectType<Nodes>(node)
119+
})
120+
// Function test (explicit assertion).
121+
visit(sampleTree, isHeading, function (node) {
138122
expectType<Heading>(node)
123+
expectType<1 | 2 | 3 | 4 | 5 | 6>(node.depth)
139124
})
140-
expectError(visit(sampleTree, headingTest, (_: Element) => {}))
141-
visit(sampleTree, elementTest, (node) => {
142-
// Not in tree.
125+
// Function test (explicit assertion).
126+
visit(sampleTree, isHeading2, function (node) {
127+
// To do: improving `InclusiveDescendant` should use `Heading & {depth: 2}`.
143128
expectType<never>(node)
144129
})
145130

146-
/* Visit with array of tests. */
147-
visit(sampleTree, ['heading', {depth: 1}, headingTest], (node) => {
131+
// ## Combined tests
132+
visit(sampleTree, ['heading', {depth: 1}, isHeading], function (node) {
148133
// Unfortunately TS casts things in arrays too vague.
149134
expectType<Root | Content>(node)
150135
})
151136

152-
/* Visit returns action. */
153-
visit(sampleTree, () => CONTINUE)
154-
visit(sampleTree, () => EXIT)
155-
visit(sampleTree, () => SKIP)
156-
expectError(visit(sampleTree, () => 'random'))
137+
// To do: update to `unist-util-is` should make this work?
138+
// visit(
139+
// sampleTree,
140+
// ['heading', {depth: 1}, isHeading] as const,
141+
// function (node) {
142+
// // Unfortunately TS casts things in arrays too vague.
143+
// expectType<Root | Content>(node)
144+
// }
145+
// )
146+
147+
// ## Return type: incorrect.
148+
// @ts-expect-error: not an action.
149+
visit(sampleTree, function () {
150+
return 'random'
151+
})
152+
// @ts-expect-error: not a tuple: missing action.
153+
visit(sampleTree, function () {
154+
return [1]
155+
})
156+
// @ts-expect-error: not a tuple: incorrect action.
157+
visit(sampleTree, function () {
158+
return ['random', 1]
159+
})
157160

158-
/* Visit returns index. */
159-
visit(sampleTree, () => 0)
160-
visit(sampleTree, () => 1)
161+
// ## Return type: action.
162+
visit(sampleTree, function () {
163+
return CONTINUE
164+
})
165+
visit(sampleTree, function () {
166+
return EXIT
167+
})
168+
visit(sampleTree, function () {
169+
return SKIP
170+
})
161171

162-
/* Visit returns tuple. */
163-
visit(sampleTree, () => [CONTINUE, 1])
164-
visit(sampleTree, () => [EXIT, 1])
165-
visit(sampleTree, () => [SKIP, 1])
166-
visit(sampleTree, () => [SKIP])
167-
expectError(visit(sampleTree, () => [1]))
168-
expectError(visit(sampleTree, () => ['random', 1]))
172+
// ## Return type: index.
173+
visit(sampleTree, function () {
174+
return 0
175+
})
176+
visit(sampleTree, function () {
177+
return 1
178+
})
169179

170-
/* Should infer children from the given tree. */
171-
visit(complexTree, (node, _, parent) => {
172-
expectType<Root | Content>(node)
173-
expectType<Extract<Root | Content, Parent> | null>(parent)
180+
// ## Return type: tuple.
181+
visit(sampleTree, function () {
182+
return [CONTINUE, 1]
183+
})
184+
visit(sampleTree, function () {
185+
return [EXIT, 1]
186+
})
187+
visit(sampleTree, function () {
188+
return [SKIP, 1]
189+
})
190+
visit(sampleTree, function () {
191+
return [SKIP]
174192
})
175193

176-
const blockquote = complexTree.children[0]
177-
if (is<Blockquote>(blockquote, 'blockquote')) {
178-
visit(blockquote, (node, _, parent) => {
179-
expectType<Content>(node)
180-
expectType<Extract<Content, Parent> | null>(parent)
194+
// ## Infer on tree
195+
visit(sampleTree, 'tableCell', function (node) {
196+
visit(node, function (node, _, parent) {
197+
expectType<TableCell | PhrasingContent>(node)
198+
expectType<
199+
| Delete
200+
| Emphasis
201+
| Footnote
202+
| Link
203+
| LinkReference
204+
| Strong
205+
| TableCell
206+
| null
207+
>(parent)
181208
})
182-
}
209+
})
183210

184-
const paragraph = complexTree.children[1]
185-
if (is<Paragraph>(paragraph, 'paragraph')) {
186-
visit(paragraph, (node, _, parent) => {
187-
expectType<Paragraph | Phrasing>(node)
188-
expectType<Paragraph | Emphasis | null>(parent)
211+
visit(sampleTree, 'definition', function (node) {
212+
visit(node, function (node, _, parent) {
213+
expectType<Definition>(node)
214+
expectType<never>(parent)
189215
})
216+
})
190217

191-
const child = paragraph.children[1]
218+
function isHeading(node: Node): node is Heading {
219+
return node ? node.type === 'heading' : false
220+
}
221+
222+
function isHeading2(node: Node): node is Heading & {depth: 2} {
223+
return isHeading(node) && node.depth === 2
224+
}
192225

193-
if (is<Emphasis>(child, 'emphasis')) {
194-
visit(child, 'blockquote', (node, index, parent) => {
195-
// `blockquote` does not exist in phrasing.
196-
expectType<never>(node)
197-
expectType<never>(index)
198-
expectType<never>(parent)
199-
})
200-
}
226+
function isHeadingLoose(node: Node) {
227+
return node ? node.type === 'heading' : false
201228
}

0 commit comments

Comments
 (0)