@@ -16,108 +16,89 @@ import fs from "node:fs/promises";
1616import path from "node:path" ;
1717import { fileURLToPath } from "node:url" ;
1818
19- import { loadESLint } from "eslint" ;
19+ import { ESLint } from "eslint" ;
20+ import type { Linter } from "eslint" ;
2021import sortJson from "sort-json" ;
2122
23+ // Import flat configs directly from flat.mjs
24+ import { recommended , strict , minimalDeprecated } from "../flat.mjs" ;
25+
2226const __filename = fileURLToPath ( import . meta. url ) ;
2327const __dirname = path . dirname ( __filename ) ;
2428
25- // Determine which config files to use based on ESLINT_USE_FLAT_CONFIG
26- // NOTE: loadESLint() returns the appropriate ESLint class based on ESLINT_USE_FLAT_CONFIG,
27- // but we still need to manually determine which config file format to use.
28- // Legacy ESLint cannot read flat config files (.mjs), and FlatESLint cannot read legacy config files (.js/.cjs).
29- const useFlatConfig = process . env . ESLINT_USE_FLAT_CONFIG === "true" ;
30-
31- // During the hybrid ESLint 8/9 migration, we need to maintain both legacy (.js/.cjs) and flat (.mjs) configs.
32- // The .eslint-print-configs directory contains standalone flat configs that can be loaded by ESLint 9
33- // without requiring the legacy config infrastructure.
34- const configDir = useFlatConfig ? "../.eslint-print-configs" : ".." ;
35-
36- // File extensions differ between legacy (.js) and flat (.mjs) config formats.
37- // Using .mjs for flat configs ensures they're treated as ES modules.
38- const configExt = useFlatConfig ? ".mjs" : ".js" ;
29+ type FlatConfigArray = Linter . Config [ ] ;
3930
4031interface ConfigToPrint {
4132 name : string ;
42- configPath : string ;
33+ config : FlatConfigArray ;
4334 sourceFilePath : string ;
4435}
4536
46- // Legacy configs use "index.js" for default and "minimal-deprecated.js" for minimal.
47- // Flat configs use "recommended.mjs" for default and "minimal.mjs" for minimal.
48- const defaultConfigFile = useFlatConfig ? "recommended.mjs" : "index.js" ;
49- const minimalConfigFile = `minimal${ useFlatConfig ? "" : "-deprecated" } ${ configExt } ` ;
50- const strictBiomeConfigFile = `strict${ useFlatConfig ? "" : "-biome" } ${ configExt } ` ;
51-
5237const configsToPrint : ConfigToPrint [ ] = [
5338 {
5439 name : "default" ,
55- configPath : path . join ( __dirname , configDir , defaultConfigFile ) ,
40+ config : recommended ,
5641 sourceFilePath : path . join ( __dirname , ".." , "src" , "file.ts" ) ,
5742 } ,
5843 {
5944 name : "minimal" ,
60- configPath : path . join ( __dirname , configDir , minimalConfigFile ) ,
45+ config : minimalDeprecated ,
6146 sourceFilePath : path . join ( __dirname , ".." , "src" , "file.ts" ) ,
6247 } ,
6348 {
6449 name : "react" ,
65- configPath : path . join ( __dirname , configDir , defaultConfigFile ) ,
50+ config : recommended ,
6651 sourceFilePath : path . join ( __dirname , ".." , "src" , "file.tsx" ) ,
6752 } ,
6853 {
6954 name : "recommended" ,
70- configPath : path . join ( __dirname , configDir , ` recommended${ configExt } ` ) ,
55+ config : recommended ,
7156 sourceFilePath : path . join ( __dirname , ".." , "src" , "file.ts" ) ,
7257 } ,
7358 {
7459 name : "strict" ,
75- configPath : path . join ( __dirname , configDir , ` strict${ configExt } ` ) ,
60+ config : strict ,
7661 sourceFilePath : path . join ( __dirname , ".." , "src" , "file.ts" ) ,
7762 } ,
7863 {
7964 name : "strict-biome" ,
80- configPath : path . join ( __dirname , configDir , strictBiomeConfigFile ) ,
65+ // strict-biome uses the same flat config as strict; biome integration is handled separately
66+ config : strict ,
8167 sourceFilePath : path . join ( __dirname , ".." , "src" , "file.ts" ) ,
8268 } ,
8369 {
8470 name : "test" ,
85- configPath : path . join ( __dirname , configDir , ` recommended${ configExt } ` ) ,
71+ config : recommended ,
8672 sourceFilePath : path . join ( __dirname , ".." , "src" , "test" , "file.ts" ) ,
8773 } ,
8874] ;
8975
9076/**
91- * Generates the applied ESLint config for a specific file and config path .
77+ * Generates the applied ESLint config for a specific file and config.
9278 */
93- async function generateConfig ( filePath : string , configPath : string ) : Promise < string > {
94- console . log ( `Generating config for ${ filePath } using ${ configPath } ` ) ;
79+ async function generateConfig ( filePath : string , config : FlatConfigArray ) : Promise < string > {
80+ console . log ( `Generating config for ${ filePath } ` ) ;
9581
96- // loadESLint() respects ESLINT_USE_FLAT_CONFIG and returns the appropriate ESLint class .
97- // However, it's the caller's responsibility to provide config files in the correct format.
98- const ESLint = await loadESLint ( ) ;
82+ // ESLint 9's default ESLint class uses flat config format .
83+ // Use overrideConfigFile: true to prevent loading eslint.config.js,
84+ // and pass the config directly via overrideConfig.
9985 const eslint = new ESLint ( {
100- overrideConfigFile : configPath ,
86+ overrideConfigFile : true ,
87+ overrideConfig : config ,
10188 } ) ;
10289
103- const config = ( await eslint . calculateConfigForFile ( filePath ) ) as unknown ;
104- if ( ! config ) {
90+ const resolvedConfig = ( await eslint . calculateConfigForFile ( filePath ) ) as unknown ;
91+ if ( ! resolvedConfig ) {
10592 console . warn ( "Warning: ESLint returned undefined config for " + filePath ) ;
10693 return "{}\n" ;
10794 }
10895
10996 // Serialize and parse to create a clean copy without any circular references or non-serializable values
110- const cleanConfig = JSON . parse ( JSON . stringify ( config ) ) ;
97+ const cleanConfig = JSON . parse ( JSON . stringify ( resolvedConfig ) ) ;
11198
112- // Remove properties that contain environment-specific paths
113- if ( useFlatConfig ) {
114- // For flat configs, remove languageOptions which has environment-specific paths and large globals
115- if ( cleanConfig . languageOptions ) {
116- delete cleanConfig . languageOptions ;
117- }
118- } else {
119- // For legacy configs, remove the parser property
120- delete cleanConfig . parser ;
99+ // Remove languageOptions which contains environment-specific paths and large globals
100+ if ( cleanConfig . languageOptions ) {
101+ delete cleanConfig . languageOptions ;
121102 }
122103
123104 // Convert numeric severities to string equivalents in rules
@@ -162,7 +143,7 @@ async function generateConfig(filePath: string, configPath: string): Promise<str
162143 await fs . mkdir ( outputPath , { recursive : true } ) ;
163144 const expectedFiles = new Set < string > ( ) ;
164145
165- for ( const { name, configPath , sourceFilePath } of configsToPrint ) {
146+ for ( const { name, config , sourceFilePath } of configsToPrint ) {
166147 const outputFilePath = path . join ( outputPath , `${ name } .json` ) ;
167148 expectedFiles . add ( `${ name } .json` ) ;
168149
@@ -173,7 +154,7 @@ async function generateConfig(filePath: string, configPath: string): Promise<str
173154 // File doesn't exist yet, which is OK - we'll create it
174155 }
175156
176- const newContent = await generateConfig ( sourceFilePath , configPath ) ;
157+ const newContent = await generateConfig ( sourceFilePath , config ) ;
177158
178159 // Only write the file if the content has changed
179160 if ( newContent !== originalContent ) {
0 commit comments