@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:
- 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)
- Merges configuration modules into JSON files in
dist/
- Creates
base.json (all common configs)
- Creates variant files:
browser.json, react.json, esm-react.json, etc.
- Consumers import via:
@gooddata/eslint-config/react � dist/react.json
-
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
- base (
.) - Core rules for all packages
- browser (
/browser) - For packages using browser APIs (document, window)
- browser-esm (
/browser-esm) - Browser + ESM import rules
- esm (
/esm) - ESM-specific import rules
- esm-vitest (
/esm-vitest) - ESM + Vitest rules
- react (
/react) - Browser + React + React Hooks rules
- react-cypress (
/react-cypress) - Browser + React + React Hooks + Cypress rules
- esm-react (
/esm-react) - Browser + React + React Hooks + ESM rules (most React libraries)
- esm-react-cypress (
/esm-react-cypress) - Browser + React + React Hooks + ESM + Cypress rules
- esm-react-vitest (
/esm-react-vitest) - Browser + React + React Hooks + ESM + Vitest rules
Usage
TypeScript Projects (Recommended)
For TypeScript projects, use the tsOverride helper which automatically configures the TypeScript parser, import resolver, and other required settings:
const { tsOverride } = require("@gooddata/eslint-config/tsOverride");
module.exports = {
extends: ["@gooddata/eslint-config/react"],
overrides: [
tsOverride(__dirname, {
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
}),
],
};
What tsOverride does:
- Sets up the TypeScript parser (
@typescript-eslint/parser)
- Configures
tsconfigRootDir to point to your project directory
- Configures the import resolver to handle TypeScript imports correctly
- Applies to
**/*.ts and **/*.tsx files
- Allows you to pass custom rule overrides as the second parameter
Non-TypeScript Projects
For non-TypeScript projects, simply extend the configuration:
module.exports = {
extends: ["@gooddata/eslint-config/react"],
};
Important Notes:
-
TypeScript Projects: Using tsOverride(__dirname, rules) is mandatory for TypeScript projects. Without it, @typescript-eslint/parser won't know where to find your tsconfig.json, and import resolution will not work correctly.
-
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 eslint (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
- Must run after: adding variants, adding configs, changing package versions
-
npm run validate - Type-checks TypeScript files
-
npm run eslint - 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.