-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update: compatibility with eslint-mdx #178
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,6 @@ node_modules/ | |
npm-debug.log | ||
.eslint-release-info.json | ||
.nyc_output | ||
package-lock.json | ||
yarn.lock | ||
types |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,11 @@ | ||
/** | ||
* @fileoverview Processes Markdown files for consumption by ESLint. | ||
* @author Brandon Mills | ||
* | ||
* @typedef {import('unist').Node} ASTNode | ||
* @typedef {ASTNode & {children: ASTNode[]}} ASTParentNode | ||
* @typedef {import('eslint').Linter.LintMessage} Message | ||
* @typedef {ASTNode & {baseIndentText: string, comments: string[], rangeMap: Array<{js: number, md: number}>}} Block | ||
*/ | ||
|
||
"use strict"; | ||
|
@@ -16,13 +21,13 @@ const SUPPORTS_AUTOFIX = true; | |
|
||
const markdown = unified().use(remarkParse); | ||
|
||
let blocks = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Lifecycle:
This was not a problem before because there was no |
||
const blocksCache = {}; | ||
|
||
/** | ||
* Performs a depth-first traversal of the Markdown AST. | ||
* @param {ASTNode} node A Markdown AST node. | ||
* @param {Object} callbacks A map of node types to callbacks. | ||
* @param {Object} [parent] The node's parent AST node. | ||
* @param {{[key: string]: (node: ASTNode, parent: ASTParentNode) => void}} callbacks A map of node types to callbacks. | ||
* @param {ASTParentNode} [parent] The node's parent AST node. | ||
* @returns {void} | ||
*/ | ||
function traverse(node, callbacks, parent) { | ||
|
@@ -40,7 +45,7 @@ function traverse(node, callbacks, parent) { | |
/** | ||
* Converts leading HTML comments to JS block comments. | ||
* @param {string} html The text content of an HTML AST node. | ||
* @returns {string[]} An array of JS block comments. | ||
* @returns {string} JS block comment. | ||
*/ | ||
function getComment(html) { | ||
const commentStart = "<!--"; | ||
|
@@ -116,9 +121,9 @@ function getIndentText(text, node) { | |
* delta at the beginning of each line. | ||
* @param {string} text The text of the file. | ||
* @param {ASTNode} node A Markdown code block AST node. | ||
* @param {comments} comments List of configuration comment strings that will be | ||
* @param {string[]} comments List of configuration comment strings that will be | ||
* inserted at the beginning of the code block. | ||
* @returns {Object[]} A list of offset-based adjustments, where lookups are | ||
* @returns {Array<{js: number, md: number}>} A list of offset-based adjustments, where lookups are | ||
* done based on the `js` key, which represents the range in the linted JS, | ||
* and the `md` key is the offset delta that, when added to the JS range, | ||
* returns the corresponding location in the original Markdown source. | ||
|
@@ -128,7 +133,7 @@ function getBlockRangeMap(text, node, comments) { | |
/* | ||
* The parser sets the fenced code block's start offset to wherever content | ||
* should normally begin (typically the first column of the line, but more | ||
* inside a list item, for example). The code block's opening fance may be | ||
* inside a list item, for example). The code block's opening fancy may be | ||
* further indented by up to three characters. If the code block has | ||
* additional indenting, the opening fence's first backtick may be up to | ||
* three whitespace characters after the start offset. | ||
|
@@ -208,15 +213,52 @@ function getBlockRangeMap(text, node, comments) { | |
return rangeMap; | ||
} | ||
|
||
const LANGUAGES_MAPPER = { | ||
javascript: "js", | ||
javascriptreact: "jsx", | ||
typescript: "ts", | ||
typescriptreact: "tsx", | ||
markdown: "md", | ||
mdown: "md", | ||
mkdn: "md" | ||
}; | ||
|
||
/** | ||
* get last item from array | ||
* @template T | ||
* @param {T[]} items array | ||
* @returns {T} last item | ||
*/ | ||
function last(items) { | ||
return items && items[items.length - 1]; | ||
} | ||
|
||
/** | ||
* get short language | ||
* @param {string} lang original language | ||
* @returns {string} short language | ||
*/ | ||
function getShortLang(lang) { | ||
const language = last( | ||
lang.split(/\s/u)[0].split(".") | ||
).toLowerCase(); | ||
|
||
return LANGUAGES_MAPPER[language] || language; | ||
} | ||
|
||
|
||
/** | ||
* Extracts lintable JavaScript code blocks from Markdown text. | ||
* @param {string} text The text of the file. | ||
* @returns {string[]} Source code strings to lint. | ||
* @param {string} filename The filename of the file | ||
* @returns {Array<string | {text: string, filename: string}>} Source code strings to lint. | ||
*/ | ||
function preprocess(text) { | ||
function preprocess(text, filename) { | ||
const ast = markdown.parse(text); | ||
const blocks = []; | ||
|
||
blocksCache[filename] = blocks; | ||
|
||
blocks = []; | ||
traverse(ast, { | ||
code(node, parent) { | ||
const comments = []; | ||
|
@@ -252,7 +294,7 @@ function preprocess(text) { | |
}); | ||
|
||
return blocks.map((block, index) => ({ | ||
filename: `${index}.${block.lang.trim().split(" ")[0]}`, | ||
filename: `${index}.${getShortLang(block.lang)}`, | ||
text: [ | ||
...block.comments, | ||
block.value, | ||
|
@@ -264,7 +306,7 @@ function preprocess(text) { | |
/** | ||
* Creates a map function that adjusts messages in a code block. | ||
* @param {Block} block A code block. | ||
* @returns {Function} A function that adjusts messages in a code block. | ||
* @returns {(message: Message) => Message} A function that adjusts messages in a code block. | ||
*/ | ||
function adjustBlock(block) { | ||
const leadingCommentLines = block.comments.reduce((count, comment) => count + comment.split("\n").length, 0); | ||
|
@@ -330,13 +372,22 @@ function excludeUnsatisfiableRules(message) { | |
|
||
/** | ||
* Transforms generated messages for output. | ||
* @param {Array<Message[]>} messages An array containing one array of messages | ||
* for each code block returned from `preprocess`. | ||
* @param {Array<Message[]>} messages An array containing one array of messages for each code block returned from `preprocess`. | ||
* @param {string} filename The filename of the file | ||
* @returns {Message[]} A flattened array of messages with mapped locations. | ||
*/ | ||
function postprocess(messages) { | ||
function postprocess(messages, filename) { | ||
const blocks = blocksCache[filename] || []; | ||
|
||
return [].concat(...messages.map((group, i) => { | ||
const adjust = adjustBlock(blocks[i]); | ||
const block = blocks[i]; | ||
|
||
// non code block message, parsed by `eslint-mdx` for example | ||
if (!block) { | ||
return group; | ||
} | ||
|
||
const adjust = adjustBlock(block); | ||
|
||
return group.map(adjust).filter(excludeUnsatisfiableRules); | ||
})); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,31 +24,36 @@ | |
"prepare": "node ./npm-prepare.js", | ||
"test": "npm run lint && npm run test-cov", | ||
"test-cov": "nyc _mocha -- -c tests/{examples,lib}/**/*.js", | ||
"types": "tsc lib/*.js -d --allowJs --emitDeclarationOnly --lib ES2015 --outDir types", | ||
"generate-release": "eslint-generate-release", | ||
"generate-alpharelease": "eslint-generate-prerelease alpha", | ||
"generate-betarelease": "eslint-generate-prerelease beta", | ||
"generate-rcrelease": "eslint-generate-prerelease rc", | ||
"publish-release": "eslint-publish-release" | ||
}, | ||
"main": "index.js", | ||
"types": "types/index.d.ts", | ||
"files": [ | ||
"index.js", | ||
"lib/index.js", | ||
"lib/processor.js" | ||
"lib", | ||
"types" | ||
], | ||
"devDependencies": { | ||
"@types/eslint": "^7.2.7", | ||
"chai": "^4.2.0", | ||
"eslint": "^6.8.0", | ||
"eslint-config-eslint": "^6.0.0", | ||
"eslint-plugin-jsdoc": "^15.9.5", | ||
"eslint-plugin-mdx": "^1.9.1", | ||
"eslint-plugin-node": "^9.0.0", | ||
"eslint-release": "^3.1.2", | ||
"mocha": "^6.2.2", | ||
"nyc": "^14.1.1" | ||
"nyc": "^14.1.1", | ||
"typescript": "^4.2.3" | ||
}, | ||
"dependencies": { | ||
"remark-parse": "^5.0.0", | ||
"unified": "^6.1.2" | ||
"remark-parse": ">=5.0.0 <9.0.0", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 9.0 has breaking change, its parser is rewritten totally. |
||
"unified": ">=6.0.0" | ||
}, | ||
"peerDependencies": { | ||
"eslint": ">=6.0.0" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The related PR has not been merged and released, disable temporarily.