Skip to content

Commit 1f965a1

Browse files
committed
perf: extract parseMdx, exports everything in package
1 parent 4bc3748 commit 1f965a1

File tree

9 files changed

+109
-91
lines changed

9 files changed

+109
-91
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<a href="https://eslint.org">
33
<img src="https://eslint.org/assets/img/logo.svg" height="50">
44
</a>
5-
<a href="https://github.com/rx-ts/eslint-plugin-mdx">
5+
<a href="#readme">
66
<img src="assets/heart.svg" height="50">
77
</a>
88
<a href="https://github.com/mdx-js/mdx">
@@ -86,6 +86,13 @@ npm i -D @rxts/eslint-plugin-mdx
8686
}
8787
```
8888

89+
## Limitations
90+
91+
1. This parser/plugin can only handle ES syntaxes for you, markdown related syntaxes will just be ignored, you can use [markdownlint](https://github.com/markdownlint/markdownlint) to lint that part.
92+
93+
2. `MDX` can render `jsx` block automatically without exporting them, but `eslint` will report `no-unused-expressions` issue which could be unexpected, so you may need to disable this rule for `*.mdx`.
94+
I'm not going to disable this rule in the recommended config, because I'm going to add a custom `mdx/no-unused-expressions` rule to replace the incompatible one, which should not affect the `jsx` codes.
95+
8996
## Changelog
9097

9198
Detailed changes for each release are documented in [CHANGELOG.md](./CHANGELOG.md).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rxts/eslint-plugin-mdx",
3-
"version": "0.4.0",
3+
"version": "0.4.1",
44
"description": "ESLint Parser/Plugin for MDX",
55
"repository": "[email protected]:rx-ts/eslint-plugin-mdx.git",
66
"author": "JounQin <[email protected]>",

src/helper.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Position } from 'unist'
2+
import { AST } from 'eslint'
3+
// SourceLocation` is not exported from estree, but it is actually working
4+
// eslint-disable-next-line import/named
5+
import { SourceLocation } from 'estree'
6+
7+
export const normalizePosition = (position: Position) => {
8+
const start = position.start.offset
9+
const end = position.end.offset
10+
return {
11+
range: [start, end] as AST.Range,
12+
loc: {
13+
...position,
14+
},
15+
start,
16+
end,
17+
}
18+
}
19+
20+
export interface BaseNode {
21+
type: string
22+
loc?: SourceLocation
23+
range?: [number, number]
24+
}
25+
26+
export function restoreNodeLocation<T extends BaseNode>(
27+
node: T,
28+
startLine: number,
29+
offset = 0,
30+
): T {
31+
if (!node || !node.loc || !node.range) {
32+
return node
33+
}
34+
35+
Object.entries(node).forEach(([key, value]) => {
36+
if (!value) {
37+
return
38+
}
39+
40+
if (Array.isArray(value)) {
41+
node[key as keyof T] = value.map(child =>
42+
restoreNodeLocation(child, startLine, offset),
43+
) as any
44+
} else {
45+
node[key as keyof T] = restoreNodeLocation(
46+
value,
47+
startLine,
48+
offset,
49+
) as T[keyof T]
50+
}
51+
})
52+
53+
const {
54+
loc: { start: startLoc, end: endLoc },
55+
range,
56+
} = node
57+
const start = range[0] + offset
58+
const end = range[1] + offset
59+
return {
60+
...node,
61+
start,
62+
end,
63+
range: [start, end],
64+
loc: {
65+
start: {
66+
line: startLine + startLoc.line,
67+
column: startLoc.column,
68+
},
69+
end: {
70+
line: startLine + endLoc.line,
71+
column: endLoc.column,
72+
},
73+
},
74+
}
75+
}

src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import path from 'path'
22

3-
export { parseForESLint } from './parser'
3+
export * from './helper'
4+
export * from './parser'
5+
export * from './regexp'
6+
export * from './traverse'
47

58
export const configs = {
69
recommended: {

src/parser.ts

Lines changed: 17 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -7,86 +7,23 @@ import remarkParse from 'remark-parse'
77
import remarkStringify from 'remark-stringify'
88
import unified from 'unified'
99

10+
import { normalizePosition, restoreNodeLocation } from './helper'
11+
import { isComment } from './regexp'
1012
import { traverse } from './traverse'
11-
import { isComment } from './utils'
1213

13-
import { Position, Parent } from 'unist'
1414
import { AST, Linter } from 'eslint'
15-
// SourceLocation` is not exported from estree, but it is actually working
16-
// eslint-disable-next-line import/named
17-
import { SourceLocation } from 'estree'
15+
import { Parent } from 'unist'
1816

19-
const normalizePosition = (position: Position) => {
20-
const start = position.start.offset
21-
const end = position.end.offset
22-
return {
23-
range: [start, end] as AST.Range,
24-
loc: {
25-
...position,
26-
},
27-
start,
28-
end,
29-
}
30-
}
31-
32-
interface BaseNode {
33-
type: string
34-
loc?: SourceLocation
35-
range?: [number, number]
36-
}
37-
38-
function normalizeLoc<T extends BaseNode>(
39-
node: T,
40-
startLine: number,
41-
offset = 0,
42-
): T {
43-
if (!node || !node.loc || !node.range) {
44-
return node
45-
}
46-
47-
Object.entries(node).forEach(([key, value]) => {
48-
if (!value) {
49-
return
50-
}
51-
52-
if (Array.isArray(value)) {
53-
node[key as keyof T] = value.map(child =>
54-
normalizeLoc(child, startLine, offset),
55-
) as any
56-
} else {
57-
node[key as keyof T] = normalizeLoc(
58-
value,
59-
startLine,
60-
offset,
61-
) as T[keyof T]
62-
}
63-
})
17+
export const AST_PROPS = ['body', 'comments', 'tokens'] as const
18+
export const ES_NODE_TYPES = ['export', 'import', 'jsx'] as const
6419

65-
const {
66-
loc: { start: startLoc, end: endLoc },
67-
range,
68-
} = node
69-
const start = range[0] + offset
70-
const end = range[1] + offset
71-
return {
72-
...node,
73-
start,
74-
end,
75-
range: [start, end],
76-
loc: {
77-
start: {
78-
line: startLine + startLoc.line,
79-
column: startLoc.column,
80-
},
81-
end: {
82-
line: startLine + endLoc.line,
83-
column: endLoc.column,
84-
},
85-
},
86-
}
87-
}
20+
export type EsNodeType = (typeof ES_NODE_TYPES)[number]
8821

89-
export const AST_PROPS = ['body', 'comments', 'tokens'] as const
22+
export const parseMdx = unified()
23+
.use<any>(remarkParse)
24+
.use<any>(remarkStringify)
25+
.use(remarkMdx)
26+
.freeze().parse
9027

9128
export const parseForESLint = (
9229
code: string,
@@ -102,7 +39,7 @@ export const parseForESLint = (
10239
parser = parser.parseForESLint || parser.parse
10340
}
10441
if (typeof parser !== 'function') {
105-
throw new Error(
42+
throw new TypeError(
10643
`Invalid custom parser for \`eslint-plugin-mdx\`: ${parser}`,
10744
)
10845
}
@@ -111,11 +48,7 @@ export const parseForESLint = (
11148
parser = esParse
11249
}
11350

114-
const root = unified()
115-
.use<any>(remarkParse)
116-
.use<any>(remarkStringify)
117-
.use(remarkMdx)
118-
.parse(code) as Parent
51+
const root = parseMdx(code) as Parent
11952

12053
const ast: AST.Program = {
12154
...normalizePosition(root.position),
@@ -128,7 +61,7 @@ export const parseForESLint = (
12861

12962
traverse(root, {
13063
enter({ position, type }) {
131-
if (!['export', 'import', 'jsx'].includes(type)) {
64+
if (!ES_NODE_TYPES.includes(type as EsNodeType)) {
13265
return
13366
}
13467

@@ -162,7 +95,9 @@ export const parseForESLint = (
16295
ast[prop].push(
16396
// unfortunately, TS complains about incompatible signature
16497
// @ts-ignore
165-
...program[prop].map(item => normalizeLoc(item, startLine, offset)),
98+
...program[prop].map(item =>
99+
restoreNodeLocation(item, startLine, offset),
100+
),
166101
),
167102
)
168103
},
File renamed without changes.

src/traverse.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
isVoidTag,
55
isComment,
66
isOpenCloseTag,
7-
} from './utils'
7+
} from './regexp'
88

99
import { Node, Parent } from 'unist'
1010

src/tsconfig.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
11
{
2-
"extends": "../tsconfig.json",
3-
"compilerOptions": {
4-
"declaration": true,
5-
"outDir": "../dist"
6-
}
2+
"extends": "../tsconfig.json"
73
}

tsconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
"compilerOptions": {
33
"allowSyntheticDefaultImports": true,
44
"baseUrl": ".",
5+
"declaration": true,
56
"esModuleInterop": true,
67
"jsx": "preserve",
78
"lib": ["esnext"],
89
"module": "commonjs",
910
"moduleResolution": "node",
11+
"outDir": "dist",
1012
"sourceMap": true,
1113
"strict": true,
1214
"strictNullChecks": false,

0 commit comments

Comments
 (0)