From 7b2928782f96c633a1f24ea80cf3f9602b857812 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 20 Aug 2021 00:50:07 +0900 Subject: [PATCH 1/2] Refactor to improve types for `function-*` rules This change removes `// @ts-nocheck` from the `function-*` rules. --- lib/rules/function-allowed-list/index.js | 9 ++- .../index.js | 56 ++++++++++++++--- .../function-comma-newline-after/index.js | 15 +++-- .../function-comma-newline-before/index.js | 15 +++-- lib/rules/function-comma-space-after/index.js | 13 ++-- .../function-comma-space-before/index.js | 13 ++-- lib/rules/function-disallowed-list/index.js | 11 ++-- .../index.js | 13 ++-- lib/rules/function-max-empty-lines/index.js | 19 +++--- lib/rules/function-name-case/index.js | 17 +++--- .../index.js | 60 +++++++++++++------ .../index.js | 29 +++++---- .../function-url-no-scheme-relative/index.js | 9 ++- lib/rules/function-url-quotes/index.js | 34 ++++++++--- .../function-url-scheme-allowed-list/index.js | 11 ++-- .../index.js | 11 ++-- lib/rules/function-whitespace-after/index.js | 50 +++++++++++----- lib/rules/functionCommaSpaceChecker.js | 24 ++++++-- lib/rules/functionCommaSpaceFix.js | 17 +++++- 19 files changed, 275 insertions(+), 151 deletions(-) diff --git a/lib/rules/function-allowed-list/index.js b/lib/rules/function-allowed-list/index.js index faae319533..5ac3b71108 100644 --- a/lib/rules/function-allowed-list/index.js +++ b/lib/rules/function-allowed-list/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const declarationValueIndex = require('../../utils/declarationValueIndex'); @@ -18,8 +16,9 @@ const messages = ruleMessages(ruleName, { rejected: (name) => `Unexpected function "${name}"`, }); -function rule(listInput) { - const list = [].concat(listInput); +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary) => { + const list = [primary].flat(); return (root, result) => { const validOptions = validateOptions(result, ruleName, { @@ -57,7 +56,7 @@ function rule(listInput) { }); }); }; -} +}; rule.primaryOptionArray = true; diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index e8ec9106e8..92fffc07a7 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const balancedMatch = require('balanced-match'); @@ -18,19 +16,28 @@ const messages = ruleMessages(ruleName, { expectedOperatorBeforeSign: (operator) => `Expected an operator before sign "${operator}"`, }); -function rule(actual, secondary, context) { +/** @typedef {{ index: number, insert: boolean }} SymbolToFix */ + +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }); + const validOptions = validateOptions(result, ruleName, { actual: primary }); if (!validOptions) { return; } + /** + * @param {string} message + * @param {import('postcss').Node} node + * @param {number} index + */ function complain(message, node, index) { report({ message, node, index, result, ruleName }); } root.walkDecls((decl) => { + /** @type {SymbolToFix[]} */ const symbolsToFix = []; valueParser(decl.value).walk((node) => { @@ -45,6 +52,10 @@ function rule(actual, secondary, context) { throw new Error(`No parens match: "${nodeText}"`); } + if (decl.source == null || decl.source.start == null) { + throw new Error('Declaration source must be present'); + } + const rawExpression = parensMatch.body; const expressionIndex = decl.source.start.column + @@ -53,12 +64,18 @@ function rule(actual, secondary, context) { node.sourceIndex; const expression = blurVariables(rawExpression); + const parensMatchStart = parensMatch.start; + checkSymbol('+'); checkSymbol('-'); checkSymbol('*'); checkSymbol('/'); + /** + * @param {string} symbol + */ function checkSymbol(symbol) { + /** @type {import('style-search').Options} */ const styleSearchOptions = { source: expression, target: symbol, @@ -67,7 +84,7 @@ function rule(actual, secondary, context) { styleSearch(styleSearchOptions, (match) => { const index = match.startIndex; - const symbolIndex = node.sourceIndex + parensMatch.start + index + 1; + const symbolIndex = node.sourceIndex + parensMatchStart + index + 1; // Deal with signs. // (@ and $ are considered "digits" here to allow for variable syntaxes @@ -170,7 +187,7 @@ function rule(actual, secondary, context) { }); if (context.fix) { - decl.value = symbolsToFix.reduce((fixedValue, { insert, index }) => { + decl.value = symbolsToFix.reduce((/** @type {string} */ fixedValue, { insert, index }) => { shiftIndexes(symbolsToFix, index, insert); return insert @@ -180,12 +197,21 @@ function rule(actual, secondary, context) { } }); }; -} +}; +/** + * @param {string} str + * @param {number} index + */ function isNewlineAtIndex(str, index) { return str[index] === '\n' || str.slice(index, index + 2) === '\r\n'; } +/** + * @param {SymbolToFix[]} symbolsToFix + * @param {number} index + * @param {boolean} insert + */ function shiftIndexes(symbolsToFix, index, insert) { symbolsToFix.forEach((symbol) => { if (symbol.index > index) { @@ -194,18 +220,34 @@ function shiftIndexes(symbolsToFix, index, insert) { }); } +/** + * @param {string} str + * @param {number} index + */ function removeCharAtIndex(str, index) { return str.slice(0, index) + str.slice(index + 1, str.length); } +/** + * @param {string} str + * @param {number} index + * @param {string} char + */ function insertCharAtIndex(str, index, char) { return str.slice(0, index) + char + str.slice(index, str.length); } +/** + * @param {string} source + */ function blurVariables(source) { return source.replace(/[$@][^)\s]+|#{.+?}/g, '0'); } +/** + * @param {string} str + * @param {number} startIndex + */ function newlineBefore(str, startIndex) { let index = startIndex; diff --git a/lib/rules/function-comma-newline-after/index.js b/lib/rules/function-comma-newline-after/index.js index 185bb34185..a387371afa 100644 --- a/lib/rules/function-comma-newline-after/index.js +++ b/lib/rules/function-comma-newline-after/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const fixer = require('../functionCommaSpaceFix'); @@ -16,12 +14,13 @@ const messages = ruleMessages(ruleName, { rejectedAfterMultiLine: () => 'Unexpected whitespace after "," in a multi-line function', }); -function rule(expectation, options, context) { - const checker = whitespaceChecker('newline', expectation, messages); +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { + const checker = whitespaceChecker('newline', primary, messages); return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: expectation, + actual: primary, possible: ['always', 'always-multi-line', 'never-multi-line'], }); @@ -40,15 +39,15 @@ function rule(expectation, options, context) { div, index, nodes, - expectation, + expectation: primary, position: 'after', - symb: context.newline, + symb: context.newline || '', }); } : null, }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-comma-newline-before/index.js b/lib/rules/function-comma-newline-before/index.js index 8c6510202d..75e803a1d9 100644 --- a/lib/rules/function-comma-newline-before/index.js +++ b/lib/rules/function-comma-newline-before/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const fixer = require('../functionCommaSpaceFix'); @@ -16,12 +14,13 @@ const messages = ruleMessages(ruleName, { rejectedBeforeMultiLine: () => 'Unexpected whitespace before "," in a multi-line function', }); -function rule(expectation, options, context) { - const checker = whitespaceChecker('newline', expectation, messages); +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { + const checker = whitespaceChecker('newline', primary, messages); return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: expectation, + actual: primary, possible: ['always', 'always-multi-line', 'never-multi-line'], }); @@ -40,15 +39,15 @@ function rule(expectation, options, context) { div, index, nodes, - expectation, + expectation: primary, position: 'before', - symb: context.newline, + symb: context.newline || '', }); } : null, }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-comma-space-after/index.js b/lib/rules/function-comma-space-after/index.js index 0a4eacd63c..7ae081098d 100644 --- a/lib/rules/function-comma-space-after/index.js +++ b/lib/rules/function-comma-space-after/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const fixer = require('../functionCommaSpaceFix'); @@ -17,12 +15,13 @@ const messages = ruleMessages(ruleName, { rejectedAfterSingleLine: () => 'Unexpected whitespace after "," in a single-line function', }); -function rule(expectation, options, context) { - const checker = whitespaceChecker('space', expectation, messages); +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { + const checker = whitespaceChecker('space', primary, messages); return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: expectation, + actual: primary, possible: ['always', 'never', 'always-single-line', 'never-single-line'], }); @@ -41,7 +40,7 @@ function rule(expectation, options, context) { div, index, nodes, - expectation, + expectation: primary, position: 'after', symb: ' ', }); @@ -49,7 +48,7 @@ function rule(expectation, options, context) { : null, }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-comma-space-before/index.js b/lib/rules/function-comma-space-before/index.js index 4bfde17c36..6eac2b9c7e 100644 --- a/lib/rules/function-comma-space-before/index.js +++ b/lib/rules/function-comma-space-before/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const fixer = require('../functionCommaSpaceFix'); @@ -17,12 +15,13 @@ const messages = ruleMessages(ruleName, { rejectedBeforeSingleLine: () => 'Unexpected whitespace before "," in a single-line function', }); -function rule(expectation, options, context) { - const checker = whitespaceChecker('space', expectation, messages); +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { + const checker = whitespaceChecker('space', primary, messages); return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: expectation, + actual: primary, possible: ['always', 'never', 'always-single-line', 'never-single-line'], }); @@ -41,7 +40,7 @@ function rule(expectation, options, context) { div, index, nodes, - expectation, + expectation: primary, position: 'before', symb: ' ', }); @@ -49,7 +48,7 @@ function rule(expectation, options, context) { : null, }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-disallowed-list/index.js b/lib/rules/function-disallowed-list/index.js index c0a671c28d..93a5d0361a 100644 --- a/lib/rules/function-disallowed-list/index.js +++ b/lib/rules/function-disallowed-list/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const declarationValueIndex = require('../../utils/declarationValueIndex'); @@ -18,10 +16,11 @@ const messages = ruleMessages(ruleName, { rejected: (name) => `Unexpected function "${name}"`, }); -function rule(list) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: list, + actual: primary, possible: [isString, isRegExp], }); @@ -41,7 +40,7 @@ function rule(list) { return; } - if (!matchesStringOrRegExp(vendor.unprefixed(node.value), list)) { + if (!matchesStringOrRegExp(vendor.unprefixed(node.value), primary)) { return; } @@ -55,7 +54,7 @@ function rule(list) { }); }); }; -} +}; rule.primaryOptionArray = true; diff --git a/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js b/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js index 319b3b9c41..000823c53f 100644 --- a/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js +++ b/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const declarationValueIndex = require('../../utils/declarationValueIndex'); @@ -17,6 +15,10 @@ const messages = ruleMessages(ruleName, { rejected: 'Unexpected nonstandard direction', }); +/** + * @param {string} source + * @param {boolean} withToPrefix + */ function isStandardDirection(source, withToPrefix) { const regexp = withToPrefix ? /^to (top|left|bottom|right)(?: (top|left|bottom|right))?$/ @@ -40,9 +42,10 @@ function isStandardDirection(source, withToPrefix) { return false; } -function rule(actual) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary) => { return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }); + const validOptions = validateOptions(result, ruleName, { actual: primary }); if (!validOptions) { return; @@ -103,7 +106,7 @@ function rule(actual) { }); }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-max-empty-lines/index.js b/lib/rules/function-max-empty-lines/index.js index 6f0fad3f48..e27e7062d2 100644 --- a/lib/rules/function-max-empty-lines/index.js +++ b/lib/rules/function-max-empty-lines/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const getDeclarationValue = require('../../utils/getDeclarationValue'); @@ -16,16 +14,22 @@ const messages = ruleMessages(ruleName, { expected: (max) => `Expected no more than ${max} empty ${max === 1 ? 'line' : 'lines'}`, }); +/** + * @param {import('postcss').Declaration} decl + */ function placeIndexOnValueStart(decl) { + if (decl.raws.between == null) throw new Error('`between` must be present'); + return decl.prop.length + decl.raws.between.length - 1; } -function rule(max, options, context) { - const maxAdjacentNewlines = max + 1; +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { + const maxAdjacentNewlines = primary + 1; return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: max, + actual: primary, possible: isNumber, }); @@ -44,6 +48,7 @@ function rule(max, options, context) { } const stringValue = getDeclarationValue(decl); + /** @type {Array<[string, string]>} */ const splittedValue = []; let sourceIndexStart = 0; @@ -76,7 +81,7 @@ function rule(max, options, context) { sourceIndexStart = node.sourceIndex + stringifiedNode.length; } else { report({ - message: messages.expected(max), + message: messages.expected(primary), node: decl, index: placeIndexOnValueStart(decl) + node.sourceIndex, result, @@ -94,7 +99,7 @@ function rule(max, options, context) { } }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-name-case/index.js b/lib/rules/function-name-case/index.js index cb1c80b841..3056875add 100644 --- a/lib/rules/function-name-case/index.js +++ b/lib/rules/function-name-case/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const declarationValueIndex = require('../../utils/declarationValueIndex'); @@ -26,17 +24,18 @@ keywordSets.camelCaseFunctionNames.forEach((func) => { mapLowercaseFunctionNamesToCamelCase.set(func.toLowerCase(), func); }); -function rule(expectation, options, context) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, secondaryOptions, context) => { return (root, result) => { const validOptions = validateOptions( result, ruleName, { - actual: expectation, + actual: primary, possible: ['lower', 'upper'], }, { - actual: options, + actual: secondaryOptions, possible: { ignoreFunctions: [isString, isRegExp], }, @@ -60,7 +59,7 @@ function rule(expectation, options, context) { const functionName = node.value; const functionNameLowerCase = functionName.toLowerCase(); - const ignoreFunctions = (options && options.ignoreFunctions) || []; + const ignoreFunctions = (secondaryOptions && secondaryOptions.ignoreFunctions) || []; if (ignoreFunctions.length > 0 && matchesStringOrRegExp(functionName, ignoreFunctions)) { return; @@ -69,11 +68,11 @@ function rule(expectation, options, context) { let expectedFunctionName = null; if ( - expectation === 'lower' && + primary === 'lower' && mapLowercaseFunctionNamesToCamelCase.has(functionNameLowerCase) ) { expectedFunctionName = mapLowercaseFunctionNamesToCamelCase.get(functionNameLowerCase); - } else if (expectation === 'lower') { + } else if (primary === 'lower') { expectedFunctionName = functionNameLowerCase; } else { expectedFunctionName = functionName.toUpperCase(); @@ -104,7 +103,7 @@ function rule(expectation, options, context) { } }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-parentheses-newline-inside/index.js b/lib/rules/function-parentheses-newline-inside/index.js index 71774d02c4..cbcddd14e8 100644 --- a/lib/rules/function-parentheses-newline-inside/index.js +++ b/lib/rules/function-parentheses-newline-inside/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const declarationValueIndex = require('../../utils/declarationValueIndex'); @@ -23,10 +21,11 @@ const messages = ruleMessages(ruleName, { rejectedClosingMultiLine: 'Unexpected whitespace before ")" in a multi-line function', }); -function rule(expectation, options, context) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: expectation, + actual: primary, possible: ['always', 'always-multi-line', 'never-multi-line'], }); @@ -54,35 +53,32 @@ function rule(expectation, options, context) { const functionString = valueParser.stringify(valueNode); const isMultiLine = !isSingleLineString(functionString); - - function containsNewline(str) { - return str.includes('\n'); - } + const containsNewline = (/** @type {string} */ str) => str.includes('\n'); // Check opening ... const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1; const checkBefore = getCheckBefore(valueNode); - if (expectation === 'always' && !containsNewline(checkBefore)) { + if (primary === 'always' && !containsNewline(checkBefore)) { if (context.fix) { hasFixed = true; - fixBeforeForAlways(valueNode, context.newline); + fixBeforeForAlways(valueNode, context.newline || ''); } else { complain(messages.expectedOpening, openingIndex); } } - if (isMultiLine && expectation === 'always-multi-line' && !containsNewline(checkBefore)) { + if (isMultiLine && primary === 'always-multi-line' && !containsNewline(checkBefore)) { if (context.fix) { hasFixed = true; - fixBeforeForAlways(valueNode, context.newline); + fixBeforeForAlways(valueNode, context.newline || ''); } else { complain(messages.expectedOpeningMultiLine, openingIndex); } } - if (isMultiLine && expectation === 'never-multi-line' && checkBefore !== '') { + if (isMultiLine && primary === 'never-multi-line' && checkBefore !== '') { if (context.fix) { hasFixed = true; fixBeforeForNever(valueNode); @@ -96,25 +92,25 @@ function rule(expectation, options, context) { const closingIndex = valueNode.sourceIndex + functionString.length - 2; const checkAfter = getCheckAfter(valueNode); - if (expectation === 'always' && !containsNewline(checkAfter)) { + if (primary === 'always' && !containsNewline(checkAfter)) { if (context.fix) { hasFixed = true; - fixAfterForAlways(valueNode, context.newline); + fixAfterForAlways(valueNode, context.newline || ''); } else { complain(messages.expectedClosing, closingIndex); } } - if (isMultiLine && expectation === 'always-multi-line' && !containsNewline(checkAfter)) { + if (isMultiLine && primary === 'always-multi-line' && !containsNewline(checkAfter)) { if (context.fix) { hasFixed = true; - fixAfterForAlways(valueNode, context.newline); + fixAfterForAlways(valueNode, context.newline || ''); } else { complain(messages.expectedClosingMultiLine, closingIndex); } } - if (isMultiLine && expectation === 'never-multi-line' && checkAfter !== '') { + if (isMultiLine && primary === 'never-multi-line' && checkAfter !== '') { if (context.fix) { hasFixed = true; fixAfterForNever(valueNode); @@ -128,6 +124,10 @@ function rule(expectation, options, context) { setDeclarationValue(decl, parsedValue.toString()); } + /** + * @param {string} message + * @param {number} offset + */ function complain(message, offset) { report({ ruleName, @@ -139,8 +139,13 @@ function rule(expectation, options, context) { } }); }; -} +}; + +/** @typedef {import('postcss-value-parser').FunctionNode} FunctionNode */ +/** + * @param {FunctionNode} valueNode + */ function getCheckBefore(valueNode) { let before = valueNode.before; @@ -160,6 +165,9 @@ function getCheckBefore(valueNode) { return before; } +/** + * @param {FunctionNode} valueNode + */ function getCheckAfter(valueNode) { let after = ''; @@ -181,6 +189,10 @@ function getCheckAfter(valueNode) { return after; } +/** + * @param {FunctionNode} valueNode + * @param {string} newline + */ function fixBeforeForAlways(valueNode, newline) { let target; @@ -204,6 +216,9 @@ function fixBeforeForAlways(valueNode, newline) { } } +/** + * @param {FunctionNode} valueNode + */ function fixBeforeForNever(valueNode) { valueNode.before = ''; @@ -221,10 +236,17 @@ function fixBeforeForNever(valueNode) { } } +/** + * @param {FunctionNode} valueNode + * @param {string} newline + */ function fixAfterForAlways(valueNode, newline) { valueNode.after = newline + valueNode.after; } +/** + * @param {FunctionNode} valueNode + */ function fixAfterForNever(valueNode) { valueNode.after = ''; diff --git a/lib/rules/function-parentheses-space-inside/index.js b/lib/rules/function-parentheses-space-inside/index.js index a7806de753..77ef2c8200 100644 --- a/lib/rules/function-parentheses-space-inside/index.js +++ b/lib/rules/function-parentheses-space-inside/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const declarationValueIndex = require('../../utils/declarationValueIndex'); @@ -25,10 +23,11 @@ const messages = ruleMessages(ruleName, { rejectedClosingSingleLine: 'Unexpected whitespace before ")" in a single-line function', }); -function rule(expectation, options, context) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: expectation, + actual: primary, possible: ['always', 'never', 'always-single-line', 'never-single-line'], }); @@ -66,7 +65,7 @@ function rule(expectation, options, context) { const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1; - if (expectation === 'always' && valueNode.before !== ' ') { + if (primary === 'always' && valueNode.before !== ' ') { if (context.fix) { hasFixed = true; valueNode.before = ' '; @@ -75,7 +74,7 @@ function rule(expectation, options, context) { } } - if (expectation === 'never' && valueNode.before !== '') { + if (primary === 'never' && valueNode.before !== '') { if (context.fix) { hasFixed = true; valueNode.before = ''; @@ -84,7 +83,7 @@ function rule(expectation, options, context) { } } - if (isSingleLine && expectation === 'always-single-line' && valueNode.before !== ' ') { + if (isSingleLine && primary === 'always-single-line' && valueNode.before !== ' ') { if (context.fix) { hasFixed = true; valueNode.before = ' '; @@ -93,7 +92,7 @@ function rule(expectation, options, context) { } } - if (isSingleLine && expectation === 'never-single-line' && valueNode.before !== '') { + if (isSingleLine && primary === 'never-single-line' && valueNode.before !== '') { if (context.fix) { hasFixed = true; valueNode.before = ''; @@ -106,7 +105,7 @@ function rule(expectation, options, context) { const closingIndex = valueNode.sourceIndex + functionString.length - 2; - if (expectation === 'always' && valueNode.after !== ' ') { + if (primary === 'always' && valueNode.after !== ' ') { if (context.fix) { hasFixed = true; valueNode.after = ' '; @@ -115,7 +114,7 @@ function rule(expectation, options, context) { } } - if (expectation === 'never' && valueNode.after !== '') { + if (primary === 'never' && valueNode.after !== '') { if (context.fix) { hasFixed = true; valueNode.after = ''; @@ -124,7 +123,7 @@ function rule(expectation, options, context) { } } - if (isSingleLine && expectation === 'always-single-line' && valueNode.after !== ' ') { + if (isSingleLine && primary === 'always-single-line' && valueNode.after !== ' ') { if (context.fix) { hasFixed = true; valueNode.after = ' '; @@ -133,7 +132,7 @@ function rule(expectation, options, context) { } } - if (isSingleLine && expectation === 'never-single-line' && valueNode.after !== '') { + if (isSingleLine && primary === 'never-single-line' && valueNode.after !== '') { if (context.fix) { hasFixed = true; valueNode.after = ''; @@ -147,6 +146,10 @@ function rule(expectation, options, context) { setDeclarationValue(decl, parsedValue.toString()); } + /** + * @param {string} message + * @param {number} offset + */ function complain(message, offset) { report({ ruleName, @@ -158,7 +161,7 @@ function rule(expectation, options, context) { } }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-url-no-scheme-relative/index.js b/lib/rules/function-url-no-scheme-relative/index.js index 85586f2b82..4b4bd5d166 100644 --- a/lib/rules/function-url-no-scheme-relative/index.js +++ b/lib/rules/function-url-no-scheme-relative/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const functionArgumentsSearch = require('../../utils/functionArgumentsSearch'); @@ -14,9 +12,10 @@ const messages = ruleMessages(ruleName, { rejected: 'Unexpected scheme-relative url', }); -function rule(actual) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary) => { return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }); + const validOptions = validateOptions(result, ruleName, { actual: primary }); if (!validOptions) { return; @@ -40,7 +39,7 @@ function rule(actual) { }); }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-url-quotes/index.js b/lib/rules/function-url-quotes/index.js index cd2beeb03c..a75f903220 100644 --- a/lib/rules/function-url-quotes/index.js +++ b/lib/rules/function-url-quotes/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const atRuleParamIndex = require('../../utils/atRuleParamIndex'); @@ -17,17 +15,18 @@ const messages = ruleMessages(ruleName, { rejected: () => 'Unexpected quotes', }); -function rule(expectation, options) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, secondaryOptions) => { return (root, result) => { const validOptions = validateOptions( result, ruleName, { - actual: expectation, + actual: primary, possible: ['always', 'never'], }, { - actual: options, + actual: secondaryOptions, possible: { except: ['empty'], }, @@ -42,12 +41,18 @@ function rule(expectation, options) { root.walkAtRules(checkAtRuleParams); root.walkDecls(checkDeclParams); + /** + * @param {import('postcss').Declaration} decl + */ function checkDeclParams(decl) { functionArgumentsSearch(decl.toString().toLowerCase(), 'url', (args, index) => { checkArgs(args, decl, index, 'url'); }); } + /** + * @param {import('postcss').AtRule} atRule + */ function checkAtRuleParams(atRule) { const atRuleParamsLowerCase = atRule.params.toLowerCase(); @@ -62,8 +67,14 @@ function rule(expectation, options) { }); } + /** + * @param {string} args + * @param {import('postcss').Node} node + * @param {number} index + * @param {string} functionName + */ function checkArgs(args, node, index, functionName) { - let shouldHasQuotes = expectation === 'always'; + let shouldHasQuotes = primary === 'always'; const leftTrimmedArgs = args.trimStart(); @@ -77,7 +88,7 @@ function rule(expectation, options) { const trimmedArg = args.trim(); const isEmptyArgument = ['', "''", '""'].includes(trimmedArg); - if (optionsMatches(options, 'except', 'empty') && isEmptyArgument) { + if (optionsMatches(secondaryOptions, 'except', 'empty') && isEmptyArgument) { shouldHasQuotes = !shouldHasQuotes; } @@ -86,16 +97,23 @@ function rule(expectation, options) { return; } + // @ts-expect-error -- Should the message include the function name? complain(messages.expected(functionName), node, complaintIndex); } else { if (!hasQuotes) { return; } + // @ts-expect-error -- Should the message include the function name? complain(messages.rejected(functionName), node, complaintIndex); } } + /** + * @param {string} message + * @param {import('postcss').Node} node + * @param {number} index + */ function complain(message, node, index) { report({ message, @@ -106,7 +124,7 @@ function rule(expectation, options) { }); } }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/function-url-scheme-allowed-list/index.js b/lib/rules/function-url-scheme-allowed-list/index.js index 5f9018e1f4..52ca450213 100644 --- a/lib/rules/function-url-scheme-allowed-list/index.js +++ b/lib/rules/function-url-scheme-allowed-list/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const functionArgumentsSearch = require('../../utils/functionArgumentsSearch'); @@ -17,10 +15,11 @@ const messages = ruleMessages(ruleName, { rejected: (scheme) => `Unexpected URL scheme "${scheme}:"`, }); -function rule(list) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: list, + actual: primary, possible: [isString, isRegExp], }); @@ -43,7 +42,7 @@ function rule(list) { return; } - if (matchesStringOrRegExp(scheme, list)) { + if (matchesStringOrRegExp(scheme, primary)) { return; } @@ -57,7 +56,7 @@ function rule(list) { }); }); }; -} +}; rule.primaryOptionArray = true; diff --git a/lib/rules/function-url-scheme-disallowed-list/index.js b/lib/rules/function-url-scheme-disallowed-list/index.js index 0228fb03a1..308f83402e 100644 --- a/lib/rules/function-url-scheme-disallowed-list/index.js +++ b/lib/rules/function-url-scheme-disallowed-list/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const functionArgumentsSearch = require('../../utils/functionArgumentsSearch'); @@ -17,10 +15,11 @@ const messages = ruleMessages(ruleName, { rejected: (scheme) => `Unexpected URL scheme "${scheme}:"`, }); -function rule(list) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: list, + actual: primary, possible: [isString, isRegExp], }); @@ -43,7 +42,7 @@ function rule(list) { return; } - if (!matchesStringOrRegExp(scheme, list)) { + if (!matchesStringOrRegExp(scheme, primary)) { return; } @@ -57,7 +56,7 @@ function rule(list) { }); }); }; -} +}; rule.primaryOptionArray = true; diff --git a/lib/rules/function-whitespace-after/index.js b/lib/rules/function-whitespace-after/index.js index 7048a045d1..44e9db2368 100644 --- a/lib/rules/function-whitespace-after/index.js +++ b/lib/rules/function-whitespace-after/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const atRuleParamIndex = require('../../utils/atRuleParamIndex'); @@ -21,10 +19,11 @@ const messages = ruleMessages(ruleName, { const ACCEPTABLE_AFTER_CLOSING_PAREN = new Set([')', ',', '}', ':', '/', undefined]); -function rule(expectation, options, context) { +/** @type {import('stylelint').StylelintRule} */ +const rule = (primary, _secondaryOptions, context) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { - actual: expectation, + actual: primary, possible: ['always', 'never'], }); @@ -32,7 +31,13 @@ function rule(expectation, options, context) { return; } - function check(node, value, getIndex, fix) { + /** + * @param {import('postcss').Node} node + * @param {string} value + * @param {number} nodeIndex + * @param {((index: number) => void) | undefined} fix + */ + function check(node, value, nodeIndex, fix) { styleSearch( { source: value, @@ -40,15 +45,22 @@ function rule(expectation, options, context) { functionArguments: 'only', }, (match) => { - checkClosingParen(value, match.startIndex + 1, node, getIndex, fix); + checkClosingParen(value, match.startIndex + 1, node, nodeIndex, fix); }, ); } - function checkClosingParen(source, index, node, getIndex, fix) { + /** + * @param {string} source + * @param {number} index + * @param {import('postcss').Node} node + * @param {number} nodeIndex + * @param {((index: number) => void) | undefined} fix + */ + function checkClosingParen(source, index, node, nodeIndex, fix) { const nextChar = source[index]; - if (expectation === 'always') { + if (primary === 'always') { // Allow for the next character to be a single empty space, // another closing parenthesis, a comma, or the end of the value if (nextChar === ' ') { @@ -76,11 +88,11 @@ function rule(expectation, options, context) { report({ message: messages.expected, node, - index: getIndex(node) + index, + index: nodeIndex + index, result, ruleName, }); - } else if (expectation === 'never') { + } else if (primary === 'never') { if (isWhitespace(nextChar)) { if (fix) { fix(index); @@ -91,7 +103,7 @@ function rule(expectation, options, context) { report({ message: messages.rejected, node, - index: getIndex(node) + index, + index: nodeIndex + index, result, ruleName, }); @@ -99,18 +111,22 @@ function rule(expectation, options, context) { } } + /** + * @param {string} value + */ function createFixer(value) { let fixed = ''; let lastIndex = 0; + /** @type {(index: number) => void} */ let applyFix; - if (expectation === 'always') { + if (primary === 'always') { applyFix = (index) => { // eslint-disable-next-line prefer-template fixed += value.slice(lastIndex, index) + ' '; lastIndex = index; }; - } else if (expectation === 'never') { + } else if (primary === 'never') { applyFix = (index) => { let whitespaceEndIndex = index + 1; @@ -121,6 +137,8 @@ function rule(expectation, options, context) { fixed += value.slice(lastIndex, index); lastIndex = whitespaceEndIndex; }; + } else { + throw new Error(`Unexpected option: "${primary}"`); } return { @@ -138,7 +156,7 @@ function rule(expectation, options, context) { const param = (atRule.raws.params && atRule.raws.params.raw) || atRule.params; const fixer = context.fix && createFixer(param); - check(atRule, param, atRuleParamIndex, fixer && fixer.applyFix); + check(atRule, param, atRuleParamIndex(atRule), fixer ? fixer.applyFix : undefined); if (fixer && fixer.hasFixed) { if (atRule.raws.params) { @@ -152,14 +170,14 @@ function rule(expectation, options, context) { const value = getDeclarationValue(decl); const fixer = context.fix && createFixer(value); - check(decl, value, declarationValueIndex, fixer && fixer.applyFix); + check(decl, value, declarationValueIndex(decl), fixer ? fixer.applyFix : undefined); if (fixer && fixer.hasFixed) { setDeclarationValue(decl, fixer.fixed); } }); }; -} +}; rule.ruleName = ruleName; rule.messages = messages; diff --git a/lib/rules/functionCommaSpaceChecker.js b/lib/rules/functionCommaSpaceChecker.js index a4a237035b..2d88c47e29 100644 --- a/lib/rules/functionCommaSpaceChecker.js +++ b/lib/rules/functionCommaSpaceChecker.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const declarationValueIndex = require('../utils/declarationValueIndex'); @@ -9,7 +7,20 @@ const report = require('../utils/report'); const setDeclarationValue = require('../utils/setDeclarationValue'); const valueParser = require('postcss-value-parser'); -module.exports = function (opts) { +/** @typedef {import('postcss-value-parser').Node} ValueParserNode */ +/** @typedef {import('postcss-value-parser').DivNode} ValueParserDivNode */ +/** @typedef {(args: { source: string, index: number, err: (message: string) => void }) => void} LocationChecker */ + +/** + * @param {{ + * root: import('postcss').Root, + * locationChecker: LocationChecker, + * fix: ((node: ValueParserDivNode, index: number, nodes: ValueParserNode[]) => boolean) | null, + * result: import('stylelint').PostcssResult, + * checkedRuleName: string, + * }} opts + */ +module.exports = function functionCommaSpaceChecker(opts) { opts.root.walkDecls((decl) => { const declValue = getDeclarationValue(decl); @@ -45,11 +56,11 @@ module.exports = function (opts) { /** * Gets the index of the comma for checking. - * @param {Node} commaNode The comma node + * @param {ValueParserDivNode} commaNode The comma node * @param {number} nodeIndex The index of the comma node * @returns {number} The index of the comma for checking */ - function getCommaCheckIndex(commaNode, nodeIndex) { + const getCommaCheckIndex = (commaNode, nodeIndex) => { let commaBefore = valueNode.before + argumentStrings.slice(0, nodeIndex).join('') + commaNode.before; @@ -58,8 +69,9 @@ module.exports = function (opts) { commaBefore = commaBefore.replace(/( *\/(\*.*\*\/(?!\S)|\/.*)|(\/(\*.*\*\/|\/.*)))/, ''); return commaBefore.length; - } + }; + /** @type {{ commaNode: ValueParserDivNode, checkIndex: number, nodeIndex: number }[]} */ const commaDataList = []; valueNode.nodes.forEach((node, nodeIndex) => { diff --git a/lib/rules/functionCommaSpaceFix.js b/lib/rules/functionCommaSpaceFix.js index d586edf4c9..aefb9a6a64 100644 --- a/lib/rules/functionCommaSpaceFix.js +++ b/lib/rules/functionCommaSpaceFix.js @@ -1,8 +1,17 @@ -// @ts-nocheck - 'use strict'; -module.exports = function (params) { +/** + * @param {{ + * div: import('postcss-value-parser').DivNode, + * index: number, + * nodes: import('postcss-value-parser').Node[], + * expectation: string, + * position: 'before' | 'after', + * symb: string, + * }} params + * @returns {boolean} + */ +module.exports = function functionCommaSpaceFix(params) { const { div, index, nodes, expectation, position, symb } = params; if (expectation.startsWith('always')) { @@ -31,4 +40,6 @@ module.exports = function (params) { return true; } + + return false; }; From 5bbae44bf8972bec5009b46d06f6602fb6ec38b8 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 20 Aug 2021 22:11:57 +0900 Subject: [PATCH 2/2] Remove @ts-expect-error --- lib/rules/function-url-quotes/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/rules/function-url-quotes/index.js b/lib/rules/function-url-quotes/index.js index 65b5f931f2..2e5c0d28c0 100644 --- a/lib/rules/function-url-quotes/index.js +++ b/lib/rules/function-url-quotes/index.js @@ -97,14 +97,12 @@ const rule = (primary, secondaryOptions) => { return; } - // @ts-expect-error -- Should the message include the function name? complain(messages.expected(functionName), node, complaintIndex); } else { if (!hasQuotes) { return; } - // @ts-expect-error -- Should the message include the function name? complain(messages.rejected(functionName), node, complaintIndex); } }