11import fs from 'node:fs' ;
22import path from 'node:path' ;
33
4+ import indentString from '@esm2cjs/indent-string' ;
45import { oneLine , stripIndent } from 'common-tags' ;
56import type { Linter } from 'eslint' ;
6- import indentString from 'indent-string' ;
77import merge from 'lodash.merge' ;
88import getLogger from 'loglevel-colored-level-prefix' ;
99import { format as prettyFormat } from 'pretty-format' ;
1010import requireRelative from 'require-relative' ;
1111
1212import type {
1313 ESLintConfig ,
14- PrettifyInput ,
1514 FormatOptions ,
1615 LogLevel ,
1716 PrettierOptions ,
18- ESLintConfigGlobalValue ,
17+ PrettifyInput ,
1918} from './types.ts' ;
20- import { getESLint , getOptionsForFormatting , requireModule } from './utils.ts' ;
19+ import {
20+ extractFileExtensions ,
21+ getESLint ,
22+ getOptionsForFormatting ,
23+ importModule ,
24+ } from './utils.ts' ;
2125
2226const logger = getLogger ( { prefix : 'prettier-eslint' } ) ;
2327
@@ -32,6 +36,23 @@ export async function format(options: FormatOptions): Promise<string> {
3236 return output ;
3337}
3438
39+ export const DEFAULT_ESLINT_EXTENSIONS = [
40+ '.cjs' ,
41+ '.cts' ,
42+ '.js' ,
43+ '.jsx' ,
44+ '.ts' ,
45+ '.tsx' ,
46+ '.mjs' ,
47+ '.mts' ,
48+ '.vue' ,
49+ '.svelte' ,
50+ ] ;
51+
52+ export const DEFAULT_ESLINT_FILES = DEFAULT_ESLINT_EXTENSIONS . map (
53+ ext => `**/*${ ext } ` ,
54+ ) ;
55+
3556/**
3657 * Analyzes and formats text with prettier and eslint, based on the identical
3758 * options as for the `format` function. It differs from `format` only in that
@@ -44,7 +65,6 @@ export async function format(options: FormatOptions): Promise<string> {
4465 * formatted string and `r.messages` is an array of message specifications
4566 * from eslint.
4667 */
47- // eslint-disable-next-line complexity
4868export async function analyze ( options : FormatOptions ) : Promise < {
4969 output : string ;
5070 messages : Linter . LintMessage [ ] ;
@@ -97,21 +117,15 @@ export async function analyze(options: FormatOptions): Promise<{
97117 } ) ,
98118 ) ;
99119
100- const eslintExtensions = eslintConfig . extensions || [
101- '.cjs' ,
102- '.cts' ,
103- '.js' ,
104- '.jsx' ,
105- '.ts' ,
106- '.tsx' ,
107- '.mjs' ,
108- '.mts' ,
109- '.vue' ,
110- '.svelte' ,
111- ] ;
112-
113120 const fileExtension = path . extname ( filePath || '' ) ;
114121
122+ // istanbul ignore next
123+ const eslintFiles = eslintConfig . files ?. length
124+ ? eslintConfig . files
125+ : DEFAULT_ESLINT_FILES ;
126+
127+ const eslintExtensions = extractFileExtensions ( eslintFiles . flat ( ) ) ;
128+
115129 // If we don't get filePath run eslint on text, otherwise only run eslint
116130 // if it's a configured extension or fall back to a "supported" file type.
117131 const onlyPrettier = filePath
@@ -124,18 +138,21 @@ export async function analyze(options: FormatOptions): Promise<{
124138 return prettify ( text ) ;
125139 }
126140
127- if ( [ '.ts' , '.tsx' ] . includes ( fileExtension ) ) {
128- formattingOptions . eslint . parser ||= require . resolve (
129- '@typescript-eslint/parser' ,
130- ) ;
131- }
141+ formattingOptions . eslint . languageOptions ??= { } ;
132142
133- if ( [ '.vue' ] . includes ( fileExtension ) ) {
134- formattingOptions . eslint . parser ||= require . resolve ( 'vue-eslint-parser' ) ;
135- }
136-
137- if ( [ '.svelte' ] . includes ( fileExtension ) ) {
138- formattingOptions . eslint . parser ||= require . resolve ( 'svelte-eslint-parser' ) ;
143+ if ( ! formattingOptions . eslint . languageOptions . parser ) {
144+ if ( [ '.ts' , '.tsx' ] . includes ( fileExtension ) ) {
145+ formattingOptions . eslint . languageOptions . parser = await importModule (
146+ '@typescript-eslint/parser' ,
147+ ) ;
148+ } else if ( [ '.vue' ] . includes ( fileExtension ) ) {
149+ formattingOptions . eslint . languageOptions . parser =
150+ await importModule ( 'vue-eslint-parser' ) ;
151+ } else if ( [ '.svelte' ] . includes ( fileExtension ) ) {
152+ formattingOptions . eslint . languageOptions . parser = await importModule (
153+ 'svelte-eslint-parser' ,
154+ ) ;
155+ }
139156 }
140157
141158 const eslintFix = createEslintFix ( formattingOptions . eslint , eslintPath ) ;
@@ -186,7 +203,7 @@ function createPrettify(formatOptions: PrettierOptions, prettierPath: string) {
186203 ${ indentString ( text , 2 ) }
187204 ` ,
188205 ) ;
189- const prettier = requireModule < typeof import ( 'prettier' ) > (
206+ const prettier = await importModule < typeof import ( 'prettier' ) > (
190207 prettierPath ,
191208 'prettier' ,
192209 ) ;
@@ -211,48 +228,36 @@ function createPrettify(formatOptions: PrettierOptions, prettierPath: string) {
211228
212229function createEslintFix ( eslintConfig : ESLintConfig , eslintPath : string ) {
213230 return async function eslintFix ( text : string , filePath ?: string ) {
214- if ( Array . isArray ( eslintConfig . globals ) ) {
215- const tempGlobals : Linter . BaseConfig [ 'globals' ] = { } ;
216- for ( const g of eslintConfig . globals as string [ ] ) {
217- const [ key , value ] = g . split ( ':' ) ;
218- tempGlobals [ key ] = value as ESLintConfigGlobalValue ;
219- }
220- eslintConfig . globals = tempGlobals ;
221- }
222-
223231 eslintConfig . overrideConfig = {
232+ languageOptions : eslintConfig . languageOptions ,
224233 rules : eslintConfig . rules ,
225- parser : eslintConfig . parser ,
226- globals : eslintConfig . globals ,
227- parserOptions : eslintConfig . parserOptions ,
228- ignorePatterns : eslintConfig . ignorePatterns || eslintConfig . ignorePattern ,
229- plugins : eslintConfig . plugins ,
230- env : eslintConfig . env ,
231- settings : eslintConfig . settings ,
232- noInlineConfig : eslintConfig . noInlineConfig ,
234+ ignores : eslintConfig . ignorePatterns ?? [ ] ,
235+ plugins : eslintConfig . plugins ?? { } ,
236+ settings : eslintConfig . settings ?? { } ,
233237 ...eslintConfig . overrideConfig ,
234238 } ;
235239
240+ delete eslintConfig . baseConfig ;
241+ delete eslintConfig . language ;
242+ delete eslintConfig . languageOptions ;
243+ delete eslintConfig . linterOptions ;
236244 delete eslintConfig . rules ;
237- delete eslintConfig . parser ;
238- delete eslintConfig . parserOptions ;
239- delete eslintConfig . globals ;
240245 delete eslintConfig . ignorePatterns ;
241- delete eslintConfig . ignorePattern ;
242246 delete eslintConfig . plugins ;
243- delete eslintConfig . env ;
244- delete eslintConfig . noInlineConfig ;
245247 delete eslintConfig . settings ;
246248
247- const eslint = getESLint ( eslintPath , eslintConfig ) ;
249+ // FIXME: Seems to be an ESLint core issue: `Key "plugins": Cannot redefine plugin`
250+ delete eslintConfig . overrideConfig . plugins ;
251+
252+ const eslint = await getESLint ( eslintPath , eslintConfig ) ;
248253 try {
249- logger . trace ( 'calling cliEngine.executeOnText with the text' ) ;
254+ logger . trace ( 'calling eslint.lintText with the text' ) ;
250255 const report = await eslint . lintText ( text , {
251256 filePath,
252257 warnIgnored : true ,
253258 } ) ;
254259 logger . trace (
255- 'executeOnText returned the following report:' ,
260+ 'eslint.lintText returned the following report:' ,
256261 prettyFormat ( report ) ,
257262 ) ;
258263 // default the output to text because if there's nothing
@@ -272,7 +277,7 @@ function createEslintFix(eslintConfig: ESLintConfig, eslintPath: string) {
272277 ) ;
273278 return { output, messages } ;
274279 } catch ( error ) {
275- logger . error ( 'eslint fix failed due to an eslint error' ) ;
280+ logger . error ( 'eslint -- fix failed due to an eslint error' ) ;
276281 throw error ;
277282 }
278283 } ;
@@ -324,19 +329,15 @@ function getTextFromFilePath(filePath: string) {
324329 * @returns An object containing options for the ESLint API.
325330 */
326331function getESLintApiOptions ( eslintConfig : ESLintConfig ) : ESLintConfig {
327- // https://eslint.org/docs/developer-guide /nodejs-api
332+ // https://eslint.org/docs/latest/integrate /nodejs-api
328333 // these options affect what calculateConfigForFile produces
329334 return {
330- ignore : eslintConfig . ignore || true ,
331- ignorePath : eslintConfig . ignorePath ,
332- allowInlineConfig : eslintConfig . allowInlineConfig || true ,
335+ ignore : eslintConfig . ignore ?? true ,
336+ allowInlineConfig : eslintConfig . allowInlineConfig ?? true ,
333337 baseConfig : eslintConfig . baseConfig ,
334338 overrideConfig : eslintConfig . overrideConfig ,
335339 overrideConfigFile : eslintConfig . overrideConfigFile ,
336340 plugins : eslintConfig . plugins ,
337- resolvePluginsRelativeTo : eslintConfig . resolvePluginsRelativeTo ,
338- rulePaths : eslintConfig . rulePaths || [ ] ,
339- useEslintrc : eslintConfig . useEslintrc || true ,
340341 } ;
341342}
342343
@@ -354,7 +355,7 @@ async function getESLintConfig(
354355 "${ filePath || process . cwd ( ) } "
355356 ` ,
356357 ) ;
357- const eslint = getESLint ( eslintPath , getESLintApiOptions ( eslintConfig ) ) ;
358+ const eslint = await getESLint ( eslintPath , getESLintApiOptions ( eslintConfig ) ) ;
358359
359360 try {
360361 logger . debug ( `getting eslint config for file at "${ filePath } "` ) ;
@@ -376,8 +377,11 @@ async function getESLintConfig(
376377 }
377378}
378379
379- function getPrettierConfig ( filePath : string | undefined , prettierPath : string ) {
380- const prettier = requireModule < typeof import ( 'prettier' ) > (
380+ async function getPrettierConfig (
381+ filePath : string | undefined ,
382+ prettierPath : string ,
383+ ) {
384+ const prettier = await importModule < typeof import ( 'prettier' ) > (
381385 prettierPath ,
382386 'prettier' ,
383387 ) ;
0 commit comments