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
+144
View File
@@ -0,0 +1,144 @@
/**
* @fileoverview Rule to require layers in CSS.
* @author Nicholas C. Zakas
*/
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
* @import { CSSRuleDefinition } from "../types.js"
* @typedef {"missingLayer" | "missingLayerName" | "missingImportLayer" | "layerNameMismatch"} UseLayersMessageIds
* @typedef {[{
* allowUnnamedLayers?: boolean,
* requireImportLayers?: boolean,
* layerNamePattern?: string
* }]} UseLayersOptions
* @typedef {CSSRuleDefinition<{ RuleOptions: UseLayersOptions, MessageIds: UseLayersMessageIds }>} UseLayersRuleDefinition
*/
//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------
export default /** @satisfies {UseLayersRuleDefinition} */ ({
meta: {
type: "problem",
docs: {
description: "Require use of layers",
url: "https://github.com/eslint/css/blob/main/docs/rules/use-layers.md",
},
schema: [
{
type: "object",
properties: {
allowUnnamedLayers: {
type: "boolean",
},
requireImportLayers: {
type: "boolean",
},
layerNamePattern: {
type: "string",
},
},
additionalProperties: false,
},
],
defaultOptions: [
{
allowUnnamedLayers: false,
requireImportLayers: true,
layerNamePattern: "",
},
],
messages: {
missingLayer: "Expected rule to be within a layer.",
missingLayerName: "Expected layer to have a name.",
missingImportLayer: "Expected import to be within a layer.",
layerNameMismatch: "Expected layer name '{{ name }}' to match pattern '{{pattern}}'.",
},
},
create(context) {
let layerDepth = 0;
const options = context.options[0];
const layerNameRegex = options.layerNamePattern
? new RegExp(options.layerNamePattern, "u")
: null;
return {
"Atrule[name=/^import$/i]"(node) {
// layer, if present, must always be the second child of the prelude
const secondChild = node.prelude?.children[1];
const layerNode = secondChild?.name === "layer" ? secondChild : null;
if (options.requireImportLayers && !layerNode) {
context.report({
loc: node.loc,
messageId: "missingImportLayer",
});
}
if (layerNode) {
const isLayerFunction = layerNode.type === "Function";
if (!options.allowUnnamedLayers && !isLayerFunction) {
context.report({
loc: layerNode.loc,
messageId: "missingLayerName",
});
}
}
},
Layer(node) {
if (!layerNameRegex) {
return;
}
const parts = node.name.split(".");
let currentPos = 0;
parts.forEach((part, index) => {
if (!layerNameRegex.test(part)) {
const startColumn = node.loc.start.column + currentPos;
const endColumn = startColumn + part.length;
context.report({
loc: {
start: {
line: node.loc.start.line,
column: startColumn,
},
end: {
line: node.loc.start.line,
column: endColumn,
},
},
messageId: "layerNameMismatch",
data: {
name: part,
pattern: options.layerNamePattern,
},
});
}
currentPos += part.length;
// add 1 to account for the . symbol
if (index < parts.length - 1) {
currentPos += 1;
}
});
},
"Atrule[name=/^layer$/i]"(node) {
layerDepth++;
if (!options.allowUnnamedLayers && !node.prelude) {
context.report({
loc: node.loc,
messageId: "missingLayerName",
});
}
},
"Atrule[name=/^layer$/i]:exit"() {
layerDepth--;
},
Rule(node) {
if (layerDepth > 0) {
return;
}
context.report({
loc: node.loc,
messageId: "missingLayer",
});
},
};
},
});