From b9785cd3a2312abd73626c1bf31a879e137dc148 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Tue, 21 Dec 2021 03:23:31 +0900 Subject: [PATCH] Refactor to improve types for `no-extra-semicolons` rule (#5768) - Remove `// @ts-nocheck` to enable type-checking. - Narrow the scope of some local variables. --- lib/rules/no-extra-semicolons/index.js | 65 +++++++++++++++++++------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/lib/rules/no-extra-semicolons/index.js b/lib/rules/no-extra-semicolons/index.js index 668b4428e5..341237d525 100644 --- a/lib/rules/no-extra-semicolons/index.js +++ b/lib/rules/no-extra-semicolons/index.js @@ -1,5 +1,3 @@ -// @ts-nocheck - 'use strict'; const isStandardSyntaxAtRule = require('../../utils/isStandardSyntaxAtRule'); @@ -15,12 +13,25 @@ const messages = ruleMessages(ruleName, { rejected: 'Unexpected extra semicolon', }); +/** + * @param {import('postcss').Node} node + * @returns {number} + */ function getOffsetByNode(node) { + // @ts-expect-error -- TS2339: Property 'document' does not exist on type 'Document | Container' if (node.parent && node.parent.document) { return 0; } - const string = node.root().source.input.css; + const root = node.root(); + + if (!root.source) throw new Error('The root node must have a source'); + + if (!node.source) throw new Error('The node must have a source'); + + if (!node.source.start) throw new Error('The source must have a start position'); + + const string = root.source.input.css; const nodeColumn = node.source.start.column; const nodeLine = node.source.start.line; let line = 1; @@ -44,17 +55,19 @@ function getOffsetByNode(node) { return index; } -function rule(actual, options, context) { +/** @type {import('stylelint').Rule} */ +const rule = (primary, _secondaryOptions, context) => { return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }); + const validOptions = validateOptions(result, ruleName, { actual: primary }); if (!validOptions) { return; } - const rawAfterRoot = root.raws.after; + if (root.raws.after && root.raws.after.trim().length !== 0) { + const rawAfterRoot = root.raws.after; - if (rawAfterRoot && rawAfterRoot.trim().length !== 0) { + /** @type {number[]} */ const fixSemiIndices = []; styleSearch({ source: rawAfterRoot, target: ';' }, (match) => { @@ -64,6 +77,8 @@ function rule(actual, options, context) { return; } + if (!root.source) throw new Error('The root node must have a source'); + complain(root.source.input.css.length - rawAfterRoot.length + match.startIndex); }); @@ -82,13 +97,13 @@ function rule(actual, options, context) { return; } - const rawBeforeNode = node.raws.before; - - if (rawBeforeNode && rawBeforeNode.trim().length !== 0) { + if (node.raws.before && node.raws.before.trim().length !== 0) { + const rawBeforeNode = node.raws.before; const allowedSemi = 0; const rawBeforeIndexStart = 0; + /** @type {number[]} */ const fixSemiIndices = []; styleSearch({ source: rawBeforeNode, target: ';' }, (match, count) => { @@ -107,22 +122,28 @@ function rule(actual, options, context) { // fix if (fixSemiIndices.length) { - node.raws.before = removeIndices(node.raws.before, fixSemiIndices); + node.raws.before = removeIndices(rawBeforeNode, fixSemiIndices); } } - const rawAfterNode = node.raws.after; + if ('after' in node.raws && node.raws.after && node.raws.after.trim().length !== 0) { + const rawAfterNode = node.raws.after; - if (rawAfterNode && rawAfterNode.trim().length !== 0) { /** * If the last child is a Less mixin followed by more than one semicolon, * node.raws.after will be populated with that semicolon. * Since we ignore Less mixins, exit here */ - if (node.last && node.last.type === 'atrule' && !isStandardSyntaxAtRule(node.last)) { + if ( + 'last' in node && + node.last && + node.last.type === 'atrule' && + !isStandardSyntaxAtRule(node.last) + ) { return; } + /** @type {number[]} */ const fixSemiIndices = []; styleSearch({ source: rawAfterNode, target: ';' }, (match) => { @@ -148,11 +169,11 @@ function rule(actual, options, context) { } } - const rawOwnSemicolon = node.raws.ownSemicolon; - - if (rawOwnSemicolon) { + if ('ownSemicolon' in node.raws && node.raws.ownSemicolon) { + const rawOwnSemicolon = node.raws.ownSemicolon; const allowedSemi = 0; + /** @type {number[]} */ const fixSemiIndices = []; styleSearch({ source: rawOwnSemicolon, target: ';' }, (match, count) => { @@ -182,6 +203,9 @@ function rule(actual, options, context) { } }); + /** + * @param {number} index + */ function complain(index) { report({ message: messages.rejected, @@ -192,6 +216,11 @@ function rule(actual, options, context) { }); } + /** + * @param {string} str + * @param {number[]} indices + * @returns {string} + */ function removeIndices(str, indices) { for (const index of indices.reverse()) { str = str.slice(0, index) + str.slice(index + 1); @@ -200,7 +229,7 @@ function rule(actual, options, context) { return str; } }; -} +}; rule.ruleName = ruleName; rule.messages = messages;