Skip to content

Commit

Permalink
Update docs for virtual filenames
Browse files Browse the repository at this point in the history
  • Loading branch information
BPScott committed Apr 15, 2021
1 parent ee0ccc6 commit 2ee9363
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 16 deletions.
41 changes: 25 additions & 16 deletions eslint-plugin-prettier.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,21 @@ function reportDifference(context, difference) {
}

/**
* get normalized filepath in case of virtual filename
* Given a filepath, get the nearest path that is a regular file.
* The filepath provided by eslint may be a virtual filepath rather than a file
* on disk. This attempts to transform a virtual path into an on-disk path
* @param {string} filepath
* @returns {string}
*/
function normalizeFilepath(filepath) {
function getOnDiskFilepath(filepath) {
try {
if (fs.statSync(filepath).isFile()) {
return filepath;
}
} catch (err) {
// https://github.com/eslint/eslint/issues/11989
if (err.code === 'ENOTDIR') {
return normalizeFilepath(path.dirname(filepath));
return getOnDiskFilepath(path.dirname(filepath));
}
}

Expand Down Expand Up @@ -138,7 +140,14 @@ module.exports = {
(context.options[1] && context.options[1].fileInfoOptions) || {};
const sourceCode = context.getSourceCode();
const filepath = context.getFilename();
const normalizedFilepath = normalizeFilepath(filepath);
// Processors that extract content from a file, such as the markdown
// plugin extracting fenced code blocks may choose to specify virtual
// file paths. If this is the case then we need to resolve prettier
// config and file info using the on-disk path instead of the virtual
// path.
// See https://github.com/eslint/eslint/issues/11989 for ideas around
// being able to get this value directly from eslint in the future.
const onDiskFilepath = getOnDiskFilepath(filepath);
const source = sourceCode.text;

return {
Expand All @@ -151,13 +160,13 @@ module.exports = {
const eslintPrettierOptions = context.options[0] || {};

const prettierRcOptions = usePrettierrc
? prettier.resolveConfig.sync(normalizedFilepath, {
? prettier.resolveConfig.sync(onDiskFilepath, {
editorconfig: true
})
: null;

const prettierFileInfo = prettier.getFileInfo.sync(
normalizedFilepath,
onDiskFilepath,
Object.assign(
{},
{ resolveConfig: true, ignorePath: '.prettierignore' },
Expand All @@ -172,18 +181,22 @@ module.exports = {

const initialOptions = {};

// for ESLint < 6.0
// it supports processors that let you extract and lint JS
// ESLint 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
// rules with fragments of JS) instead of guessing the parser
// based off the filename. Otherwise, for instance, on a .md file we
// end up trying to run prettier over a fragment of JS using the
// markdown parser, which throws an error.
// If we can't infer the parser from from the filename, either
// because no filename was provided or because there is no parser
// found for the filename, use javascript.
// Processors may set virtual filenames for these extracted blocks.
// If they do so then we want to trust the file extension they
// provide, and no override is needed.
// If the processor does not set any virtual filename (signified by
// `filepath` and `onDiskFilepath` being equal) AND we can't
// infer the parser from the filename, either because no filename
// was provided or because there is no parser found for the
// filename, use javascript.
// This is added to the options first, so that
// prettierRcOptions and eslintPrettierOptions can still override
// the parser.
Expand All @@ -193,13 +206,9 @@ 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 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 === normalizedFilepath &&
filepath === onDiskFilepath &&
parserBlocklist.indexOf(prettierFileInfo.inferredParser) !== -1
) {
// Prettier v1.16.0 renamed the `babylon` parser to `babel`
Expand Down
7 changes: 7 additions & 0 deletions test/prettier.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ ruleTester.run('prettier', rule, {
code: 'a();;;;;;\n',
filename: 'node_modules/dummy.js'
},
// ESLint processors can provide virtual filenames. E.g. fenced code blocks
// in a markdown file may be processed with the filenames
// `a-markdown-file.md/1.js` / `a-markdown-file.md/2.js`
// If we try and pass those filenames into prettier's `resolveConfig` and
// `getFileInfo` methods they throw up because the it doesn't like treating
// `markdown-file.md` as a directory.
// Make sure we handle that case internally so this does not crash
{
code: `('');\n`,
filename: path.join(__filename, '0_fake_virtual_name.js')
Expand Down

0 comments on commit 2ee9363

Please sign in to comment.