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
+152
View File
@@ -0,0 +1,152 @@
/**
* @fileoverview Rule to prevent duplicate imports in CSS.
* @author Nicholas C. Zakas
*/
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
* @import { CSSRuleDefinition } from "../types.js"
* @typedef {"duplicateImport" | "removeDuplicateImportWithModifiers" | "removeDuplicateImportWithoutModifiers"} NoDuplicateKeysMessageIds
* @typedef {CSSRuleDefinition<{ RuleOptions: [], MessageIds: NoDuplicateKeysMessageIds }>} NoDuplicateImportsRuleDefinition
*/
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
/**
* Get the end index of import statement including a following newline if present.
* @param {string} text The full text of the source code.
* @param {number} end The end index of the import statement.
* @returns {number} The end index of the import statement including a following newline.
*/
function getImportEnd(text, end) {
let removeEnd = end;
// Remove the node, and also remove a following newline if present
if (text[removeEnd] === "\r") {
removeEnd += text[removeEnd + 1] === "\n" ? 2 : 1;
}
else if (text[removeEnd] === "\n" || text[removeEnd] === "\f") {
removeEnd += 1;
}
return removeEnd;
}
/**
* Get the modifiers of an import statement.
* @param {Object} importNode The import node to get modifiers from.
* @param {Object} sourceCode The source code object.
* @returns {string[]} An array of modifiers for the import statement.
*/
function getImportModifiers(importNode, sourceCode) {
const importModifiers = [];
const importHasModifiers = importNode.prelude?.children.length > 1;
if (importHasModifiers) {
importNode.prelude?.children.slice(1).forEach(modifier => {
const modifierText = sourceCode.getText(modifier).trim();
importModifiers.push(modifierText);
});
}
return importModifiers;
}
/**
* Get the fix for a duplicate import statement.
* @param {Object} fixer The fixer object.
* @param {string} text The full text of the source code.
* @param {number} start The start index of the import statement to fix.
* @param {number} end The end index of the import statement to fix.
* @param {boolean} hasModifiers A boolean indicating whether the import statement has modifiers that differ from the original import.
* @returns {Object|null} A fix object if a fix is applicable, or null if no fix should be applied.
*/
function getFixForImport(fixer, text, start, end, hasModifiers) {
const removeEnd = getImportEnd(text, end);
if (hasModifiers) {
return fixer.removeRange([start, removeEnd]);
}
return null;
}
//-----------------------------------------------------------------------------
// Rule
//-----------------------------------------------------------------------------
export default /** @satisfies {NoDuplicateImportsRuleDefinition} */ ({
meta: {
type: "problem",
fixable: "code",
hasSuggestions: true,
docs: {
description: "Disallow duplicate @import rules",
recommended: true,
url: "https://github.com/eslint/css/blob/main/docs/rules/no-duplicate-imports.md",
},
messages: {
duplicateImport: "Unexpected duplicate @import rule for '{{url}}'.",
removeDuplicateImportWithModifiers: "Remove duplicate @import rule with modifier(s) - {{modifiers}}.",
removeDuplicateImportWithoutModifiers: "Remove duplicate @import rule without modifiers.",
},
},
create(context) {
const { sourceCode } = context;
const imports = [];
return {
"Atrule[name=/^import$/i]"(node) {
const url = node.prelude?.children[0].value;
const hasImport = imports.some(importNode => importNode.prelude?.children[0].value === url);
if (hasImport) {
const firstImportNode = imports.find(importNode => importNode.prelude?.children[0].value === url);
const [firstImportStart, firstImportEnd] = sourceCode.getRange(firstImportNode);
const firstImportHasModifiers = firstImportNode.prelude?.children.length > 1;
const nodeHasModifiers = node.prelude?.children.length > 1;
const [start, end] = sourceCode.getRange(node);
const text = sourceCode.text;
const firstImportModifiers = getImportModifiers(firstImportNode, sourceCode);
const duplicateImportModifiers = getImportModifiers(node, sourceCode);
const hasSameModifiers = firstImportModifiers.length ===
duplicateImportModifiers.length &&
firstImportModifiers.every((modifier, index) => modifier === duplicateImportModifiers[index]);
context.report({
loc: node.loc,
messageId: "duplicateImport",
data: { url },
fix(fixer) {
const hasModifiers = (!firstImportHasModifiers &&
!nodeHasModifiers) ||
hasSameModifiers;
return getFixForImport(fixer, text, start, end, hasModifiers);
},
suggest: [
{
messageId: firstImportHasModifiers
? "removeDuplicateImportWithModifiers"
: "removeDuplicateImportWithoutModifiers",
data: {
modifiers: firstImportModifiers.join(" "),
},
fix(fixer) {
const hasModifiers = (firstImportHasModifiers ||
nodeHasModifiers) &&
!hasSameModifiers;
return getFixForImport(fixer, text, firstImportStart, firstImportEnd, hasModifiers);
},
},
{
messageId: nodeHasModifiers
? "removeDuplicateImportWithModifiers"
: "removeDuplicateImportWithoutModifiers",
data: {
modifiers: duplicateImportModifiers.join(" "),
},
fix(fixer) {
const hasModifiers = (firstImportHasModifiers ||
nodeHasModifiers) &&
!hasSameModifiers;
return getFixForImport(fixer, text, start, end, hasModifiers);
},
},
],
});
}
else {
imports.push(node);
}
},
};
},
});