From 535c3afe41454a69a0763c1db04e66a734d6f4c3 Mon Sep 17 00:00:00 2001 From: JounQin Date: Sat, 23 Dec 2023 16:43:47 +0800 Subject: [PATCH] feat: support parsing `markdown` via `eslint-mdx` natively --- .changeset/slimy-boats-fold.md | 2 +- .changeset/warm-cougars-attack.md | 5 +++++ .prettierrc | 10 +++++++++- eslint-plugin-prettier.js | 14 ++++++++++++-- eslint.config.js | 8 -------- package.json | 3 ++- patches/@types__eslint@8.44.7.patch | 14 ++++++++++++++ pnpm-lock.yaml | 8 ++++++-- test/fixtures/md.md | 5 +++++ test/prettier.js | 22 +++++++++++++++++++++- worker.js | 27 ++++++++++++++++++++++----- 11 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 .changeset/warm-cougars-attack.md create mode 100644 patches/@types__eslint@8.44.7.patch create mode 100644 test/fixtures/md.md diff --git a/.changeset/slimy-boats-fold.md b/.changeset/slimy-boats-fold.md index c7c36eb9..cbf5752b 100644 --- a/.changeset/slimy-boats-fold.md +++ b/.changeset/slimy-boats-fold.md @@ -1,5 +1,5 @@ --- -'eslint-plugin-prettier': patch +"eslint-plugin-prettier": patch --- Add exports mapping to package.json, to allow `import eslintPluginRecommended from 'eslint-plugin-prettier/recommended'` to work as expected. diff --git a/.changeset/warm-cougars-attack.md b/.changeset/warm-cougars-attack.md new file mode 100644 index 00000000..2324f3e2 --- /dev/null +++ b/.changeset/warm-cougars-attack.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-prettier": patch +--- + +feat: support parsing `markdown` via `eslint-mdx` natively diff --git a/.prettierrc b/.prettierrc index 7289f52e..ca1fd8d1 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,13 @@ { "arrowParens": "avoid", "singleQuote": true, - "plugins": ["@prettier/plugin-pug", "prettier-plugin-pkg"] + "plugins": ["@prettier/plugin-pug", "prettier-plugin-pkg"], + "overrides": [ + { + "files": ".changeset/**/*", + "options": { + "singleQuote": false + } + } + ] } diff --git a/eslint-plugin-prettier.js b/eslint-plugin-prettier.js index bf817ab4..74cd8c04 100644 --- a/eslint-plugin-prettier.js +++ b/eslint-plugin-prettier.js @@ -9,8 +9,10 @@ * @typedef {import('eslint').AST.Range} Range * @typedef {import('eslint').AST.SourceLocation} SourceLocation * @typedef {import('eslint').ESLint.Plugin} Plugin + * @typedef {import('eslint').ESLint.ObjectMetaProperties} ObjectMetaProperties * @typedef {import('prettier').FileInfoOptions} FileInfoOptions - * @typedef {import('prettier').Options & { onDiskFilepath: string, parserPath: string, usePrettierrc?: boolean }} Options + * @typedef {import('prettier').Options} PrettierOptions + * @typedef {PrettierOptions & { onDiskFilepath: string, parserMeta?: ObjectMetaProperties['meta'], parserPath?: string, usePrettierrc?: boolean }} Options */ 'use strict'; @@ -167,10 +169,12 @@ const eslintPluginPrettier = { } /** - * @type {{}} + * @type {PrettierOptions} */ const eslintPrettierOptions = context.options[0] || {}; + const parser = context.languageOptions?.parser; + // prettier.format() may throw a SyntaxError if it cannot parse the // source code it is given. Usually for JS files this isn't a // problem as ESLint will report invalid syntax before trying to @@ -190,6 +194,12 @@ const eslintPluginPrettier = { ...eslintPrettierOptions, filepath, onDiskFilepath, + parserMeta: + parser && + (parser.meta ?? { + name: parser.name, + version: parser.version, + }), parserPath: context.parserPath, usePrettierrc, }, diff --git a/eslint.config.js b/eslint.config.js index ad706b4e..9e1eff12 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -25,14 +25,6 @@ module.exports = [ 'eslint-plugin/report-message-format': ['error', '^[^a-z].*\\.$'], }, }, - { - files: ['**/*.md'], - rules: { 'prettier/prettier': ['error', { parser: 'markdown' }] }, - }, - { - files: ['**/*.mdx'], - rules: { 'prettier/prettier': ['error', { parser: 'mdx' }] }, - }, // Global ignores // If a config block only contains an `ignores` key, then the globs are // ignored globally diff --git a/package.json b/package.json index c5c9262f..20ab6404 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,8 @@ }, "pnpm": { "patchedDependencies": { - "@graphql-eslint/eslint-plugin@3.20.0": "patches/@graphql-eslint__eslint-plugin@3.20.0.patch" + "@graphql-eslint/eslint-plugin@3.20.0": "patches/@graphql-eslint__eslint-plugin@3.20.0.patch", + "@types/eslint@8.44.7": "patches/@types__eslint@8.44.7.patch" } } } diff --git a/patches/@types__eslint@8.44.7.patch b/patches/@types__eslint@8.44.7.patch new file mode 100644 index 00000000..d5239c91 --- /dev/null +++ b/patches/@types__eslint@8.44.7.patch @@ -0,0 +1,14 @@ +diff --git a/index.d.ts b/index.d.ts +index 75ae420e38148c632230763f26382ef0d9024427..6b8b08da2e25b54dedf41f1db0f2ba6e2c718b30 100644 +--- a/index.d.ts ++++ b/index.d.ts +@@ -753,6 +753,9 @@ export namespace Rule { + id: string; + options: any[]; + settings: { [name: string]: any }; ++ languageOptions: { ++ parser: Linter.ParserModule; ++ } & Linter.ParserOptions; + parserPath: string; + parserOptions: Linter.ParserOptions; + parserServices: SourceCode.ParserServices; \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e33634d..c3ba111b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ patchedDependencies: '@graphql-eslint/eslint-plugin@3.20.0': hash: e3c7i33vtfs4mivhjwqctijsoe path: patches/@graphql-eslint__eslint-plugin@3.20.0.patch + '@types/eslint@8.44.7': + hash: r7mjcwcmok5lsw2nb63qlph4sa + path: patches/@types__eslint@8.44.7.patch dependencies: prettier-linter-helpers: @@ -44,7 +47,7 @@ devDependencies: version: 3.0.0(prettier@3.0.0) '@types/eslint': specifier: ^8.44.7 - version: 8.44.7 + version: 8.44.7(patch_hash=r7mjcwcmok5lsw2nb63qlph4sa) '@types/prettier-linter-helpers': specifier: ^1.0.1 version: 1.0.4 @@ -1216,12 +1219,13 @@ packages: '@types/ms': 0.7.31 dev: true - /@types/eslint@8.44.7: + /@types/eslint@8.44.7(patch_hash=r7mjcwcmok5lsw2nb63qlph4sa): resolution: {integrity: sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==} dependencies: '@types/estree': 1.0.1 '@types/json-schema': 7.0.12 dev: true + patched: true /@types/estree-jsx@1.0.0: resolution: {integrity: sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ==} diff --git a/test/fixtures/md.md b/test/fixtures/md.md new file mode 100644 index 00000000..4ab8063b --- /dev/null +++ b/test/fixtures/md.md @@ -0,0 +1,5 @@ +# Heading + +```ts +export declare const x = 1 +``` diff --git a/test/prettier.js b/test/prettier.js index 6a0109eb..aeba89f4 100644 --- a/test/prettier.js +++ b/test/prettier.js @@ -38,7 +38,7 @@ const eslint = new ESLint({ extends: 'plugin:prettier/recommended', overrides: [ { - files: '*.mdx', + files: ['*.{md,mdx}'], extends: 'plugin:mdx/recommended', settings: { 'mdx/code-block': true, @@ -228,6 +228,26 @@ mdxRuleTester.run('eslint-plugin-mdx', rule, { ], }); +runFixture('*.md', [ + [ + { + column: 27, + endColumn: 27, + endLine: 4, + fix: { + range: [43, 43], + text: ';', + }, + line: 4, + message: 'Insert `;`', + messageId: 'insert', + nodeType: null, + ruleId: 'prettier/prettier', + severity: 2, + }, + ], +]); + runFixture('*.mdx', [ [ { diff --git a/worker.js b/worker.js index a2237407..8a8a802c 100644 --- a/worker.js +++ b/worker.js @@ -2,7 +2,8 @@ /** * @typedef {import('prettier').FileInfoOptions} FileInfoOptions - * @typedef {import('prettier').Options & { onDiskFilepath: string, parserPath?: string, usePrettierrc?: boolean }} Options + * @typedef {import('eslint').ESLint.ObjectMetaProperties} ObjectMetaProperties + * @typedef {import('prettier').Options & { onDiskFilepath: string, parserMeta?: ObjectMetaProperties['meta'], parserPath?: string, usePrettierrc?: boolean }} Options */ const { runAsWorker } = require('synckit'); @@ -24,6 +25,7 @@ runAsWorker( { filepath, onDiskFilepath, + parserMeta, parserPath, usePrettierrc, ...eslintPrettierOptions @@ -58,7 +60,7 @@ runAsWorker( return; } - const initialOptions = {}; + const initialOptions = { parser: inferredParser ?? 'babel' }; // ESLint supports processors that let you extract and lint JS // fragments within a non-JS language. In the cases where prettier @@ -94,9 +96,9 @@ runAsWorker( // 2. `eslint-plugin-html` // 3. `eslint-plugin-markdown@1` (replacement: `eslint-plugin-markdown@2+`) // 4. `eslint-plugin-svelte3` (replacement: `eslint-plugin-svelte@2+`) - const parserBlocklist = [null, 'markdown', 'html']; + const parserBlocklist = ['html']; - let inferParserToBabel = parserBlocklist.includes(inferredParser); + let inferParserToBabel = parserBlocklist.includes(initialOptions.parser); switch (inferredParser) { // it could be processed by `@graphql-eslint/eslint-plugin` or `eslint-plugin-graphql` @@ -109,12 +111,24 @@ runAsWorker( } break; } + case 'markdown': { + // it could be processed by `eslint-plugin-markdown@1` or correctly parsed by `eslint-mdx` + if ( + (typeof parserMeta !== 'undefined' && + parserMeta.name !== 'eslint-mdx') || + (typeof parserPath === 'string' && + !/([\\/])eslint-mdx\1/.test(parserPath)) + ) { + inferParserToBabel = true; + } + break; + } // it could be processed by `@ota-meshi/eslint-plugin-svelte`, `eslint-plugin-svelte` or `eslint-plugin-svelte3` case 'svelte': { // The `source` would be modified by `eslint-plugin-svelte3` if ( typeof parserPath === 'string' && - !parserPath.includes('svelte-eslint-parser') + !/([\\/])svelte-eslint-parser\1/.test(parserPath) ) { // We do not support `eslint-plugin-svelte3`, // the users should run `prettier` on `.svelte` files manually @@ -152,6 +166,9 @@ runAsWorker( } } + /** + * @type {import('prettier').Options} + */ const prettierOptions = { ...initialOptions, ...prettierRcOptions,