Skip to content

Commit

Permalink
feat: introduce fullControl option
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Dec 2, 2023
1 parent 1882a36 commit a333607
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 100 deletions.
6 changes: 5 additions & 1 deletion eslint-plugin-prettier.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const { INSERT, DELETE, REPLACE } = generateDifferences;

// Lazily-loaded Prettier.
/**
* @type {(source: string, options: Options, fileInfoOptions: FileInfoOptions) => string}
* @type {(source: string, options: Options, fileInfoOptions: FileInfoOptions, fullControl: boolean) => string}
*/
let prettierFormat;

Expand Down Expand Up @@ -115,6 +115,7 @@ const eslintPluginPrettier = {
properties: {},
additionalProperties: true,
},
fullControl: { type: 'boolean' },
},
additionalProperties: true,
},
Expand All @@ -133,6 +134,8 @@ const eslintPluginPrettier = {
*/
const fileInfoOptions =
(context.options[1] && context.options[1].fileInfoOptions) || {};
const fullControl =
(context.options[1] && context.options[1].fullControl) || false;

// `context.getSourceCode()` was deprecated in ESLint v8.40.0 and replaced
// with the `sourceCode` property.
Expand Down Expand Up @@ -192,6 +195,7 @@ const eslintPluginPrettier = {
usePrettierrc,
},
fileInfoOptions,
fullControl,
);
} catch (err) {
if (!(err instanceof SyntaxError)) {
Expand Down
203 changes: 104 additions & 99 deletions worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ runAsWorker(
...eslintPrettierOptions
},
eslintFileInfoOptions,
fullControl = false,
) => {
if (!prettier) {
prettier = await import('prettier');
Expand All @@ -40,114 +41,118 @@ runAsWorker(
})
: null;

const { ignored, inferredParser } = await prettier.getFileInfo(
onDiskFilepath,
{
resolveConfig: false,
withNodeModules: false,
ignorePath: '.prettierignore',
plugins: /** @type {string[] | undefined} */ (
prettierRcOptions ? prettierRcOptions.plugins : undefined
),
...eslintFileInfoOptions,
},
);
const initialOptions = {};

// Skip if file is ignored using a .prettierignore file
if (ignored) {
return;
}
// Full control mode bypass Prettier's ignorefile and parser detection logic.
// Let ESLint take care of it.
if (!fullControl) {
const { ignored, inferredParser } = await prettier.getFileInfo(
onDiskFilepath,
{
resolveConfig: false,
withNodeModules: false,
ignorePath: '.prettierignore',
plugins: /** @type {string[] | undefined} */ (
prettierRcOptions ? prettierRcOptions.plugins : undefined
),
...eslintFileInfoOptions,
},
);

const initialOptions = {};
// Skip if file is ignored using a .prettierignore file
if (ignored) {
return;
}

// 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.
// 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.
//
// `parserBlocklist` should contain the list of prettier parser
// names for file types where:
// * Prettier supports parsing the file type
// * There is an ESLint processor that extracts JavaScript snippets
// from the file type.
if (filepath === onDiskFilepath) {
// The following list means the plugin process source into js content
// but with same filename, so we need to change the parser to `babel`
// by default.
// Related ESLint plugins are:
// 1. `eslint-plugin-graphql` (replacement: `@graphql-eslint/eslint-plugin`)
// 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'];
// 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.
// 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.
//
// `parserBlocklist` should contain the list of prettier parser
// names for file types where:
// * Prettier supports parsing the file type
// * There is an ESLint processor that extracts JavaScript snippets
// from the file type.
if (filepath === onDiskFilepath) {
// The following list means the plugin process source into js content
// but with same filename, so we need to change the parser to `babel`
// by default.
// Related ESLint plugins are:
// 1. `eslint-plugin-graphql` (replacement: `@graphql-eslint/eslint-plugin`)
// 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'];

let inferParserToBabel = parserBlocklist.includes(inferredParser);
let inferParserToBabel = parserBlocklist.includes(inferredParser);

switch (inferredParser) {
// it could be processed by `@graphql-eslint/eslint-plugin` or `eslint-plugin-graphql`
case 'graphql': {
if (
// for `eslint-plugin-graphql`, see https://github.com/apollographql/eslint-plugin-graphql/blob/master/src/index.js#L416
source.startsWith('ESLintPluginGraphQLFile`')
) {
inferParserToBabel = true;
switch (inferredParser) {
// it could be processed by `@graphql-eslint/eslint-plugin` or `eslint-plugin-graphql`
case 'graphql': {
if (
// for `eslint-plugin-graphql`, see https://github.com/apollographql/eslint-plugin-graphql/blob/master/src/index.js#L416
source.startsWith('ESLintPluginGraphQLFile`')
) {
inferParserToBabel = true;
}
break;
}
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')
) {
// We do not support `eslint-plugin-svelte3`,
// the users should run `prettier` on `.svelte` files manually
return;
// 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')
) {
// We do not support `eslint-plugin-svelte3`,
// the users should run `prettier` on `.svelte` files manually
return;
}
}
}
}

if (inferParserToBabel) {
initialOptions.parser = 'babel';
}
} else {
// Similar to https://github.com/prettier/stylelint-prettier/pull/22
// In all of the following cases ESLint extracts a part of a file to
// be formatted and there exists a prettier parser for the whole file.
// If you're interested in prettier you'll want a fully formatted file so
// you're about to run prettier over the whole file anyway.
// Therefore running prettier over just the style section is wasteful, so
// skip it.
const parserBlocklist = [
'babel',
'babylon',
'flow',
'typescript',
'vue',
'markdown',
'html',
'mdx',
'angular',
'svelte',
];
if (parserBlocklist.includes(/** @type {string} */ (inferredParser))) {
return;
if (inferParserToBabel) {
initialOptions.parser = 'babel';
}
} else {
// Similar to https://github.com/prettier/stylelint-prettier/pull/22
// In all of the following cases ESLint extracts a part of a file to
// be formatted and there exists a prettier parser for the whole file.
// If you're interested in prettier you'll want a fully formatted file so
// you're about to run prettier over the whole file anyway.
// Therefore running prettier over just the style section is wasteful, so
// skip it.
const parserBlocklist = [
'babel',
'babylon',
'flow',
'typescript',
'vue',
'markdown',
'html',
'mdx',
'angular',
'svelte',
];
if (parserBlocklist.includes(/** @type {string} */ (inferredParser))) {
return;
}
}
}

Expand Down

0 comments on commit a333607

Please sign in to comment.