Skip to content

Commit

Permalink
Refactor to improve types for declaration-* rules
Browse files Browse the repository at this point in the history
This change removes  `// @ts-nocheck` from the remaining `declaration-*` rules.
Also, this adds 2 type parameters to the `StylelintRule` type to pass the type-check.
  • Loading branch information
ybiquitous committed Aug 18, 2021
1 parent 578fb89 commit dddf856
Show file tree
Hide file tree
Showing 14 changed files with 126 additions and 80 deletions.
2 changes: 2 additions & 0 deletions lib/rules/declaration-bang-space-after/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ const rule = (primary, _secondaryOptions, context) => {

return true;
}

return false;
}
: null,
});
Expand Down
2 changes: 2 additions & 0 deletions lib/rules/declaration-bang-space-before/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ const rule = (primary, _secondaryOptions, context) => {

return true;
}

return false;
}
: null,
});
Expand Down
14 changes: 8 additions & 6 deletions lib/rules/declaration-colon-newline-after/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-nocheck

'use strict';

const declarationValueIndex = require('../../utils/declarationValueIndex');
Expand All @@ -16,12 +14,13 @@ const messages = ruleMessages(ruleName, {
expectedAfterMultiLine: () => 'Expected newline after ":" with a multi-line declaration',
});

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'],
});

Expand Down Expand Up @@ -57,6 +56,9 @@ function rule(expectation, options, context) {
err: (m) => {
if (context.fix) {
const between = decl.raws.between;

if (between == null) throw new Error('`between` must be present');

const betweenStart = declarationValueIndex(decl) - between.length;
const sliceIndex = indexToCheck - betweenStart + 1;
const betweenBefore = between.slice(0, sliceIndex);
Expand All @@ -83,7 +85,7 @@ function rule(expectation, options, context) {
}
});
};
}
};

rule.ruleName = ruleName;
rule.messages = messages;
Expand Down
19 changes: 11 additions & 8 deletions lib/rules/declaration-colon-space-after/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-nocheck

'use strict';

const declarationColonSpaceChecker = require('../declarationColonSpaceChecker');
Expand All @@ -16,12 +14,13 @@ const messages = ruleMessages(ruleName, {
expectedAfterSingleLine: () => 'Expected single space after ":" with a single-line declaration',
});

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'],
});

Expand All @@ -39,24 +38,28 @@ function rule(expectation, options, context) {
const colonIndex = index - declarationValueIndex(decl);
const between = decl.raws.between;

if (expectation.startsWith('always')) {
if (between == null) throw new Error('`between` must be present');

if (primary.startsWith('always')) {
decl.raws.between =
between.slice(0, colonIndex) + between.slice(colonIndex).replace(/^:\s*/, ': ');

return true;
}

if (expectation === 'never') {
if (primary === 'never') {
decl.raws.between =
between.slice(0, colonIndex) + between.slice(colonIndex).replace(/^:\s*/, ':');

return true;
}

return false;
}
: null,
});
};
}
};

rule.ruleName = ruleName;
rule.messages = messages;
Expand Down
19 changes: 11 additions & 8 deletions lib/rules/declaration-colon-space-before/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-nocheck

'use strict';

const declarationColonSpaceChecker = require('../declarationColonSpaceChecker');
Expand All @@ -15,12 +13,13 @@ const messages = ruleMessages(ruleName, {
rejectedBefore: () => 'Unexpected whitespace before ":"',
});

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'],
});

Expand All @@ -38,24 +37,28 @@ function rule(expectation, options, context) {
const colonIndex = index - declarationValueIndex(decl);
const between = decl.raws.between;

if (expectation === 'always') {
if (between == null) throw new Error('`between` must be present');

if (primary === 'always') {
decl.raws.between =
between.slice(0, colonIndex).replace(/\s*$/, ' ') + between.slice(colonIndex);

return true;
}

if (expectation === 'never') {
if (primary === 'never') {
decl.raws.between =
between.slice(0, colonIndex).replace(/\s*$/, '') + between.slice(colonIndex);

return true;
}

return false;
}
: null,
});
};
}
};

rule.ruleName = ruleName;
rule.messages = messages;
Expand Down
38 changes: 24 additions & 14 deletions lib/rules/declaration-empty-line-before/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-nocheck

'use strict';

const addEmptyLineBefore = require('../../utils/addEmptyLineBefore');
Expand All @@ -17,6 +15,7 @@ const removeEmptyLinesBefore = require('../../utils/removeEmptyLinesBefore');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const { isAtRule, isRule } = require('../../utils/typeGuards');

const ruleName = 'declaration-empty-line-before';

Expand All @@ -25,17 +24,18 @@ const messages = ruleMessages(ruleName, {
rejected: 'Unexpected empty line before declaration',
});

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'],
},
{
actual: options,
actual: secondaryOptions,
possible: {
except: ['first-nested', 'after-comment', 'after-declaration'],
ignore: [
Expand All @@ -57,11 +57,19 @@ function rule(expectation, options, context) {
const prop = decl.prop;
const parent = decl.parent;

if (parent == null) {
return;
}

// Ignore the first node
if (isFirstNodeOfRoot(decl)) {
return;
}

if (!isAtRule(parent) && !isRule(parent)) {
return;
}

if (!isStandardSyntaxDeclaration(decl)) {
return;
}
Expand All @@ -71,38 +79,38 @@ function rule(expectation, options, context) {
}

// Optionally ignore the node if a comment precedes it
if (optionsMatches(options, 'ignore', 'after-comment') && isAfterComment(decl)) {
if (optionsMatches(secondaryOptions, 'ignore', 'after-comment') && isAfterComment(decl)) {
return;
}

// Optionally ignore the node if a declaration precedes it
if (
optionsMatches(options, 'ignore', 'after-declaration') &&
optionsMatches(secondaryOptions, 'ignore', 'after-declaration') &&
isAfterStandardPropertyDeclaration(decl)
) {
return;
}

// Optionally ignore the node if it is the first nested
if (optionsMatches(options, 'ignore', 'first-nested') && isFirstNested(decl)) {
if (optionsMatches(secondaryOptions, 'ignore', 'first-nested') && isFirstNested(decl)) {
return;
}

// Optionally ignore nodes inside single-line blocks
if (
optionsMatches(options, 'ignore', 'inside-single-line-block') &&
optionsMatches(secondaryOptions, 'ignore', 'inside-single-line-block') &&
isSingleLineString(blockString(parent))
) {
return;
}

let expectEmptyLineBefore = expectation === 'always';
let expectEmptyLineBefore = primary === 'always';

// Optionally reverse the expectation if any exceptions apply
if (
(optionsMatches(options, 'except', 'first-nested') && isFirstNested(decl)) ||
(optionsMatches(options, 'except', 'after-comment') && isAfterComment(decl)) ||
(optionsMatches(options, 'except', 'after-declaration') &&
(optionsMatches(secondaryOptions, 'except', 'first-nested') && isFirstNested(decl)) ||
(optionsMatches(secondaryOptions, 'except', 'after-comment') && isAfterComment(decl)) ||
(optionsMatches(secondaryOptions, 'except', 'after-declaration') &&
isAfterStandardPropertyDeclaration(decl))
) {
expectEmptyLineBefore = !expectEmptyLineBefore;
Expand All @@ -118,6 +126,8 @@ function rule(expectation, options, context) {

// Fix
if (context.fix) {
if (context.newline == null) return;

if (expectEmptyLineBefore) {
addEmptyLineBefore(decl, context.newline);
} else {
Expand All @@ -132,7 +142,7 @@ function rule(expectation, options, context) {
report({ message, node: decl, result, ruleName });
});
};
}
};

rule.ruleName = ruleName;
rule.messages = messages;
Expand Down
9 changes: 4 additions & 5 deletions lib/rules/declaration-no-important/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-nocheck

'use strict';

const report = require('../../utils/report');
Expand All @@ -12,9 +10,10 @@ const messages = ruleMessages(ruleName, {
rejected: 'Unexpected !important',
});

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;
Expand All @@ -34,7 +33,7 @@ function rule(actual) {
});
});
};
}
};

rule.ruleName = ruleName;
rule.messages = messages;
Expand Down
22 changes: 13 additions & 9 deletions lib/rules/declaration-property-unit-allowed-list/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-nocheck

'use strict';

const declarationValueIndex = require('../../utils/declarationValueIndex');
Expand All @@ -19,17 +17,18 @@ const messages = ruleMessages(ruleName, {
rejected: (property, unit) => `Unexpected unit "${unit}" for property "${property}"`,
});

function rule(list, options) {
/** @type {import('stylelint').StylelintRule<Record<string, string[]>>} */
const rule = (primary, secondaryOptions) => {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: list,
actual: primary,
possible: [isPlainObject],
},
{
actual: options,
actual: secondaryOptions,
possible: {
ignore: ['inside-function'],
},
Expand All @@ -47,10 +46,15 @@ function rule(list, options) {

const unprefixedProp = vendor.unprefixed(prop);

const propKey = Object.keys(list).find((propIdentifier) =>
const propKey = Object.keys(primary).find((propIdentifier) =>
matchesStringOrRegExp(unprefixedProp, propIdentifier),
);
const propList = list[propKey];

if (!propKey) {
return;
}

const propList = primary[propKey];

if (!propList) {
return;
Expand All @@ -63,7 +67,7 @@ function rule(list, options) {
return false;
}

if (optionsMatches(options, 'ignore', 'inside-function')) {
if (optionsMatches(secondaryOptions, 'ignore', 'inside-function')) {
return false;
}
}
Expand All @@ -88,7 +92,7 @@ function rule(list, options) {
});
});
};
}
};

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

0 comments on commit dddf856

Please sign in to comment.