From 234a74cf92970c36c26b1477615814f8349a1ef9 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Tue, 8 Nov 2022 17:42:52 -0800 Subject: [PATCH 01/11] Add support to checkAgainstRule with custom rules --- lib/utils/__tests__/checkAgainstRule.test.js | 82 ++++++++++++++++++++ lib/utils/checkAgainstRule.js | 36 +++++---- types/stylelint/index.d.ts | 7 +- 3 files changed, 109 insertions(+), 16 deletions(-) diff --git a/lib/utils/__tests__/checkAgainstRule.test.js b/lib/utils/__tests__/checkAgainstRule.test.js index 13a2ee22a6..adaf080476 100644 --- a/lib/utils/__tests__/checkAgainstRule.test.js +++ b/lib/utils/__tests__/checkAgainstRule.test.js @@ -1,8 +1,34 @@ 'use strict'; +const report = require('../report'); +const validateOptions = require('../validateOptions'); const checkAgainstRule = require('../checkAgainstRule'); const postcss = require('postcss'); +const mockRuleName = 'custom/no-empty-source'; +const mockResult = { + stylelint: { + config: { + pluginFunctions: { + [mockRuleName]: (primary) => (root, result) => { + const validOptions = validateOptions(result, mockRuleName, { + actual: primary, + }); + + if (!validOptions || root.source.input.css) return; + + report({ + result, + message: 'Unexpected empty source', + ruleName: mockRuleName, + node: root, + }); + }, + }, + }, + }, +}; + describe('checkAgainstRule', () => { it('does nothing with no errors', () => { const root = postcss.parse('a {} @media {}'); @@ -60,4 +86,60 @@ describe('checkAgainstRule', () => { expect(warnings[0].line).toBe(3); expect(warnings[0].column).toBe(1); }); + + it('checks against custom rule (passing)', () => { + const root = postcss.parse('.not-empty {}'); + + const warnings = []; + + checkAgainstRule( + { + ruleName: mockRuleName, + result: mockResult, + ruleSettings: true, + root, + }, + (warning) => warnings.push(warning), + ); + + expect(warnings).toHaveLength(0); + }); + + it('checks against custom rule (failing)', () => { + const root = postcss.parse(''); + + const warnings = []; + + checkAgainstRule( + { + ruleName: mockRuleName, + result: mockResult, + ruleSettings: true, + root, + }, + (warning) => warnings.push(warning), + ); + + expect(warnings).toHaveLength(1); + expect(warnings[0].rule).toBe(mockRuleName); + expect(warnings[0].line).toBe(1); + expect(warnings[0].column).toBe(1); + }); + + test('throws when checking against custom rule without result object', () => { + expect(() => { + const root = postcss.parse(''); + + const warnings = []; + + checkAgainstRule( + { + ruleName: mockRuleName, + ruleSettings: true, + root, + }, + (warning) => warnings.push(warning), + ); + }).toThrow(`Rule '${mockRuleName}' does not exist`); + }); }); diff --git a/lib/utils/checkAgainstRule.js b/lib/utils/checkAgainstRule.js index 83706c5d3e..3340589cc4 100644 --- a/lib/utils/checkAgainstRule.js +++ b/lib/utils/checkAgainstRule.js @@ -3,6 +3,7 @@ const normalizeRuleSettings = require('../normalizeRuleSettings'); const Result = require('postcss/lib/result'); const rules = require('../rules'); +const { isPlainObject } = require('./validateTypes'); /** * Useful for third-party code (e.g. plugins) to run a PostCSS Root @@ -10,32 +11,37 @@ const rules = require('../rules'); * @template T * @template {Object} O * @param {{ - ruleName: string, - ruleSettings: import('stylelint').ConfigRuleSettings, - root: import('postcss').Root, - }} options + * ruleName: string, + * ruleSettings: import('stylelint').ConfigRuleSettings, + * root: import('postcss').Root, + * result?: import('stylelint').PostcssResult, + * }} options * @param {(warning: import('postcss').Warning) => void} callback * @returns {void} */ function checkAgainstRule(options, callback) { - if (!options) - throw new Error( - "checkAgainstRule requires an options object with 'ruleName', 'ruleSettings', and 'root' properties", - ); + if (!isPlainObject(options)) throw new Error('checkAgainstRule requires an options object'); if (!callback) throw new Error('checkAgainstRule requires a callback'); - if (!options.ruleName) throw new Error("checkAgainstRule requires a 'ruleName' option"); + const { ruleName, ruleSettings, root, result } = options; - const rule = rules[options.ruleName]; + if (!ruleName) throw new Error("checkAgainstRule requires a 'ruleName' option"); - if (!rule) throw new Error(`Rule '${options.ruleName}' does not exist`); + const rule = + rules[ruleName] || + (result && + result.stylelint.config && + result.stylelint.config.pluginFunctions && + result.stylelint.config.pluginFunctions[ruleName]); - if (!options.ruleSettings) throw new Error("checkAgainstRule requires a 'ruleSettings' option"); + if (!rule) throw new Error(`Rule '${ruleName}' does not exist`); - if (!options.root) throw new Error("checkAgainstRule requires a 'root' option"); + if (!ruleSettings) throw new Error("checkAgainstRule requires a 'ruleSettings' option"); - const settings = normalizeRuleSettings(options.ruleSettings, options.ruleName); + if (!root) throw new Error("checkAgainstRule requires a 'root' option"); + + const settings = normalizeRuleSettings(ruleSettings, rule); if (!settings) { return; @@ -44,7 +50,7 @@ function checkAgainstRule(options, callback) { // @ts-expect-error - this error should not occur with PostCSS 8 const tmpPostcssResult = new Result(); - rule(settings[0], /** @type {O} */ (settings[1]), {})(options.root, tmpPostcssResult); + rule(settings[0], /** @type {O} */ (settings[1]), {})(root, tmpPostcssResult); for (const warning of tmpPostcssResult.warnings()) callback(warning); } diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index b3b10781ff..34ae8e624f 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -509,7 +509,12 @@ declare module 'stylelint' { * against a specific rule and do something with the warnings */ checkAgainstRule: ( - options: { ruleName: string; ruleSettings: ConfigRuleSettings; root: PostCSS.Root }, + options: { + ruleName: string; + ruleSettings: ConfigRuleSettings; + root: PostCSS.Root; + result?: PostcssResult; + }, callback: (warning: PostCSS.Warning) => void, ) => void; }; From d7682a662007f9ee7180eec3ea28c59b5cc4c315 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Tue, 8 Nov 2022 17:43:43 -0800 Subject: [PATCH 02/11] Add support for normalize built-in and custom rule settings --- lib/__tests__/normalizeRuleSettings.test.js | 50 ++++++++++++--------- lib/normalizeAllRuleSettings.js | 6 +-- lib/normalizeRuleSettings.js | 22 ++------- 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/lib/__tests__/normalizeRuleSettings.test.js b/lib/__tests__/normalizeRuleSettings.test.js index 6f57e77aa4..ceea8c5037 100644 --- a/lib/__tests__/normalizeRuleSettings.test.js +++ b/lib/__tests__/normalizeRuleSettings.test.js @@ -1,74 +1,84 @@ 'use strict'; +const rules = require('../rules'); +const createPlugin = require('../createPlugin'); const normalizeRuleSettings = require('../normalizeRuleSettings'); +const mockRule = createPlugin('mock-rule', () => () => {}); +const mockRuleWithPrimaryOptionArray = createPlugin( + 'mock-rule-with-primary-option-array', + () => () => {}, +); + +mockRuleWithPrimaryOptionArray.primaryOptionArray = true; + describe('rules whose primary option IS NOT an array', () => { it('solo null returns null', () => { - expect(normalizeRuleSettings(null, 'foo')).toBeNull(); + expect(normalizeRuleSettings(null, mockRule)).toBeNull(); }); it('arrayed null returns null', () => { - expect(normalizeRuleSettings([null], 'foo')).toBeNull(); + expect(normalizeRuleSettings([null], mockRule)).toBeNull(); }); it('solo number returns arrayed number', () => { - const actual = normalizeRuleSettings(2, 'foo'); + const actual = normalizeRuleSettings(2, mockRule); const expected = [2]; expect(actual).toEqual(expected); }); it('arrayed number returns arrayed number if rule is not special', () => { - const actual = normalizeRuleSettings([2], 'foo'); + const actual = normalizeRuleSettings([2], mockRule); const expected = [2]; expect(actual).toEqual(expected); }); it('arrayed number with secondary options returns same', () => { - const actual = normalizeRuleSettings([2, { severity: 'warning' }], 'block-no-empty'); + const actual = normalizeRuleSettings([2, { severity: 'warning' }], rules['block-no-empty']); const expected = [2, { severity: 'warning' }]; expect(actual).toEqual(expected); }); it('solo string returns arrayed string', () => { - const actual = normalizeRuleSettings('always', 'foo'); + const actual = normalizeRuleSettings('always', mockRule); const expected = ['always']; expect(actual).toEqual(expected); }); it('arrayed string returns arrayed string', () => { - const actual = normalizeRuleSettings(['always'], 'foo'); + const actual = normalizeRuleSettings(['always'], mockRule); const expected = ['always']; expect(actual).toEqual(expected); }); it('arrayed string with secondary options returns same', () => { - const actual = normalizeRuleSettings(['always', { severity: 'warning' }], 'foo'); + const actual = normalizeRuleSettings(['always', { severity: 'warning' }], mockRule); const expected = ['always', { severity: 'warning' }]; expect(actual).toEqual(expected); }); it('solo boolean returns arrayed boolean', () => { - const actual = normalizeRuleSettings(true, 'foo'); + const actual = normalizeRuleSettings(true, mockRule); const expected = [true]; expect(actual).toEqual(expected); }); it('arrayed boolean returns arrayed boolean if rule is not special', () => { - const actual = normalizeRuleSettings([false], 'foo'); + const actual = normalizeRuleSettings([false], mockRule); const expected = [false]; expect(actual).toEqual(expected); }); it('arrayed boolean with secondary options returns same', () => { - const actual = normalizeRuleSettings([true, { severity: 'warning' }], 'block-no-empty'); + const actual = normalizeRuleSettings([true, { severity: 'warning' }], rules['block-no-empty']); const expected = [true, { severity: 'warning' }]; expect(actual).toEqual(expected); @@ -77,22 +87,22 @@ describe('rules whose primary option IS NOT an array', () => { describe('rules whose primary option CAN BE an array', () => { it('solo null returns null', () => { - expect(normalizeRuleSettings(null, 'foo')).toBeNull(); + expect(normalizeRuleSettings(null, mockRule)).toBeNull(); }); it('arrayed null returns null', () => { - expect(normalizeRuleSettings([null], 'foo')).toBeNull(); + expect(normalizeRuleSettings([null], mockRule)).toBeNull(); }); it('solo primary option array is nested within an array', () => { - const actual = normalizeRuleSettings(['calc', 'rgba'], 'function-allowed-list', true); + const actual = normalizeRuleSettings(['calc', 'rgba'], rules['function-allowed-list']); const expected = [['calc', 'rgba']]; expect(actual).toEqual(expected); }); it('primary option array in an array', () => { - const actual = normalizeRuleSettings([['calc', 'rgba']], 'function-allowed-list', true); + const actual = normalizeRuleSettings([['calc', 'rgba']], rules['function-allowed-list']); const expected = [['calc', 'rgba']]; expect(actual).toEqual(expected); @@ -101,8 +111,7 @@ describe('rules whose primary option CAN BE an array', () => { it('nested primary option array returns same', () => { const actual = normalizeRuleSettings( [['calc', 'rgba'], { severity: 'warning' }], - 'function-allowed-list', - true, + rules['function-allowed-list'], ); const expected = [['calc', 'rgba'], { severity: 'warning' }]; @@ -110,14 +119,14 @@ describe('rules whose primary option CAN BE an array', () => { }); it('string as first primary option returns same', () => { - const actual = normalizeRuleSettings(['alphabetical', { severity: 'warning' }], 'rulename-bar'); + const actual = normalizeRuleSettings(['alphabetical', { severity: 'warning' }], mockRule); const expected = ['alphabetical', { severity: 'warning' }]; expect(actual).toEqual(expected); }); it('primary option array with length of 2', () => { - const actual = normalizeRuleSettings([{ foo: 1 }, { foo: 2 }], 'rulename-bar', true); + const actual = normalizeRuleSettings([{ foo: 1 }, { foo: 2 }], mockRuleWithPrimaryOptionArray); const expected = [[{ foo: 1 }, { foo: 2 }]]; expect(actual).toEqual(expected); @@ -126,8 +135,7 @@ describe('rules whose primary option CAN BE an array', () => { it('primary option array with length of 2 and secondary options', () => { const actual = normalizeRuleSettings( [[{ foo: 1 }, { foo: 2 }], { severity: 'warning' }], - 'rulename-bar', - true, + mockRuleWithPrimaryOptionArray, ); const expected = [[{ foo: 1 }, { foo: 2 }], { severity: 'warning' }]; diff --git a/lib/normalizeAllRuleSettings.js b/lib/normalizeAllRuleSettings.js index 9f2adb198b..5ffbb0f596 100644 --- a/lib/normalizeAllRuleSettings.js +++ b/lib/normalizeAllRuleSettings.js @@ -20,11 +20,7 @@ function normalizeAllRuleSettings(config) { const rule = rules[ruleName] || (config.pluginFunctions && config.pluginFunctions[ruleName]); if (rule) { - normalizedRules[ruleName] = normalizeRuleSettings( - rawRuleSettings, - ruleName, - rule.primaryOptionArray, - ); + normalizedRules[ruleName] = normalizeRuleSettings(rawRuleSettings, rule); } else { normalizedRules[ruleName] = []; } diff --git a/lib/normalizeRuleSettings.js b/lib/normalizeRuleSettings.js index e2d31512dd..f60cad7e6a 100644 --- a/lib/normalizeRuleSettings.js +++ b/lib/normalizeRuleSettings.js @@ -1,6 +1,5 @@ 'use strict'; -const rules = require('./rules'); const { isPlainObject } = require('./utils/validateTypes'); // Rule settings can take a number of forms, e.g. @@ -19,17 +18,10 @@ const { isPlainObject } = require('./utils/validateTypes'); * @template T * @template {Object} O * @param {import('stylelint').ConfigRuleSettings} rawSettings - * @param {string} ruleName - * @param {boolean} [primaryOptionArray] If primaryOptionArray is not provided, we try to get it from the rules themselves, which will not work for plugins + * @param {import('stylelint').Rule} [rule] * @return {[T] | [T, O] | null} */ -module.exports = function normalizeRuleSettings( - rawSettings, - ruleName, - // If primaryOptionArray is not provided, we try to get it from the - // rules themselves, which will not work for plugins - primaryOptionArray, -) { +module.exports = function normalizeRuleSettings(rawSettings, rule) { if (rawSettings === null || rawSettings === undefined) { return null; } @@ -43,15 +35,7 @@ module.exports = function normalizeRuleSettings( return null; } - if (primaryOptionArray === undefined) { - const rule = rules[ruleName]; - - if (rule && 'primaryOptionArray' in rule) { - primaryOptionArray = rule.primaryOptionArray; - } - } - - if (!primaryOptionArray) { + if (rule && !rule.primaryOptionArray) { return rawSettings; } // Everything below is a rule that CAN have an array for a primary option ... From 5bd44cfc4c3e5133e7486741bc7a2cd324445600 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Tue, 8 Nov 2022 22:23:44 -0800 Subject: [PATCH 03/11] Update checkAgainstRule documentation to describe the new behavior --- docs/developer-guide/plugins.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/developer-guide/plugins.md b/docs/developer-guide/plugins.md index 3b9318833d..79ba419cf9 100644 --- a/docs/developer-guide/plugins.md +++ b/docs/developer-guide/plugins.md @@ -232,13 +232,14 @@ Validates the options for your rule. ### `stylelint.utils.checkAgainstRule` -Checks CSS against a standard Stylelint rule _within your own rule_. This function provides power and flexibility for plugins authors who wish to modify, constrain, or extend the functionality of existing Stylelint rules. +Checks CSS against a standard or custom Stylelint rule _within your own rule_. This function provides power and flexibility for plugins authors who wish to modify, constrain, or extend the functionality of existing Stylelint rules. It accepts an options object and a callback that is invoked with warnings from the specified rule. The options are: - `ruleName`: the name of the rule you are invoking - `ruleSettings`: settings for the rule you are invoking - `root`: the root node to run this rule against +- `result?`: the postcss result for resolving and invoking custom rules Use the warning to create a _new_ warning _from your plugin rule_ that you report with `stylelint.utils.report`. @@ -259,7 +260,8 @@ function myPluginRule(primaryOption, secondaryOptionObject) { { ruleName: "at-rule-no-unknown", ruleSettings: [primaryOption, defaultedOptions], - root: postcssRoot + root: postcssRoot, + result: postcssResult }, (warning) => { stylelint.utils.report({ From fe3723eea210a7487ccbea650a0764e5fb286f0d Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Tue, 8 Nov 2022 23:01:12 -0800 Subject: [PATCH 04/11] Replace primary/secondary indexed access with destructuring --- lib/normalizeRuleSettings.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/normalizeRuleSettings.js b/lib/normalizeRuleSettings.js index f60cad7e6a..1169259a8a 100644 --- a/lib/normalizeRuleSettings.js +++ b/lib/normalizeRuleSettings.js @@ -31,7 +31,9 @@ module.exports = function normalizeRuleSettings(rawSettings, rule) { } // Everything below is an array ... - if (rawSettings.length > 0 && (rawSettings[0] === null || rawSettings[0] === undefined)) { + const [primary, secondary] = rawSettings; + + if (rawSettings.length > 0 && (primary === null || primary === undefined)) { return null; } @@ -42,11 +44,11 @@ module.exports = function normalizeRuleSettings(rawSettings, rule) { // (they might also have something else, e.g. rule-properties-order can // have the string "alphabetical") - if (rawSettings.length === 1 && Array.isArray(rawSettings[0])) { + if (rawSettings.length === 1 && Array.isArray(primary)) { return rawSettings; } - if (rawSettings.length === 2 && !isPlainObject(rawSettings[0]) && isPlainObject(rawSettings[1])) { + if (rawSettings.length === 2 && !isPlainObject(primary) && isPlainObject(secondary)) { return rawSettings; } From 1062128d14ee133f9b752f1e4c45e1128ea9ea7e Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Tue, 8 Nov 2022 23:04:52 -0800 Subject: [PATCH 05/11] Update docs/developer-guide/plugins.md Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> --- docs/developer-guide/plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/plugins.md b/docs/developer-guide/plugins.md index 79ba419cf9..11b11245c1 100644 --- a/docs/developer-guide/plugins.md +++ b/docs/developer-guide/plugins.md @@ -239,7 +239,7 @@ It accepts an options object and a callback that is invoked with warnings from t - `ruleName`: the name of the rule you are invoking - `ruleSettings`: settings for the rule you are invoking - `root`: the root node to run this rule against -- `result?`: the postcss result for resolving and invoking custom rules +- `result?`: the PostCSS result for resolving and invoking custom rules Use the warning to create a _new_ warning _from your plugin rule_ that you report with `stylelint.utils.report`. From 339005b8d3072a8b6708387c76ce5d9aec4b7e95 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Tue, 8 Nov 2022 23:48:09 -0800 Subject: [PATCH 06/11] Add utility to abstract common Stylelint rule resolution --- lib/lintPostcssResult.js | 4 ++-- lib/normalizeAllRuleSettings.js | 4 ++-- lib/utils/checkAgainstRule.js | 9 ++------- lib/utils/getStylelintRule.js | 10 ++++++++++ 4 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 lib/utils/getStylelintRule.js diff --git a/lib/lintPostcssResult.js b/lib/lintPostcssResult.js index 0cde1a0af9..9e09c74b00 100644 --- a/lib/lintPostcssResult.js +++ b/lib/lintPostcssResult.js @@ -4,6 +4,7 @@ const assignDisabledRanges = require('./assignDisabledRanges'); const getOsEol = require('./utils/getOsEol'); const reportUnknownRuleNames = require('./reportUnknownRuleNames'); const rules = require('./rules'); +const getStylelintRule = require('./utils/getStylelintRule'); /** @typedef {import('stylelint').LinterOptions} LinterOptions */ /** @typedef {import('stylelint').PostcssResult} PostcssResult */ @@ -62,8 +63,7 @@ function lintPostcssResult(stylelintOptions, postcssResult, config) { : []; for (const ruleName of ruleNames) { - const ruleFunction = - rules[ruleName] || (config.pluginFunctions && config.pluginFunctions[ruleName]); + const ruleFunction = getStylelintRule(ruleName, config); if (ruleFunction === undefined) { performRules.push( diff --git a/lib/normalizeAllRuleSettings.js b/lib/normalizeAllRuleSettings.js index 5ffbb0f596..e9ea800c90 100644 --- a/lib/normalizeAllRuleSettings.js +++ b/lib/normalizeAllRuleSettings.js @@ -1,7 +1,7 @@ 'use strict'; const normalizeRuleSettings = require('./normalizeRuleSettings'); -const rules = require('./rules'); +const getStylelintRule = require('./utils/getStylelintRule'); /** @typedef {import('stylelint').ConfigRules} StylelintConfigRules */ /** @typedef {import('stylelint').Config} StylelintConfig */ @@ -17,7 +17,7 @@ function normalizeAllRuleSettings(config) { const normalizedRules = {}; for (const [ruleName, rawRuleSettings] of Object.entries(config.rules)) { - const rule = rules[ruleName] || (config.pluginFunctions && config.pluginFunctions[ruleName]); + const rule = getStylelintRule(ruleName, config); if (rule) { normalizedRules[ruleName] = normalizeRuleSettings(rawRuleSettings, rule); diff --git a/lib/utils/checkAgainstRule.js b/lib/utils/checkAgainstRule.js index 3340589cc4..72f2028927 100644 --- a/lib/utils/checkAgainstRule.js +++ b/lib/utils/checkAgainstRule.js @@ -2,8 +2,8 @@ const normalizeRuleSettings = require('../normalizeRuleSettings'); const Result = require('postcss/lib/result'); -const rules = require('../rules'); const { isPlainObject } = require('./validateTypes'); +const getStylelintRule = require('./getStylelintRule'); /** * Useful for third-party code (e.g. plugins) to run a PostCSS Root @@ -28,12 +28,7 @@ function checkAgainstRule(options, callback) { if (!ruleName) throw new Error("checkAgainstRule requires a 'ruleName' option"); - const rule = - rules[ruleName] || - (result && - result.stylelint.config && - result.stylelint.config.pluginFunctions && - result.stylelint.config.pluginFunctions[ruleName]); + const rule = getStylelintRule(ruleName, result && result.stylelint.config); if (!rule) throw new Error(`Rule '${ruleName}' does not exist`); diff --git a/lib/utils/getStylelintRule.js b/lib/utils/getStylelintRule.js new file mode 100644 index 0000000000..401cf5a52d --- /dev/null +++ b/lib/utils/getStylelintRule.js @@ -0,0 +1,10 @@ +const rules = require('../rules'); + +/** + * @param {string} ruleName + * @param {import('stylelint').Config | undefined} [config] + * @returns {import('stylelint').Rule | undefined} + */ +module.exports = function getStylelintRule(ruleName, config) { + return rules[ruleName] || (config && config.pluginFunctions && config.pluginFunctions[ruleName]); +}; From b5636bd966adeda34c6a9d52b7a2dbc137e0b5b9 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Wed, 9 Nov 2022 08:52:16 -0800 Subject: [PATCH 07/11] Improve checkAgainstRule error messages --- lib/utils/checkAgainstRule.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/utils/checkAgainstRule.js b/lib/utils/checkAgainstRule.js index 72f2028927..c137f3bcc2 100644 --- a/lib/utils/checkAgainstRule.js +++ b/lib/utils/checkAgainstRule.js @@ -20,21 +20,21 @@ const getStylelintRule = require('./getStylelintRule'); * @returns {void} */ function checkAgainstRule(options, callback) { - if (!isPlainObject(options)) throw new Error('checkAgainstRule requires an options object'); + if (!isPlainObject(options)) throw new Error('Expected an options object'); - if (!callback) throw new Error('checkAgainstRule requires a callback'); + if (!callback) throw new Error('Expected a callback function'); const { ruleName, ruleSettings, root, result } = options; - if (!ruleName) throw new Error("checkAgainstRule requires a 'ruleName' option"); + if (!ruleName) throw new Error('Expected a "ruleName" option'); const rule = getStylelintRule(ruleName, result && result.stylelint.config); if (!rule) throw new Error(`Rule '${ruleName}' does not exist`); - if (!ruleSettings) throw new Error("checkAgainstRule requires a 'ruleSettings' option"); + if (!ruleSettings) throw new Error('Expected a "ruleSettings" option'); - if (!root) throw new Error("checkAgainstRule requires a 'root' option"); + if (!root) throw new Error('Expected a "root" option'); const settings = normalizeRuleSettings(ruleSettings, rule); From 9f600025bc4282df765654c0f22908284bc4210d Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Wed, 9 Nov 2022 09:03:44 -0800 Subject: [PATCH 08/11] Simplify checkAgainstRule types --- lib/utils/checkAgainstRule.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/lib/utils/checkAgainstRule.js b/lib/utils/checkAgainstRule.js index c137f3bcc2..fc4620d238 100644 --- a/lib/utils/checkAgainstRule.js +++ b/lib/utils/checkAgainstRule.js @@ -8,16 +8,8 @@ const getStylelintRule = require('./getStylelintRule'); /** * Useful for third-party code (e.g. plugins) to run a PostCSS Root * against a specific rule and do something with the warnings - * @template T - * @template {Object} O - * @param {{ - * ruleName: string, - * ruleSettings: import('stylelint').ConfigRuleSettings, - * root: import('postcss').Root, - * result?: import('stylelint').PostcssResult, - * }} options - * @param {(warning: import('postcss').Warning) => void} callback - * @returns {void} + * + * @type {typeof import('stylelint').utils.checkAgainstRule} */ function checkAgainstRule(options, callback) { if (!isPlainObject(options)) throw new Error('Expected an options object'); @@ -45,11 +37,9 @@ function checkAgainstRule(options, callback) { // @ts-expect-error - this error should not occur with PostCSS 8 const tmpPostcssResult = new Result(); - rule(settings[0], /** @type {O} */ (settings[1]), {})(root, tmpPostcssResult); + rule(settings[0], /** @type {Object} */ (settings[1]), {})(root, tmpPostcssResult); for (const warning of tmpPostcssResult.warnings()) callback(warning); } -module.exports = /** @type {typeof import('stylelint').utils.checkAgainstRule} */ ( - checkAgainstRule -); +module.exports = checkAgainstRule; From 6a53fc709c4847a360b66dcce429dd6808a5fb51 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Wed, 9 Nov 2022 18:59:19 -0800 Subject: [PATCH 09/11] Use double quotes for error messages --- lib/utils/__tests__/checkAgainstRule.test.js | 2 +- lib/utils/checkAgainstRule.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/__tests__/checkAgainstRule.test.js b/lib/utils/__tests__/checkAgainstRule.test.js index adaf080476..fffaef6833 100644 --- a/lib/utils/__tests__/checkAgainstRule.test.js +++ b/lib/utils/__tests__/checkAgainstRule.test.js @@ -140,6 +140,6 @@ describe('checkAgainstRule', () => { }, (warning) => warnings.push(warning), ); - }).toThrow(`Rule '${mockRuleName}' does not exist`); + }).toThrow(`Rule "${mockRuleName}" does not exist`); }); }); diff --git a/lib/utils/checkAgainstRule.js b/lib/utils/checkAgainstRule.js index fc4620d238..062a1ebdcb 100644 --- a/lib/utils/checkAgainstRule.js +++ b/lib/utils/checkAgainstRule.js @@ -22,7 +22,7 @@ function checkAgainstRule(options, callback) { const rule = getStylelintRule(ruleName, result && result.stylelint.config); - if (!rule) throw new Error(`Rule '${ruleName}' does not exist`); + if (!rule) throw new Error(`Rule "${ruleName}" does not exist`); if (!ruleSettings) throw new Error('Expected a "ruleSettings" option'); From 8b9c26daabe81364d2007504ec9bd3150651cbdf Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:01:21 -0800 Subject: [PATCH 10/11] Add changeset entry --- .changeset/little-forks-burn.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/little-forks-burn.md diff --git a/.changeset/little-forks-burn.md b/.changeset/little-forks-burn.md new file mode 100644 index 0000000000..f48c6147e9 --- /dev/null +++ b/.changeset/little-forks-burn.md @@ -0,0 +1,5 @@ +--- +"stylelint": minor +--- + +Added: to `checkAgainstRule` with custom rules From 7aab43bdb9c2b7955504348ac5ffc2311511192a Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:06:14 -0800 Subject: [PATCH 11/11] Fix typo --- .changeset/little-forks-burn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/little-forks-burn.md b/.changeset/little-forks-burn.md index f48c6147e9..e8b1703792 100644 --- a/.changeset/little-forks-burn.md +++ b/.changeset/little-forks-burn.md @@ -2,4 +2,4 @@ "stylelint": minor --- -Added: to `checkAgainstRule` with custom rules +Added: support to `checkAgainstRule` with custom rules