From a6cd1f3277b082a258d232f980df9f6b791e479f Mon Sep 17 00:00:00 2001 From: Caleb Jasik Date: Thu, 27 Oct 2022 08:06:17 -0500 Subject: [PATCH] Change `extends` in `overrides` to be consistent with `plugins` behaviour (#6380) Co-authored-by: Richard Hallows Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> --- .changeset/grumpy-scissors-watch.md | 5 ++++ docs/migration-guide/to-15.md | 23 ++++++++++++++++ docs/user-guide/configure.md | 2 ++ lib/__tests__/applyOverrides.test.js | 39 ++++++++++++++++++++++++++++ lib/augmentConfig.js | 19 +++++++++++++- 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 .changeset/grumpy-scissors-watch.md create mode 100644 docs/migration-guide/to-15.md diff --git a/.changeset/grumpy-scissors-watch.md b/.changeset/grumpy-scissors-watch.md new file mode 100644 index 0000000000..7979660b68 --- /dev/null +++ b/.changeset/grumpy-scissors-watch.md @@ -0,0 +1,5 @@ +--- +"stylelint": major +--- + +Change: `extends` in `overrides` to merge to be consistent with `plugins` behaviour diff --git a/docs/migration-guide/to-15.md b/docs/migration-guide/to-15.md new file mode 100644 index 0000000000..dfdfdd7948 --- /dev/null +++ b/docs/migration-guide/to-15.md @@ -0,0 +1,23 @@ +# Migrating to 15.0.0 + +## Change of `overrides.extends` behavior + +We changed the `overrides.extends` behavior to merge rather than replace, to make it consistent with the `overrides.plugins`. + +If you would like to keep the previous behavior, you should change your config to: + +```diff json +{ +- "extends": ["config-a"], + "overrides": [ + { + "rules": ["*.module.css"], + "extends": ["config-b"] + }, ++ { ++ "rules": ["*.css"], ++ "extends": ["config-a"] ++ } + ] +} +``` diff --git a/docs/user-guide/configure.md b/docs/user-guide/configure.md index 4fb0e6c8a7..5e72f91f06 100644 --- a/docs/user-guide/configure.md +++ b/docs/user-guide/configure.md @@ -297,6 +297,8 @@ Here is how overrides work in a configuration file: - Glob pattern overrides have higher precedence than the regular configuration in the same config file. Multiple overrides within the same config are applied in order. That is, the last override block in a config file always has the highest precedence. - A glob specific configuration works almost the same as any other Stylelint config. Override blocks can contain any configuration options that are valid in a regular config. - Multiple glob patterns can be provided within a single override block. A file must match at least one of the supplied patterns for the configuration to apply. +- `customSyntax` will be replaced by overrides. +- `plugins`, `extends`, `rules`, etc. will be appended. ## `defaultSeverity` diff --git a/lib/__tests__/applyOverrides.test.js b/lib/__tests__/applyOverrides.test.js index 37c86d9a18..5a0f35cf98 100644 --- a/lib/__tests__/applyOverrides.test.js +++ b/lib/__tests__/applyOverrides.test.js @@ -198,6 +198,45 @@ describe('two matching overrides', () => { expect(applied).toEqual(expectedConfig); }); + + test('with extends', () => { + const config = { + extends: ['stylelint-config1'], + rules: { + 'block-no-empty': true, + 'unit-disallowed-list': ['px'], + }, + overrides: [ + { + files: ['*.module.css'], + extends: ['stylelint-config2'], + rules: { + 'color-no-hex': true, + }, + }, + { + files: ['*.css'], + extends: ['stylelint-config3'], + rules: { + 'block-no-empty': null, + }, + }, + ], + }; + + const expectedConfig = { + extends: ['stylelint-config1', 'stylelint-config2', 'stylelint-config3'], + rules: { + 'block-no-empty': null, + 'unit-disallowed-list': ['px'], + 'color-no-hex': true, + }, + }; + + const applied = applyOverrides(config, __dirname, path.join(__dirname, 'style.module.css')); + + expect(applied).toEqual(expectedConfig); + }); }); describe('no matching overrides', () => { diff --git a/lib/augmentConfig.js b/lib/augmentConfig.js index 636ffde31f..7ff45b1ee7 100644 --- a/lib/augmentConfig.js +++ b/lib/augmentConfig.js @@ -8,6 +8,7 @@ const normalizeAllRuleSettings = require('./normalizeAllRuleSettings'); const normalizePath = require('normalize-path'); const path = require('path'); +/** @typedef {import('stylelint').ConfigExtends} StyleLintConfigExtends */ /** @typedef {import('stylelint').ConfigPlugins} StylelintConfigPlugins */ /** @typedef {import('stylelint').ConfigProcessor} StylelintConfigProcessor */ /** @typedef {import('stylelint').ConfigProcessors} StylelintConfigProcessors */ @@ -234,7 +235,7 @@ function loadExtendedConfig(stylelint, configDir, extendLookup) { /** * When merging configs (via extends) - * - plugin and processor arrays are joined + * - plugin, extends, overrides, processor arrays are joined * - rules are merged via Object.assign, so there is no attempt made to * merge any given rule's settings. If b contains the same rule as a, * b's rule settings will override a's rule settings entirely. @@ -289,6 +290,21 @@ function mergeConfigs(a, b) { } } + /** @type {{extends: StyleLintConfigExtends}} */ + const extendsMerger = {}; + + if (a.extends || b.extends) { + extendsMerger.extends = []; + + if (a.extends) { + extendsMerger.extends = extendsMerger.extends.concat(a.extends); + } + + if (b.extends) { + extendsMerger.extends = [...new Set(extendsMerger.extends.concat(b.extends))]; + } + } + const rulesMerger = {}; if (a.rules || b.rules) { @@ -298,6 +314,7 @@ function mergeConfigs(a, b) { const result = { ...a, ...b, + ...extendsMerger, ...processorMerger, ...pluginMerger, ...overridesMerger,