99 * @typedef {import('eslint').AST.Range } Range
1010 * @typedef {import('eslint').AST.SourceLocation } SourceLocation
1111 * @typedef {import('eslint').ESLint.Plugin } Plugin
12+ * @typedef {import('prettier').FileInfoOptions } FileInfoOptions
13+ * @typedef {import('prettier').Options & { onDiskFilepath: string, parserPath: string, usePrettierrc?: boolean } } Options
1214 */
1315
1416'use strict' ;
@@ -34,9 +36,9 @@ const { INSERT, DELETE, REPLACE } = generateDifferences;
3436
3537// Lazily-loaded Prettier.
3638/**
37- * @type {typeof import('prettier') }
39+ * @type {(source: string, options: Options, fileInfoOptions: FileInfoOptions) => string }
3840 */
39- let prettier ;
41+ let prettierFormat ;
4042
4143// ------------------------------------------------------------------------------
4244// Rule Definition
@@ -123,7 +125,10 @@ const eslintPluginPrettier = {
123125 create ( context ) {
124126 const usePrettierrc =
125127 ! context . options [ 1 ] || context . options [ 1 ] . usePrettierrc !== false ;
126- const eslintFileInfoOptions =
128+ /**
129+ * @type {FileInfoOptions }
130+ */
131+ const fileInfoOptions =
127132 ( context . options [ 1 ] && context . options [ 1 ] . fileInfoOptions ) || { } ;
128133 const sourceCode = context . getSourceCode ( ) ;
129134 const filepath = context . getFilename ( ) ;
@@ -136,136 +141,19 @@ const eslintPluginPrettier = {
136141 const source = sourceCode . text ;
137142
138143 return {
139- // eslint-disable-next-line sonarjs/cognitive-complexity
140144 Program ( ) {
141- if ( ! prettier ) {
145+ if ( ! prettierFormat ) {
142146 // Prettier is expensive to load, so only load it if needed.
143- prettier = require ( 'prettier' ) ;
147+ prettierFormat = require ( 'synckit' ) . createSyncFn (
148+ require . resolve ( './worker' ) ,
149+ ) ;
144150 }
145151
152+ /**
153+ * @type {{} }
154+ */
146155 const eslintPrettierOptions = context . options [ 0 ] || { } ;
147156
148- const prettierRcOptions = usePrettierrc
149- ? prettier . resolveConfig . sync ( onDiskFilepath , {
150- editorconfig : true ,
151- } )
152- : null ;
153-
154- const { ignored, inferredParser } = prettier . getFileInfo . sync (
155- onDiskFilepath ,
156- {
157- resolveConfig : false ,
158- withNodeModules : false ,
159- ignorePath : '.prettierignore' ,
160- plugins : prettierRcOptions ? prettierRcOptions . plugins : null ,
161- ...eslintFileInfoOptions ,
162- } ,
163- ) ;
164-
165- // Skip if file is ignored using a .prettierignore file
166- if ( ignored ) {
167- return ;
168- }
169-
170- const initialOptions = { } ;
171-
172- // ESLint supports processors that let you extract and lint JS
173- // fragments within a non-JS language. In the cases where prettier
174- // supports the same language as a processor, we want to process
175- // the provided source code as javascript (as ESLint provides the
176- // rules with fragments of JS) instead of guessing the parser
177- // based off the filename. Otherwise, for instance, on a .md file we
178- // end up trying to run prettier over a fragment of JS using the
179- // markdown parser, which throws an error.
180- // Processors may set virtual filenames for these extracted blocks.
181- // If they do so then we want to trust the file extension they
182- // provide, and no override is needed.
183- // If the processor does not set any virtual filename (signified by
184- // `filepath` and `onDiskFilepath` being equal) AND we can't
185- // infer the parser from the filename, either because no filename
186- // was provided or because there is no parser found for the
187- // filename, use javascript.
188- // This is added to the options first, so that
189- // prettierRcOptions and eslintPrettierOptions can still override
190- // the parser.
191- //
192- // `parserBlocklist` should contain the list of prettier parser
193- // names for file types where:
194- // * Prettier supports parsing the file type
195- // * There is an ESLint processor that extracts JavaScript snippets
196- // from the file type.
197- if ( filepath === onDiskFilepath ) {
198- // The following list means the plugin process source into js content
199- // but with same filename, so we need to change the parser to `babel`
200- // by default.
201- // Related ESLint plugins are:
202- // 1. `eslint-plugin-graphql` (replacement: `@graphql-eslint/eslint-plugin`)
203- // 2. `eslint-plugin-html`
204- // 3. `eslint-plugin-markdown@1` (replacement: `eslint-plugin-markdown@2+`)
205- // 4. `eslint-plugin-svelte3` (replacement: `eslint-plugin-svelte@2+`)
206- const parserBlocklist = [ null , 'markdown' , 'html' ] ;
207-
208- let inferParserToBabel = parserBlocklist . includes ( inferredParser ) ;
209-
210- switch ( inferredParser ) {
211- // it could be processed by `@graphql-eslint/eslint-plugin` or `eslint-plugin-graphql`
212- case 'graphql' : {
213- if (
214- // for `eslint-plugin-graphql`, see https://github.com/apollographql/eslint-plugin-graphql/blob/master/src/index.js#L416
215- source . startsWith ( 'ESLintPluginGraphQLFile`' )
216- ) {
217- inferParserToBabel = true ;
218- }
219- break ;
220- }
221- // it could be processed by `@ota-meshi/eslint-plugin-svelte`, `eslint-plugin-svelte` or `eslint-plugin-svelte3`
222- case 'svelte' : {
223- // The `source` would be modified by `eslint-plugin-svelte3`
224- if ( ! context . parserPath . includes ( 'svelte-eslint-parser' ) ) {
225- // We do not support `eslint-plugin-svelte3`,
226- // the users should run `prettier` on `.svelte` files manually
227- return ;
228- }
229- }
230- }
231-
232- if ( inferParserToBabel ) {
233- initialOptions . parser = 'babel' ;
234- }
235- } else {
236- // Similar to https://github.com/prettier/stylelint-prettier/pull/22
237- // In all of the following cases ESLint extracts a part of a file to
238- // be formatted and there exists a prettier parser for the whole file.
239- // If you're interested in prettier you'll want a fully formatted file so
240- // you're about to run prettier over the whole file anyway.
241- // Therefore running prettier over just the style section is wasteful, so
242- // skip it.
243- const parserBlocklist = [
244- 'babel' ,
245- 'babylon' ,
246- 'flow' ,
247- 'typescript' ,
248- 'vue' ,
249- 'markdown' ,
250- 'html' ,
251- 'mdx' ,
252- 'angular' ,
253- 'svelte' ,
254- ] ;
255- if (
256- parserBlocklist . includes ( /** @type {string } */ ( inferredParser ) )
257- ) {
258- return ;
259- }
260- }
261-
262- const prettierOptions = {
263- ...initialOptions ,
264- ...prettierRcOptions ,
265- ...eslintPrettierOptions ,
266- filepath,
267- } ;
268-
269157 // prettier.format() may throw a SyntaxError if it cannot parse the
270158 // source code it is given. Usually for JS files this isn't a
271159 // problem as ESLint will report invalid syntax before trying to
@@ -279,7 +167,17 @@ const eslintPluginPrettier = {
279167 */
280168 let prettierSource ;
281169 try {
282- prettierSource = prettier . format ( source , prettierOptions ) ;
170+ prettierSource = prettierFormat (
171+ source ,
172+ {
173+ ...eslintPrettierOptions ,
174+ filepath,
175+ onDiskFilepath,
176+ parserPath : context . parserPath ,
177+ usePrettierrc,
178+ } ,
179+ fileInfoOptions ,
180+ ) ;
283181 } catch ( err ) {
284182 if ( ! ( err instanceof SyntaxError ) ) {
285183 throw err ;
@@ -308,6 +206,10 @@ const eslintPluginPrettier = {
308206 return ;
309207 }
310208
209+ if ( prettierSource == null ) {
210+ return ;
211+ }
212+
311213 if ( source !== prettierSource ) {
312214 const differences = generateDifferences ( source , prettierSource ) ;
313215
0 commit comments