Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add overrides option to config (#3128) #5521

Merged
merged 4 commits into from Sep 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/user-guide/configure.md
Expand Up @@ -418,3 +418,35 @@ If the globs are absolute paths, they are used as is. If they are relative, they
The `ignoreFiles` property is stripped from extended configs: only the root-level config can ignore files.

_Note that this is not an efficient method for ignoring lots of files._ If you want to ignore a lot of files efficiently, use [`.stylelintignore`](ignore-code.md) or adjust your files globs.

## `overrides`

You can provide configurations under the `overrides` key that will only apply to files that match specific glob patterns, using the same format you would pass on the command line (e.g., `app/**/*.test.css`).

It is possible to override settings based on file glob patterns in your configuration by using the `overrides` key. An example of using the `overrides` key is as follows:

In your `.stylelintrc.json`:

```json
{
"rules": {
"string-quotes": "double"
},

"overrides": [
{
"files": ["components/**/*.css", "pages/**/*.css"],
"rules": {
"string-quotes": "single"
}
}
]
}
```

Here is how overrides work in a configuration file:

- The patterns are applied against the file path relative to the directory of the config file. For example, if your config file has the path `/Users/person/workspace/any-project/.stylelintrc.js` and the file you want to lint has the path `/Users/person/workspace/any-project/components/card.css`, then the pattern provided in `.stylelintrc.js` will be executed against the relative path `components/card.css`.
- 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.
303 changes: 303 additions & 0 deletions lib/__tests__/applyOverrides.test.js
@@ -0,0 +1,303 @@
'use strict';

const path = require('path');
const { applyOverrides } = require('../augmentConfig');

test('no overrides', () => {
const config = {
rules: {
'block-no-empty': true,
},
};

const applied = applyOverrides(config, __dirname, path.join(__dirname, 'style.css'));

expect(applied).toEqual(config);
});

describe('single matching override', () => {
test('simple override', () => {
const config = {
rules: {
'block-no-empty': true,
},
overrides: [
{
files: ['*.module.css'],
rules: {
'color-no-hex': true,
},
},
],
};

const expectedConfig = {
rules: {
'block-no-empty': true,
'color-no-hex': true,
},
};

const applied = applyOverrides(config, __dirname, path.join(__dirname, 'style.module.css'));

expect(applied).toEqual(expectedConfig);
});

test('simple override, files is a string', () => {
const config = {
rules: {
'block-no-empty': true,
},
overrides: [
{
files: '*.module.css',
rules: {
'color-no-hex': true,
},
},
],
};

const expectedConfig = {
rules: {
'block-no-empty': true,
'color-no-hex': true,
},
};

const applied = applyOverrides(config, __dirname, path.join(__dirname, 'style.module.css'));

expect(applied).toEqual(expectedConfig);
});

test('with plugins', () => {
const config = {
plugins: ['stylelint-plugin'],
rules: {
'block-no-empty': true,
},
overrides: [
{
files: ['*.module.css'],
plugins: ['stylelint-plugin2'],
rules: {
'color-no-hex': true,
},
},
],
};

const expectedConfig = {
plugins: ['stylelint-plugin', 'stylelint-plugin2'],
rules: {
'block-no-empty': true,
'color-no-hex': true,
},
};

const applied = applyOverrides(config, __dirname, path.join(__dirname, 'style.module.css'));

expect(applied).toEqual(expectedConfig);
});
});

describe('two matching overrides', () => {
test('simple override', () => {
const config = {
rules: {
'block-no-empty': true,
'unit-disallowed-list': ['px'],
},
overrides: [
{
files: ['*.module.css'],
rules: {
'color-no-hex': true,
},
},
{
files: ['*.css'],
rules: {
'block-no-empty': null,
},
},
],
};

const expectedConfig = {
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);
});

test('with plugins', () => {
const config = {
plugins: ['stylelint-plugin'],
rules: {
'block-no-empty': true,
'unit-disallowed-list': ['px'],
},
overrides: [
{
files: ['*.module.css'],
plugins: ['stylelint-plugin2'],
rules: {
'color-no-hex': true,
},
},
{
files: ['*.css'],
plugins: ['stylelint-plugin3'],
rules: {
'block-no-empty': null,
},
},
],
};

const expectedConfig = {
plugins: ['stylelint-plugin', 'stylelint-plugin2', 'stylelint-plugin3'],
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', () => {
test('simple override', () => {
const config = {
rules: {
'block-no-empty': true,
},
overrides: [
{
files: ['*.no-module.css'],
rules: {
'color-no-hex': true,
},
},
],
};

const expectedConfig = {
rules: {
'block-no-empty': true,
},
};

const applied = applyOverrides(config, __dirname, path.join(__dirname, 'style.module.css'));

expect(applied).toEqual(expectedConfig);
});

test('with plugins', () => {
const config = {
plugins: ['stylelint-plugin'],
rules: {
'block-no-empty': true,
},
overrides: [
{
files: ['*.no-module.css'],
plugins: ['stylelint-plugin2'],
rules: {
'color-no-hex': true,
},
},
],
};

const expectedConfig = {
plugins: ['stylelint-plugin'],
rules: {
'block-no-empty': true,
},
};

const applied = applyOverrides(config, __dirname, path.join(__dirname, 'style.module.css'));

expect(applied).toEqual(expectedConfig);
});
});

test('overrides is not an array', () => {
const config = {
rules: {
'block-no-empty': true,
},
overrides: {
files: ['*.module.css'],
rules: {
'color-no-hex': true,
},
},
};

expect(() => {
applyOverrides(config, __dirname, path.join(__dirname, 'style.module.css'));
}).toThrowErrorMatchingInlineSnapshot(
`"The \`overrides\` configuration property should be an array, e.g. { \\"overrides\\": [{ \\"files\\": \\"*.css\\", \\"rules\\": {} }] }."`,
);
});

test('`files` is missing', () => {
const config = {
rules: {
'block-no-empty': true,
},
overrides: [
{
rules: {
'color-no-hex': true,
},
},
],
};

expect(() => {
applyOverrides(config, __dirname, path.join(__dirname, 'style.module.css'));
}).toThrowErrorMatchingInlineSnapshot(
`"Every object in the \`overrides\` configuration property should have a \`files\` property with globs, e.g. { \\"overrides\\": [{ \\"files\\": \\"*.css\\", \\"rules\\": {} }] }."`,
);
});

test('if glob is absolute path', () => {
const config = {
rules: {
'block-no-empty': true,
},
overrides: [
{
files: [path.join(__dirname, 'style.css')],
rules: {
'color-no-hex': true,
},
},
],
};

const expectedConfig = {
rules: {
'block-no-empty': true,
'color-no-hex': true,
},
};

const applied = applyOverrides(config, __dirname, path.join(__dirname, 'style.css'));

expect(applied).toEqual(expectedConfig);
});
@@ -0,0 +1,15 @@
{
"extends": "./plugin-and-one-rule.json",
"plugins": ["../plugin-warn-about-bar"],
"rules": {
"plugin/warn-about-bar": "always"
},
"overrides": [
{
"files": ["*.css"],
"rules": {
"block-no-empty": true
}
}
]
}
14 changes: 14 additions & 0 deletions lib/__tests__/fixtures/config-overrides/extending-simple-rule.json
@@ -0,0 +1,14 @@
{
"extends": "./simple-rule",
"rules": {
"block-no-empty": true
},
"overrides": [
{
"files": ["*.css"],
"rules": {
"color-named": "never"
}
}
]
}
14 changes: 14 additions & 0 deletions lib/__tests__/fixtures/config-overrides/plugin-and-one-rule.json
@@ -0,0 +1,14 @@
{
"plugins": ["../plugin-warn-about-foo"],
"rules": {
"plugin/warn-about-foo": "always"
},
"overrides": [
{
"files": ["*.css"],
"rules": {
"color-named": "never"
}
}
]
}
5 changes: 5 additions & 0 deletions lib/__tests__/fixtures/config-overrides/simple-rule.json
@@ -0,0 +1,5 @@
{
"rules": {
"color-named": "always-where-possible"
}
}
5 changes: 5 additions & 0 deletions lib/__tests__/fixtures/config-overrides/style.css
@@ -0,0 +1,5 @@
.foo {
color: pink;
}

.bar {}