Skip to content

Commit 2ee9363

Browse files
committed
Update docs for virtual filenames
1 parent ee0ccc6 commit 2ee9363

File tree

2 files changed

+32
-16
lines changed

2 files changed

+32
-16
lines changed

eslint-plugin-prettier.js

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,21 @@ function reportDifference(context, difference) {
6262
}
6363

6464
/**
65-
* get normalized filepath in case of virtual filename
65+
* Given a filepath, get the nearest path that is a regular file.
66+
* The filepath provided by eslint may be a virtual filepath rather than a file
67+
* on disk. This attempts to transform a virtual path into an on-disk path
6668
* @param {string} filepath
6769
* @returns {string}
6870
*/
69-
function normalizeFilepath(filepath) {
71+
function getOnDiskFilepath(filepath) {
7072
try {
7173
if (fs.statSync(filepath).isFile()) {
7274
return filepath;
7375
}
7476
} catch (err) {
7577
// https://github.com/eslint/eslint/issues/11989
7678
if (err.code === 'ENOTDIR') {
77-
return normalizeFilepath(path.dirname(filepath));
79+
return getOnDiskFilepath(path.dirname(filepath));
7880
}
7981
}
8082

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

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

153162
const prettierRcOptions = usePrettierrc
154-
? prettier.resolveConfig.sync(normalizedFilepath, {
163+
? prettier.resolveConfig.sync(onDiskFilepath, {
155164
editorconfig: true
156165
})
157166
: null;
158167

159168
const prettierFileInfo = prettier.getFileInfo.sync(
160-
normalizedFilepath,
169+
onDiskFilepath,
161170
Object.assign(
162171
{},
163172
{ resolveConfig: true, ignorePath: '.prettierignore' },
@@ -172,18 +181,22 @@ module.exports = {
172181

173182
const initialOptions = {};
174183

175-
// for ESLint < 6.0
176-
// it supports processors that let you extract and lint JS
184+
// ESLint supports processors that let you extract and lint JS
177185
// fragments within a non-JS language. In the cases where prettier
178186
// supports the same language as a processor, we want to process
179187
// the provided source code as javascript (as ESLint provides the
180188
// rules with fragments of JS) instead of guessing the parser
181189
// based off the filename. Otherwise, for instance, on a .md file we
182190
// end up trying to run prettier over a fragment of JS using the
183191
// markdown parser, which throws an error.
184-
// If we can't infer the parser from from the filename, either
185-
// because no filename was provided or because there is no parser
186-
// found for the filename, use javascript.
192+
// Processors may set virtual filenames for these extracted blocks.
193+
// If they do so then we want to trust the file extension they
194+
// provide, and no override is needed.
195+
// If the processor does not set any virtual filename (signified by
196+
// `filepath` and `onDiskFilepath` being equal) AND we can't
197+
// infer the parser from the filename, either because no filename
198+
// was provided or because there is no parser found for the
199+
// filename, use javascript.
187200
// This is added to the options first, so that
188201
// prettierRcOptions and eslintPrettierOptions can still override
189202
// the parser.
@@ -193,13 +206,9 @@ module.exports = {
193206
// * Prettier supports parsing the file type
194207
// * There is an ESLint processor that extracts JavaScript snippets
195208
// from the file type.
196-
//
197-
// for ESLint >= 6.0
198-
// it supports virtual filename, if filepath is not same as normalizedFilepath,
199-
// it means filepath is virtual name, and we can guess the file type by prettier automatically
200209
const parserBlocklist = [null, 'graphql', 'markdown', 'html'];
201210
if (
202-
filepath === normalizedFilepath &&
211+
filepath === onDiskFilepath &&
203212
parserBlocklist.indexOf(prettierFileInfo.inferredParser) !== -1
204213
) {
205214
// Prettier v1.16.0 renamed the `babylon` parser to `babel`

test/prettier.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ ruleTester.run('prettier', rule, {
6767
code: 'a();;;;;;\n',
6868
filename: 'node_modules/dummy.js'
6969
},
70+
// ESLint processors can provide virtual filenames. E.g. fenced code blocks
71+
// in a markdown file may be processed with the filenames
72+
// `a-markdown-file.md/1.js` / `a-markdown-file.md/2.js`
73+
// If we try and pass those filenames into prettier's `resolveConfig` and
74+
// `getFileInfo` methods they throw up because the it doesn't like treating
75+
// `markdown-file.md` as a directory.
76+
// Make sure we handle that case internally so this does not crash
7077
{
7178
code: `('');\n`,
7279
filename: path.join(__filename, '0_fake_virtual_name.js')

0 commit comments

Comments
 (0)