diff --git a/docs/user-guide/configure.md b/docs/user-guide/configure.md index 256457b121..d61d275a4c 100644 --- a/docs/user-guide/configure.md +++ b/docs/user-guide/configure.md @@ -158,6 +158,26 @@ For example: The report is considered to be a lint error. +### `disableFix` + +You can set the `disableFix` secondary option to disable autofix _on a per rule basis_. + +For example: + +```json +{ + "rules": { + "indentation": [ + 2, + { + "except": ["value"], + "disableFix": true + } + ] + } +} +``` + ## Disable Errors These configurations provide extra validation for `stylelint-disable` comments. This can be helpful for enforcing useful and well-documented disables. diff --git a/lib/__tests__/standalone-fix.test.js b/lib/__tests__/standalone-fix.test.js index dd1dde9032..f45ea5c0aa 100644 --- a/lib/__tests__/standalone-fix.test.js +++ b/lib/__tests__/standalone-fix.test.js @@ -226,3 +226,96 @@ describe('writing fixes to files', () => { expect(fileContent.startsWith('\uFEFF')).toBe(true); }); }); + +it('one rule being disabled', async () => { + const code = ` + a { + color: red; + }`; + + const result = await standalone({ + code, + config: { + rules: { + indentation: [ + 2, + { + disableFix: true, + }, + ], + }, + }, + fix: true, + }); + + expect(JSON.parse(result.output)[0].errored).toBe(true); +}); + +it('two rules being disabled', async () => { + const code = ` + a { + COLOR: red; + }`; + + const result = await standalone({ + code, + config: { + rules: { + indentation: [ + 2, + { + disableFix: true, + }, + ], + 'property-case': [ + 'lower', + { + disableFix: true, + }, + ], + }, + }, + fix: true, + }); + + const warnings = JSON.parse(result.output)[0].warnings; + + expect(warnings.some((w) => w.text === 'Expected indentation of 0 spaces (indentation)')).toBe( + true, + ); + expect(warnings.some((w) => w.text === 'Expected "COLOR" to be "color" (property-case)')).toBe( + true, + ); +}); + +it('one rule being disabled and another still autofixing', async () => { + const code = ` + a { + COLOR: red; + }`; + + const result = await standalone({ + code, + config: { + rules: { + indentation: [ + 2, + { + disableFix: true, + }, + ], + 'property-case': ['lower'], + }, + }, + fix: true, + }); + + const warnings = JSON.parse(result.output)[0].warnings; + + expect(warnings.some((w) => w.text === 'Expected indentation of 0 spaces (indentation)')).toBe( + true, + ); + expect(warnings.some((w) => w.text === 'Expected "COLOR" to be "color" (property-case)')).toBe( + false, + ); +}); diff --git a/lib/lintPostcssResult.js b/lib/lintPostcssResult.js index 648076d82d..a1e7225075 100644 --- a/lib/lintPostcssResult.js +++ b/lib/lintPostcssResult.js @@ -88,6 +88,12 @@ function lintPostcssResult(stylelintOptions, postcssResult, config) { // Log the rule's severity in the PostCSS result const defaultSeverity = config.defaultSeverity || 'error'; + // disableFix in secondary option + const disableFix = (secondaryOptions && secondaryOptions.disableFix === true) || false; + + if (disableFix) { + postcssResult.stylelint.ruleDisableFix = true; + } postcssResult.stylelint.ruleSeverities[ruleName] = (secondaryOptions && secondaryOptions.severity) || defaultSeverity; @@ -98,6 +104,7 @@ function lintPostcssResult(stylelintOptions, postcssResult, config) { postcssRoots.map((postcssRoot) => ruleFunction(primaryOption, secondaryOptions, { fix: + !disableFix && stylelintOptions.fix && // Next two conditionals are temporary measures until #2643 is resolved isFileFixCompatible && diff --git a/lib/standalone.js b/lib/standalone.js index cd5b210a85..e6c650cb78 100644 --- a/lib/standalone.js +++ b/lib/standalone.js @@ -151,7 +151,12 @@ module.exports = function (options) { const postcssResult = stylelintResult._postcssResult; const returnValue = prepareReturnValue([stylelintResult], options, formatterFunction); - if (options.fix && postcssResult && !postcssResult.stylelint.ignored) { + if ( + options.fix && + postcssResult && + !postcssResult.stylelint.ignored && + !postcssResult.stylelint.ruleDisableFix + ) { if (!postcssResult.stylelint.disableWritingFix) { // If we're fixing, the output should be the fixed code returnValue.output = postcssResult.root.toString(postcssResult.opts.syntax); @@ -251,6 +256,7 @@ module.exports = function (options) { postcssResult.root && postcssResult.opts && !postcssResult.stylelint.ignored && + !postcssResult.stylelint.ruleDisableFix && options.fix && !postcssResult.stylelint.disableWritingFix ) { diff --git a/lib/utils/validateOptions.js b/lib/utils/validateOptions.js index 67fd8fb64a..ddf45acb13 100644 --- a/lib/utils/validateOptions.js +++ b/lib/utils/validateOptions.js @@ -3,7 +3,7 @@ const arrayEqual = require('./arrayEqual'); const { isPlainObject } = require('is-plain-object'); -const IGNORED_OPTIONS = new Set(['severity', 'message', 'reportDisables']); +const IGNORED_OPTIONS = new Set(['severity', 'message', 'reportDisables', 'disableFix']); /** @typedef {(value: unknown) => boolean} PossibleFunc */ diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index 8e83d41a5c..8ca318ad6a 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -81,6 +81,7 @@ declare module 'stylelint' { stylelintError?: boolean; disableWritingFix?: boolean; config?: StylelintConfig; + ruleDisableFix?: boolean; }; type EmptyResult = {