Skip to content

Commit

Permalink
perf: extract parseMdx, exports everything in package
Browse files Browse the repository at this point in the history
  • Loading branch information
JounQin committed Jul 31, 2019
1 parent 4bc3748 commit 1f965a1
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 91 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<a href="https://eslint.org">
<img src="https://eslint.org/assets/img/logo.svg" height="50">
</a>
<a href="/~https://github.com/rx-ts/eslint-plugin-mdx">
<a href="#readme">
<img src="assets/heart.svg" height="50">
</a>
<a href="/~https://github.com/mdx-js/mdx">
Expand Down Expand Up @@ -86,6 +86,13 @@ npm i -D @rxts/eslint-plugin-mdx
}
```

## Limitations

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.

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`.
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.

## Changelog

Detailed changes for each release are documented in [CHANGELOG.md](./CHANGELOG.md).
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rxts/eslint-plugin-mdx",
"version": "0.4.0",
"version": "0.4.1",
"description": "ESLint Parser/Plugin for MDX",
"repository": "git@github.com:rx-ts/eslint-plugin-mdx.git",
"author": "JounQin <admin@1stg.me>",
Expand Down
75 changes: 75 additions & 0 deletions src/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Position } from 'unist'
import { AST } from 'eslint'
// SourceLocation` is not exported from estree, but it is actually working
// eslint-disable-next-line import/named
import { SourceLocation } from 'estree'

export const normalizePosition = (position: Position) => {
const start = position.start.offset
const end = position.end.offset
return {
range: [start, end] as AST.Range,
loc: {
...position,
},
start,
end,
}
}

export interface BaseNode {
type: string
loc?: SourceLocation
range?: [number, number]
}

export function restoreNodeLocation<T extends BaseNode>(
node: T,
startLine: number,
offset = 0,
): T {
if (!node || !node.loc || !node.range) {
return node
}

Object.entries(node).forEach(([key, value]) => {
if (!value) {
return
}

if (Array.isArray(value)) {
node[key as keyof T] = value.map(child =>
restoreNodeLocation(child, startLine, offset),
) as any
} else {
node[key as keyof T] = restoreNodeLocation(
value,
startLine,
offset,
) as T[keyof T]
}
})

const {
loc: { start: startLoc, end: endLoc },
range,
} = node
const start = range[0] + offset
const end = range[1] + offset
return {
...node,
start,
end,
range: [start, end],
loc: {
start: {
line: startLine + startLoc.line,
column: startLoc.column,
},
end: {
line: startLine + endLoc.line,
column: endLoc.column,
},
},
}
}
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import path from 'path'

export { parseForESLint } from './parser'
export * from './helper'
export * from './parser'
export * from './regexp'
export * from './traverse'

export const configs = {
recommended: {
Expand Down
99 changes: 17 additions & 82 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,86 +7,23 @@ import remarkParse from 'remark-parse'
import remarkStringify from 'remark-stringify'
import unified from 'unified'

import { normalizePosition, restoreNodeLocation } from './helper'
import { isComment } from './regexp'
import { traverse } from './traverse'
import { isComment } from './utils'

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

const normalizePosition = (position: Position) => {
const start = position.start.offset
const end = position.end.offset
return {
range: [start, end] as AST.Range,
loc: {
...position,
},
start,
end,
}
}

interface BaseNode {
type: string
loc?: SourceLocation
range?: [number, number]
}

function normalizeLoc<T extends BaseNode>(
node: T,
startLine: number,
offset = 0,
): T {
if (!node || !node.loc || !node.range) {
return node
}

Object.entries(node).forEach(([key, value]) => {
if (!value) {
return
}

if (Array.isArray(value)) {
node[key as keyof T] = value.map(child =>
normalizeLoc(child, startLine, offset),
) as any
} else {
node[key as keyof T] = normalizeLoc(
value,
startLine,
offset,
) as T[keyof T]
}
})
export const AST_PROPS = ['body', 'comments', 'tokens'] as const
export const ES_NODE_TYPES = ['export', 'import', 'jsx'] as const

const {
loc: { start: startLoc, end: endLoc },
range,
} = node
const start = range[0] + offset
const end = range[1] + offset
return {
...node,
start,
end,
range: [start, end],
loc: {
start: {
line: startLine + startLoc.line,
column: startLoc.column,
},
end: {
line: startLine + endLoc.line,
column: endLoc.column,
},
},
}
}
export type EsNodeType = (typeof ES_NODE_TYPES)[number]

export const AST_PROPS = ['body', 'comments', 'tokens'] as const
export const parseMdx = unified()
.use<any>(remarkParse)
.use<any>(remarkStringify)
.use(remarkMdx)
.freeze().parse

export const parseForESLint = (
code: string,
Expand All @@ -102,7 +39,7 @@ export const parseForESLint = (
parser = parser.parseForESLint || parser.parse
}
if (typeof parser !== 'function') {
throw new Error(
throw new TypeError(
`Invalid custom parser for \`eslint-plugin-mdx\`: ${parser}`,
)
}
Expand All @@ -111,11 +48,7 @@ export const parseForESLint = (
parser = esParse
}

const root = unified()
.use<any>(remarkParse)
.use<any>(remarkStringify)
.use(remarkMdx)
.parse(code) as Parent
const root = parseMdx(code) as Parent

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

traverse(root, {
enter({ position, type }) {
if (!['export', 'import', 'jsx'].includes(type)) {
if (!ES_NODE_TYPES.includes(type as EsNodeType)) {
return
}

Expand Down Expand Up @@ -162,7 +95,9 @@ export const parseForESLint = (
ast[prop].push(
// unfortunately, TS complains about incompatible signature
// @ts-ignore
...program[prop].map(item => normalizeLoc(item, startLine, offset)),
...program[prop].map(item =>
restoreNodeLocation(item, startLine, offset),
),
),
)
},
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/traverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
isVoidTag,
isComment,
isOpenCloseTag,
} from './utils'
} from './regexp'

import { Node, Parent } from 'unist'

Expand Down
6 changes: 1 addition & 5 deletions src/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"declaration": true,
"outDir": "../dist"
}
"extends": "../tsconfig.json"
}
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"declaration": true,
"esModuleInterop": true,
"jsx": "preserve",
"lib": ["esnext"],
"module": "commonjs",
"moduleResolution": "node",
"outDir": "dist",
"sourceMap": true,
"strict": true,
"strictNullChecks": false,
Expand Down

0 comments on commit 1f965a1

Please sign in to comment.