Skip to content

Commit

Permalink
feat: processor support for normalize comments in jsx [ci skip]
Browse files Browse the repository at this point in the history
  • Loading branch information
JounQin committed Aug 1, 2019
1 parent 1b5e4fb commit 114831c
Show file tree
Hide file tree
Showing 19 changed files with 277 additions and 398 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<img src="https://eslint.org/assets/img/logo.svg" height="50">
</a>
<a href="#readme">
<img src="assets/heart.svg" height="50">
<img src="https://rx-ts.github.io/assets/heart.svg" height="50">
</a>
<a href="https://github.com/mdx-js/mdx">
<img src="https://mdx-logo.now.sh" height="50">
Expand Down
11 changes: 0 additions & 11 deletions assets/heart.svg

This file was deleted.

3 changes: 3 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
preset: 'ts-jest',
}
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
"eslint": ">=5.0.0"
},
"dependencies": {
"@mdx-js/mdx": "^1.1.0",
"remark-mdx": "^1.1.0",
"remark-mdx": "^1.1.4",
"remark-parse": "^7.0.0",
"remark-stringify": "^7.0.1",
"unified": "^8.3.2"
Expand All @@ -39,21 +38,23 @@
"@commitlint/config-conventional": "^8.1.0",
"@rxts/eslint-plugin-mdx": "file:config",
"@types/eslint": "^4.16.6",
"@types/jest": "^24.0.16",
"@types/node": "^12.6.8",
"@types/react": "^16.8.23",
"@types/react": "^16.8.24",
"@types/unist": "^2.0.3",
"babel-eslint": "^10.0.2",
"commitlint": "^8.1.0",
"eslint": "^6.1.0",
"eslint-config-1stg": "~5.4.1",
"eslint-plugin-jest": "^22.14.0",
"eslint-plugin-jest": "^22.14.1",
"husky": "^3.0.2",
"jest": "^24.8.0",
"lint-staged": "^9.2.1",
"prettier": "1.18.2",
"prettier-config-1stg": "^0.1.0",
"react": "^16.8.6",
"standard-version": "^7.0.0",
"ts-jest": "^24.0.2",
"ts-node": "^8.3.0",
"typescript": "^3.5.3"
}
Expand Down
67 changes: 66 additions & 1 deletion src/helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Position } from 'unist'
import {
isOpenTag,
isCloseTag,
isComment,
isSelfClosingTag,
isOpenCloseTag,
} from './regexp'

import { Node, Position } from 'unist'
import { AST } from 'eslint'
// SourceLocation` is not exported from estree, but it is actually working
// eslint-disable-next-line import/named
Expand Down Expand Up @@ -73,3 +81,60 @@ export function restoreNodeLocation<T extends BaseNode>(
},
}
}

// fix #7
export const combineJsxNodes = (nodes: Node[]) => {
let offset = 0
const jsxNodes: Node[] = []
const { length } = nodes
return nodes.reduce<Node[]>((acc, node, index) => {
if (node.type === 'jsx') {
const rawText = node.value as string
if (isOpenTag(rawText as string)) {
offset++
jsxNodes.push(node)
} else {
if (isCloseTag(rawText)) {
offset--
} else if (
!isComment(rawText) &&
!isSelfClosingTag(rawText) &&
!isOpenCloseTag(rawText)
) {
const { start } = node.position
throw Object.assign(
new SyntaxError(
`'Unknown node type: ${JSON.stringify(
node.type,
)}, text: ${JSON.stringify(rawText)}`,
),
{
lineNumber: start.line,
column: start.column,
index: start.offset,
},
)
}

jsxNodes.push(node)

if (!offset || index === length - 1) {
acc.push({
type: 'jsx',
value: jsxNodes.reduce((acc, { value }) => (acc += value), ''),
position: {
start: jsxNodes[0].position.start,
end: jsxNodes[jsxNodes.length - 1].position.end,
},
})
jsxNodes.length = 0
}
}
} else if (offset) {
jsxNodes.push(node)
} else {
acc.push(node)
}
return acc
}, [])
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import path from 'path'

export * from './helper'
export * from './normalizer'
export * from './parser'
export * from './processors'
export * from './regexp'
export * from './rules'
export * from './traverse'
Expand Down
46 changes: 46 additions & 0 deletions src/normalizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { isComment, COMMENT_CONTENT_REGEX } from './regexp'
import { mdxProcessor } from './parser'

import { Node, Parent } from 'unist'

export const normalizeJsxNode = (node: Node) => {
const rawText = node.value as string
if (!isComment(rawText)) {
const matched = rawText.match(COMMENT_CONTENT_REGEX)
if (matched) {
node.value = rawText.replace(
COMMENT_CONTENT_REGEX,
(_matched, $0) => `{/*${$0}*/}`,
)
}
}
return node
}

export const normalizeMdx = (source: string) => {
const lines = source.split('\n').length
const { children } = mdxProcessor.parse(source) as Parent
let lastLine: number
return children.reduce((result, node, index) => {
const {
position: { start, end },
} = node
const startLine = start.line
const endLine = end.line
if (lastLine != null && lastLine !== startLine) {
result += '\n'.repeat(startLine - lastLine)
}
if (node.type === 'jsx') {
result += normalizeJsxNode(node).value
} else {
result += source.slice(start.offset, end.offset)
}

if (index === children.length - 1 && endLine < lines) {
result += '\n'.repeat(lines - endLine)
}

lastLine = endLine
return result
}, '')
}
22 changes: 11 additions & 11 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ export const ES_NODE_TYPES = ['export', 'import', 'jsx'] as const

export type EsNodeType = (typeof ES_NODE_TYPES)[number]

export const parseMdx = unified()
export const mdxProcessor = unified()
.use<any>(remarkParse)
.use<any>(remarkStringify)
.use(remarkMdx)
.freeze().parse
.freeze()

export const parseForESLint = (
code: string,
Expand Down Expand Up @@ -53,7 +53,7 @@ export const parseForESLint = (
}
}

const root = parseMdx(code) as Parent
const root = mdxProcessor.parse(code) as Parent

const ast: AST.Program = {
...normalizePosition(root.position),
Expand All @@ -65,36 +65,36 @@ export const parseForESLint = (
}

traverse(root, {
enter({ position, type }) {
if (!ES_NODE_TYPES.includes(type as EsNodeType)) {
enter(node) {
if (!ES_NODE_TYPES.includes(node.type as EsNodeType)) {
return
}

const rawText = code.slice(position.start.offset, position.end.offset)
const rawText = node.value as string

// fix #4
if (isComment(rawText)) {
return
}

const node = normalizePosition(position)
const startLine = node.loc.start.line - 1 //! line is 1-indexed, change to 0-indexed to simplify usage
const { loc, start } = normalizePosition(node.position)
const startLine = loc.start.line - 1 //! line is 1-indexed, change to 0-indexed to simplify usage

let program: AST.Program

try {
program = parser(rawText, options)
} catch (e) {
if (e instanceof SyntaxError) {
e.index += node.start
e.column += node.loc.start.column
e.index += start
e.column += loc.start.column
e.lineNumber += startLine
}

throw e
}

const offset = node.start - program.range[0]
const offset = start - program.range[0]

AST_PROPS.forEach(prop =>
ast[prop].push(
Expand Down
10 changes: 10 additions & 0 deletions src/processors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { normalizeMdx } from './normalizer'

export const processors = {
'.mdx': {
preprocess(code: string) {
return [normalizeMdx(code)]
},
supportsAutofix: true,
},
}
14 changes: 11 additions & 3 deletions src/regexp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,29 @@ const attributeValue =
')'
const attribute =
'(?:\\s+' + attributeName + '(?:\\s*=\\s*' + attributeValue + ')?)'

const openTag = '<[A-Za-z]*[A-Za-z0-9\\.\\-]*' + attribute + '*\\s*>'
const closeTag = '<\\s*\\/[A-Za-z]*[A-Za-z0-9\\.\\-]*\\s*>'
const voidTag = '<[A-Za-z]*[A-Za-z0-9\\.\\-]*' + attribute + '*\\s*\\/?>'
const selfClosingTag = '<[A-Za-z]*[A-Za-z0-9\\.\\-]*' + attribute + '*\\s*\\/?>'
const comment = '<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->'
const commentOpen = '<!---?'
const commentClose = '-->'

export const OPEN_TAG_REGEX = new RegExp(`^(?:${openTag})$`)
export const CLOSE_TAG_REGEX = new RegExp(`^(?:${closeTag})$`)
export const OPEN_CLOSE_TAG_REGEX = new RegExp(
`^(?:${openTag + '[\\s\\S]*' + closeTag})$`,
)
export const VOID_TAG_REGEX = new RegExp(`^(?:${voidTag})$`)
export const SELF_CLOSING_TAG_REGEX = new RegExp(`^(?:${selfClosingTag})$`)
export const COMMENT_REGEX = new RegExp(`^(?:${comment})$`)
export const COMMENT_CONTENT_REGEX = new RegExp(
`${commentOpen}([\\s\\S]*?)${commentClose}`,
'g',
)

export const isOpenTag = (text: string) => OPEN_TAG_REGEX.test(text)
export const isCloseTag = (text: string) => CLOSE_TAG_REGEX.test(text)
export const isOpenCloseTag = (text: string) => OPEN_CLOSE_TAG_REGEX.test(text)
export const isVoidTag = (text: string) => VOID_TAG_REGEX.test(text)
export const isSelfClosingTag = (text: string) =>
SELF_CLOSING_TAG_REGEX.test(text)
export const isComment = (text: string) => COMMENT_REGEX.test(text)
5 changes: 3 additions & 2 deletions src/rules/no-unused-expressions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// eslint-disable-next-line @typescript-eslint/no-triple-slash-reference
/// <reference path="../../types.d.ts" />

import ESLintNoUnUsedExpressions from 'eslint/lib/rules/no-unused-expressions'
import esLintNoUnUsedExpressions from 'eslint/lib/rules/no-unused-expressions'

import { Rule } from 'eslint'
import { ExpressionStatement, Node } from 'estree'
Expand All @@ -17,8 +17,9 @@ export interface ExpressionStatementWithParent extends ExpressionStatement {
}

export const noUnUsedExpressions: Rule.RuleModule = {
...esLintNoUnUsedExpressions,
create(context) {
const esLintRuleListener = ESLintNoUnUsedExpressions.create(context)
const esLintRuleListener = esLintNoUnUsedExpressions.create(context)
return {
ExpressionStatement(node: ExpressionStatementWithParent) {
if (
Expand Down
Loading

0 comments on commit 114831c

Please sign in to comment.