@ec0lint/plugin-css
Version:
ec0lint plugin that provides rules to verify CSS definition objects
147 lines (146 loc) • 6.59 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../utils");
const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
const regexp_1 = require("../utils/regexp");
const casing_1 = require("../utils/casing");
const resource_1 = require("../utils/resource");
exports.default = (0, utils_1.createRule)("no-length-zero-unit", {
meta: {
docs: {
description: "disallow units for zero lengths",
category: "Best Practices",
recommended: false,
standard: true,
stylelint: "length-zero-no-unit",
},
schema: [
{
type: "object",
properties: {
ignoreProperties: {
type: "array",
items: {
type: "string",
},
uniqueItems: true,
minItems: 1,
},
ignoreFunctions: {
type: "array",
items: {
type: "string",
},
uniqueItems: true,
minItems: 1,
},
ignoreCustomProperties: { type: "boolean" },
},
additionalProperties: false,
},
],
fixable: "code",
messages: {
unexpected: "Unexpected unit.",
},
type: "suggestion",
},
create(context) {
var _a, _b, _c, _d, _e;
const ignoreFunctions = [
...((_b = (_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.ignoreFunctions) !== null && _b !== void 0 ? _b : []),
].map(regexp_1.toRegExp);
const ignoreProperties = [
...((_d = (_c = context.options[0]) === null || _c === void 0 ? void 0 : _c.ignoreProperties) !== null && _d !== void 0 ? _d : []),
].map(regexp_1.toRegExp);
const ignoreCustomProperties = Boolean((_e = context.options[0]) === null || _e === void 0 ? void 0 : _e.ignoreCustomProperties);
function createVisitor(cssContext) {
function ignorePropName(name) {
return (name === "line-height" ||
name === "flex" ||
ignoreProperties.some((r) => r.test(name)) ||
(ignoreCustomProperties && name.startsWith("--")));
}
return {
onProperty(property) {
const prop = property.getName();
if (!prop ||
ignorePropName(prop.name) ||
((0, casing_1.isCamelCase)(prop.name) &&
ignorePropName((0, casing_1.kebabCase)(prop.name)))) {
return;
}
const value = property.getValue();
if (!value) {
return;
}
const parsedValue = value.parsed;
parsedValue.walk((valueNode, valueNodeIndex, valueNodes) => {
if (prop.name === "font" &&
valueNodeIndex > 0 &&
valueNodes[valueNodeIndex - 1].type === "div" &&
valueNodes[valueNodeIndex - 1].value === "/")
return undefined;
const { value: textValue, sourceIndex } = valueNode;
if (isMathFunction(valueNode))
return false;
if (valueNode.type === "function" &&
ignoreFunctions.some((r) => r.test(valueNode.value)))
return false;
if (valueNode.type !== "word")
return undefined;
const numberUnit = postcss_value_parser_1.default.unit(textValue);
if (numberUnit === false)
return undefined;
const { number, unit } = numberUnit;
if (unit === "" ||
!isLengthUnit(unit) ||
unit.toLowerCase() === "fr" ||
Number(number) !== 0)
return undefined;
const sourceCode = context.getSourceCode();
const startIndex = value.expression.range[0] +
sourceIndex +
number.length +
1;
const endIndex = startIndex + unit.length;
const loc = value.directExpression
? {
start: sourceCode.getLocFromIndex(startIndex),
end: sourceCode.getLocFromIndex(endIndex),
}
: undefined;
context.report({
node: value.expression,
loc,
messageId: "unexpected",
fix(fixer) {
if (cssContext.isFixable(value.directExpression) &&
sourceCode.text.slice(startIndex, endIndex) === unit) {
return fixer.removeRange([
startIndex,
endIndex,
]);
}
return null;
},
});
return undefined;
});
},
};
}
return (0, utils_1.defineCSSVisitor)(context, {
createVisitor,
});
},
});
function isMathFunction(node) {
return (node.type === "function" && resource_1.MATH_FUNCTIONS.has(node.value.toLowerCase()));
}
function isLengthUnit(unit) {
return resource_1.LENGTH_UNITS.has(unit.toLowerCase());
}