pasika 0.10.117 → 0.10.118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/eslint/pasika/index.js +136 -40
- package/package.json +1 -1
|
@@ -10095,6 +10095,7 @@ function themeClasses(name) {
|
|
|
10095
10095
|
}
|
|
10096
10096
|
return ["bg", "text", "border", "fill", "stroke", "ring"].map((prefix) => `${prefix}-${stem}`);
|
|
10097
10097
|
}
|
|
10098
|
+
if (name.startsWith("--background-image-")) return [`bg-${name.slice("--background-image-".length)}`];
|
|
10098
10099
|
if (name.startsWith("--spacing-")) {
|
|
10099
10100
|
const stem = name.slice("--spacing-".length);
|
|
10100
10101
|
return [
|
|
@@ -11160,6 +11161,7 @@ var themeOperations = [
|
|
|
11160
11161
|
{ property: "tab-size", prefix: "tab", namespace: "tab-size" },
|
|
11161
11162
|
{ property: "border-radius", prefix: "rounded", namespace: "radius" },
|
|
11162
11163
|
{ property: "background-color", prefix: "bg", namespace: "color" },
|
|
11164
|
+
{ property: "background-image", prefix: "bg", namespace: "background-image" },
|
|
11163
11165
|
{ property: "color", prefix: "text", namespace: "color" },
|
|
11164
11166
|
{ property: "border-color", prefix: "border", namespace: "color" },
|
|
11165
11167
|
{ property: "outline-color", prefix: "outline", namespace: "color" },
|
|
@@ -11194,10 +11196,11 @@ function themeCandidate(name, utilityText) {
|
|
|
11194
11196
|
const typed = /^@utility\s+[\w-]+\s*\{\s*@apply\s+(?<kind>[a-z]+)-\((?<hint>[a-z-]+:)?(?<variable>--[a-z][a-z0-9-]*)\);?\s*\}$/u.exec(
|
|
11195
11197
|
source
|
|
11196
11198
|
);
|
|
11197
|
-
const
|
|
11199
|
+
const direct = /^@utility\s+[\w-]+\s*\{\s*(?<property>[a-z-]+)\s*:\s*(?<value>[^;{}\r\n]+);?\s*\}$/u.exec(source);
|
|
11200
|
+
const property = arbitrary?.groups?.property ?? direct?.groups?.property;
|
|
11198
11201
|
const matched = themeOperations.find((operation) => {
|
|
11199
|
-
if (!name.startsWith(`${operation.prefix}-`) || name.length <= operation.prefix.length + 1) return false;
|
|
11200
11202
|
if (property) return property === operation.property;
|
|
11203
|
+
if (!name.startsWith(`${operation.prefix}-`) || name.length <= operation.prefix.length + 1) return false;
|
|
11201
11204
|
if (typed?.groups?.kind !== operation.prefix) return false;
|
|
11202
11205
|
const hint = typed.groups.hint;
|
|
11203
11206
|
if (operation.prefix === "text") return hint === (operation.property === "font-size" ? "length:" : "color:");
|
|
@@ -11207,7 +11210,7 @@ function themeCandidate(name, utilityText) {
|
|
|
11207
11210
|
return hint === void 0;
|
|
11208
11211
|
});
|
|
11209
11212
|
if (!matched) return void 0;
|
|
11210
|
-
const value = arbitrary?.groups?.value?.trim() ?? (typed?.groups?.variable ? `var(${typed.groups.variable})` : void 0);
|
|
11213
|
+
const value = arbitrary?.groups?.value?.trim() ?? direct?.groups?.value?.trim() ?? (typed?.groups?.variable ? `var(${typed.groups.variable})` : void 0);
|
|
11211
11214
|
if (!value) return void 0;
|
|
11212
11215
|
const simple = /^-?(?:\d+(?:\.\d+)?|\.\d+)(?:px|rem|em|%|vh|vw|ch|deg)?$/u.test(value);
|
|
11213
11216
|
const variable = /^var\(--[a-z][a-z0-9-]*\)$/u.test(value);
|
|
@@ -11216,9 +11219,9 @@ function themeCandidate(name, utilityText) {
|
|
|
11216
11219
|
const ratio = /^\d+\/\d+$/u.test(value);
|
|
11217
11220
|
if (!simple && !variable && !color && !keyword && !ratio) return void 0;
|
|
11218
11221
|
if (matched.property === "font-size" && value.startsWith("-")) return void 0;
|
|
11219
|
-
const stem = name.slice(matched.prefix.length + 1);
|
|
11222
|
+
const stem = name.startsWith(`${matched.prefix}-`) ? name.slice(matched.prefix.length + 1) : void 0;
|
|
11220
11223
|
return {
|
|
11221
|
-
token: `--${matched.namespace}-${stem}
|
|
11224
|
+
token: stem ? `--${matched.namespace}-${stem}` : "",
|
|
11222
11225
|
property: matched.property,
|
|
11223
11226
|
value,
|
|
11224
11227
|
prefix: matched.prefix,
|
|
@@ -11282,6 +11285,37 @@ var utilityMappingRule = {
|
|
|
11282
11285
|
includeReferences: false
|
|
11283
11286
|
});
|
|
11284
11287
|
const projectTokens = graph.definitions.filter((symbol) => symbol.kind === "theme-token");
|
|
11288
|
+
const rootDefinitions = /* @__PURE__ */ new Map();
|
|
11289
|
+
for (const symbol of graph.definitions) {
|
|
11290
|
+
if (symbol.kind !== "root-variable" || !symbol.value) continue;
|
|
11291
|
+
const values = rootDefinitions.get(symbol.name) ?? [];
|
|
11292
|
+
values.push({ value: symbol.value, scope: symbol.scope ?? "default" });
|
|
11293
|
+
rootDefinitions.set(symbol.name, values);
|
|
11294
|
+
}
|
|
11295
|
+
function comparableValue(value, visited = /* @__PURE__ */ new Set()) {
|
|
11296
|
+
const text = value.trim();
|
|
11297
|
+
const match = /^var\((?<name>--[a-z][a-z0-9-]*)\)$/u.exec(text);
|
|
11298
|
+
const name = match?.groups?.name;
|
|
11299
|
+
if (!name) {
|
|
11300
|
+
const hex = /^#(?<digits>[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/iu.exec(text)?.groups?.digits;
|
|
11301
|
+
if (!hex) return text;
|
|
11302
|
+
const lower = hex.toLowerCase();
|
|
11303
|
+
return lower.length <= 4 ? Array.from(lower, (digit) => digit + digit).join("") : lower;
|
|
11304
|
+
}
|
|
11305
|
+
if (visited.has(name)) return void 0;
|
|
11306
|
+
const values = rootDefinitions.get(name);
|
|
11307
|
+
if (!values) return text;
|
|
11308
|
+
if (values.length !== 1 || values[0]?.scope !== "default") return void 0;
|
|
11309
|
+
const next = values[0].value;
|
|
11310
|
+
if (!next) return void 0;
|
|
11311
|
+
visited.add(name);
|
|
11312
|
+
return comparableValue(next, visited);
|
|
11313
|
+
}
|
|
11314
|
+
function equivalentValue(a, b) {
|
|
11315
|
+
const left = comparableValue(a);
|
|
11316
|
+
const right = comparableValue(b);
|
|
11317
|
+
return left !== void 0 && right !== void 0 && left === right;
|
|
11318
|
+
}
|
|
11285
11319
|
return {
|
|
11286
11320
|
"StyleSheet:exit"(node) {
|
|
11287
11321
|
const utilities = atrulesNamed(node, "utility");
|
|
@@ -11307,6 +11341,16 @@ var utilityMappingRule = {
|
|
|
11307
11341
|
themeDefinitions.set(child.property, definitions);
|
|
11308
11342
|
}
|
|
11309
11343
|
}
|
|
11344
|
+
for (const cssRule of node.children) {
|
|
11345
|
+
if (cssRule.type !== "Rule") continue;
|
|
11346
|
+
if (context.sourceCode.getText(cssRule.prelude).trim() !== ":root") continue;
|
|
11347
|
+
for (const child of blockChildren2(cssRule)) {
|
|
11348
|
+
if (child.type !== "Declaration" || !child.property.startsWith("--")) continue;
|
|
11349
|
+
if (rootDefinitions.has(child.property)) continue;
|
|
11350
|
+
const value = declarationValue(context.sourceCode.getText(child));
|
|
11351
|
+
if (value) rootDefinitions.set(child.property, [{ value, scope: "default" }]);
|
|
11352
|
+
}
|
|
11353
|
+
}
|
|
11310
11354
|
const rootValues = /* @__PURE__ */ new Map();
|
|
11311
11355
|
const overriddenVariables = /* @__PURE__ */ new Set();
|
|
11312
11356
|
for (const cssRule of rules(node)) {
|
|
@@ -11358,56 +11402,104 @@ var utilityMappingRule = {
|
|
|
11358
11402
|
break;
|
|
11359
11403
|
}
|
|
11360
11404
|
}
|
|
11405
|
+
function matchingThemeToken(candidate) {
|
|
11406
|
+
const namespacePrefix = `--${candidate.namespace}-`;
|
|
11407
|
+
const available = [
|
|
11408
|
+
...projectTokens,
|
|
11409
|
+
...[...themeDefinitions].flatMap(
|
|
11410
|
+
([name, definitions]) => definitions.length === 1 && definitions[0] ? [{ name, value: definitions[0].value }] : []
|
|
11411
|
+
)
|
|
11412
|
+
];
|
|
11413
|
+
for (const token of available) {
|
|
11414
|
+
if (!token.value || !token.name.startsWith(namespacePrefix) || token.name.includes("--", 2)) continue;
|
|
11415
|
+
if (!equivalentValue(candidate.value, token.value)) continue;
|
|
11416
|
+
if (candidate.namespace === "text" && ["--line-height", "--letter-spacing", "--font-weight"].some(
|
|
11417
|
+
(suffix) => themeDefinitions.has(`${token.name}${suffix}`) || projectTokens.some((entry) => entry.name === `${token.name}${suffix}`)
|
|
11418
|
+
)) {
|
|
11419
|
+
continue;
|
|
11420
|
+
}
|
|
11421
|
+
const generatedClass = `${candidate.prefix}-${token.name.slice(namespacePrefix.length)}`;
|
|
11422
|
+
if (candidate.namespace === "color" && !themeClasses(token.name).includes(generatedClass)) continue;
|
|
11423
|
+
return generatedClass;
|
|
11424
|
+
}
|
|
11425
|
+
return void 0;
|
|
11426
|
+
}
|
|
11361
11427
|
for (const utility of utilities) {
|
|
11362
11428
|
const name = preludeIdentifiers(utility)[0] ?? "unknown";
|
|
11363
|
-
const
|
|
11364
|
-
|
|
11429
|
+
const candidate = themeCandidate(name, context.sourceCode.getText(utility));
|
|
11430
|
+
const equivalent = candidate ? matchingThemeToken(candidate) : void 0;
|
|
11431
|
+
if (candidate && equivalent && nameCounts.get(name) === 1) {
|
|
11365
11432
|
context.report({
|
|
11366
11433
|
node: utility,
|
|
11367
|
-
message: `Custom utility "${name}"
|
|
11434
|
+
message: `Custom utility "${name}" duplicates theme-generated "${equivalent}" (${candidate.property}: ${candidate.value}); reuse "${equivalent}" instead.`
|
|
11368
11435
|
});
|
|
11369
11436
|
continue;
|
|
11370
11437
|
}
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
if (!candidate) continue;
|
|
11374
|
-
const tokenAlreadyDefined = themeTokens.has(candidate.token) || projectTokens.some((token) => token.name === candidate.token);
|
|
11375
|
-
const namespacePrefix = `--${candidate.namespace}-`;
|
|
11376
|
-
const staticValue = !/\b(?:var|env|attr|calc|min|max|clamp|color-mix|light-dark)\s*\(/iu.test(
|
|
11377
|
-
candidate.value
|
|
11378
|
-
);
|
|
11379
|
-
const valueKey = (value) => {
|
|
11380
|
-
const input = value.trim();
|
|
11381
|
-
const digits = /^#(?<hex>[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/iu.exec(input)?.groups?.hex;
|
|
11382
|
-
if (digits) {
|
|
11383
|
-
const lower = digits.toLowerCase();
|
|
11384
|
-
return lower.length <= 4 ? Array.from(lower, (digit) => digit + digit).join("") : lower;
|
|
11385
|
-
}
|
|
11386
|
-
return input;
|
|
11387
|
-
};
|
|
11388
|
-
const sameOperation = (token, value) => token.startsWith(namespacePrefix) && !token.includes("--", 2) && valueKey(value) === valueKey(candidate.value) && (candidate.namespace !== "text" || !["--line-height", "--letter-spacing", "--font-weight"].some(
|
|
11389
|
-
(suffix) => themeDefinitions.has(`${token}${suffix}`) || projectTokens.some((definition) => definition.name === `${token}${suffix}`)
|
|
11390
|
-
));
|
|
11391
|
-
const equivalent = staticValue ? projectTokens.find((token) => token.value !== void 0 && sameOperation(token.name, token.value)) : void 0;
|
|
11392
|
-
const localEquivalent = staticValue ? [...themeDefinitions].find(
|
|
11393
|
-
([token, definitions]) => definitions.length === 1 && definitions[0]?.value !== void 0 && sameOperation(token, definitions[0].value)
|
|
11394
|
-
)?.[0] : void 0;
|
|
11395
|
-
const existingToken = equivalent?.name ?? localEquivalent;
|
|
11396
|
-
if (tokenAlreadyDefined && !existingToken) continue;
|
|
11397
|
-
if (existingToken) {
|
|
11398
|
-
const className = `${candidate.prefix}-${existingToken.slice(namespacePrefix.length)}`;
|
|
11438
|
+
const operationCount = applyOperationCount(utility);
|
|
11439
|
+
if (operationCount !== 1) {
|
|
11399
11440
|
context.report({
|
|
11400
11441
|
node: utility,
|
|
11401
|
-
message: `Custom utility "${name}"
|
|
11442
|
+
message: `Custom utility "${name}" maps ${String(operationCount)} Tailwind operations; custom utilities must map exactly one operation. Reusable presentation belongs in a component.`
|
|
11402
11443
|
});
|
|
11403
11444
|
continue;
|
|
11404
11445
|
}
|
|
11446
|
+
if (nameCounts.get(name) !== 1 || !candidate?.token) continue;
|
|
11447
|
+
const tokenAlreadyDefined = themeTokens.has(candidate.token) || projectTokens.some((token) => token.name === candidate.token);
|
|
11448
|
+
if (tokenAlreadyDefined) continue;
|
|
11405
11449
|
if (candidate.property === "font-size" && themeTokens.has(`--color-${name.slice(5)}`)) continue;
|
|
11406
11450
|
context.report({
|
|
11407
11451
|
node: utility,
|
|
11408
11452
|
message: `Custom utility "${name}" only maps ${candidate.property} (${candidate.value}); prefer @theme inline token "${candidate.token}" backed by a :root variable. Preserve the class name and compare compiled CSS before removing the utility.`
|
|
11409
11453
|
});
|
|
11410
11454
|
}
|
|
11455
|
+
function checkClass(cssNode) {
|
|
11456
|
+
if (cssNode.type === "Atrule") {
|
|
11457
|
+
if (cssNode.name !== "layer" || !("prelude" in cssNode) || !cssNode.prelude) return;
|
|
11458
|
+
if (context.sourceCode.getText(cssNode.prelude).trim() === "base") return;
|
|
11459
|
+
for (const child of blockChildren2(cssNode)) checkClass(child);
|
|
11460
|
+
return;
|
|
11461
|
+
}
|
|
11462
|
+
if (cssNode.type !== "Rule") return;
|
|
11463
|
+
const selector = context.sourceCode.getText(cssNode.prelude).trim();
|
|
11464
|
+
if (!/^\.[a-zA-Z_][\w-]*$/u.test(selector)) return;
|
|
11465
|
+
const children = blockChildren2(cssNode);
|
|
11466
|
+
if (children.length !== 1) return;
|
|
11467
|
+
const only = children[0];
|
|
11468
|
+
if (!only) return;
|
|
11469
|
+
const declarationText = context.sourceCode.getText(only).trim();
|
|
11470
|
+
if (only.type === "Atrule" && only.name === "apply") {
|
|
11471
|
+
const alias = /^@apply\s+(?<name>[a-z][a-z0-9-]*)\s*;?$/u.exec(declarationText)?.groups?.name;
|
|
11472
|
+
if (alias && [...projectTokens.map((token) => token.name), ...themeDefinitions.keys()].some(
|
|
11473
|
+
(name) => themeClasses(name).includes(alias)
|
|
11474
|
+
)) {
|
|
11475
|
+
context.report({
|
|
11476
|
+
node: cssNode,
|
|
11477
|
+
message: `CSS class "${selector}" only aliases theme-generated "${alias}"; use "${alias}" instead.`
|
|
11478
|
+
});
|
|
11479
|
+
return;
|
|
11480
|
+
}
|
|
11481
|
+
}
|
|
11482
|
+
const found = only.type === "Declaration" ? /^(?<property>[a-z-]+)\s*:\s*(?<value>[^;{}]+);?$/u.exec(declarationText) : /^@apply\s+\[(?<property>[a-z-]+):(?<value>[^\]\r\n;{}]+)\]\s*;?$/u.exec(declarationText);
|
|
11483
|
+
const property = found?.groups?.property;
|
|
11484
|
+
const value = found?.groups?.value?.trim();
|
|
11485
|
+
if (!property || !value || value.endsWith("!important")) return;
|
|
11486
|
+
const operation = themeOperations.find((item) => item.property === property);
|
|
11487
|
+
if (!operation) return;
|
|
11488
|
+
const candidate = {
|
|
11489
|
+
token: "",
|
|
11490
|
+
property,
|
|
11491
|
+
value,
|
|
11492
|
+
prefix: operation.prefix,
|
|
11493
|
+
namespace: operation.namespace
|
|
11494
|
+
};
|
|
11495
|
+
const equivalent = matchingThemeToken(candidate);
|
|
11496
|
+
if (!equivalent) return;
|
|
11497
|
+
context.report({
|
|
11498
|
+
node: cssNode,
|
|
11499
|
+
message: `CSS class "${selector}" duplicates theme-generated "${equivalent}" (${property}: ${value}); use "${equivalent}" instead.`
|
|
11500
|
+
});
|
|
11501
|
+
}
|
|
11502
|
+
for (const cssRule of node.children) checkClass(cssRule);
|
|
11411
11503
|
}
|
|
11412
11504
|
};
|
|
11413
11505
|
}
|
|
@@ -11588,7 +11680,7 @@ import { existsSync as existsSync12, readdirSync as readdirSync6, readFileSync a
|
|
|
11588
11680
|
import { createRequire } from "module";
|
|
11589
11681
|
import path79 from "path";
|
|
11590
11682
|
var PREFIX_NAMESPACES = {
|
|
11591
|
-
bg: ["color"],
|
|
11683
|
+
bg: ["color", "background-image"],
|
|
11592
11684
|
text: ["color", "text"],
|
|
11593
11685
|
border: ["color"],
|
|
11594
11686
|
ring: ["color"],
|
|
@@ -11994,8 +12086,12 @@ function readInventory(files) {
|
|
|
11994
12086
|
for (const block of themeBlocks(css2)) {
|
|
11995
12087
|
if (/--\*\s*:\s*initial\b/.test(block)) defaultsReset = true;
|
|
11996
12088
|
for (const decl of block.matchAll(/--(?<namespace>[a-z][a-z0-9]*)-(?<token>[a-z0-9][a-z0-9_-]*)\s*:/g)) {
|
|
11997
|
-
|
|
11998
|
-
|
|
12089
|
+
let namespace = decl.groups?.namespace;
|
|
12090
|
+
let token = decl.groups?.token;
|
|
12091
|
+
if (namespace === "background" && token?.startsWith("image-")) {
|
|
12092
|
+
namespace = "background-image";
|
|
12093
|
+
token = token.slice("image-".length);
|
|
12094
|
+
}
|
|
11999
12095
|
if (!namespace || !token) continue;
|
|
12000
12096
|
let set = themeTokensByNamespace.get(namespace);
|
|
12001
12097
|
if (!set) {
|