Skip to content

Commit 8c1cba1

Browse files
authored
build(eslint-config-fluid): remove ESLint 8/9 compatibility layer (#26272)
1 parent 429e144 commit 8c1cba1

File tree

8 files changed

+32
-102
lines changed

8 files changed

+32
-102
lines changed

common/build/eslint-config-fluid/.eslint-print-configs/README.md

Lines changed: 0 additions & 28 deletions
This file was deleted.

common/build/eslint-config-fluid/.eslint-print-configs/minimal.mjs

Lines changed: 0 additions & 2 deletions
This file was deleted.

common/build/eslint-config-fluid/.eslint-print-configs/recommended.mjs

Lines changed: 0 additions & 2 deletions
This file was deleted.

common/build/eslint-config-fluid/.eslint-print-configs/strict.mjs

Lines changed: 0 additions & 2 deletions
This file was deleted.

common/build/eslint-config-fluid/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"format": "npm run prettier:fix",
2020
"prettier": "prettier --check . --cache --ignore-path ../../../.prettierignore",
2121
"prettier:fix": "prettier --write . --cache --ignore-path ../../../.prettierignore",
22-
"print-configs": "cross-env ESLINT_USE_FLAT_CONFIG=true tsx scripts/print-configs.ts printed-configs",
22+
"print-configs": "tsx scripts/print-configs.ts printed-configs",
2323
"test": "echo TODO: add tests"
2424
},
2525
"dependencies": {
@@ -47,7 +47,6 @@
4747
"@fluidframework/build-common": "^2.0.3",
4848
"@types/sort-json": "^2.0.3",
4949
"concurrently": "^9.2.1",
50-
"cross-env": "^10.1.0",
5150
"eslint": "~9.39.1",
5251
"jiti": "^2.6.1",
5352
"mocha-multi-reporters": "^1.5.1",

common/build/eslint-config-fluid/pnpm-lock.yaml

Lines changed: 0 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/build/eslint-config-fluid/scripts/print-configs.ts

Lines changed: 31 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,108 +16,89 @@ import fs from "node:fs/promises";
1616
import path from "node:path";
1717
import { fileURLToPath } from "node:url";
1818

19-
import { loadESLint } from "eslint";
19+
import { ESLint } from "eslint";
20+
import type { Linter } from "eslint";
2021
import sortJson from "sort-json";
2122

23+
// Import flat configs directly from flat.mjs
24+
import { recommended, strict, minimalDeprecated } from "../flat.mjs";
25+
2226
const __filename = fileURLToPath(import.meta.url);
2327
const __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

4031
interface 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-
5237
const 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) {

pnpm-lock.yaml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)