eslint-plugin-markdown
Advanced tools
Comparing version 3.0.1 to 4.0.1
@@ -9,6 +9,37 @@ /** | ||
const processor = require("./processor"); | ||
const pkg = require("../package.json"); | ||
module.exports = { | ||
const rulesConfig = { | ||
// The Markdown parser automatically trims trailing | ||
// newlines from code blocks. | ||
"eol-last": "off", | ||
// In code snippets and examples, these rules are often | ||
// counterproductive to clarity and brevity. | ||
"no-undef": "off", | ||
"no-unused-expressions": "off", | ||
"no-unused-vars": "off", | ||
"padded-blocks": "off", | ||
// Adding a "use strict" directive at the top of every | ||
// code block is tedious and distracting. The config | ||
// opts into strict mode parsing without the directive. | ||
strict: "off", | ||
// The processor will not receive a Unicode Byte Order | ||
// Mark from the Markdown parser. | ||
"unicode-bom": "off" | ||
}; | ||
const plugin = { | ||
meta: { | ||
name: pkg.name, | ||
version: pkg.version | ||
}, | ||
processors: { | ||
markdown: processor | ||
}, | ||
configs: { | ||
recommended: { | ||
"recommended-legacy": { | ||
plugins: ["markdown"], | ||
@@ -33,22 +64,3 @@ overrides: [ | ||
rules: { | ||
// The Markdown parser automatically trims trailing | ||
// newlines from code blocks. | ||
"eol-last": "off", | ||
// In code snippets and examples, these rules are often | ||
// counterproductive to clarity and brevity. | ||
"no-undef": "off", | ||
"no-unused-expressions": "off", | ||
"no-unused-vars": "off", | ||
"padded-blocks": "off", | ||
// Adding a "use strict" directive at the top of every | ||
// code block is tedious and distracting. The config | ||
// opts into strict mode parsing without the directive. | ||
strict: "off", | ||
// The processor will not receive a Unicode Byte Order | ||
// Mark from the Markdown parser. | ||
"unicode-bom": "off" | ||
...rulesConfig | ||
} | ||
@@ -58,6 +70,35 @@ } | ||
} | ||
} | ||
}; | ||
plugin.configs.recommended = [ | ||
{ | ||
plugins: { | ||
markdown: plugin | ||
} | ||
}, | ||
processors: { | ||
markdown: processor | ||
{ | ||
files: ["**/*.md"], | ||
processor: "markdown/markdown" | ||
}, | ||
{ | ||
files: ["**/*.md/**"], | ||
languageOptions: { | ||
parserOptions: { | ||
ecmaFeatures: { | ||
// Adding a "use strict" directive at the top of | ||
// every code block is tedious and distracting, so | ||
// opt into strict mode parsing without the | ||
// directive. | ||
impliedStrict: true | ||
} | ||
} | ||
}, | ||
rules: { | ||
...rulesConfig | ||
} | ||
} | ||
}; | ||
]; | ||
module.exports = plugin; |
@@ -8,7 +8,5 @@ /** | ||
* @typedef {import('eslint/lib/shared/types').LintMessage} Message | ||
* | ||
* @typedef {Object} ASTNode | ||
* @property {string} type | ||
* @property {string} [lang] | ||
* | ||
* @property {string} type The type of node. | ||
* @property {string} [lang] The language that the node is in | ||
* @typedef {Object} RangeMap | ||
@@ -21,8 +19,9 @@ * @property {number} indent Number of code block indent characters trimmed from | ||
* original Markdown. | ||
* | ||
* @typedef {Object} BlockBase | ||
* @property {string} baseIndentText | ||
* @property {string[]} comments | ||
* @property {RangeMap[]} rangeMap | ||
* | ||
* @property {string} baseIndentText Leading whitespace text for the block. | ||
* @property {string[]} comments Comments inside of the JavaScript code. | ||
* @property {RangeMap[]} rangeMap 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. | ||
* @typedef {ASTNode & BlockBase} Block | ||
@@ -34,7 +33,8 @@ */ | ||
const parse = require("mdast-util-from-markdown"); | ||
const pkg = require("../package.json"); | ||
const UNSATISFIABLE_RULES = [ | ||
const UNSATISFIABLE_RULES = new Set([ | ||
"eol-last", // The Markdown parser strips trailing newlines in code fences | ||
"unicode-bom" // Code blocks will begin in the middle of Markdown files | ||
]; | ||
]); | ||
const SUPPORTS_AUTOFIX = true; | ||
@@ -382,3 +382,3 @@ | ||
function excludeUnsatisfiableRules(message) { | ||
return message && UNSATISFIABLE_RULES.indexOf(message.ruleId) < 0; | ||
return message && !UNSATISFIABLE_RULES.has(message.ruleId); | ||
} | ||
@@ -398,10 +398,14 @@ | ||
return [].concat(...messages.map((group, i) => { | ||
return messages.flatMap((group, i) => { | ||
const adjust = adjustBlock(blocks[i]); | ||
return group.map(adjust).filter(excludeUnsatisfiableRules); | ||
})); | ||
}); | ||
} | ||
module.exports = { | ||
meta: { | ||
name: `${pkg.name}/markdown`, | ||
version: pkg.version | ||
}, | ||
preprocess, | ||
@@ -408,0 +412,0 @@ postprocess, |
{ | ||
"name": "eslint-plugin-markdown", | ||
"version": "3.0.1", | ||
"version": "4.0.1", | ||
"description": "An ESLint plugin to lint JavaScript in Markdown code fences.", | ||
@@ -23,11 +23,10 @@ "license": "MIT", | ||
"scripts": { | ||
"lint": "eslint --ext js,md .", | ||
"lint": "eslint .", | ||
"prepare": "node ./npm-prepare.js", | ||
"test": "npm run lint && npm run test-cov", | ||
"test-cov": "nyc _mocha -- -c tests/{examples,lib}/**/*.js", | ||
"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" | ||
"release:generate:latest": "eslint-generate-release", | ||
"release:generate:alpha": "eslint-generate-prerelease alpha", | ||
"release:generate:beta": "eslint-generate-prerelease beta", | ||
"release:generate:rc": "eslint-generate-prerelease rc", | ||
"release:publish": "eslint-publish-release", | ||
"test": "nyc _mocha -- -c tests/{examples,lib}/**/*.js --timeout 30000" | ||
}, | ||
@@ -41,8 +40,8 @@ "main": "index.js", | ||
"devDependencies": { | ||
"@eslint/js": "^8.56.0", | ||
"chai": "^4.2.0", | ||
"eslint": "^7.32.0", | ||
"eslint-config-eslint": "^7.0.0", | ||
"eslint-plugin-jsdoc": "^37.0.3", | ||
"eslint-plugin-node": "^11.1.0", | ||
"eslint": "^8.56.0", | ||
"eslint-config-eslint": "^9.0.0", | ||
"eslint-release": "^3.1.2", | ||
"globals": "^13.24.0", | ||
"mocha": "^6.2.2", | ||
@@ -55,7 +54,7 @@ "nyc": "^14.1.1" | ||
"peerDependencies": { | ||
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" | ||
"eslint": ">=8" | ||
}, | ||
"engines": { | ||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" | ||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" | ||
} | ||
} |
230
README.md
@@ -20,3 +20,3 @@ # eslint-plugin-markdown | ||
Install the plugin alongside ESLint v6 or greater: | ||
Install the plugin alongside ESLint v8 or greater: | ||
@@ -29,8 +29,21 @@ ```sh | ||
Extending the `plugin:markdown/recommended` config will enable the Markdown processor on all `.md` files: | ||
In your `eslint.config.js` file, import `eslint-plugin-markdown` and include the recommended config to enable the Markdown processor on all `.md` files: | ||
```js | ||
// eslint.config.js | ||
import markdown from "eslint-plugin-markdown"; | ||
export default [ | ||
...markdown.configs.recommended | ||
// your other configs here | ||
]; | ||
``` | ||
If you are still using the deprecated `.eslintrc.js` file format for ESLint, you can extend the `plugin:markdown/recommended-legacy` config to enable the Markdown processor on all `.md` files: | ||
```js | ||
// .eslintrc.js | ||
module.exports = { | ||
extends: "plugin:markdown/recommended" | ||
extends: "plugin:markdown/recommended-legacy" | ||
}; | ||
@@ -41,9 +54,45 @@ ``` | ||
Add the plugin to your `.eslintrc` and use the `processor` option in an `overrides` entry to enable the plugin's `markdown/markdown` processor on Markdown files. | ||
You can manually include the Markdown processor by setting the `processor` option in your configuration file for all `.md` files. | ||
Each fenced code block inside a Markdown document has a virtual filename appended to the Markdown file's path. | ||
The virtual filename's extension will match the fenced code block's syntax tag, so for example, <code>```js</code> code blocks in <code>README.md</code> would match <code>README.md/*.js</code>. | ||
[`overrides` glob patterns](https://eslint.org/docs/user-guide/configuring#configuration-based-on-glob-patterns) for these virtual filenames can customize configuration for code blocks without affecting regular code. | ||
You can use glob patterns for these virtual filenames to customize configuration for code blocks without affecting regular code. | ||
For more information on configuring processors, refer to the [ESLint documentation](https://eslint.org/docs/user-guide/configuring#specifying-processor). | ||
Here's an example: | ||
```js | ||
// eslint.config.js | ||
import markdown from "eslint-plugin-markdown"; | ||
export default [ | ||
{ | ||
// 1. Add the plugin | ||
plugins: { | ||
markdown | ||
} | ||
}, | ||
{ | ||
// 2. Enable the Markdown processor for all .md files. | ||
files: ["**/*.md"], | ||
processor: "markdown/markdown" | ||
}, | ||
{ | ||
// 3. Optionally, customize the configuration ESLint uses for ```js | ||
// fenced code blocks inside .md files. | ||
files: ["**/*.md/*.js"], | ||
// ... | ||
rules: { | ||
// ... | ||
} | ||
} | ||
// your other configs here | ||
]; | ||
``` | ||
In the deprecated `.eslintrc.js` format: | ||
```js | ||
// .eslintrc.js | ||
@@ -76,3 +125,3 @@ module.exports = { | ||
For example, `no-undef` would flag variables that are declared outside of a code snippet because they aren't relevant to the example. | ||
The `plugin:markdown/recommended` config disables these rules in Markdown files: | ||
The `markdown.configs.recommended` config disables these rules in Markdown files: | ||
@@ -84,10 +133,44 @@ - [`no-undef`](https://eslint.org/docs/rules/no-undef) | ||
Use [`overrides` glob patterns](https://eslint.org/docs/user-guide/configuring#configuration-based-on-glob-patterns) to disable more rules just for Markdown code blocks: | ||
Use glob patterns to disable more rules just for Markdown code blocks: | ||
```js | ||
// / eslint.config.js | ||
import markdown from "eslint-plugin-markdown"; | ||
export default [ | ||
{ | ||
plugins: { | ||
markdown | ||
} | ||
}, | ||
{ | ||
files: ["**/*.md"], | ||
processor: "markdown/markdown" | ||
}, | ||
{ | ||
// 1. Target ```js code blocks in .md files. | ||
files: ["**/*.md/*.js"], | ||
rules: { | ||
// 2. Disable other rules. | ||
"no-console": "off", | ||
"import/no-unresolved": "off" | ||
} | ||
} | ||
// your other configs here | ||
]; | ||
``` | ||
And in the deprecated `.eslintrc.js` format: | ||
```js | ||
// .eslintrc.js | ||
module.exports = { | ||
// ... | ||
plugins: ["markdown"], | ||
overrides: [ | ||
// ... | ||
{ | ||
files: ["**/*.md"], | ||
processor: "markdown/markdown" | ||
}, | ||
{ | ||
// 1. Target ```js code blocks in .md files. | ||
@@ -108,3 +191,3 @@ files: ["**/*.md/*.js"], | ||
`"use strict"` directives in every code block would be annoying. | ||
The `plugin:markdown/recommended` config enables the [`impliedStrict` parser option](https://eslint.org/docs/user-guide/configuring#specifying-parser-options) and disables the [`strict` rule](https://eslint.org/docs/rules/strict) in Markdown files. | ||
The `markdown.configs.recommended` config enables the [`impliedStrict` parser option](https://eslint.org/docs/user-guide/configuring#specifying-parser-options) and disables the [`strict` rule](https://eslint.org/docs/rules/strict) in Markdown files. | ||
This opts into strict mode parsing without repeated `"use strict"` directives. | ||
@@ -115,3 +198,3 @@ | ||
Markdown code blocks are not real files, so ESLint's file-format rules do not apply. | ||
The `plugin:markdown/recommended` config disables these rules in Markdown files: | ||
The `markdown.configs.recommended` config disables these rules in Markdown files: | ||
@@ -121,107 +204,9 @@ - [`eol-last`](https://eslint.org/docs/rules/eol-last): The Markdown parser trims trailing newlines from code blocks. | ||
#### Migrating from `eslint-plugin-markdown` v1 | ||
`eslint-plugin-markdown` v1 used an older version of ESLint's processor API. | ||
The Markdown processor automatically ran on `.md`, `.mkdn`, `.mdown`, and `.markdown` files, and it only extracted fenced code blocks marked with `js`, `javascript`, `jsx`, or `node` syntax. | ||
Configuration specifically for fenced code blocks went inside an `overrides` entry with a `files` pattern matching the containing Markdown document's filename that applied to all fenced code blocks inside the file. | ||
```js | ||
// .eslintrc.js for eslint-plugin-markdown v1 | ||
module.exports = { | ||
plugins: ["markdown"], | ||
overrides: [ | ||
{ | ||
files: ["**/*.md"], | ||
// In v1, configuration for fenced code blocks went inside an | ||
// `overrides` entry with a .md pattern, for example: | ||
parserOptions: { | ||
ecmaFeatures: { | ||
impliedStrict: true | ||
} | ||
}, | ||
rules: { | ||
"no-console": "off" | ||
} | ||
} | ||
] | ||
}; | ||
``` | ||
[RFC3](https://github.com/eslint/rfcs/blob/master/designs/2018-processors-improvements/README.md) designed a new processor API to remove these limitations, and the new API was [implemented](https://github.com/eslint/eslint/pull/11552) as part of ESLint v6. | ||
`eslint-plugin-markdown` v2 uses this new API. | ||
```bash | ||
$ npm install --save-dev eslint@latest eslint-plugin-markdown@latest | ||
``` | ||
All of the Markdown file extensions that were previously hard-coded are now fully configurable in `.eslintrc.js`. | ||
Use the new `processor` option to apply the `markdown/markdown` processor on any Markdown documents matching a `files` pattern. | ||
Each fenced code block inside a Markdown document has a virtual filename appended to the Markdown file's path. | ||
The virtual filename's extension will match the fenced code block's syntax tag, so for example, <code>```js</code> code blocks in <code>README.md</code> would match <code>README.md/*.js</code>. | ||
```js | ||
// eslintrc.js for eslint-plugin-markdown v2 | ||
module.exports = { | ||
plugins: ["markdown"], | ||
overrides: [ | ||
{ | ||
// In v2, explicitly apply eslint-plugin-markdown's `markdown` | ||
// processor on any Markdown files you want to lint. | ||
files: ["**/*.md"], | ||
processor: "markdown/markdown" | ||
}, | ||
{ | ||
// In v2, configuration for fenced code blocks is separate from the | ||
// containing Markdown file. Each code block has a virtual filename | ||
// appended to the Markdown file's path. | ||
files: ["**/*.md/*.js"], | ||
// Configuration for fenced code blocks goes with the override for | ||
// the code block's virtual filename, for example: | ||
parserOptions: { | ||
ecmaFeatures: { | ||
impliedStrict: true | ||
} | ||
}, | ||
rules: { | ||
"no-console": "off" | ||
} | ||
} | ||
] | ||
}; | ||
``` | ||
If you need to precisely mimic the behavior of v1 with the hard-coded Markdown extensions and fenced code block syntaxes, you can use those as glob patterns in `overrides[].files`: | ||
```js | ||
// eslintrc.js for v2 mimicking v1 behavior | ||
module.exports = { | ||
plugins: ["markdown"], | ||
overrides: [ | ||
{ | ||
files: ["**/*.{md,mkdn,mdown,markdown}"], | ||
processor: "markdown/markdown" | ||
}, | ||
{ | ||
files: ["**/*.{md,mkdn,mdown,markdown}/*.{js,javascript,jsx,node}"] | ||
// ... | ||
} | ||
] | ||
}; | ||
``` | ||
### Running | ||
#### ESLint v7 | ||
If you are using an `eslint.config.js` file, then you can run ESLint as usual and it will pick up file patterns in your config file. The `--ext` option is not available when using flat config. | ||
You can run ESLint as usual and do not need to use the `--ext` option. | ||
ESLint v7 [automatically lints file extensions specified in `overrides[].files` patterns in config files](https://github.com/eslint/rfcs/blob/0253e3a95511c65d622eaa387eb73f824249b467/designs/2019-additional-lint-targets/README.md). | ||
If you are using an `.eslintrc.*` file, then you can run ESLint as usual and it will pick up file extensions specified in `overrides[].files` patterns in config files. | ||
#### ESLint v6 | ||
Use the [`--ext` option](https://eslint.org/docs/user-guide/command-line-interface#ext) to include `.js` and `.md` extensions in ESLint's file search: | ||
```sh | ||
eslint --ext js,md . | ||
``` | ||
### Autofixing | ||
@@ -261,4 +246,4 @@ | ||
```jsx | ||
// This can be linted too if you add `.jsx` files to `overrides` in ESLint v7 | ||
// or pass `--ext jsx` in ESLint v6. | ||
// This can be linted too if you add `.jsx` files to file patterns in the `eslint.config.js`. | ||
// Or `overrides[].files` in `eslintrc.*`. | ||
var div = <div className="jsx"></div>; | ||
@@ -276,11 +261,4 @@ ``` | ||
Unless a fenced code block's syntax appears as a file extension in `overrides[].files` in ESLint v7, it will be ignored. | ||
If using ESLint v6, you must also include the extension with the `--ext` option. | ||
Unless a fenced code block's syntax appears as a file extension in file patterns in your config file, it will be ignored. | ||
````markdown | ||
```python | ||
print("This doesn't get linted either.") | ||
``` | ||
```` | ||
## Configuration Comments | ||
@@ -292,6 +270,6 @@ | ||
This example enables the `browser` environment, disables the `no-alert` rule, and configures the `quotes` rule to prefer single quotes: | ||
This example enables the `alert` global variable, disables the `no-alert` rule, and configures the `quotes` rule to prefer single quotes: | ||
````markdown | ||
<!-- eslint-env browser --> | ||
<!-- global alert --> | ||
<!-- eslint-disable no-alert --> | ||
@@ -308,5 +286,5 @@ <!-- eslint quotes: ["error", "single"] --> | ||
````markdown | ||
Assuming `no-alert` is enabled in `.eslintrc`, the first code block will have no error from `no-alert`: | ||
Assuming `no-alert` is enabled in `eslint.config.js`, the first code block will have no error from `no-alert`: | ||
<!-- eslint-env browser --> | ||
<!-- global alert --> | ||
<!-- eslint-disable no-alert --> | ||
@@ -320,3 +298,3 @@ | ||
<!-- eslint-env browser --> | ||
<!-- global alert --> | ||
@@ -323,0 +301,0 @@ ```js |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
446
30542
342