pasika 0.10.110 → 0.10.112

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.
@@ -10375,24 +10375,170 @@ function applyOperationCount(utility) {
10375
10375
  }
10376
10376
  return count;
10377
10377
  }
10378
+ function themeCandidate(name, utilityText) {
10379
+ const match = /^@utility\s+[\w-]+\s*\{\s*@apply\s+\[(?<property>font-size|letter-spacing):(?<value>[^\]\r\n;{}]+)\];?\s*\}$/u.exec(
10380
+ utilityText.trim()
10381
+ );
10382
+ if (!match) return void 0;
10383
+ const property = match.groups?.property;
10384
+ const value = match.groups?.value?.trim();
10385
+ if (!property || !value) return void 0;
10386
+ const prefix = property === "font-size" ? "text-" : "tracking-";
10387
+ if (!name.startsWith(prefix) || name.length === prefix.length) return void 0;
10388
+ const length = /^-?(?:\d+(?:\.\d+)?|\.\d+)(?:px|rem|em|%)$/u.test(value);
10389
+ const variable = /^var\(--[a-z][a-z0-9-]*\)$/u.test(value);
10390
+ if (!length && !variable) return void 0;
10391
+ if (property === "font-size" && value.startsWith("-")) return void 0;
10392
+ return { token: `--${name}`, property, value };
10393
+ }
10394
+ var builtInTextSizes = /* @__PURE__ */ new Set([
10395
+ "xs",
10396
+ "sm",
10397
+ "base",
10398
+ "lg",
10399
+ "xl",
10400
+ "2xl",
10401
+ "3xl",
10402
+ "4xl",
10403
+ "5xl",
10404
+ "6xl",
10405
+ "7xl",
10406
+ "8xl",
10407
+ "9xl"
10408
+ ]);
10409
+ var builtInTracking = /* @__PURE__ */ new Set(["tighter", "tight", "normal", "wide", "wider", "widest"]);
10410
+ function lengthIdentity(value) {
10411
+ const match = /^(?<number>-?(?:\d+(?:\.\d*)?|\.\d+))(?<unit>px|rem|em|%)$/u.exec(value);
10412
+ const number = match?.groups?.number;
10413
+ const unit = match?.groups?.unit;
10414
+ if (!number || !unit) return void 0;
10415
+ return `${String(Number(number))}${unit}`;
10416
+ }
10417
+ function declarationValue(text) {
10418
+ const match = /^--[\w-]+\s*:\s*(?<value>[^;{}]+);?$/u.exec(text.trim());
10419
+ return match?.groups?.value?.trim();
10420
+ }
10421
+ function canonicalToken(name) {
10422
+ if (name.startsWith("--text-")) return builtInTextSizes.has(name.slice("--text-".length));
10423
+ if (name.startsWith("--tracking-")) return builtInTracking.has(name.slice("--tracking-".length));
10424
+ return false;
10425
+ }
10426
+ function isTypographySize(name) {
10427
+ return /^--text-(?!.*--)(?:[a-z0-9]+-)*[a-z0-9]+$/u.test(name);
10428
+ }
10378
10429
  var utilityMappingRule = {
10379
10430
  meta: {
10380
- schema: [],
10431
+ schema: [
10432
+ {
10433
+ type: "object",
10434
+ properties: { preferThemeTokens: { type: "boolean" } },
10435
+ additionalProperties: false
10436
+ }
10437
+ ],
10381
10438
  type: "problem",
10382
10439
  docs: {
10383
- description: "Require custom utilities to map exactly one Tailwind styling operation."
10440
+ description: "Require one styling operation per custom utility; optionally suggest theme-generated utilities."
10384
10441
  }
10385
10442
  },
10386
10443
  create(context) {
10444
+ const option = context.options[0];
10445
+ const preferThemeTokens = typeof option === "object" && option !== null && "preferThemeTokens" in option && option.preferThemeTokens === true;
10387
10446
  return {
10388
10447
  "StyleSheet:exit"(node) {
10389
- for (const utility of atrulesNamed(node, "utility")) {
10448
+ const utilities = atrulesNamed(node, "utility");
10449
+ const themeTokens = /* @__PURE__ */ new Set();
10450
+ const nameCounts = /* @__PURE__ */ new Map();
10451
+ if (preferThemeTokens) {
10452
+ let identity2 = function(value) {
10453
+ const variable = /^var\((?<name>--[a-z][a-z0-9-]*)\)$/u.exec(value);
10454
+ if (!variable) return lengthIdentity(value);
10455
+ const name = variable.groups?.name;
10456
+ if (!name || overriddenVariables.has(name)) return void 0;
10457
+ const definitions = rootValues.get(name);
10458
+ const definition = definitions?.length === 1 ? definitions[0] : void 0;
10459
+ return definition ? lengthIdentity(definition) : void 0;
10460
+ };
10461
+ var identity = identity2;
10462
+ for (const theme of atrulesNamed(node, "theme")) {
10463
+ for (const child of blockChildren2(theme)) {
10464
+ if (child.type === "Declaration") themeTokens.add(child.property);
10465
+ }
10466
+ }
10467
+ for (const utility of utilities) {
10468
+ const name = preludeIdentifiers(utility)[0] ?? "";
10469
+ nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1);
10470
+ }
10471
+ const themeDefinitions = /* @__PURE__ */ new Map();
10472
+ for (const theme of atrulesNamed(node, "theme")) {
10473
+ for (const child of blockChildren2(theme)) {
10474
+ if (child.type !== "Declaration") continue;
10475
+ const value = declarationValue(context.sourceCode.getText(child));
10476
+ if (!value) continue;
10477
+ const definitions = themeDefinitions.get(child.property) ?? [];
10478
+ definitions.push({ node: child, value });
10479
+ themeDefinitions.set(child.property, definitions);
10480
+ }
10481
+ }
10482
+ const rootValues = /* @__PURE__ */ new Map();
10483
+ const overriddenVariables = /* @__PURE__ */ new Set();
10484
+ for (const cssRule of rules(node)) {
10485
+ if (!("prelude" in cssRule) || !cssRule.prelude) continue;
10486
+ const isRoot = context.sourceCode.getText(cssRule.prelude).trim() === ":root";
10487
+ for (const child of blockChildren2(cssRule)) {
10488
+ if (child.type !== "Declaration" || !child.property.startsWith("--")) continue;
10489
+ if (!isRoot) {
10490
+ overriddenVariables.add(child.property);
10491
+ continue;
10492
+ }
10493
+ const value = declarationValue(context.sourceCode.getText(child));
10494
+ if (!value) continue;
10495
+ const definitions = rootValues.get(child.property) ?? [];
10496
+ definitions.push(value);
10497
+ rootValues.set(child.property, definitions);
10498
+ }
10499
+ }
10500
+ for (const [name, definitions] of themeDefinitions) {
10501
+ const definition = definitions.length === 1 ? definitions[0] : void 0;
10502
+ if (!definition || canonicalToken(name)) continue;
10503
+ if (!isTypographySize(name) && !name.startsWith("--tracking-")) continue;
10504
+ if (name.includes("--", 2)) continue;
10505
+ const value = identity2(definition.value);
10506
+ if (!value) continue;
10507
+ const prefix = name.startsWith("--text-") ? "--text-" : "--tracking-";
10508
+ for (const [other, otherDefinitions] of themeDefinitions) {
10509
+ const otherDefinition = otherDefinitions.length === 1 ? otherDefinitions[0] : void 0;
10510
+ if (!canonicalToken(other) || !other.startsWith(prefix) || !otherDefinition) continue;
10511
+ if (identity2(otherDefinition.value) !== value) continue;
10512
+ if (prefix === "--text-" && ["--line-height", "--letter-spacing", "--font-weight"].some(
10513
+ (suffix) => themeDefinitions.has(`${name}${suffix}`) || themeDefinitions.has(`${other}${suffix}`)
10514
+ )) {
10515
+ continue;
10516
+ }
10517
+ context.report({
10518
+ node: definition.node,
10519
+ message: `Theme token "${name}" duplicates the configured built-in "${other}" (${value}); reuse the "${other.slice(2)}" utility instead of defining an equivalent theme token.`
10520
+ });
10521
+ break;
10522
+ }
10523
+ }
10524
+ }
10525
+ for (const utility of utilities) {
10390
10526
  const name = preludeIdentifiers(utility)[0] ?? "unknown";
10391
10527
  const operationCount = applyOperationCount(utility);
10392
- if (operationCount === 1) continue;
10528
+ if (operationCount !== 1) {
10529
+ context.report({
10530
+ node: utility,
10531
+ message: `Custom utility "${name}" maps ${String(operationCount)} Tailwind operations; custom utilities must map exactly one operation. Reusable presentation belongs in a component.`
10532
+ });
10533
+ continue;
10534
+ }
10535
+ if (!preferThemeTokens || nameCounts.get(name) !== 1) continue;
10536
+ const candidate = themeCandidate(name, context.sourceCode.getText(utility));
10537
+ if (!candidate || themeTokens.has(candidate.token)) continue;
10538
+ if (candidate.property === "font-size" && themeTokens.has(`--color-${name.slice(5)}`)) continue;
10393
10539
  context.report({
10394
10540
  node: utility,
10395
- message: `Custom utility "${name}" maps ${String(operationCount)} Tailwind operations; custom utilities must map exactly one operation. Reusable presentation belongs in a component.`
10541
+ 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.`
10396
10542
  });
10397
10543
  }
10398
10544
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.110",
3
+ "version": "0.10.112",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",