Skip to content

Commit cb846e9

Browse files
authored
feat: bump (dev)Dependencies, fix: consider first jsx node as open tag - close #272 (#273)
* feat: bump (dev)Dependencies, fix related errors * fix: consider first jsx node as open tag - close #272 the parsed remark-mdx jsx nodes are not precise, but it will be resolved in v2 * chore: lerna config changes * test: do not use user environment related test cases * fix: open close tag regex, push remaining nodes * fix: part of #207, markdown in jsx is still not supported improve fixtures test case * test: remove unnecessary fixture * docs: add comments for // @ts-ignore and never
1 parent dd0227f commit cb846e9

24 files changed

+2796
-3529
lines changed

.babelrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"presets": [
3+
"@1stg"
4+
]
5+
}

CONTRIBUTING.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ Hi! 👋 We’re excited that you’re interested in contributing!
1111
This project is a [lerna][] monorepo, so packages releasing is controlled by [lerna][].
1212

1313
1. Make sure you have both GitHub repository and npm write permissions at the same time.
14-
2. You need a GitHub token with a `public_repo` scope as `GH_TOKEN` in the
15-
environment to publish
16-
3. Run `yarn deploy` simply, or `GH_TOKEN=xxx yarn deploy` to export `GH_TOKEN` at one time.
14+
2. You need a GitHub token with a `public_repo` scope as `GH_TOKEN` in the environment to publish
15+
3. Run `yarn release` simply, or `GH_TOKEN=xxx yarn release` to export `GH_TOKEN` at one time.
1716

1817
[contributing]: https://mdxjs.com/contributing
1918
[lerna]: https://github.com/lerna/lerna

lerna.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@
1515
"allowBranch": [
1616
"develop",
1717
"master"
18-
],
19-
"conventionalCommits": true,
20-
"gitReset": false,
21-
"message": "chore(release): %s"
18+
]
2219
},
2320
"publish": {
2421
"ignoreChanges": [
2522
"**/test/**",
26-
"*.md"
27-
]
23+
"**/*.md"
24+
],
25+
"message": "chore: release package(s)"
2826
}
2927
}
3028
}

package.json

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,42 @@
1414
"build:r": "r -p",
1515
"build:ts": "tsc -b",
1616
"clean": "rimraf packages/*/{lib,*.tsbuildinfo}",
17-
"deploy": "lerna publish --create-release github --yes",
1817
"lint": "run-p lint:*",
1918
"lint:es": "cross-env PARSER_NO_WATCH=true eslint . --cache --ext js,md,ts -f friendly",
2019
"lint:ts": "tslint -p . -t stylish",
2120
"lint:tsc": "tsc",
2221
"postinstall": "yarn-deduplicate || exit 0",
23-
"predeploy": "yarn build",
24-
"prelint": "yarn build",
22+
"prelint": "yarn build:ts",
23+
"prerelease": "yarn build",
2524
"pretest": "yarn clean",
25+
"release": "lerna publish --conventional-commits --create-release github --yes",
2626
"test": "ts-node --skip-ignore node_modules/.bin/jest",
2727
"type-coverage": "type-coverage --cache --detail --ignore-catch --ignore-files '**/*.d.ts' --strict --update"
2828
},
2929
"devDependencies": {
30-
"@1stg/lib-config": "^0.5.5",
31-
"@1stg/tslint-config": "^0.9.2",
32-
"@types/eslint": "^7.2.0",
30+
"@1stg/lib-config": "^1.0.5",
31+
"@1stg/tslint-config": "^1.0.0",
32+
"@types/eslint": "^7.2.6",
3333
"@types/jest": "^26.0.20",
34-
"@types/node": "^14.14.22",
35-
"@types/react": "^16.14.2",
34+
"@types/node": "^14.14.31",
35+
"@types/react": "^17.0.2",
3636
"@types/rebass": "^4.0.7",
3737
"@types/unist": "^2.0.3",
3838
"eslint-mdx": "link:packages/eslint-mdx/src",
3939
"eslint-plugin-mdx": "link:packages/eslint-plugin-mdx/src",
4040
"lerna": "^3.22.1",
4141
"npm-run-all": "^4.1.5",
42-
"react": "^16.14.0",
43-
"ts-jest": "^26.5.0",
44-
"ts-node": "^8.10.2",
42+
"react": "^17.0.1",
43+
"ts-jest": "^26.5.1",
44+
"ts-node": "^9.1.1",
4545
"tslint": "^6.1.3",
46-
"type-coverage": "^2.14.8",
47-
"typescript": "^3.9.7",
48-
"yarn-deduplicate": "^2.1.1"
46+
"type-coverage": "^2.15.1",
47+
"typescript": "^4.3.0-dev.20210220",
48+
"yarn-deduplicate": "^3.1.0"
4949
},
5050
"resolutions": {
51-
"prettier": "^2.2.1"
51+
"prettier": "^2.2.1",
52+
"tslib": "^2.1.0"
5253
},
5354
"commitlint": {
5455
"extends": [

packages/eslint-mdx/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"espree": "^7.3.1",
3737
"remark-mdx": "^1.6.22",
3838
"remark-parse": "^8.0.3",
39-
"tslib": "^2.0.1",
39+
"tslib": "^2.1.0",
4040
"unified": "^9.1.0"
4141
}
4242
}

packages/eslint-mdx/src/helper.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414

1515
export const FALLBACK_PARSERS = [
1616
'@typescript-eslint/parser',
17+
'@babel/eslint-parser',
1718
'babel-eslint',
1819
] as const
1920

@@ -89,33 +90,31 @@ export interface BaseNode {
8990
range?: [number, number]
9091
}
9192

92-
export function restoreNodeLocation<T extends BaseNode>(
93+
export const maybeBaseNode = (node: unknown): node is BaseNode =>
94+
typeof node === 'object' && 'loc' in node && 'range' in node
95+
96+
export function restoreNodeLocation<T>(
9397
node: T,
9498
startLine: number,
9599
offset: number,
96100
): T {
97-
if (!node || !node.loc || !node.range) {
101+
if (!maybeBaseNode(node)) {
98102
return node
99103
}
100104

101-
Object.entries(node).forEach(([key, value]) => {
105+
for (const entry of Object.entries(node)) {
106+
const [key, value] = entry as [keyof BaseNode, BaseNode[keyof BaseNode]]
107+
102108
if (!value) {
103-
return
109+
continue
104110
}
105111

106-
if (Array.isArray(value)) {
107-
node[key as keyof T] = (value.map(child =>
108-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
109-
restoreNodeLocation(child, startLine, offset),
110-
) as unknown) as T[keyof T]
111-
} else {
112-
node[key as keyof T] = restoreNodeLocation(
113-
value,
114-
startLine,
115-
offset,
116-
) as T[keyof T]
117-
}
118-
})
112+
// ts doesn't understand the relationship between `key` and restored `value`
113+
// @ts-ignore
114+
node[key] = Array.isArray(value)
115+
? value.map(child => restoreNodeLocation(child, startLine, offset))
116+
: restoreNodeLocation(value, startLine, offset)
117+
}
119118

120119
const {
121120
loc: { start: startLoc, end: endLoc },

packages/eslint-mdx/src/parser.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,12 @@ export class Parser {
9393
position: {
9494
start: { line, column, offset: startOffset },
9595
},
96+
data,
9697
} = node
9798

9899
Object.assign(node, {
99100
data: {
100-
...node.data,
101+
...data,
101102
jsxType: 'JSXElementWithHTMLComments',
102103
comments,
103104
// jsx in paragraph is considered as plain html in mdx, what means html style comments are valid
@@ -154,6 +155,7 @@ export class Parser {
154155
return this.parseForESLint(code, options).ast
155156
}
156157

158+
// eslint-disable-next-line sonarjs/cognitive-complexity
157159
parseForESLint(code: string, options: ParserOptions) {
158160
const extname = path.extname(options.filePath)
159161
const isMdx = DEFAULT_EXTENSIONS.concat(options.extensions || []).includes(
@@ -191,7 +193,9 @@ export class Parser {
191193

192194
let normalized = this.normalizeJsxNode(node, parent, options)
193195
normalized = Array.isArray(normalized) ? normalized : [normalized]
194-
normalized.forEach(_node => this._nodeToAst(_node, options))
196+
for (const normalizedNode of normalized) {
197+
this._nodeToAst(normalizedNode, options)
198+
}
195199
},
196200
})
197201
}
@@ -288,6 +292,7 @@ export class Parser {
288292
position: {
289293
start: { line, offset },
290294
},
295+
data,
291296
} = node
292297

293298
return expression.children.reduce<Node[]>((nodes, jsNode) => {
@@ -311,7 +316,7 @@ export class Parser {
311316
const endOffset = range[1] - OFFSET
312317
nodes.push({
313318
type: 'jsx',
314-
data: nodes.length > 0 ? null : node.data,
319+
data: nodes.length > 0 ? null : data,
315320
value: value.slice(startOffset, endOffset),
316321
position: {
317322
start: {
@@ -371,16 +376,13 @@ export class Parser {
371376

372377
const offset = start - program.range[0]
373378

374-
AST_PROPS.forEach(prop =>
379+
for (const prop of AST_PROPS)
375380
this._ast[prop].push(
376-
// unfortunately, TS complains about incompatible signature
377-
// @ts-ignore
378-
...program[prop].map(item =>
379-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
381+
// ts doesn't understand the mixed type
382+
...program[prop].map((item: never) =>
380383
restoreNodeLocation(item, startLine, offset),
381384
),
382-
),
383-
)
385+
)
384386
}
385387
}
386388

packages/eslint-mdx/src/regexp.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,22 @@ export const commentContent = `${commentOpen}([\\s\\S]*?)${commentClose}`
3131
export const OPEN_TAG_REGEX = new RegExp(`^(?:${openTag})$`)
3232
export const CLOSE_TAG_REGEX = new RegExp(`^(?:${closeTag})$`)
3333
export const OPEN_CLOSE_TAG_REGEX = new RegExp(
34-
`^(?:${openTag + '[\\s\\S]*' + closeTag})$`,
34+
`^(?:${openTag + '[^<]*' + closeTag})$`,
3535
)
3636
export const SELF_CLOSING_TAG_REGEX = new RegExp(`^(?:${selfClosingTag})$`)
3737
export const COMMENT_REGEX = new RegExp(`^(?:${comment})$`)
3838
export const COMMENT_CONTENT_REGEX = new RegExp(commentContent)
3939
export const COMMENT_CONTENT_REGEX_GLOBAL = new RegExp(commentContent, 'g')
4040

41-
export const isOpenTag = (text: string) => OPEN_TAG_REGEX.test(text)
42-
export const isCloseTag = (text: string) => CLOSE_TAG_REGEX.test(text)
43-
export const isComment = (text: string) => COMMENT_REGEX.test(text)
41+
export const isOpenTag = (text: string) => OPEN_TAG_REGEX.test(text.trim())
42+
export const isCloseTag = (text: string) => CLOSE_TAG_REGEX.test(text.trim())
43+
export const isComment = (text: string) => COMMENT_REGEX.test(text.trim())
4444

4545
// the following functions are only declared for robustness and should never be called
4646
/* istanbul ignore next */
47-
export const isOpenCloseTag = (text: string) => OPEN_CLOSE_TAG_REGEX.test(text)
48-
// prettier-ignore
47+
export const isOpenCloseTag = (text: string) =>
48+
OPEN_CLOSE_TAG_REGEX.test(text.trim())
49+
4950
/* istanbul ignore next */
50-
export const isSelfClosingTag = (text: string) => SELF_CLOSING_TAG_REGEX.test(text)
51+
export const isSelfClosingTag = (text: string) =>
52+
SELF_CLOSING_TAG_REGEX.test(text.trim())

packages/eslint-mdx/src/traverse.ts

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class Traverse {
3737
// fix #7
3838
combineJsxNodes(nodes: Node[], parent?: Parent) {
3939
let offset = 0
40+
let hasOpenTag = false
4041
const jsxNodes: Node[] = []
4142
const { length } = nodes
4243
// eslint-disable-next-line sonarjs/cognitive-complexity
@@ -45,6 +46,7 @@ export class Traverse {
4546
const value = node.value as string
4647
if (isOpenTag(value)) {
4748
offset++
49+
hasOpenTag = true
4850
jsxNodes.push(node)
4951
} else {
5052
if (
@@ -56,40 +58,54 @@ export class Traverse {
5658
// prettier-ignore
5759
/* istanbul ignore next */
5860
else if (
59-
!isComment(value) &&
60-
!isSelfClosingTag(value) &&
61-
!isOpenCloseTag(value)
61+
isComment(value) ||
62+
isSelfClosingTag(value) ||
63+
isOpenCloseTag(value)
6264
) {
65+
jsxNodes.push(node)
66+
} else {
67+
// #272, we consider the first jsx node as open tag although it's not precise
68+
if (!index) {
69+
offset++
70+
hasOpenTag = true
71+
}
6372
try {
6473
// fix #138
6574
const nodes = parser.normalizeJsxNode(node, parent)
6675
jsxNodes.push(...(Array.isArray(nodes) ? nodes : [nodes]))
6776
} catch {
68-
// should never happen, just for robustness
69-
const { start } = node.position
70-
throw Object.assign(
71-
new SyntaxError('unknown jsx node: ' + JSON.stringify(value)),
72-
{
73-
lineNumber: start.line,
74-
column: start.column,
75-
index: start.offset,
76-
},
77-
)
77+
// #272 related
78+
if (offset) {
79+
jsxNodes.push(node)
80+
} else {
81+
// should never happen, just for robustness
82+
const { start } = node.position
83+
throw Object.assign(
84+
new SyntaxError('unknown jsx node: ' + JSON.stringify(value)),
85+
{
86+
lineNumber: start.line,
87+
column: start.column,
88+
index: start.offset,
89+
},
90+
)
91+
}
7892
}
79-
} else {
80-
jsxNodes.push(node)
8193
}
8294

8395
if (!offset) {
8496
// fix #158
85-
const firstOpenTagIndex = jsxNodes.findIndex(node =>
86-
isOpenTag(node.value as string),
97+
const firstOpenTagIndex = jsxNodes.findIndex(
98+
node => typeof node.value === 'string' && isOpenTag(node.value),
8799
)
88100
if (firstOpenTagIndex === -1) {
89-
acc.push(...jsxNodes)
101+
if (hasOpenTag) {
102+
acc.push(this.combineLeftJsxNodes(jsxNodes))
103+
} else {
104+
acc.push(...jsxNodes)
105+
}
90106
} else {
91-
acc.push(...jsxNodes.slice(0, firstOpenTagIndex))
92107
acc.push(
108+
...jsxNodes.slice(0, firstOpenTagIndex),
93109
this.combineLeftJsxNodes(jsxNodes.slice(firstOpenTagIndex)),
94110
)
95111
}
@@ -120,7 +136,9 @@ export class Traverse {
120136
if (children) {
121137
const parent = node as Parent
122138
children = node.children = this.combineJsxNodes(children, parent)
123-
children.forEach(child => this.traverse(child, parent))
139+
for (const child of children) {
140+
this.traverse(child, parent)
141+
}
124142
}
125143

126144
this._enter(node, parent)

packages/eslint-plugin-mdx/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@
3535
"dependencies": {
3636
"cosmiconfig": "^7.0.0",
3737
"eslint-mdx": "^1.8.2",
38-
"eslint-plugin-react": "^7.21.2",
38+
"eslint-plugin-react": "^7.22.0",
3939
"remark-mdx": "^1.6.22",
4040
"remark-parse": "^8.0.3",
4141
"remark-stringify": "^8.1.1",
42-
"tslib": "^2.0.1",
42+
"tslib": "^2.1.0",
4343
"unified": "^9.1.0",
44-
"vfile": "^4.1.1"
44+
"vfile": "^4.2.1"
4545
},
4646
"optionalDependencies": {
4747
"rebass": "^4.0.7"

0 commit comments

Comments
 (0)