first commit

This commit is contained in:
2026-08-06 12:00:39 +10:00
commit 91221f5a8e
3259 changed files with 569069 additions and 0 deletions
@@ -0,0 +1,268 @@
//-----------------------------------------------------------------------------
// 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);
},
},
],
});
}
},
};
},
});