@gooddata/eslint-config
Unified, modular ESLint configuration for the GoodData.UI monorepo.
Purpose
This package consolidates ESLint configurations that were previously scattered across:
- SDK root directory configurations
- Separate library in gdc-ui
The new approach provides:
- ESLint v8 and v9 support - Dual configuration format for both legacy and flat config
- Modular structure - Rules organized by plugin/concern in separate files
- Easy to edit - Find and modify rules in logical locations
- Unified configs - Single source of truth for linting standards
- Type-safe - TypeScript-based configuration with validation
- Variant support - Different preset combinations for various project types
How It Works
Architecture
-
Configuration Modules (src/configurations/)
- Each file represents a plugin or concern (typescript, react, header, etc.)
- Defines: required packages, plugin name, rules, extends, overrides
- Type-safe with
IConfiguration<RulePrefix> interface
-
Index File (src/index.ts)
common array: Base rules applied to all configs
variants object: Different combinations for specific use cases
-
Build Process (npm run build)
- Generates both ESLint v8 (JSON) and v9 (JS flat config) formats
- Creates
base.json + base.js (all common configs)
- Creates variant files:
browser.json/.js, react.json/.js, etc.
- Package exports use conditional exports:
require → JSON (v8), import → JS (v9)
-
Package Sync (npm run update-package)
- Auto-updates
package.json dependencies and peer dependencies
- Reads package requirements from each configuration module
- Syncs versions to ensure consistency
Available Variants
For a complete list of available variants and their required packages, see:
OxLint Migration Variants
This package also provides oxlint-* variants designed to be used alongside @gooddata/oxlint-config. These variants have certain ESLint plugins disabled because those rules are handled by oxlint instead, providing faster linting performance.
How it works:
- eslint-config
oxlint-* variants contain ESLint rules that oxlint does not support
- oxlint-config contains the equivalent rules handled by oxlint
- Together, they provide the same coverage as the standard ESLint variants, but with oxlint handling the bulk of the work for better performance
Example usage:
import config from "@gooddata/eslint-config/oxlint-esm-react-vitest";
export default config;
See @gooddata/oxlint-config for the corresponding oxlint configuration and detailed setup instructions.
ESLint v8 vs v9 Support
This package supports both ESLint v8 (legacy config) and ESLint v9 (flat config):
| v8 | Legacy JSON | .eslintrc.js | require() |
| v9 | Flat Config | eslint.config.js | import |
The package uses conditional exports to automatically serve the correct format:
- CommonJS/require → Returns JSON config for ESLint v8
- ESM/import → Returns JS flat config for ESLint v9
Some packages differ between v8 and v9 (e.g., eslint-plugin-header vs eslint-plugin-headers). See PACKAGES_V8.md and PACKAGES_V9.md for the complete list of required packages for each version.
Usage
ESLint v9 (Flat Config) - Recommended
For ESLint v9 with flat config, create an eslint.config.js or eslint.config.ts file:
import config from "@gooddata/eslint-config/esm-react-vitest";
export default config;
To add custom rules or overrides:
import config from "@gooddata/eslint-config/esm-react-vitest";
export default [
...config,
{
rules: {
"no-console": "warn",
},
},
];
To add TypeScript-specific rule overrides, specify a files pattern:
import config from "@gooddata/eslint-config/esm-react-vitest";
export default [
...config,
{
files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
rules: {
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-explicit-any": "warn",
},
},
];
ESLint v8 (Legacy Config)
For ESLint v8, use the legacy JSON-based configuration:
module.exports = {
extends: ["@gooddata/eslint-config/react"],
overrides: [
{
files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
rules: {
"@typescript-eslint/no-namespace": "off",
},
},
],
};
Non-TypeScript Projects (v8)
For non-TypeScript projects using ESLint v8, simply extend the configuration:
module.exports = {
extends: ["@gooddata/eslint-config/react"],
};
Important Notes:
-
Peer Dependencies: Only packages from the common configuration are listed in peerDependencies. Variant-specific packages (e.g., eslint-plugin-react for the react variant) are not included as peer dependencies since they're not required by all consumers.
-
Verify Dependencies: After adopting a configuration, run npm run lint (or your lint command) to ensure all necessary dependencies are present in your project. If you get plugin errors, install the missing packages.
Development Guide
Adding a New Configuration Module
- Create a new file in
src/configurations/ (e.g., my-plugin.ts):
import type { IConfiguration } from "../types.js";
const configuration: IConfiguration<"my-plugin"> = {
packages: [
{
name: "eslint-plugin-my-plugin",
version: "1.0.0",
},
],
plugin: "my-plugin",
extends: ["plugin:my-plugin/recommended"],
rules: {
"my-plugin/some-rule": "error",
},
};
export default configuration;
- Export it from
src/configurations/index.ts:
export { default as myPlugin } from "./my-plugin.js";
- Add it to
common or a variant in src/index.ts:
export const common = [
myPlugin,
];
Adding a New Variant
- Add the variant to
src/index.ts:
export const variants = {
"my-variant": [browserEnv, myPlugin],
};
Upgrading a Package Version
- Update the version in the configuration file (e.g.,
src/configurations/typescript.ts):
const configuration: IConfiguration = {
packages: [
{
name: "@typescript-eslint/parser",
version: "8.50.0",
},
],
};
Modifying Rules
- Find the relevant configuration file in
src/configurations/
- Update the rules object:
rules: {
"my-plugin/some-rule": "off",
"my-plugin/another-rule": ["error", { option: true }],
}
- Run
npm run build to regenerate JSON configs
Scripts
-
npm run build - Generates JSON configuration files in dist/
- Merges all configuration modules
- Creates base.json and all variant files
- Required before publishing or testing changes
-
npm run update-package - Syncs dependencies and exports
- Updates
devDependencies and peerDependencies in package.json
- Updates
exports field based on available variants
- Generates
PACKAGES_V8.md and PACKAGES_V9.md documentation files
- Must run after: adding variants, adding configs, changing package versions
-
npm run validate - Type-checks TypeScript files
-
npm run lint - Lints the configuration source code
Configuration Structure
Each configuration module follows this structure:
interface IConfiguration<RulePrefix extends string = ""> {
packages?: Array<{
name: string;
version: string;
}>;
parser?: string;
plugin?: string;
extends?: string[];
parserOptions?: Record<string, number | string>;
rules?: Rules<RulePrefix>;
override?: {
files: string[];
parser?: string;
plugin?: string;
extends?: string[];
parserOptions?: Record<string, number | string>;
rules?: Rules<RulePrefix>;
settings?: Record<string, object>;
env?: Record<string, boolean>;
ignorePatterns?: string[];
};
settings?: Record<string, object>;
env?: Record<string, boolean>;
ignorePatterns?: string[];
}
Notes:
- The base configuration automatically ignores
**/dist/**/*.* and **/esm/**/*.* files.
- ESM vs ImportESM: The
esm configuration sets parserOptions.sourceType: "module" to tell the parser to treat files as ES modules. The importEsm configuration adds the eslint-plugin-import-esm plugin which enforces ESM-specific import rules (e.g., requiring .js extensions in imports). Most ESM projects need both.
Formatting
This package is decoupled from formatters. The formatter configuration (included in all variants by default) disables ESLint rules that conflict with code formatters, allowing you to use any formatter of your choice (Prettier, oxfmt, Biome, etc.) as a separate tool.
How it works:
- ESLint focuses solely on code quality and best practices
- Formatting rules (indentation, spacing, quotes, etc.) are disabled
- Run your formatter separately (e.g.,
prettier --write or oxfmt)
Recommended setup:
- Install your preferred formatter (e.g.,
prettier or oxfmt)
- Configure it separately (e.g.,
.prettierrc or oxfmt.json)
- Run formatting as a separate step in your workflow or editor
This approach provides:
- Flexibility - Choose any formatter without changing ESLint config
- Performance - Formatters run faster when not piped through ESLint
- Clarity - Clear separation between linting (code quality) and formatting (code style)