core/stylelint/no-custom-property-alias.stylelint.rule.ts
core/stylelint/no-custom-property-alias.stylelint.rule.ts is a file in GovLab Quality. 53 lines of code and 12 definitions.
import type { Declaration, Root } from "postcss";
import stylelint, { type PostcssResult, type Rule } from "stylelint";
import type { GovlabStylelintMeta } from "#types/stylelint.types";
import valueParser from "postcss-value-parser";
import { withRuleId } from "#core/formatters/stylelint.formatter";
const { createPlugin, utils } = stylelint;
const ruleName = "govlab/no-custom-property-alias";
const ruleId = "no_custom_property_alias";
const ROOT_SELECTOR = ":root";
export const RULE_META: GovlabStylelintMeta = {
meta: {
canonical: ["design-tokens"],
description:
"Forbid root-level custom properties that alias another variable (--a: var(--b)); a scoped re-binding to a root token is the sanctioned form",
fixable: false,
},
ruleId,
ruleName,
};
const messages = utils.ruleMessages(ruleName, {
alias: (name: string): string =>
withRuleId(
`Root variable '${name}' is a bare alias of another variable. A root token holds its literal value; only a scoped selector may re-bind a property to a root token.`,
ruleId,
),
});
const isSoleVarReference = function isSoleVarReference(value: string): boolean {
const nodes = valueParser(value).nodes.filter((node) => node.type !== "space" && node.type !== "div");
const [first] = nodes;
return nodes.length === 1 && first?.type === "function" && first.value === "var";
};
const isRootDeclaration = function isRootDeclaration(decl: Declaration): boolean {
const { parent } = decl;
if (parent?.type !== "rule" || !("selector" in parent)) {
return false;
}
return typeof parent.selector === "string" && parent.selector.trim() === ROOT_SELECTOR;
};
const rule: Rule = Object.assign(
(primary: unknown) =>
(root: Root, result: PostcssResult): void => {
if (primary !== true) {
return;
}
root.walkDecls((decl: Declaration) => {
if (decl.prop.startsWith("--") && isRootDeclaration(decl) && isSoleVarReference(decl.value)) {
utils.report({ message: messages.alias(decl.prop), node: decl, result, ruleName, word: decl.prop });
}
});
},
{ messages, ruleName },
);
export default createPlugin(ruleName, rule);