269 lines
11 KiB
JavaScript
269 lines
11 KiB
JavaScript
//-----------------------------------------------------------------------------
|
|
// Type Definitions
|
|
//-----------------------------------------------------------------------------
|
|
/**
|
|
* @import { CSSRuleDefinition } from "../types.js"
|
|
* @typedef {"notLogicalProperty" | "notLogicalValue" | "notLogicalUnit" | "replaceWithLogicalProperty" | "replaceWithLogicalValue" | "replaceWithLogicalUnit"} PreferLogicalPropertiesMessageIds
|
|
* @typedef {[{
|
|
* allowProperties?: string[],
|
|
* allowUnits?: string[]
|
|
* }]} PreferLogicalPropertiesOptions
|
|
* @typedef {CSSRuleDefinition<{ RuleOptions: PreferLogicalPropertiesOptions, MessageIds: PreferLogicalPropertiesMessageIds }>} PreferLogicalPropertiesRuleDefinition
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
// Helpers
|
|
//-----------------------------------------------------------------------------
|
|
const propertiesReplacements = new Map([
|
|
["bottom", "inset-block-end"],
|
|
["border-bottom", "border-block-end"],
|
|
["border-bottom-color", "border-block-end-color"],
|
|
["border-bottom-left-radius", "border-end-start-radius"],
|
|
["border-bottom-right-radius", "border-end-end-radius"],
|
|
["border-bottom-style", "border-block-end-style"],
|
|
["border-bottom-width", "border-block-end-width"],
|
|
["border-left", "border-inline-start"],
|
|
["border-left-color", "border-inline-start-color"],
|
|
["border-left-style", "border-inline-start-style"],
|
|
["border-left-width", "border-inline-start-width"],
|
|
["border-right", "border-inline-end"],
|
|
["border-right-color", "border-inline-end-color"],
|
|
["border-right-style", "border-inline-end-style"],
|
|
["border-right-width", "border-inline-end-width"],
|
|
["border-top", "border-block-start"],
|
|
["border-top-color", "border-block-start-color"],
|
|
["border-top-left-radius", "border-start-start-radius"],
|
|
["border-top-right-radius", "border-start-end-radius"],
|
|
["border-top-style", "border-block-start-style"],
|
|
["border-top-width", "border-block-start-width"],
|
|
["contain-intrinsic-height", "contain-intrinsic-block-size"],
|
|
["contain-intrinsic-width", "contain-intrinsic-inline-size"],
|
|
["height", "block-size"],
|
|
["left", "inset-inline-start"],
|
|
["margin-bottom", "margin-block-end"],
|
|
["margin-left", "margin-inline-start"],
|
|
["margin-right", "margin-inline-end"],
|
|
["margin-top", "margin-block-start"],
|
|
["max-height", "max-block-size"],
|
|
["max-width", "max-inline-size"],
|
|
["min-height", "min-block-size"],
|
|
["min-width", "min-inline-size"],
|
|
["overflow-x", "overflow-inline"],
|
|
["overflow-y", "overflow-block"],
|
|
["overscroll-behavior-x", "overscroll-behavior-inline"],
|
|
["overscroll-behavior-y", "overscroll-behavior-block"],
|
|
["padding-bottom", "padding-block-end"],
|
|
["padding-left", "padding-inline-start"],
|
|
["padding-right", "padding-inline-end"],
|
|
["padding-top", "padding-block-start"],
|
|
["right", "inset-inline-end"],
|
|
["scroll-margin-bottom", "scroll-margin-block-end"],
|
|
["scroll-margin-left", "scroll-margin-inline-start"],
|
|
["scroll-margin-right", "scroll-margin-inline-end"],
|
|
["scroll-margin-top", "scroll-margin-block-start"],
|
|
["scroll-padding-bottom", "scroll-padding-block-end"],
|
|
["scroll-padding-left", "scroll-padding-inline-start"],
|
|
["scroll-padding-right", "scroll-padding-inline-end"],
|
|
["scroll-padding-top", "scroll-padding-block-start"],
|
|
["top", "inset-block-start"],
|
|
["width", "inline-size"],
|
|
]);
|
|
const propertyValuesReplacements = new Map([
|
|
[
|
|
"text-align",
|
|
{
|
|
left: "start",
|
|
right: "end",
|
|
},
|
|
],
|
|
[
|
|
"resize",
|
|
{
|
|
horizontal: "inline",
|
|
vertical: "block",
|
|
},
|
|
],
|
|
[
|
|
"caption-side",
|
|
{
|
|
left: "inline-start",
|
|
right: "inline-end",
|
|
},
|
|
],
|
|
[
|
|
"box-orient",
|
|
{
|
|
horizontal: "inline-axis",
|
|
vertical: "block-axis",
|
|
},
|
|
],
|
|
[
|
|
"float",
|
|
{
|
|
left: "inline-start",
|
|
right: "inline-end",
|
|
},
|
|
],
|
|
[
|
|
"clear",
|
|
{
|
|
left: "inline-start",
|
|
right: "inline-end",
|
|
},
|
|
],
|
|
]);
|
|
const unitReplacements = new Map([
|
|
["cqh", "cqb"],
|
|
["cqw", "cqi"],
|
|
["dvh", "dvb"],
|
|
["dvw", "dvi"],
|
|
["lvh", "lvb"],
|
|
["lvw", "lvi"],
|
|
["svh", "svb"],
|
|
["svw", "svi"],
|
|
["vh", "vb"],
|
|
["vw", "vi"],
|
|
]);
|
|
//-----------------------------------------------------------------------------
|
|
// Rule Definition
|
|
//-----------------------------------------------------------------------------
|
|
export default /** @satisfies {PreferLogicalPropertiesRuleDefinition} */ ({
|
|
meta: {
|
|
type: "problem",
|
|
hasSuggestions: true,
|
|
docs: {
|
|
description: "Enforce the use of logical properties",
|
|
url: "https://github.com/eslint/css/blob/main/docs/rules/prefer-logical-properties.md",
|
|
},
|
|
schema: [
|
|
{
|
|
type: "object",
|
|
properties: {
|
|
allowProperties: {
|
|
type: "array",
|
|
items: {
|
|
enum: Array.from(propertiesReplacements.keys()),
|
|
},
|
|
uniqueItems: true,
|
|
},
|
|
allowUnits: {
|
|
type: "array",
|
|
items: {
|
|
enum: Array.from(unitReplacements.keys()),
|
|
},
|
|
uniqueItems: true,
|
|
},
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
defaultOptions: [
|
|
{
|
|
allowProperties: [],
|
|
allowUnits: [],
|
|
},
|
|
],
|
|
messages: {
|
|
notLogicalProperty: "Expected logical property '{{replacement}}' instead of '{{property}}'.",
|
|
notLogicalValue: "Expected logical value '{{replacement}}' instead of '{{value}}'.",
|
|
notLogicalUnit: "Expected logical unit '{{replacement}}' instead of '{{unit}}'.",
|
|
replaceWithLogicalProperty: "Replace '{{property}}' with logical property '{{replacement}}'.",
|
|
replaceWithLogicalValue: "Replace '{{value}}' with logical value '{{replacement}}'.",
|
|
replaceWithLogicalUnit: "Replace '{{unit}}' with logical unit '{{replacement}}'.",
|
|
},
|
|
},
|
|
create(context) {
|
|
const [{ allowProperties, allowUnits }] = context.options;
|
|
return {
|
|
Declaration(node) {
|
|
const parent = context.sourceCode.getParent(node);
|
|
if (parent.type === "SupportsDeclaration") {
|
|
return;
|
|
}
|
|
const propertyReplacement = propertiesReplacements.get(node.property);
|
|
if (propertyReplacement &&
|
|
!allowProperties.includes(node.property)) {
|
|
context.report({
|
|
loc: node.loc,
|
|
messageId: "notLogicalProperty",
|
|
data: {
|
|
property: node.property,
|
|
replacement: propertyReplacement,
|
|
},
|
|
suggest: [
|
|
{
|
|
messageId: "replaceWithLogicalProperty",
|
|
data: {
|
|
property: node.property,
|
|
replacement: propertyReplacement,
|
|
},
|
|
fix(fixer) {
|
|
return fixer.replaceTextRange([
|
|
node.loc.start.offset,
|
|
node.loc.start.offset +
|
|
node.property.length,
|
|
], propertyReplacement);
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
const valueReplacements = propertyValuesReplacements.get(node.property);
|
|
if (valueReplacements &&
|
|
node.value.type === "Value" &&
|
|
node.value.children[0].type === "Identifier") {
|
|
const identifier = node.value.children[0];
|
|
const nodeValue = identifier.name;
|
|
const valueReplacement = valueReplacements[nodeValue];
|
|
if (valueReplacement) {
|
|
context.report({
|
|
loc: identifier.loc,
|
|
messageId: "notLogicalValue",
|
|
data: {
|
|
value: nodeValue,
|
|
replacement: valueReplacement,
|
|
},
|
|
suggest: [
|
|
{
|
|
messageId: "replaceWithLogicalValue",
|
|
data: {
|
|
value: nodeValue,
|
|
replacement: valueReplacement,
|
|
},
|
|
fix(fixer) {
|
|
return fixer.replaceText(identifier, valueReplacement);
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
}
|
|
},
|
|
Dimension(node) {
|
|
const unitReplacement = unitReplacements.get(node.unit);
|
|
if (unitReplacement && !allowUnits.includes(node.unit)) {
|
|
context.report({
|
|
loc: node.loc,
|
|
messageId: "notLogicalUnit",
|
|
data: {
|
|
unit: node.unit,
|
|
replacement: unitReplacement,
|
|
},
|
|
suggest: [
|
|
{
|
|
messageId: "replaceWithLogicalUnit",
|
|
data: {
|
|
unit: node.unit,
|
|
replacement: unitReplacement,
|
|
},
|
|
fix(fixer) {
|
|
return fixer.replaceText(node, node.value + unitReplacement);
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
},
|
|
};
|
|
},
|
|
});
|