stylelint-plugin-logical-css
Version:
A Stylelint plugin to enforce the use of logical CSS properties, values and units.
129 lines (106 loc) • 3.66 kB
JavaScript
import stylelint from 'stylelint';
import { ruleName, ruleMessages, ruleMeta } from './base.js';
import { vendorPrefixes } from '../../utils/vendorPrefixes.js';
import { physicalProperties } from '../../utils/physical.js';
import { isPhysicalProperty } from '../../utils/isPhysicalProperty.js';
import { isPhysicalValue } from '../../utils/isPhysicalValue.js';
import { physicalPropertiesMap } from '../../utils/physicalPropertiesMap.js';
import { physicalValuesMap } from '../../utils/physicalValuesMap.js';
const ruleFunction = (_, options, context) => {
return (root, result) => {
const validOptions = stylelint.utils.validateOptions(result, ruleName);
if (!validOptions) {
return;
}
root.walkDecls((decl) => {
let rootProp = decl.prop;
if (
Array.isArray(options?.ignore) &&
options?.ignore.includes(rootProp)
) {
return;
}
vendorPrefixes.forEach(
(prefix) => (rootProp = rootProp.replace(prefix, '')),
);
const isValidProp = [
...Object.values(physicalProperties),
'transition',
].includes(rootProp);
if (!isValidProp) return;
const isCaptionSideProperty = rootProp === 'caption-side';
const isBlockAxisCaptionSideValue =
isCaptionSideProperty && ['top', 'bottom'].includes(decl.value);
const isTransitionProperty = rootProp === 'transition';
const physicalTransitionProperties =
isTransitionProperty &&
Object.values(physicalProperties)
.filter((property) => !(options?.ignore || []).includes(property))
.flatMap((property) => {
const exp = new RegExp(`(^|[^\\w-])${property}([^\\w-]|$)`);
return decl.value.match(exp);
})
.filter((p) => p && p.trim());
const propIsPhysical = isPhysicalProperty(decl.prop);
const valueIsPhysical = isPhysicalValue(decl.value);
if (
(!propIsPhysical &&
!valueIsPhysical &&
!physicalTransitionProperties.length) ||
isBlockAxisCaptionSideValue
) {
return;
}
let message;
if (propIsPhysical) {
message = ruleMessages.unexpectedProp(
decl.prop,
physicalPropertiesMap[rootProp],
);
}
if (valueIsPhysical) {
message = ruleMessages.unexpectedValue(
decl.prop,
decl.value,
physicalValuesMap[rootProp][decl.value],
);
}
if (physicalTransitionProperties.length) {
const propertyToFlag = physicalTransitionProperties[0].trim();
message = ruleMessages.unexpectedTransitionValue(
propertyToFlag,
physicalPropertiesMap[propertyToFlag],
);
}
if (context.fix) {
if (propIsPhysical) {
decl.prop = physicalPropertiesMap[rootProp];
}
if (valueIsPhysical) {
decl.value = physicalValuesMap[rootProp][decl.value];
}
if (physicalTransitionProperties.length) {
let newValue = decl.value;
physicalTransitionProperties.forEach((property) => {
newValue = newValue.replace(
property.trim(),
physicalPropertiesMap[property.trim()],
);
});
decl.value = newValue;
}
return;
}
stylelint.utils.report({
message,
node: decl,
result,
ruleName,
});
});
};
};
ruleFunction.ruleName = ruleName;
ruleFunction.messages = ruleMessages;
ruleFunction.meta = ruleMeta;
export default stylelint.createPlugin(ruleName, ruleFunction);