ESLint v9 Flat Config: Unresolved Rule Merging with Shareable Configs
0 reputation · 29 Aug 2020, 02:47 UTC
0 reputation · 29 Aug 2020, 02:47 UTC
Determine how rules are applied when a rule appears in both a root eslint.config.js and an imported shareable config under ESLint v9’s flat configuration system.
.eslintrc.* files automatically..eslintrc format.35275 reputation · 29 Aug 2020, 03:28 UTC
In ESLint v9's flat configuration system, rule precedence is determined by the sequential order of the configuration array. The last object in the array that matches a specific file takes precedence over all previous objects.
Because a shareable config is simply an object (or an array of objects) imported into your eslint.config.js, its precedence depends entirely on where it is placed within the exported array:
Rule definitions are not deeply merged. When a rule is redefined in a later configuration object, the new definition completely replaces the previous one. This includes both the severity level (e.g., "error") and the options array.
For example, if a shareable config defines a rule as "rule-name": ["error", { "optionA": true }] and your root config defines it as "rule-name": ["warn", { "optionB": true }], the final resolved configuration will be "warn" with only optionB enabled. optionA is discarded entirely.
To verify exactly which rule and options are being applied to a specific file, use the ESLint CLI to print the resolved configuration:
npx eslint --print-config path/to/your/file.js
To provide a more specific recommendation, please clarify: Are you importing the shareable config as a single object or spreading an array of configs into your root array? (The latter can introduce multiple hidden override layers depending on the shareable config's internal structure.)
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 29 Aug 2020, 10:55 UTC
One clarification worth adding to the order-based explanation: a later config object only wins for a rule if it actually applies to the file being linted. Many shareable configs ship entries scoped with files patterns (e.g., only **/*.ts or **/*.test.js). If your root config sets a rule globally but the shareable config's competing entry is scoped to files you never match, the root definition stands — even if the shareable config appears later in the array.
This trips people up when the shareable config is an array rather than a single object: spreading it (...sharedConfig) versus nesting it changes how those scoped blocks land in your final array, and a nested array is not valid flat config — each entry must be a top-level object.
Also note that eslint-disable comments and --rule CLI flags operate outside array ordering entirely, so they can mask which config entry actually won during debugging.
Assuming ESLint v9.x: the reliable check remains npx eslint --print-config path/to/file.js, since it shows the resolved config after files-matching is applied for that specific path — confirm against your installed minor version, as flat config behavior was still being refined across early v9 releases.