Skip to content

Commit

Permalink
Fix extending rules within overrides (#5683)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Nov 8, 2021
1 parent e4a2e13 commit 7829275
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 18 deletions.
@@ -0,0 +1,5 @@
{
"rules": {
"at-rule-no-unknown": true
}
}
5 changes: 5 additions & 0 deletions lib/__tests__/fixtures/config-overrides/indent4.json
@@ -0,0 +1,5 @@
{
"rules": {
"indentation": 4
}
}
5 changes: 5 additions & 0 deletions lib/__tests__/fixtures/config-overrides/indent8.json
@@ -0,0 +1,5 @@
{
"rules": {
"indentation": 8
}
}
5 changes: 5 additions & 0 deletions lib/__tests__/fixtures/config-overrides/scss.json
@@ -0,0 +1,5 @@
{
"rules": {
"at-rule-no-unknown": null
}
}
3 changes: 3 additions & 0 deletions lib/__tests__/fixtures/config-overrides/style.scss
@@ -0,0 +1,3 @@
a {
@extend .foo;
}
117 changes: 117 additions & 0 deletions lib/__tests__/overrides.test.js
Expand Up @@ -116,6 +116,123 @@ describe('single input file. all overrides are matching', () => {
expect(linted.results[0].warnings[0].rule).toBe('block-no-empty');
expect(linted.results[0].warnings[1].rule).toBe('color-named');
});

it('priority to apply overrides: apply overrides extends', async () => {
const linted = await standalone({
files: [path.join(fixturesPath, 'style.css')],
config: {
extends: [path.join(fixturesPath, 'indent4.json')],
overrides: [
{
files: ['*.css'],
extends: [path.join(fixturesPath, 'indent8.json')],
},
],
},
configBasedir: fixturesPath,
});

expect(linted.results).toHaveLength(1);
expect(linted.results[0].warnings).toHaveLength(1);
expect(linted.results[0].warnings[0].rule).toBe('indentation');
expect(linted.results[0].warnings[0].text).toBe(
'Expected indentation of 8 spaces (indentation)',
);
});

it('priority to apply overrides: apply overrides rules', async () => {
const linted = await standalone({
files: [path.join(fixturesPath, 'style.css')],
config: {
extends: [path.join(fixturesPath, 'indent4.json')],
rules: {
indentation: 3,
},
overrides: [
{
files: ['*.css'],
extends: [path.join(fixturesPath, 'indent8.json')],
rules: {
indentation: 'tab',
},
},
],
},
configBasedir: fixturesPath,
});

expect(linted.results).toHaveLength(1);
expect(linted.results[0].warnings).toHaveLength(1);
expect(linted.results[0].warnings[0].rule).toBe('indentation');
expect(linted.results[0].warnings[0].text).toBe('Expected indentation of 1 tab (indentation)');
});

it('priority to apply overrides: apply rules', async () => {
const linted = await standalone({
files: [path.join(fixturesPath, 'style.css')],
config: {
extends: [path.join(fixturesPath, 'indent4.json')],
rules: {
indentation: 'tab',
},
overrides: [
{
files: ['*.css'],
extends: [path.join(fixturesPath, 'indent8.json')],
},
],
},
configBasedir: fixturesPath,
});

expect(linted.results).toHaveLength(1);
expect(linted.results[0].warnings).toHaveLength(1);
expect(linted.results[0].warnings[0].rule).toBe('indentation');
expect(linted.results[0].warnings[0].text).toBe('Expected indentation of 1 tab (indentation)');
});

// https://github.com/stylelint/stylelint/issues/5656
it('priority to apply overrides (scss): scss extends', async () => {
const linted = await standalone({
files: [path.join(fixturesPath, 'style.scss')],
config: {
extends: ['./at-rule-no-unknown.json'],
overrides: [
{
files: '**/*.scss',
extends: ['./scss.json'],
},
],
},
configBasedir: fixturesPath,
});

expect(linted.results).toHaveLength(1);
expect(linted.results[0].warnings).toHaveLength(0);
});

// https://github.com/stylelint/stylelint/issues/5656
it('priority to apply overrides (scss): apply rules', async () => {
const linted = await standalone({
files: [path.join(fixturesPath, 'style.scss')],
config: {
rules: {
'at-rule-no-unknown': true,
},
overrides: [
{
files: '**/*.scss',
extends: ['./scss.json'],
},
],
},
configBasedir: fixturesPath,
});

expect(linted.results).toHaveLength(1);
expect(linted.results[0].warnings).toHaveLength(1);
expect(linted.results[0].warnings[0].rule).toBe('at-rule-no-unknown');
});
});

it('override is not matching', async () => {
Expand Down
65 changes: 48 additions & 17 deletions lib/augmentConfig.js
Expand Up @@ -26,46 +26,56 @@ const path = require('path');
* @param {StylelintInternalApi} stylelint
* @param {StylelintConfig} config
* @param {string} configDir
* @param {boolean} [allowOverrides]
* @param {boolean} allowOverrides
* @param {string} rootConfigDir
* @param {string} [filePath]
* @returns {Promise<StylelintConfig>}
*/
async function augmentConfigBasic(stylelint, config, configDir, allowOverrides, filePath) {
async function augmentConfigBasic(
stylelint,
config,
configDir,
allowOverrides,
rootConfigDir,
filePath,
) {
let augmentedConfig = config;

if (allowOverrides) {
augmentedConfig = addOptions(stylelint, augmentedConfig);
}

augmentedConfig = await extendConfig(stylelint, augmentedConfig, configDir);

if (filePath) {
while (augmentedConfig.overrides) {
augmentedConfig = applyOverrides(augmentedConfig, configDir, filePath);
augmentedConfig = await extendConfig(stylelint, augmentedConfig, configDir);
}
augmentedConfig = applyOverrides(augmentedConfig, rootConfigDir, filePath);
}

augmentedConfig = await extendConfig(
stylelint,
augmentedConfig,
configDir,
rootConfigDir,
filePath,
);

return absolutizePaths(augmentedConfig, configDir);
}

/**
* Extended configs need to be run through augmentConfigBasic
* but do not need the full treatment. Things like pluginFunctions
* will be resolved and added by the parent config.
* @param {StylelintInternalApi} stylelint
* @param {StylelintCosmiconfigResult} [cosmiconfigResult]
* @returns {Promise<StylelintCosmiconfigResult>}
*/
async function augmentConfigExtended(stylelint, cosmiconfigResult) {
async function augmentConfigExtended(cosmiconfigResult) {
if (!cosmiconfigResult) {
return null;
}

const configDir = path.dirname(cosmiconfigResult.filepath || '');
const { config } = cosmiconfigResult;

const augmentedConfig = await augmentConfigBasic(stylelint, config, configDir);
const augmentedConfig = absolutizePaths(config, configDir);

return {
config: augmentedConfig,
Expand All @@ -89,7 +99,14 @@ async function augmentConfigFull(stylelint, filePath, cosmiconfigResult) {

const configDir = stylelint._options.configBasedir || path.dirname(filepath || '');

let augmentedConfig = await augmentConfigBasic(stylelint, config, configDir, true, filePath);
let augmentedConfig = await augmentConfigBasic(
stylelint,
config,
configDir,
true,
configDir,
filePath,
);

augmentedConfig = addPluginFunctions(augmentedConfig);
augmentedConfig = addProcessorFunctions(augmentedConfig);
Expand Down Expand Up @@ -161,9 +178,11 @@ function absolutizeProcessors(processors, configDir) {
* @param {StylelintInternalApi} stylelint
* @param {StylelintConfig} config
* @param {string} configDir
* @param {string} rootConfigDir
* @param {string} [filePath]
* @return {Promise<StylelintConfig>}
*/
async function extendConfig(stylelint, config, configDir) {
async function extendConfig(stylelint, config, configDir, rootConfigDir, filePath) {
if (config.extends === undefined) {
return config;
}
Expand All @@ -177,7 +196,19 @@ async function extendConfig(stylelint, config, configDir) {
const extendResult = await loadExtendedConfig(stylelint, configDir, extendLookup);

if (extendResult) {
resultConfig = mergeConfigs(resultConfig, extendResult.config);
let extendResultConfig = extendResult.config;
const extendConfigDir = path.dirname(extendResult.filepath || '');

extendResultConfig = await augmentConfigBasic(
stylelint,
extendResultConfig,
extendConfigDir,
false,
rootConfigDir,
filePath,
);

resultConfig = mergeConfigs(resultConfig, extendResultConfig);
}
}

Expand Down Expand Up @@ -379,11 +410,11 @@ function addProcessorFunctions(config) {

/**
* @param {StylelintConfig} fullConfig
* @param {string} configDir
* @param {string} rootConfigDir
* @param {string} filePath
* @return {StylelintConfig}
*/
function applyOverrides(fullConfig, configDir, filePath) {
function applyOverrides(fullConfig, rootConfigDir, filePath) {
let { overrides, ...config } = fullConfig;

if (!overrides) {
Expand Down Expand Up @@ -412,7 +443,7 @@ function applyOverrides(fullConfig, configDir, filePath) {
return glob;
}

return globjoin(configDir, glob);
return globjoin(rootConfigDir, glob);
})
// Glob patterns for micromatch should be in POSIX-style
.map((s) => normalizePath(s));
Expand Down
2 changes: 1 addition & 1 deletion lib/createStylelint.js
Expand Up @@ -27,7 +27,7 @@ function createStylelint(options = {}) {
const stylelint = { _options: options };

stylelint._extendExplorer = cosmiconfig('', {
transform: augmentConfig.augmentConfigExtended.bind(null, stylelint),
transform: augmentConfig.augmentConfigExtended,
stopDir: STOP_DIR,
});

Expand Down

0 comments on commit 7829275

Please sign in to comment.