From b692acd8986824a7bd03db94dd64848289e15989 Mon Sep 17 00:00:00 2001 From: JounQin Date: Tue, 23 Mar 2021 17:48:19 +0800 Subject: [PATCH 1/2] feat: support processor virtual filename close #393 --- eslint-plugin-prettier.js | 59 +++++++++++++++++++++++++++++++++++---- test/prettier.js | 4 +++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/eslint-plugin-prettier.js b/eslint-plugin-prettier.js index 5916b5cd..4bfbef0f 100644 --- a/eslint-plugin-prettier.js +++ b/eslint-plugin-prettier.js @@ -9,6 +9,8 @@ // Requirements // ------------------------------------------------------------------------------ +const path = require('path'); + const { showInvisibles, generateDifferences @@ -25,6 +27,9 @@ const { INSERT, DELETE, REPLACE } = generateDifferences; // ------------------------------------------------------------------------------ // Lazily-loaded Prettier. +/** + * @type {import('prettier')} + */ let prettier; // ------------------------------------------------------------------------------ @@ -55,6 +60,41 @@ function reportDifference(context, difference) { }); } +/** + * Get prettier config options, handle virtual filename correctly + * @template T + * @template O + * @param {(filepath: string, options: O) => T} getter + * @param {string} filepath + * @param {O} options + * @returns {{result: T, realpath: string}} + */ +function tryToGet(getter, filepath, options) { + try { + return { + result: getter(filepath, options), + realpath: filepath + }; + } catch (err) { + // https://github.com/eslint/eslint/issues/11989 + if ( + err.code !== 'ENOTDIR' || + !/[/\\]\d+_[^/\\]*\.[\da-z]+$/i.test(filepath) + ) { + throw err; + } + try { + filepath = path.dirname(filepath); + return { + result: getter(filepath, options), + realpath: filepath + }; + } catch (e) { + throw err; + } + } +} + // ------------------------------------------------------------------------------ // Module Definition // ------------------------------------------------------------------------------ @@ -124,12 +164,13 @@ module.exports = { const eslintPrettierOptions = context.options[0] || {}; const prettierRcOptions = usePrettierrc - ? prettier.resolveConfig.sync(filepath, { + ? tryToGet(prettier.resolveConfig.sync, filepath, { editorconfig: true - }) + }).result : null; - const prettierFileInfo = prettier.getFileInfo.sync( + const { realpath, result: prettierFileInfo } = tryToGet( + prettier.getFileInfo.sync, filepath, Object.assign( {}, @@ -145,7 +186,8 @@ module.exports = { const initialOptions = {}; - // ESLint suppports processors that let you extract and lint JS + // for ESLint < 6.0 + // it supports processors that let you extract and lint JS // fragments within a non-JS language. In the cases where prettier // supports the same language as a processor, we want to process // the provided source code as javascript (as ESLint provides the @@ -165,8 +207,13 @@ module.exports = { // * Prettier supports parsing the file type // * There is an ESLint processor that extracts JavaScript snippets // from the file type. + // + // for ESLint >= 6.0 + // it supports virtual filename, if filepath is not same as realpath, + // it means filepath is virtual name, and we can guess the file type by prettier automatically const parserBlocklist = [null, 'graphql', 'markdown', 'html']; if ( + filepath === realpath && parserBlocklist.indexOf(prettierFileInfo.inferredParser) !== -1 ) { // Prettier v1.16.0 renamed the `babylon` parser to `babel` @@ -187,7 +234,7 @@ module.exports = { ); // prettier.format() may throw a SyntaxError if it cannot parse the - // source code it is given. Ususally for JS files this isn't a + // source code it is given. Usually for JS files this isn't a // problem as ESLint will report invalid syntax before trying to // pass it to the prettier plugin. However this might be a problem // for non-JS languages that are handled by a plugin. Notably Vue @@ -205,7 +252,7 @@ module.exports = { let message = 'Parsing error: ' + err.message; // Prettier's message contains a codeframe style preview of the - // invalid code and the line/column at which the error occured. + // invalid code and the line/column at which the error occurred. // ESLint shows those pieces of information elsewhere already so // remove them from the message if (err.codeFrame) { diff --git a/test/prettier.js b/test/prettier.js index c5e8a6af..ff859461 100644 --- a/test/prettier.js +++ b/test/prettier.js @@ -66,6 +66,10 @@ ruleTester.run('prettier', rule, { { code: 'a();;;;;;\n', filename: 'node_modules/dummy.js' + }, + { + code: `('');\n`, + filename: path.join(__filename, '0_fake_virtual_name.js') } ], invalid: [ From 03dc7bfe95ae9d31f633d11113b5c633d3b07780 Mon Sep 17 00:00:00 2001 From: JounQin Date: Tue, 13 Apr 2021 09:41:45 +0000 Subject: [PATCH 2/2] refactor: normalize filepath first in case recursive virtual filename --- eslint-plugin-prettier.js | 50 ++++++++++++++------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/eslint-plugin-prettier.js b/eslint-plugin-prettier.js index 4bfbef0f..3e446cfc 100644 --- a/eslint-plugin-prettier.js +++ b/eslint-plugin-prettier.js @@ -9,6 +9,7 @@ // Requirements // ------------------------------------------------------------------------------ +const fs = require('fs'); const path = require('path'); const { @@ -61,38 +62,23 @@ function reportDifference(context, difference) { } /** - * Get prettier config options, handle virtual filename correctly - * @template T - * @template O - * @param {(filepath: string, options: O) => T} getter + * get normalized filepath in case of virtual filename * @param {string} filepath - * @param {O} options - * @returns {{result: T, realpath: string}} + * @returns {string} */ -function tryToGet(getter, filepath, options) { +function normalizeFilepath(filepath) { try { - return { - result: getter(filepath, options), - realpath: filepath - }; + if (fs.statSync(filepath).isFile()) { + return filepath; + } } catch (err) { // https://github.com/eslint/eslint/issues/11989 - if ( - err.code !== 'ENOTDIR' || - !/[/\\]\d+_[^/\\]*\.[\da-z]+$/i.test(filepath) - ) { - throw err; - } - try { - filepath = path.dirname(filepath); - return { - result: getter(filepath, options), - realpath: filepath - }; - } catch (e) { - throw err; + if (err.code === 'ENOTDIR') { + return normalizeFilepath(path.dirname(filepath)); } } + + return filepath; } // ------------------------------------------------------------------------------ @@ -152,6 +138,7 @@ module.exports = { (context.options[1] && context.options[1].fileInfoOptions) || {}; const sourceCode = context.getSourceCode(); const filepath = context.getFilename(); + const normalizedFilepath = normalizeFilepath(filepath); const source = sourceCode.text; return { @@ -164,14 +151,13 @@ module.exports = { const eslintPrettierOptions = context.options[0] || {}; const prettierRcOptions = usePrettierrc - ? tryToGet(prettier.resolveConfig.sync, filepath, { + ? prettier.resolveConfig.sync(normalizedFilepath, { editorconfig: true - }).result + }) : null; - const { realpath, result: prettierFileInfo } = tryToGet( - prettier.getFileInfo.sync, - filepath, + const prettierFileInfo = prettier.getFileInfo.sync( + normalizedFilepath, Object.assign( {}, { resolveConfig: true, ignorePath: '.prettierignore' }, @@ -209,11 +195,11 @@ module.exports = { // from the file type. // // for ESLint >= 6.0 - // it supports virtual filename, if filepath is not same as realpath, + // it supports virtual filename, if filepath is not same as normalizedFilepath, // it means filepath is virtual name, and we can guess the file type by prettier automatically const parserBlocklist = [null, 'graphql', 'markdown', 'html']; if ( - filepath === realpath && + filepath === normalizedFilepath && parserBlocklist.indexOf(prettierFileInfo.inferredParser) !== -1 ) { // Prettier v1.16.0 renamed the `babylon` parser to `babel`