Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to improve types for no-extra-semicolons rule #5768

Merged
merged 1 commit into from Dec 20, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 47 additions & 18 deletions lib/rules/no-extra-semicolons/index.js
@@ -1,5 +1,3 @@
// @ts-nocheck

'use strict';

const isStandardSyntaxAtRule = require('../../utils/isStandardSyntaxAtRule');
Expand All @@ -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<ChildNode>'
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;
Expand All @@ -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) => {
Expand All @@ -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);
});

Expand All @@ -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) => {
Expand All @@ -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) => {
Expand All @@ -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) => {
Expand Down Expand Up @@ -182,6 +203,9 @@ function rule(actual, options, context) {
}
});

/**
* @param {number} index
*/
function complain(index) {
report({
message: messages.rejected,
Expand All @@ -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);
Expand All @@ -200,7 +229,7 @@ function rule(actual, options, context) {
return str;
}
};
}
};

rule.ruleName = ruleName;
rule.messages = messages;
Expand Down