pasika 0.4.1 → 0.4.3

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.
@@ -66,11 +66,12 @@ function componentFromVariable(node) {
66
66
  smart: containsSmartCall(initializer)
67
67
  };
68
68
  }
69
- function parseComponentInfo(text, filename) {
69
+ function parseComponentInfo(text, filename, options) {
70
70
  const sourceFile = ts.createSourceFile(path.resolve(filename), text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
71
71
  const components = [];
72
72
  for (const statement of sourceFile.statements) {
73
- if (!isExported(statement)) continue;
73
+ const exported = isExported(statement);
74
+ if (!exported && !options?.includeNonExported) continue;
74
75
  if (ts.isFunctionDeclaration(statement)) {
75
76
  const component = componentFromFunction(statement);
76
77
  if (component) components.push(component);
@@ -350,59 +351,26 @@ var importBoundariesRule = {
350
351
  };
351
352
 
352
353
  // eslint/rules/no-mixed-concerns.ts
353
- function getExportName(declaration) {
354
- if (declaration?.type === "FunctionDeclaration" && declaration.id) {
355
- return declaration.id.name ?? null;
356
- }
357
- if (declaration?.type === "VariableDeclaration") {
358
- const declarator = declaration.declarations?.[0];
359
- if (declarator?.id?.type === "Identifier" && (declarator.init?.type === "ArrowFunctionExpression" || declarator.init?.type === "FunctionExpression")) {
360
- return declarator.id.name ?? null;
361
- }
362
- }
363
- if (declaration?.type === "ArrowFunctionExpression") {
364
- return "default";
365
- }
366
- if (declaration?.type === "FunctionExpression" && declaration.id) {
367
- return declaration.id.name ?? null;
368
- }
369
- return null;
370
- }
371
354
  var noMixedConcernsRule = {
372
355
  meta: {
373
356
  schema: [],
374
357
  type: "problem",
375
358
  docs: {
376
- description: "Enforce one exported React component per .tsx file."
359
+ description: "Enforce one React component per .tsx file, counting private components."
377
360
  }
378
361
  },
379
362
  create(context) {
380
363
  if (!context.filename.endsWith(".tsx")) return {};
381
- let exportedComponentCount = 0;
382
- const extraExports = [];
383
- function registerExport(name) {
384
- exportedComponentCount++;
385
- if (exportedComponentCount > 1) {
386
- extraExports.push({ name });
387
- }
388
- }
364
+ const sourceCode = context.sourceCode.text;
389
365
  return {
390
- ExportNamedDeclaration(node) {
391
- const name = getExportName(node.declaration);
392
- if (name) registerExport(name);
393
- },
394
- ExportDefaultDeclaration(node) {
395
- const name = getExportName(node.declaration);
396
- if (name) registerExport(name);
397
- },
398
366
  "Program:exit"() {
399
- if (exportedComponentCount > 1) {
400
- for (const extra of extraExports) {
401
- context.report({
402
- loc: { line: 1, column: 0 },
403
- message: `File exports multiple components. "${extra.name}" is an extra component export. Move it to its own file. Each .tsx file MUST contain exactly one component. See docs/code-organization-guide/rules/no-mixed-concerns-rule.md`
404
- });
405
- }
367
+ const components = parseComponentInfo(sourceCode, context.filename, { includeNonExported: true });
368
+ if (components.length <= 1) return;
369
+ for (const extra of components.slice(1)) {
370
+ context.report({
371
+ loc: { line: 1, column: 0 },
372
+ message: `File defines multiple components. "${extra.name}" is an extra component. Move it to its own file. Each component file MUST define exactly one component. See docs/code-organization-guide/rules/no-mixed-concerns-rule.md`
373
+ });
406
374
  }
407
375
  }
408
376
  };
@@ -478,6 +446,397 @@ var noArbitraryTailwindRule = {
478
446
  }
479
447
  };
480
448
 
449
+ // eslint/rules/unknown-utility.ts
450
+ import { readdirSync, readFileSync, statSync } from "fs";
451
+ import path4 from "path";
452
+ var PREFIX_NAMESPACES = {
453
+ bg: ["color"],
454
+ text: ["color", "text"],
455
+ border: ["color"],
456
+ ring: ["color"],
457
+ fill: ["color"],
458
+ stroke: ["color"],
459
+ accent: ["color"],
460
+ caret: ["color"],
461
+ decoration: ["color"],
462
+ outline: ["color"],
463
+ divide: ["color"],
464
+ from: ["color"],
465
+ via: ["color"],
466
+ to: ["color"],
467
+ shadow: ["color", "shadow"],
468
+ rounded: ["radius"],
469
+ font: ["font"],
470
+ tracking: ["tracking"],
471
+ leading: ["leading"],
472
+ opacity: ["opacity"],
473
+ z: ["z"],
474
+ blur: ["blur"],
475
+ "backdrop-blur": ["blur"],
476
+ "ring-offset": ["color"]
477
+ };
478
+ var PREFIX_BUILTINS = {
479
+ bg: [
480
+ "none",
481
+ "cover",
482
+ "contain",
483
+ "auto",
484
+ "fixed",
485
+ "local",
486
+ "scroll",
487
+ "center",
488
+ "top",
489
+ "bottom",
490
+ "left",
491
+ "right",
492
+ "left-top",
493
+ "left-bottom",
494
+ "right-top",
495
+ "right-bottom",
496
+ "repeat",
497
+ "no-repeat",
498
+ "repeat-x",
499
+ "repeat-y",
500
+ "repeat-round",
501
+ "repeat-space",
502
+ "clip-border",
503
+ "clip-padding",
504
+ "clip-content",
505
+ "clip-text",
506
+ "origin-border",
507
+ "origin-padding",
508
+ "origin-content",
509
+ "gradient-to-t",
510
+ "gradient-to-tr",
511
+ "gradient-to-r",
512
+ "gradient-to-br",
513
+ "gradient-to-b",
514
+ "gradient-to-bl",
515
+ "gradient-to-l",
516
+ "gradient-to-tl",
517
+ "linear-to-t",
518
+ "linear-to-tr",
519
+ "linear-to-r",
520
+ "linear-to-br",
521
+ "linear-to-b",
522
+ "linear-to-bl",
523
+ "linear-to-l",
524
+ "linear-to-tl"
525
+ ],
526
+ text: [
527
+ "left",
528
+ "center",
529
+ "right",
530
+ "justify",
531
+ "start",
532
+ "end",
533
+ "wrap",
534
+ "nowrap",
535
+ "balance",
536
+ "pretty",
537
+ "ellipsis",
538
+ "clip",
539
+ "truncate",
540
+ "uppercase",
541
+ "lowercase",
542
+ "capitalize",
543
+ "normal-case",
544
+ "underline",
545
+ "overline",
546
+ "line-through",
547
+ "no-underline"
548
+ ],
549
+ border: [
550
+ "solid",
551
+ "dashed",
552
+ "dotted",
553
+ "double",
554
+ "hidden",
555
+ "none",
556
+ "collapse",
557
+ "separate",
558
+ "x",
559
+ "y",
560
+ "t",
561
+ "r",
562
+ "b",
563
+ "l",
564
+ "s",
565
+ "e"
566
+ ],
567
+ ring: ["inset"],
568
+ divide: ["x", "y", "reverse"],
569
+ from: ["t", "r", "b", "l", "tr", "tl", "br", "bl", "top", "right", "bottom", "left"],
570
+ via: ["t", "r", "b", "l", "tr", "tl", "br", "bl", "top", "right", "bottom", "left"],
571
+ to: ["t", "r", "b", "l", "tr", "tl", "br", "bl", "top", "right", "bottom", "left"],
572
+ fill: ["none"],
573
+ stroke: ["none"],
574
+ accent: ["auto"],
575
+ decoration: ["solid", "double", "dotted", "dashed", "wavy", "none"],
576
+ outline: ["none", "hidden", "dashed", "dotted", "double", "solid"],
577
+ font: ["thin", "extralight", "light", "normal", "medium", "semibold", "bold", "extrabold", "black"]
578
+ };
579
+ var DEFAULT_TOKENS = {
580
+ text: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl", "7xl", "8xl", "9xl"],
581
+ radius: ["none", "xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl", "full"],
582
+ font: ["sans", "serif", "mono"],
583
+ shadow: ["2xs", "xs", "sm", "md", "lg", "xl", "2xl", "inner", "none"],
584
+ tracking: ["tighter", "tight", "normal", "wide", "wider", "widest"],
585
+ leading: ["none", "tight", "snug", "normal", "relaxed", "loose"],
586
+ opacity: [
587
+ "0",
588
+ "5",
589
+ "10",
590
+ "15",
591
+ "20",
592
+ "25",
593
+ "30",
594
+ "35",
595
+ "40",
596
+ "45",
597
+ "50",
598
+ "55",
599
+ "60",
600
+ "65",
601
+ "70",
602
+ "75",
603
+ "80",
604
+ "85",
605
+ "90",
606
+ "95",
607
+ "100"
608
+ ],
609
+ z: ["0", "10", "20", "30", "40", "50", "auto"],
610
+ blur: ["none", "xs", "sm", "md", "lg", "xl", "2xl", "3xl"],
611
+ animate: ["none", "spin", "ping", "pulse", "bounce"],
612
+ ease: ["linear", "in", "out", "in-out"],
613
+ aspect: ["auto", "video", "square"]
614
+ };
615
+ var DEFAULT_PALETTE_FAMILIES = /* @__PURE__ */ new Set([
616
+ "red",
617
+ "orange",
618
+ "amber",
619
+ "yellow",
620
+ "lime",
621
+ "green",
622
+ "emerald",
623
+ "teal",
624
+ "cyan",
625
+ "sky",
626
+ "blue",
627
+ "indigo",
628
+ "violet",
629
+ "purple",
630
+ "fuchsia",
631
+ "pink",
632
+ "rose",
633
+ "slate",
634
+ "gray",
635
+ "zinc",
636
+ "neutral",
637
+ "stone"
638
+ ]);
639
+ var DEFAULT_PALETTE_SHADES = /* @__PURE__ */ new Set(["50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "950"]);
640
+ var DEFAULT_COLOR_SPECIALS = /* @__PURE__ */ new Set(["white", "black", "transparent", "current", "inherit"]);
641
+ var NUMERIC_TOKEN_RE = /^-?(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
642
+ var SIDE_WIDTH_TOKEN_RE = /^(?:x|y|t|r|b|l|s|e)-(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
643
+ var OFFSET_TOKEN_RE = /^offset-(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
644
+ function stylesheetFiles(dir) {
645
+ let entries;
646
+ try {
647
+ entries = readdirSync(dir);
648
+ } catch {
649
+ return [];
650
+ }
651
+ return entries.flatMap((entry) => {
652
+ if (entry.startsWith(".") || entry === "node_modules") return [];
653
+ const entryPath = path4.join(dir, entry);
654
+ let stats;
655
+ try {
656
+ stats = statSync(entryPath);
657
+ } catch {
658
+ return [];
659
+ }
660
+ if (stats.isDirectory()) return stylesheetFiles(entryPath);
661
+ return path4.extname(entry) === ".css" ? [entryPath] : [];
662
+ });
663
+ }
664
+ function themeBlocks(css2) {
665
+ const blocks = [];
666
+ let index = 0;
667
+ while (index < css2.length) {
668
+ const match = /@theme(?:\s+inline)?\s*\{/.exec(css2.slice(index));
669
+ if (!match) break;
670
+ const open = index + match.index + match[0].length - 1;
671
+ let depth = 1;
672
+ let cursor = open + 1;
673
+ for (; cursor < css2.length && depth > 0; cursor++) {
674
+ if (css2[cursor] === "{") depth++;
675
+ else if (css2[cursor] === "}") depth--;
676
+ }
677
+ blocks.push(css2.slice(open + 1, cursor - 1));
678
+ index = cursor;
679
+ }
680
+ return blocks;
681
+ }
682
+ function readInventory(files) {
683
+ const utilities = /* @__PURE__ */ new Set();
684
+ const utilityPrefixes = /* @__PURE__ */ new Set();
685
+ const themeTokensByNamespace = /* @__PURE__ */ new Map();
686
+ let defaultsReset = false;
687
+ for (const file of files) {
688
+ let css2;
689
+ try {
690
+ css2 = readFileSync(file, "utf8");
691
+ } catch {
692
+ continue;
693
+ }
694
+ for (const name of css2.matchAll(/@utility\s+(?<utilityName>[a-zA-Z0-9_-]+)/g)) {
695
+ const utilityName = name.groups?.utilityName;
696
+ if (!utilityName) continue;
697
+ utilities.add(utilityName);
698
+ const dash = utilityName.indexOf("-");
699
+ if (dash > 0) utilityPrefixes.add(utilityName.slice(0, dash));
700
+ }
701
+ for (const block of themeBlocks(css2)) {
702
+ if (/--\*\s*:\s*initial\b/.test(block)) defaultsReset = true;
703
+ for (const decl of block.matchAll(/--(?<namespace>[a-z][a-z0-9]*)-(?<token>[a-z0-9][a-z0-9_-]*)\s*:/g)) {
704
+ const namespace = decl.groups?.namespace;
705
+ const token = decl.groups?.token;
706
+ if (!namespace || !token) continue;
707
+ let set = themeTokensByNamespace.get(namespace);
708
+ if (!set) {
709
+ set = /* @__PURE__ */ new Set();
710
+ themeTokensByNamespace.set(namespace, set);
711
+ }
712
+ set.add(token);
713
+ }
714
+ }
715
+ }
716
+ return { utilities, utilityPrefixes, themeTokens: themeTokensByNamespace, defaultsReset };
717
+ }
718
+ function isColorToken(token, inventory) {
719
+ if (inventory.themeTokens.get("color")?.has(token)) return true;
720
+ if (inventory.defaultsReset) return false;
721
+ if (DEFAULT_COLOR_SPECIALS.has(token)) return true;
722
+ const hyphen = token.lastIndexOf("-");
723
+ if (hyphen === -1) return false;
724
+ return DEFAULT_PALETTE_FAMILIES.has(token.slice(0, hyphen)) && DEFAULT_PALETTE_SHADES.has(token.slice(hyphen + 1));
725
+ }
726
+ function isKnown(className, inventory) {
727
+ if (className.includes("[") || className.includes("(")) return true;
728
+ const base = className.replace(/!+$/, "").split("/")[0] ?? "";
729
+ const segments = base.split(":");
730
+ const utility = segments[segments.length - 1] ?? "";
731
+ if (!utility.includes("-")) return true;
732
+ const dash = utility.indexOf("-");
733
+ const firstTwo = utility.split("-").slice(0, 2).join("-");
734
+ const compound = PREFIX_NAMESPACES[firstTwo] ? firstTwo : void 0;
735
+ const prefix = compound ?? utility.slice(0, dash);
736
+ const token = compound ? utility.slice(compound.length + 1) : utility.slice(dash + 1);
737
+ if (!token) return true;
738
+ if (inventory.utilities.has(utility)) return true;
739
+ const namespaces = PREFIX_NAMESPACES[prefix];
740
+ if (!namespaces) {
741
+ const projectTokens = inventory.themeTokens.get(prefix);
742
+ if (projectTokens) {
743
+ if (projectTokens.has(token)) return true;
744
+ if (!inventory.defaultsReset && DEFAULT_TOKENS[prefix]?.includes(token)) return true;
745
+ return false;
746
+ }
747
+ return !inventory.utilityPrefixes.has(prefix);
748
+ }
749
+ if (NUMERIC_TOKEN_RE.test(token) || SIDE_WIDTH_TOKEN_RE.test(token) || OFFSET_TOKEN_RE.test(token)) return true;
750
+ if (PREFIX_BUILTINS[prefix]?.includes(token)) return true;
751
+ for (const namespace of namespaces) {
752
+ if (namespace === "color") {
753
+ if (isColorToken(token, inventory)) return true;
754
+ continue;
755
+ }
756
+ if (inventory.themeTokens.get(namespace)?.has(token)) return true;
757
+ if (!inventory.defaultsReset && DEFAULT_TOKENS[namespace]?.includes(token)) return true;
758
+ }
759
+ return false;
760
+ }
761
+ var CLASS_HELPERS2 = /* @__PURE__ */ new Set(["cn", "clsx", "twMerge", "twJoin"]);
762
+ var unknownUtilityRule = {
763
+ meta: {
764
+ schema: [],
765
+ type: "problem",
766
+ docs: {
767
+ description: "Require component class names to be a custom utility, theme-generated utility, or built-in."
768
+ }
769
+ },
770
+ create(context) {
771
+ const sourceRoot2 = path4.resolve("src");
772
+ let inventory;
773
+ try {
774
+ if (!statSync(sourceRoot2).isDirectory()) return {};
775
+ inventory = readInventory(stylesheetFiles(sourceRoot2));
776
+ } catch {
777
+ return {};
778
+ }
779
+ function reportUnknownClasses(node, value) {
780
+ const seen = /* @__PURE__ */ new Set();
781
+ for (const candidate of value.split(/\s+/)) {
782
+ if (!candidate || seen.has(candidate)) continue;
783
+ seen.add(candidate);
784
+ if (!isKnown(candidate, inventory)) {
785
+ context.report({
786
+ node,
787
+ message: `Utility class "${candidate}" is not a custom @utility, a theme-generated utility, or a built-in Tailwind utility.`
788
+ });
789
+ }
790
+ }
791
+ }
792
+ function checkExpression(node, expression) {
793
+ if (!expression) return;
794
+ if (expression.type === "Literal") {
795
+ if (typeof expression.value === "string") reportUnknownClasses(node, expression.value);
796
+ return;
797
+ }
798
+ if (expression.type === "TemplateLiteral") {
799
+ for (const quasi of expression.quasis) reportUnknownClasses(node, quasi.value.raw);
800
+ return;
801
+ }
802
+ if (expression.type === "LogicalExpression") {
803
+ checkExpression(node, expression.left);
804
+ checkExpression(node, expression.right);
805
+ return;
806
+ }
807
+ if (expression.type === "ConditionalExpression") {
808
+ checkExpression(node, expression.consequent);
809
+ checkExpression(node, expression.alternate);
810
+ return;
811
+ }
812
+ if (expression.type === "ArrayExpression") {
813
+ for (const element of expression.elements) checkExpression(node, element);
814
+ return;
815
+ }
816
+ if (expression.type === "ObjectExpression") {
817
+ for (const property of expression.properties) {
818
+ if (property.type === "Property") checkExpression(node, property.key);
819
+ }
820
+ }
821
+ }
822
+ return {
823
+ JSXAttribute(node) {
824
+ const attributeName = node.name?.name;
825
+ if (attributeName !== "className" && attributeName !== "class") return;
826
+ const value = node.value;
827
+ if (!value) return;
828
+ checkExpression(node, value.type === "JSXExpressionContainer" ? value.expression : value);
829
+ },
830
+ CallExpression(node) {
831
+ if (node.callee.type !== "Identifier" || !CLASS_HELPERS2.has(node.callee.name)) return;
832
+ for (const argument of node.arguments) {
833
+ checkExpression(node, argument);
834
+ }
835
+ }
836
+ };
837
+ }
838
+ };
839
+
481
840
  // eslint/rules/enforce-cn-merge.ts
482
841
  function classCount(str) {
483
842
  return str.split(/\s+/).filter(Boolean).length;
@@ -494,7 +853,9 @@ function isOuterLayoutClass(className) {
494
853
  function stringArguments(node) {
495
854
  if (node?.type === "Literal" && typeof node.value === "string") return [node.value];
496
855
  if (node?.type === "TemplateLiteral") return node.quasis.map((quasi) => quasi.value.raw);
497
- if (node?.type === "ConditionalExpression") return [...stringArguments(node.consequent), ...stringArguments(node.alternate)];
856
+ if (node?.type === "ConditionalExpression") {
857
+ return [...stringArguments(node.consequent), ...stringArguments(node.alternate)];
858
+ }
498
859
  if (node?.type === "LogicalExpression") return [...stringArguments(node.left), ...stringArguments(node.right)];
499
860
  if (node?.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "cn") {
500
861
  return node.arguments.flatMap((argument) => stringArguments(argument));
@@ -510,7 +871,10 @@ var enforceCnMergeRule = {
510
871
  const element = node.parent.parent;
511
872
  const isComponentProp = isComponentName(element.openingElement?.name);
512
873
  if (isComponentProp && attributeName !== "className" && attributeName.endsWith("ClassName")) {
513
- context.report({ node, message: "Expose appearance through typed variant props instead of separate internal class-name props. See docs/styling-guide/rules/class-composition-rule.md" });
874
+ context.report({
875
+ node,
876
+ message: "Expose appearance through typed variant props instead of separate internal class-name props. See docs/styling-guide/rules/class-composition-rule.md"
877
+ });
514
878
  return;
515
879
  }
516
880
  if (attributeName !== "className" && attributeName !== "class") return;
@@ -519,31 +883,60 @@ var enforceCnMergeRule = {
519
883
  if (isComponentProp && attributeName === "className") {
520
884
  const expression = valueNode.type === "JSXExpressionContainer" ? valueNode.expression : valueNode;
521
885
  const invalidClass = stringArguments(expression).flatMap((value) => value.split(/\s+/).filter(Boolean)).find((className) => !isOuterLayoutClass(className));
522
- if (invalidClass) context.report({ node, message: `Passed className contains non-layout utility "${invalidClass}". Expose appearance through typed variant props. See docs/styling-guide/rules/class-composition-rule.md` });
886
+ if (invalidClass) {
887
+ context.report({
888
+ node,
889
+ message: `Passed className contains non-layout utility "${invalidClass}". Expose appearance through typed variant props. See docs/styling-guide/rules/class-composition-rule.md`
890
+ });
891
+ }
523
892
  return;
524
893
  }
525
894
  if (valueNode.type === "Literal" && typeof valueNode.value === "string") {
526
- if (classCount(valueNode.value) > 5) context.report({ node, message: "Static className with more than 5 classes must use cn() with grouped string literals. See docs/styling-guide/rules/class-composition-rule.md" });
895
+ if (classCount(valueNode.value) > 5) {
896
+ context.report({
897
+ node,
898
+ message: "Static className with more than 5 classes must use cn() with grouped string literals. See docs/styling-guide/rules/class-composition-rule.md"
899
+ });
900
+ }
527
901
  return;
528
902
  }
529
903
  if (valueNode.type !== "JSXExpressionContainer") return;
530
904
  const expr = valueNode.expression;
531
905
  if (expr.type === "BinaryExpression" && expr.operator === "+") {
532
- context.report({ node, message: "Use cn() instead of + operator for className. See docs/styling-guide/rules/class-composition-rule.md" });
906
+ context.report({
907
+ node,
908
+ message: "Use cn() instead of + operator for className. See docs/styling-guide/rules/class-composition-rule.md"
909
+ });
533
910
  return;
534
911
  }
535
912
  if (expr.type === "TemplateLiteral" && expr.expressions.length > 0) {
536
- context.report({ node, message: "Use cn() instead of template literals with conditionals for className. See docs/styling-guide/rules/class-composition-rule.md" });
913
+ context.report({
914
+ node,
915
+ message: "Use cn() instead of template literals with conditionals for className. See docs/styling-guide/rules/class-composition-rule.md"
916
+ });
917
+ return;
918
+ }
919
+ if (expr.type === "LogicalExpression" || expr.type === "ConditionalExpression") {
920
+ context.report({
921
+ node,
922
+ message: "Use cn() for conditional classes in className. See docs/styling-guide/rules/class-composition-rule.md"
923
+ });
537
924
  return;
538
925
  }
539
926
  if (expr.type === "Literal" && typeof expr.value === "string" && classCount(expr.value) > 5) {
540
- context.report({ node, message: "Static className with more than 5 classes must use cn() with grouped string literals. See docs/styling-guide/rules/class-composition-rule.md" });
927
+ context.report({
928
+ node,
929
+ message: "Static className with more than 5 classes must use cn() with grouped string literals. See docs/styling-guide/rules/class-composition-rule.md"
930
+ });
541
931
  return;
542
932
  }
543
933
  if (expr.type === "CallExpression" && expr.callee.type === "Identifier" && expr.callee.name === "cn") {
544
934
  for (const arg of expr.arguments) {
545
935
  if (arg.type === "Literal" && typeof arg.value === "string" && classCount(arg.value) > 5) {
546
- context.report({ node: arg, message: "Each cn() string argument must contain at most 5 class names. Group by styling concern. See docs/styling-guide/rules/class-composition-rule.md" });
936
+ context.report({
937
+ node: arg,
938
+ message: "Each cn() string argument must contain at most 5 class names. Group by styling concern. See docs/styling-guide/rules/class-composition-rule.md"
939
+ });
547
940
  }
548
941
  }
549
942
  }
@@ -653,7 +1046,7 @@ var enforceCvaVariantPropsRule = {
653
1046
  };
654
1047
 
655
1048
  // eslint/rules/enforce-barrel-exports.ts
656
- import path4 from "path";
1049
+ import path5 from "path";
657
1050
  import fs from "fs";
658
1051
  function isPascalCase3(str) {
659
1052
  return /^[A-Z][A-Za-z0-9]*$/.test(str);
@@ -673,14 +1066,14 @@ var enforceBarrelExportsRule = {
673
1066
  create(context) {
674
1067
  const filename = context.filename;
675
1068
  if (!filename) return {};
676
- const baseName = path4.basename(filename);
1069
+ const baseName = path5.basename(filename);
677
1070
  if (baseName !== "index.ts" && baseName !== "index.cts" && baseName !== "index.mts") return {};
678
- const dirPath = path4.dirname(filename);
679
- const folderName = path4.basename(dirPath);
680
- const parentFolderName = path4.basename(path4.dirname(dirPath));
1071
+ const dirPath = path5.dirname(filename);
1072
+ const folderName = path5.basename(dirPath);
1073
+ const parentFolderName = path5.basename(path5.dirname(dirPath));
681
1074
  if (SUPPORT_FOLDERS.has(folderName)) return {};
682
1075
  if (!isPascalCase3(folderName) && !isKebabCase2(folderName)) return {};
683
- const matchingTsx = fs.existsSync(path4.join(dirPath, `${folderName}.tsx`)) ? folderName : null;
1076
+ const matchingTsx = fs.existsSync(path5.join(dirPath, `${folderName}.tsx`)) ? folderName : null;
684
1077
  if (!matchingTsx) return {};
685
1078
  if (!isPascalCase3(parentFolderName) && !isKebabCase2(parentFolderName)) return {};
686
1079
  const reExportedNames = /* @__PURE__ */ new Set();
@@ -715,15 +1108,15 @@ var enforceBarrelExportsRule = {
715
1108
  };
716
1109
 
717
1110
  // eslint/rules/component-placement.ts
718
- import path8 from "path";
1111
+ import path9 from "path";
719
1112
 
720
1113
  // eslint/project/index.ts
721
- import { readdirSync, statSync } from "fs";
722
- import path6 from "path";
1114
+ import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1115
+ import path7 from "path";
723
1116
 
724
1117
  // eslint/project/parse-module.ts
725
- import path5 from "path";
726
- import { readFileSync } from "fs";
1118
+ import path6 from "path";
1119
+ import { readFileSync as readFileSync2 } from "fs";
727
1120
  import ts2 from "typescript";
728
1121
  var isPascalCase4 = (name) => /^[A-Z][A-Za-z0-9]*$/.test(name);
729
1122
  var isHookName = (name) => /^use[A-Z]/.test(name);
@@ -760,7 +1153,7 @@ function classifyValue(name, initializer, isTsx) {
760
1153
  return "constant";
761
1154
  }
762
1155
  function parseModule(file) {
763
- const text = readFileSync(file, "utf8");
1156
+ const text = readFileSync2(file, "utf8");
764
1157
  const isTsx = file.endsWith(".tsx") || file.endsWith(".jsx");
765
1158
  const sourceFile = ts2.createSourceFile(file, text, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
766
1159
  const imports = [];
@@ -823,7 +1216,7 @@ function parseModule(file) {
823
1216
  exports.push({ name: statement.name.text, kind: "type", line: lineOf(sourceFile, statement) });
824
1217
  }
825
1218
  }
826
- return { file: path5.resolve(file), imports, exports };
1219
+ return { file: path6.resolve(file), imports, exports };
827
1220
  }
828
1221
 
829
1222
  // eslint/project/index.ts
@@ -834,28 +1227,28 @@ var symbolKey = (file, name) => `${file}\0${name}`;
834
1227
  function listSourceFiles(dir) {
835
1228
  let entries;
836
1229
  try {
837
- entries = readdirSync(dir);
1230
+ entries = readdirSync2(dir);
838
1231
  } catch {
839
1232
  return [];
840
1233
  }
841
1234
  return entries.flatMap((entry) => {
842
1235
  if (entry.startsWith(".") || entry === "node_modules") return [];
843
- const entryPath = path6.join(dir, entry);
1236
+ const entryPath = path7.join(dir, entry);
844
1237
  let stats;
845
1238
  try {
846
- stats = statSync(entryPath);
1239
+ stats = statSync2(entryPath);
847
1240
  } catch {
848
1241
  return [];
849
1242
  }
850
1243
  if (stats.isDirectory()) return listSourceFiles(entryPath);
851
- return MODULE_EXTENSIONS.includes(path6.extname(entry)) ? [entryPath] : [];
1244
+ return MODULE_EXTENSIONS.includes(path7.extname(entry)) ? [entryPath] : [];
852
1245
  });
853
1246
  }
854
1247
  function fingerprint(files) {
855
1248
  let total = 0;
856
1249
  for (const file of files) {
857
1250
  try {
858
- total += statSync(file).mtimeMs;
1251
+ total += statSync2(file).mtimeMs;
859
1252
  } catch {
860
1253
  }
861
1254
  }
@@ -864,20 +1257,20 @@ function fingerprint(files) {
864
1257
  function resolveSpecifier(fromFile, specifier, sourceRoot2) {
865
1258
  let base;
866
1259
  if (specifier.startsWith("@/")) {
867
- base = path6.resolve(sourceRoot2, specifier.slice(2));
1260
+ base = path7.resolve(sourceRoot2, specifier.slice(2));
868
1261
  } else if (specifier.startsWith(".")) {
869
- base = path6.resolve(path6.dirname(fromFile), specifier);
1262
+ base = path7.resolve(path7.dirname(fromFile), specifier);
870
1263
  } else {
871
1264
  return void 0;
872
1265
  }
873
1266
  const candidates = [
874
1267
  base,
875
1268
  ...MODULE_EXTENSIONS.map((extension) => `${base}${extension}`),
876
- ...INDEX_BASENAMES.map((name) => path6.join(base, name))
1269
+ ...INDEX_BASENAMES.map((name) => path7.join(base, name))
877
1270
  ];
878
1271
  for (const candidate of candidates) {
879
1272
  try {
880
- if (statSync(candidate).isFile()) return candidate;
1273
+ if (statSync2(candidate).isFile()) return candidate;
881
1274
  } catch {
882
1275
  }
883
1276
  }
@@ -917,7 +1310,7 @@ function getProjectIndex(sourceRoot2) {
917
1310
  return cache.index;
918
1311
  }
919
1312
  try {
920
- if (!statSync(sourceRoot2).isDirectory()) return void 0;
1313
+ if (!statSync2(sourceRoot2).isDirectory()) return void 0;
921
1314
  } catch {
922
1315
  return void 0;
923
1316
  }
@@ -933,11 +1326,11 @@ function getProjectIndex(sourceRoot2) {
933
1326
  }
934
1327
 
935
1328
  // eslint/project/ccf.ts
936
- import path7 from "path";
1329
+ import path8 from "path";
937
1330
  var SUPPORT_FOLDERS2 = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
938
1331
  function segmentsOf(file, sourceRoot2) {
939
- const relative = path7.relative(sourceRoot2, file);
940
- return relative.startsWith("..") ? [] : relative.split(path7.sep);
1332
+ const relative = path8.relative(sourceRoot2, file);
1333
+ return relative.startsWith("..") ? [] : relative.split(path8.sep);
941
1334
  }
942
1335
  function folderSegmentsOf(file, sourceRoot2) {
943
1336
  return segmentsOf(file, sourceRoot2).slice(0, -1);
@@ -1016,7 +1409,7 @@ function resolveSupportPlacement(supportFile, supportFolder, index) {
1016
1409
  }
1017
1410
  function describeConsumers(consumers, sourceRoot2) {
1018
1411
  const shown = 3;
1019
- const names = consumers.map((consumer) => path7.relative(path7.dirname(sourceRoot2), consumer).split(path7.sep).join("/")).sort((left, right) => left.localeCompare(right));
1412
+ const names = consumers.map((consumer) => path8.relative(path8.dirname(sourceRoot2), consumer).split(path8.sep).join("/")).sort((left, right) => left.localeCompare(right));
1020
1413
  if (names.length <= shown) return names.join(", ");
1021
1414
  return `${names.slice(0, shown).join(", ")} and ${String(names.length - shown)} more`;
1022
1415
  }
@@ -1039,10 +1432,10 @@ var componentPlacementRule = {
1039
1432
  create(context) {
1040
1433
  const filename = context.filename;
1041
1434
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
1042
- const sourceRoot2 = path8.resolve("src");
1435
+ const sourceRoot2 = path9.resolve("src");
1043
1436
  const index = getProjectIndex(sourceRoot2);
1044
1437
  if (!index) return {};
1045
- const componentFile = path8.resolve(filename);
1438
+ const componentFile = path9.resolve(filename);
1046
1439
  const segments = segmentsOf(componentFile, sourceRoot2);
1047
1440
  if (segments.length === 0) return {};
1048
1441
  if (isUnderApp(segments) || isConfigModule(segments)) return {};
@@ -1075,7 +1468,7 @@ var componentPlacementRule = {
1075
1468
  };
1076
1469
 
1077
1470
  // eslint/rules/support-file-placement.ts
1078
- import path9 from "path";
1471
+ import path10 from "path";
1079
1472
  var CONFIG_OWNED_FOLDERS = /* @__PURE__ */ new Set(["types", "constants"]);
1080
1473
  var REASON_TEXT2 = {
1081
1474
  "app-consumer": "a file under src/app/ imports it, so it belongs to the app-wide support folder",
@@ -1094,8 +1487,8 @@ var supportFilePlacementRule = {
1094
1487
  }
1095
1488
  },
1096
1489
  create(context) {
1097
- const sourceRoot2 = path9.resolve("src");
1098
- const supportFile = path9.resolve(context.filename);
1490
+ const sourceRoot2 = path10.resolve("src");
1491
+ const supportFile = path10.resolve(context.filename);
1099
1492
  const currentFolder = folderSegmentsOf(supportFile, sourceRoot2);
1100
1493
  const supportFolder = currentFolder[currentFolder.length - 1];
1101
1494
  if (supportFolder === void 0 || !SUPPORT_FOLDERS2.has(supportFolder)) return {};
@@ -1120,7 +1513,7 @@ var supportFilePlacementRule = {
1120
1513
 
1121
1514
  // eslint/rules/application-structure.ts
1122
1515
  import fs2 from "fs";
1123
- import path10 from "path";
1516
+ import path11 from "path";
1124
1517
  var MODULE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
1125
1518
  var ROUTING_FILES = /* @__PURE__ */ new Set([
1126
1519
  "default",
@@ -1149,7 +1542,7 @@ function report2(context, message) {
1149
1542
  };
1150
1543
  }
1151
1544
  function isCodeFile(filename) {
1152
- return MODULE_EXTENSIONS2.has(path10.extname(filename));
1545
+ return MODULE_EXTENSIONS2.has(path11.extname(filename));
1153
1546
  }
1154
1547
  function isRootSupportFolder(folder) {
1155
1548
  return SUPPORT_FOLDERS2.has(folder);
@@ -1170,7 +1563,7 @@ function expectedSupportFolder(kinds) {
1170
1563
  function configModuleRoot(filename, sourceRoot2) {
1171
1564
  const segments = segmentsOf(filename, sourceRoot2);
1172
1565
  if (segments[0] !== "config" || segments.length < 3) return void 0;
1173
- return path10.join(sourceRoot2, "config", segments[1] ?? "");
1566
+ return path11.join(sourceRoot2, "config", segments[1] ?? "");
1174
1567
  }
1175
1568
  function componentFolderStart(segments) {
1176
1569
  if (segments[0] === "features") return 2;
@@ -1183,12 +1576,12 @@ function componentFolderViolation(segments, sourceRoot2) {
1183
1576
  for (let depth = segments.length - 2; depth >= start; depth -= 1) {
1184
1577
  const folder = segments[depth];
1185
1578
  if (!folder || SUPPORT_FOLDERS2.has(folder)) continue;
1186
- const folderPath = path10.join(sourceRoot2, ...segments.slice(0, depth + 1));
1579
+ const folderPath = path11.join(sourceRoot2, ...segments.slice(0, depth + 1));
1187
1580
  const label = `src/${segments.slice(0, depth + 1).join("/")}/`;
1188
- if (!fs2.existsSync(path10.join(folderPath, `${folder}.tsx`))) {
1581
+ if (!fs2.existsSync(path11.join(folderPath, `${folder}.tsx`))) {
1189
1582
  return `A folder that is not a support folder must be a component folder; add "${folder}.tsx" to ${label} or move its files into a support folder.`;
1190
1583
  }
1191
- if (!fs2.existsSync(path10.join(folderPath, "index.ts"))) {
1584
+ if (!fs2.existsSync(path11.join(folderPath, "index.ts"))) {
1192
1585
  return `A component folder must have an index.ts that named-re-exports its component; add index.ts to ${label}.`;
1193
1586
  }
1194
1587
  }
@@ -1203,8 +1596,8 @@ var applicationStructureRule = {
1203
1596
  }
1204
1597
  },
1205
1598
  create(context) {
1206
- const filename = path10.resolve(context.filename);
1207
- const sourceRoot2 = path10.resolve("src");
1599
+ const filename = path11.resolve(context.filename);
1600
+ const sourceRoot2 = path11.resolve("src");
1208
1601
  const segments = segmentsOf(filename, sourceRoot2);
1209
1602
  if (segments.length === 0) return {};
1210
1603
  const [topLevel, secondLevel] = segments;
@@ -1234,42 +1627,42 @@ var applicationStructureRule = {
1234
1627
  );
1235
1628
  }
1236
1629
  const moduleRoot = configModuleRoot(filename, sourceRoot2);
1237
- if (moduleRoot && !fs2.existsSync(path10.join(moduleRoot, "index.ts"))) {
1630
+ if (moduleRoot && !fs2.existsSync(path11.join(moduleRoot, "index.ts"))) {
1238
1631
  return report2(
1239
1632
  context,
1240
- `Add src/config/${path10.basename(moduleRoot)}/index.ts as the configuration module entry point.`
1633
+ `Add src/config/${path11.basename(moduleRoot)}/index.ts as the configuration module entry point.`
1241
1634
  );
1242
1635
  }
1243
- if (moduleRoot && segments.length === 3 && path10.basename(filename) !== "index.ts") {
1636
+ if (moduleRoot && segments.length === 3 && path11.basename(filename) !== "index.ts") {
1244
1637
  const kinds2 = exportedKinds(filename);
1245
1638
  const expected2 = expectedSupportFolder(kinds2);
1246
1639
  if (expected2 !== void 0) {
1247
1640
  return report2(
1248
1641
  context,
1249
- `Move this configuration support file into src/config/${path10.basename(moduleRoot)}/${expected2}/.`
1642
+ `Move this configuration support file into src/config/${path11.basename(moduleRoot)}/${expected2}/.`
1250
1643
  );
1251
1644
  }
1252
1645
  }
1253
1646
  }
1254
1647
  if (topLevel === "app" && isCodeFile(filename)) {
1255
- const basename = path10.basename(filename, path10.extname(filename));
1256
- const currentFolder2 = path10.basename(path10.dirname(filename));
1648
+ const basename = path11.basename(filename, path11.extname(filename));
1649
+ const currentFolder2 = path11.basename(path11.dirname(filename));
1257
1650
  if (SUPPORT_FOLDERS2.has(currentFolder2)) {
1258
1651
  return report2(
1259
1652
  context,
1260
1653
  "src/app/ may contain routing files and framework assets, but ordinary components and support files must live outside src/app/."
1261
1654
  );
1262
1655
  }
1263
- if (!ROUTING_FILES.has(basename) && path10.extname(filename) !== ".css") {
1656
+ if (!ROUTING_FILES.has(basename) && path11.extname(filename) !== ".css") {
1264
1657
  return report2(
1265
1658
  context,
1266
1659
  "src/app/ may contain routing files and framework assets, but ordinary components and support files must live outside src/app/."
1267
1660
  );
1268
1661
  }
1269
1662
  }
1270
- const currentFolder = path10.basename(path10.dirname(filename));
1663
+ const currentFolder = path11.basename(path11.dirname(filename));
1271
1664
  const kinds = exportedKinds(filename);
1272
- const isConfigModuleRoot = topLevel === "config" && segments.length === 3 && path10.basename(filename) === "index.ts";
1665
+ const isConfigModuleRoot = topLevel === "config" && segments.length === 3 && path11.basename(filename) === "index.ts";
1273
1666
  if (isConfigModuleRoot) return {};
1274
1667
  if (!SUPPORT_FOLDERS2.has(currentFolder)) {
1275
1668
  const expected2 = expectedSupportFolder(kinds);
@@ -1286,7 +1679,7 @@ var applicationStructureRule = {
1286
1679
  if (kinds.has("component")) {
1287
1680
  return report2(
1288
1681
  context,
1289
- `A support folder must not contain a component; move ${path10.basename(filename)} beside ${currentFolder}/.`
1682
+ `A support folder must not contain a component; move ${path11.basename(filename)} beside ${currentFolder}/.`
1290
1683
  );
1291
1684
  }
1292
1685
  const expected = expectedSupportFolder(kinds);
@@ -1303,7 +1696,7 @@ var applicationStructureRule = {
1303
1696
  };
1304
1697
 
1305
1698
  // eslint/rules/named-exports.ts
1306
- import path11 from "path";
1699
+ import path12 from "path";
1307
1700
  var FRAMEWORK_DEFAULT_EXPORT_FILES = /* @__PURE__ */ new Set([
1308
1701
  "default",
1309
1702
  "error",
@@ -1315,9 +1708,9 @@ var FRAMEWORK_DEFAULT_EXPORT_FILES = /* @__PURE__ */ new Set([
1315
1708
  "template"
1316
1709
  ]);
1317
1710
  function isFrameworkDefaultExportFile(filename) {
1318
- const normalized = filename.replaceAll(path11.sep, "/");
1711
+ const normalized = filename.replaceAll(path12.sep, "/");
1319
1712
  if (!normalized.includes("/src/app/")) return false;
1320
- const basename = path11.basename(filename, path11.extname(filename));
1713
+ const basename = path12.basename(filename, path12.extname(filename));
1321
1714
  return FRAMEWORK_DEFAULT_EXPORT_FILES.has(basename);
1322
1715
  }
1323
1716
  var namedExportsRule = {
@@ -1342,7 +1735,7 @@ var namedExportsRule = {
1342
1735
  };
1343
1736
 
1344
1737
  // eslint/rules/data-testid-case.ts
1345
- import path12 from "path";
1738
+ import path13 from "path";
1346
1739
  var NEXT_ROUTING_FILES2 = /* @__PURE__ */ new Set([
1347
1740
  "page",
1348
1741
  "layout",
@@ -1366,9 +1759,9 @@ var dataTestIdCaseRule = {
1366
1759
  }
1367
1760
  },
1368
1761
  create(context) {
1369
- const filename = path12.resolve(context.filename);
1762
+ const filename = path13.resolve(context.filename);
1370
1763
  if (!filename.endsWith(".tsx")) return {};
1371
- const base = path12.basename(filename, path12.extname(filename));
1764
+ const base = path13.basename(filename, path13.extname(filename));
1372
1765
  if (NEXT_ROUTING_FILES2.has(base)) return {};
1373
1766
  const text = context.sourceCode.text;
1374
1767
  const components = parseComponentInfo(text, filename);
@@ -1410,7 +1803,7 @@ function toKebabCase(value) {
1410
1803
 
1411
1804
  // eslint/rules/support-folder-shape.ts
1412
1805
  import fs3 from "fs";
1413
- import path13 from "path";
1806
+ import path14 from "path";
1414
1807
  var SUPPORT_FOLDERS3 = /* @__PURE__ */ new Set(["constants", "types", "schemas"]);
1415
1808
  var INDEX_NAMES = /* @__PURE__ */ new Set(["index.ts", "index.tsx", "index.mts", "index.cts"]);
1416
1809
  var supportFolderShapeRule = {
@@ -1422,12 +1815,12 @@ var supportFolderShapeRule = {
1422
1815
  }
1423
1816
  },
1424
1817
  create(context) {
1425
- const filename = path13.resolve(context.filename);
1426
- const baseName = path13.basename(filename);
1818
+ const filename = path14.resolve(context.filename);
1819
+ const baseName = path14.basename(filename);
1427
1820
  if (!INDEX_NAMES.has(baseName)) return {};
1428
- const folder = path13.basename(path13.dirname(filename));
1821
+ const folder = path14.basename(path14.dirname(filename));
1429
1822
  if (!SUPPORT_FOLDERS3.has(folder)) return {};
1430
- const directory = path13.dirname(filename);
1823
+ const directory = path14.dirname(filename);
1431
1824
  let entries;
1432
1825
  try {
1433
1826
  entries = fs3.readdirSync(directory);
@@ -1445,7 +1838,7 @@ var supportFolderShapeRule = {
1445
1838
  const exportPattern = /export\s+(?:\{[^}]*\}|\*[^;]*)\s+from\s+["'](?<specifier>\.[^"']+)["']/g;
1446
1839
  for (const match of source.matchAll(exportPattern)) {
1447
1840
  const specifier = match.groups?.specifier;
1448
- if (specifier) exportedFiles.add(path13.basename(specifier));
1841
+ if (specifier) exportedFiles.add(path14.basename(specifier));
1449
1842
  }
1450
1843
  const missing = siblingModules.filter((entry) => {
1451
1844
  const stem = entry.replace(/\.(?:[cm]?tsx?|jsx?)$/, "");
@@ -1462,7 +1855,7 @@ var supportFolderShapeRule = {
1462
1855
  };
1463
1856
 
1464
1857
  // eslint/rules/import-through-index.ts
1465
- import path14 from "path";
1858
+ import path15 from "path";
1466
1859
  var importThroughIndexRule = {
1467
1860
  meta: {
1468
1861
  schema: [],
@@ -1472,7 +1865,7 @@ var importThroughIndexRule = {
1472
1865
  }
1473
1866
  },
1474
1867
  create(context) {
1475
- const filename = path14.resolve(context.filename);
1868
+ const filename = path15.resolve(context.filename);
1476
1869
  const sourceRoot2 = sourceRootOf(filename);
1477
1870
  return {
1478
1871
  Program(node) {
@@ -1484,7 +1877,7 @@ var importThroughIndexRule = {
1484
1877
  (segment) => ["constants", "types", "schemas"].includes(segment)
1485
1878
  );
1486
1879
  const supportFolder = supportFolderIndex >= 0 ? targetSegments[supportFolderIndex] : void 0;
1487
- if (!supportFolder || path14.basename(target).startsWith("index.")) continue;
1880
+ if (!supportFolder || path15.basename(target).startsWith("index.")) continue;
1488
1881
  const folderIndex = targetSegments.slice(0, supportFolderIndex + 1);
1489
1882
  const expected = `@/${folderIndex.join("/")}`;
1490
1883
  context.report({
@@ -1506,13 +1899,13 @@ function importSpecifiers(source) {
1506
1899
  return specifiers;
1507
1900
  }
1508
1901
  function sourceRootOf(filename) {
1509
- const marker = `${path14.sep}src${path14.sep}`;
1902
+ const marker = `${path15.sep}src${path15.sep}`;
1510
1903
  const srcIndex = filename.lastIndexOf(marker);
1511
- return srcIndex >= 0 ? filename.slice(0, srcIndex + marker.length - 1) : path14.resolve("src");
1904
+ return srcIndex >= 0 ? filename.slice(0, srcIndex + marker.length - 1) : path15.resolve("src");
1512
1905
  }
1513
1906
 
1514
1907
  // eslint/rules/util-file-name.ts
1515
- import path15 from "path";
1908
+ import path16 from "path";
1516
1909
  function toKebabCase2(value) {
1517
1910
  return value.replace(/(?<lower>[a-z0-9])(?<upper>[A-Z])/g, "$<lower>-$<upper>").replace(/(?<first>[A-Z])(?<rest>[A-Z][a-z])/g, "$<first>-$<rest>").toLowerCase();
1518
1911
  }
@@ -1525,7 +1918,7 @@ var utilFileNameRule = {
1525
1918
  }
1526
1919
  },
1527
1920
  create(context) {
1528
- const filename = path15.resolve(context.filename);
1921
+ const filename = path16.resolve(context.filename);
1529
1922
  const segments = filename.replace(/\\/g, "/").split("/");
1530
1923
  if (!segments.includes("utils")) return {};
1531
1924
  let module;
@@ -1539,13 +1932,13 @@ var utilFileNameRule = {
1539
1932
  const functionName = functions[0]?.name;
1540
1933
  if (!functionName) return {};
1541
1934
  const expected = toKebabCase2(functionName);
1542
- const actual = path15.basename(filename, path15.extname(filename));
1935
+ const actual = path16.basename(filename, path16.extname(filename));
1543
1936
  if (!expected || actual === expected) return {};
1544
1937
  return {
1545
1938
  Program(node) {
1546
1939
  context.report({
1547
1940
  node,
1548
- message: `A utility file exporting ${functionName} must be named ${expected}.${path15.extname(filename).slice(1)}.`
1941
+ message: `A utility file exporting ${functionName} must be named ${expected}.${path16.extname(filename).slice(1)}.`
1549
1942
  });
1550
1943
  }
1551
1944
  };
@@ -1553,7 +1946,7 @@ var utilFileNameRule = {
1553
1946
  };
1554
1947
 
1555
1948
  // eslint/rules/no-util-barrel.ts
1556
- import path16 from "path";
1949
+ import path17 from "path";
1557
1950
  var noUtilBarrelRule = {
1558
1951
  meta: {
1559
1952
  schema: [],
@@ -1563,7 +1956,7 @@ var noUtilBarrelRule = {
1563
1956
  }
1564
1957
  },
1565
1958
  create(context) {
1566
- const filename = path16.resolve(context.filename);
1959
+ const filename = path17.resolve(context.filename);
1567
1960
  const sourceRoot2 = sourceRootOf2(filename);
1568
1961
  return {
1569
1962
  Program(node) {
@@ -1572,7 +1965,7 @@ var noUtilBarrelRule = {
1572
1965
  if (!target) continue;
1573
1966
  const segments = target.replace(/\\/g, "/").split("/");
1574
1967
  const utilsIndex = segments.lastIndexOf("utils");
1575
- if (utilsIndex < 0 || !path16.basename(target).startsWith("index.")) continue;
1968
+ if (utilsIndex < 0 || !path17.basename(target).startsWith("index.")) continue;
1576
1969
  context.report({
1577
1970
  node,
1578
1971
  message: `Import utilities directly instead of through "${specifier}". See docs/code-organization-guide/rules/utilities-rule.md`
@@ -1592,9 +1985,9 @@ function importSpecifiers2(source) {
1592
1985
  return specifiers;
1593
1986
  }
1594
1987
  function sourceRootOf2(filename) {
1595
- const marker = `${path16.sep}src${path16.sep}`;
1988
+ const marker = `${path17.sep}src${path17.sep}`;
1596
1989
  const srcIndex = filename.lastIndexOf(marker);
1597
- return srcIndex >= 0 ? filename.slice(0, srcIndex + marker.length - 1) : path16.resolve("src");
1990
+ return srcIndex >= 0 ? filename.slice(0, srcIndex + marker.length - 1) : path17.resolve("src");
1598
1991
  }
1599
1992
 
1600
1993
  // eslint/rules/jsx-hygiene.ts
@@ -1970,12 +2363,12 @@ var cvaBooleanVariantsRule = {
1970
2363
  };
1971
2364
 
1972
2365
  // eslint/rules/cross-feature-import.ts
1973
- import path17 from "path";
2366
+ import path18 from "path";
1974
2367
  var FEATURES_SEGMENT = "features";
1975
2368
  function featureNameOf(resolvedPath, sourceRoot2) {
1976
- const relative = path17.relative(sourceRoot2, resolvedPath);
2369
+ const relative = path18.relative(sourceRoot2, resolvedPath);
1977
2370
  if (relative.startsWith("..")) return void 0;
1978
- const segments = relative.split(path17.sep);
2371
+ const segments = relative.split(path18.sep);
1979
2372
  if (segments[0] !== FEATURES_SEGMENT || segments.length < 2) return void 0;
1980
2373
  return segments[1];
1981
2374
  }
@@ -1990,10 +2383,10 @@ var crossFeatureImportRule = {
1990
2383
  create(context) {
1991
2384
  const filename = context.filename;
1992
2385
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
1993
- const sourceRoot2 = path17.resolve("src");
1994
- const fileRelative = path17.relative(sourceRoot2, filename);
2386
+ const sourceRoot2 = path18.resolve("src");
2387
+ const fileRelative = path18.relative(sourceRoot2, filename);
1995
2388
  if (fileRelative.startsWith("..")) return {};
1996
- const fileSegments = fileRelative.split(path17.sep);
2389
+ const fileSegments = fileRelative.split(path18.sep);
1997
2390
  const isInCompositions = fileSegments[0] === "compositions";
1998
2391
  const isInApp = fileSegments[0] === "app";
1999
2392
  const isConfig = fileSegments[0] === "config";
@@ -2007,9 +2400,9 @@ var crossFeatureImportRule = {
2007
2400
  if (typeof source.value !== "string") return;
2008
2401
  let resolved;
2009
2402
  if (source.value.startsWith("@/")) {
2010
- resolved = path17.resolve(sourceRoot2, source.value.slice(2));
2403
+ resolved = path18.resolve(sourceRoot2, source.value.slice(2));
2011
2404
  } else if (source.value.startsWith(".")) {
2012
- resolved = path17.resolve(path17.dirname(filename), source.value);
2405
+ resolved = path18.resolve(path18.dirname(filename), source.value);
2013
2406
  }
2014
2407
  if (!resolved) return;
2015
2408
  const feature = featureNameOf(resolved, sourceRoot2);
@@ -2028,7 +2421,7 @@ var crossFeatureImportRule = {
2028
2421
  };
2029
2422
 
2030
2423
  // eslint/rules/pure-function-extract.ts
2031
- import path18 from "path";
2424
+ import path19 from "path";
2032
2425
  function isComponentLikeName(name) {
2033
2426
  return /^[A-Z]/.test(name);
2034
2427
  }
@@ -2056,10 +2449,10 @@ var pureFunctionExtractRule = {
2056
2449
  create(context) {
2057
2450
  const filename = context.filename;
2058
2451
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
2059
- const sourceRoot2 = path18.resolve("src");
2060
- const relative = path18.relative(sourceRoot2, filename);
2452
+ const sourceRoot2 = path19.resolve("src");
2453
+ const relative = path19.relative(sourceRoot2, filename);
2061
2454
  if (relative.startsWith("..")) return {};
2062
- const segments = relative.split(path18.sep);
2455
+ const segments = relative.split(path19.sep);
2063
2456
  if (segments[0] === "utils") return {};
2064
2457
  if (segments[0] === "app") return {};
2065
2458
  const supportFolders = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
@@ -2099,7 +2492,7 @@ var pureFunctionExtractRule = {
2099
2492
  };
2100
2493
 
2101
2494
  // eslint/rules/hook-complexity.ts
2102
- import path19 from "path";
2495
+ import path20 from "path";
2103
2496
  import ts4 from "typescript";
2104
2497
  var REACT_HOOKS = /* @__PURE__ */ new Set([
2105
2498
  "useState",
@@ -2151,10 +2544,10 @@ var hookComplexityRule = {
2151
2544
  },
2152
2545
  create(context) {
2153
2546
  const filename = context.filename;
2154
- const sourceRoot2 = path19.resolve("src");
2155
- const relative = path19.relative(sourceRoot2, filename);
2547
+ const sourceRoot2 = path20.resolve("src");
2548
+ const relative = path20.relative(sourceRoot2, filename);
2156
2549
  if (relative.startsWith("..")) return {};
2157
- const segments = relative.split(path19.sep);
2550
+ const segments = relative.split(path20.sep);
2158
2551
  const sourceText = context.sourceCode.text;
2159
2552
  function checkHook(node, name, body, exported) {
2160
2553
  if (!exported) return;
@@ -2196,9 +2589,9 @@ var hookComplexityRule = {
2196
2589
  };
2197
2590
 
2198
2591
  // eslint/rules/locale-dotted-path.ts
2199
- import path20 from "path";
2592
+ import path21 from "path";
2200
2593
  function isInLocalesDir(filename) {
2201
- const segments = path20.resolve(filename).split(path20.sep);
2594
+ const segments = path21.resolve(filename).split(path21.sep);
2202
2595
  const srcIdx = segments.lastIndexOf("src");
2203
2596
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
2204
2597
  }
@@ -2247,9 +2640,9 @@ var localeDottedPathRule = {
2247
2640
  };
2248
2641
 
2249
2642
  // eslint/rules/locales-location.ts
2250
- import path21 from "path";
2643
+ import path22 from "path";
2251
2644
  function isLocalesFile(filename) {
2252
- const segments = path21.resolve(filename).split(path21.sep);
2645
+ const segments = path22.resolve(filename).split(path22.sep);
2253
2646
  const srcIdx = segments.lastIndexOf("src");
2254
2647
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
2255
2648
  }
@@ -2268,7 +2661,7 @@ var localesLocationRule = {
2268
2661
  create(context) {
2269
2662
  if (isLocalesFile(context.filename)) return {};
2270
2663
  const filename = context.filename;
2271
- const segments = path21.resolve(filename).split(path21.sep);
2664
+ const segments = path22.resolve(filename).split(path22.sep);
2272
2665
  const srcIdx = segments.lastIndexOf("src");
2273
2666
  if (srcIdx === -1) return {};
2274
2667
  const folder = segments[srcIdx + 1];
@@ -2290,7 +2683,7 @@ var localesLocationRule = {
2290
2683
  };
2291
2684
 
2292
2685
  // eslint/rules/hook-extraction.ts
2293
- import path22 from "path";
2686
+ import path23 from "path";
2294
2687
  var hookExtractionRule = {
2295
2688
  meta: {
2296
2689
  schema: [],
@@ -2300,8 +2693,8 @@ var hookExtractionRule = {
2300
2693
  }
2301
2694
  },
2302
2695
  create(context) {
2303
- const sourceRoot2 = path22.resolve("src");
2304
- const file = path22.resolve(context.filename);
2696
+ const sourceRoot2 = path23.resolve("src");
2697
+ const file = path23.resolve(context.filename);
2305
2698
  const segments = segmentsOf(file, sourceRoot2);
2306
2699
  if (segments.length === 0) return {};
2307
2700
  const index = getProjectIndex(sourceRoot2);
@@ -2327,7 +2720,7 @@ var hookExtractionRule = {
2327
2720
  };
2328
2721
 
2329
2722
  // eslint/rules/value-extraction.ts
2330
- import path23 from "path";
2723
+ import path24 from "path";
2331
2724
  var valueExtractionRule = {
2332
2725
  meta: {
2333
2726
  schema: [],
@@ -2337,8 +2730,8 @@ var valueExtractionRule = {
2337
2730
  }
2338
2731
  },
2339
2732
  create(context) {
2340
- const sourceRoot2 = path23.resolve("src");
2341
- const file = path23.resolve(context.filename);
2733
+ const sourceRoot2 = path24.resolve("src");
2734
+ const file = path24.resolve(context.filename);
2342
2735
  const segments = segmentsOf(file, sourceRoot2);
2343
2736
  if (segments.length === 0 || segments[0] !== "app") return {};
2344
2737
  const index = getProjectIndex(sourceRoot2);
@@ -2359,7 +2752,7 @@ var valueExtractionRule = {
2359
2752
  };
2360
2753
 
2361
2754
  // eslint/rules/config-extraction.ts
2362
- import path24 from "path";
2755
+ import path25 from "path";
2363
2756
  var configExtractionRule = {
2364
2757
  meta: {
2365
2758
  schema: [],
@@ -2369,8 +2762,8 @@ var configExtractionRule = {
2369
2762
  }
2370
2763
  },
2371
2764
  create(context) {
2372
- const sourceRoot2 = path24.resolve("src");
2373
- const file = path24.resolve(context.filename);
2765
+ const sourceRoot2 = path25.resolve("src");
2766
+ const file = path25.resolve(context.filename);
2374
2767
  const segments = segmentsOf(file, sourceRoot2);
2375
2768
  if (segments.length < 3 || segments[0] !== "config") return {};
2376
2769
  if (SUPPORT_FOLDERS2.has(segments[2] ?? "")) return {};
@@ -2408,7 +2801,7 @@ var configExtractionRule = {
2408
2801
  };
2409
2802
 
2410
2803
  // eslint/rules/component-nesting.ts
2411
- import path25 from "path";
2804
+ import path26 from "path";
2412
2805
  var componentNestingRule = {
2413
2806
  meta: {
2414
2807
  schema: [],
@@ -2418,8 +2811,8 @@ var componentNestingRule = {
2418
2811
  }
2419
2812
  },
2420
2813
  create(context) {
2421
- const sourceRoot2 = path25.resolve("src");
2422
- const file = path25.resolve(context.filename);
2814
+ const sourceRoot2 = path26.resolve("src");
2815
+ const file = path26.resolve(context.filename);
2423
2816
  const segments = segmentsOf(file, sourceRoot2);
2424
2817
  if (segments.length !== 4 || segments[0] !== "features") return {};
2425
2818
  const index = getProjectIndex(sourceRoot2);
@@ -2452,7 +2845,7 @@ var componentNestingRule = {
2452
2845
  };
2453
2846
 
2454
2847
  // eslint/rules/stay-flat.ts
2455
- import path26 from "path";
2848
+ import path27 from "path";
2456
2849
  var stayFlatRule = {
2457
2850
  meta: {
2458
2851
  schema: [],
@@ -2462,8 +2855,8 @@ var stayFlatRule = {
2462
2855
  }
2463
2856
  },
2464
2857
  create(context) {
2465
- const sourceRoot2 = path26.resolve("src");
2466
- const file = path26.resolve(context.filename);
2858
+ const sourceRoot2 = path27.resolve("src");
2859
+ const file = path27.resolve(context.filename);
2467
2860
  const segments = segmentsOf(file, sourceRoot2);
2468
2861
  if (segments.length !== 3 || segments[0] !== "features") return {};
2469
2862
  const index = getProjectIndex(sourceRoot2);
@@ -2503,7 +2896,7 @@ var stayFlatRule = {
2503
2896
  };
2504
2897
 
2505
2898
  // eslint/rules/type-extraction.ts
2506
- import path27 from "path";
2899
+ import path28 from "path";
2507
2900
  var typeExtractionRule = {
2508
2901
  meta: {
2509
2902
  schema: [],
@@ -2513,8 +2906,8 @@ var typeExtractionRule = {
2513
2906
  }
2514
2907
  },
2515
2908
  create(context) {
2516
- const sourceRoot2 = path27.resolve("src");
2517
- const file = path27.resolve(context.filename);
2909
+ const sourceRoot2 = path28.resolve("src");
2910
+ const file = path28.resolve(context.filename);
2518
2911
  const segments = segmentsOf(file, sourceRoot2);
2519
2912
  if (segments.length === 0) return {};
2520
2913
  const index = getProjectIndex(sourceRoot2);
@@ -2560,8 +2953,8 @@ var typeExtractionRule = {
2560
2953
  };
2561
2954
 
2562
2955
  // eslint/rules/locale-placement.ts
2563
- import path28 from "path";
2564
- import { readFileSync as readFileSync2 } from "fs";
2956
+ import path29 from "path";
2957
+ import { readFileSync as readFileSync3 } from "fs";
2565
2958
  import ts5 from "typescript";
2566
2959
  var LOCALE_ACCESS = /\blocales\.(?<key>[A-Za-z_$][\w$]*)/g;
2567
2960
  var camelCase = (name) => name.replace(/-[a-z]/g, (match) => match.slice(1).toUpperCase());
@@ -2607,8 +3000,8 @@ var localePlacementRule = {
2607
3000
  }
2608
3001
  },
2609
3002
  create(context) {
2610
- const sourceRoot2 = path28.resolve("src");
2611
- const file = path28.resolve(context.filename);
3003
+ const sourceRoot2 = path29.resolve("src");
3004
+ const file = path29.resolve(context.filename);
2612
3005
  const segments = segmentsOf(file, sourceRoot2);
2613
3006
  if (segments.length === 0) return {};
2614
3007
  const index = getProjectIndex(sourceRoot2);
@@ -2618,7 +3011,7 @@ var localePlacementRule = {
2618
3011
  return candidateSegments.length === 2 && candidateSegments[0] === "locales" && candidateSegments[1]?.startsWith("index.");
2619
3012
  });
2620
3013
  if (!localesFile || file !== localesFile) return {};
2621
- const placement = localePlacement(readFileSync2(localesFile, "utf8"));
3014
+ const placement = localePlacement(readFileSync3(localesFile, "utf8"));
2622
3015
  if (!placement) return {};
2623
3016
  const keyReaders = /* @__PURE__ */ new Map();
2624
3017
  const keyFeatures = /* @__PURE__ */ new Map();
@@ -2630,7 +3023,7 @@ var localePlacementRule = {
2630
3023
  );
2631
3024
  if (!importsLocales) continue;
2632
3025
  const candidateSegments = segmentsOf(candidateFile, sourceRoot2);
2633
- for (const match of readFileSync2(candidateFile, "utf8").matchAll(LOCALE_ACCESS)) {
3026
+ for (const match of readFileSync3(candidateFile, "utf8").matchAll(LOCALE_ACCESS)) {
2634
3027
  const key = match.groups?.key;
2635
3028
  if (key === void 0) continue;
2636
3029
  const readers = keyReaders.get(key) ?? /* @__PURE__ */ new Set();
@@ -2686,7 +3079,7 @@ var localePlacementRule = {
2686
3079
  };
2687
3080
 
2688
3081
  // eslint/rules/sole-state-owner.ts
2689
- import path29 from "path";
3082
+ import path30 from "path";
2690
3083
  import ts6 from "typescript";
2691
3084
  function findStateHooks(node) {
2692
3085
  const hooks = [];
@@ -2766,7 +3159,7 @@ var soleStateOwnerRule = {
2766
3159
  }
2767
3160
  },
2768
3161
  create(context) {
2769
- const filename = path29.resolve(context.filename);
3162
+ const filename = path30.resolve(context.filename);
2770
3163
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
2771
3164
  const text = context.sourceCode.text;
2772
3165
  const components = parseComponentInfo(text, filename);
@@ -2845,7 +3238,7 @@ function usesOutsideJsx(declaration, hook, children) {
2845
3238
  }
2846
3239
 
2847
3240
  // eslint/rules/locale-key-shape.ts
2848
- import path30 from "path";
3241
+ import path31 from "path";
2849
3242
  var MAX_KEY_LENGTH = 30;
2850
3243
  var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
2851
3244
  "Button",
@@ -2893,8 +3286,9 @@ var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
2893
3286
  "ScrollBar"
2894
3287
  ]);
2895
3288
  var CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
3289
+ var ENGLISH = /^[A-Za-z0-9_]*$/;
2896
3290
  function isLocalesFile2(filename) {
2897
- const segments = path30.resolve(filename).split(path30.sep);
3291
+ const segments = path31.resolve(filename).split(path31.sep);
2898
3292
  const srcIdx = segments.lastIndexOf("src");
2899
3293
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
2900
3294
  }
@@ -2907,6 +3301,14 @@ function reportFor(context, node, message) {
2907
3301
  context.report({ node, message });
2908
3302
  }
2909
3303
  function checkKey(context, node, name) {
3304
+ if (!ENGLISH.test(name)) {
3305
+ reportFor(
3306
+ context,
3307
+ node,
3308
+ `Locale key "${name}" must be English; write it in the Latin alphabet. See docs/code-organization-guide/rules/locales-rule.md`
3309
+ );
3310
+ return;
3311
+ }
2910
3312
  if (!CAMEL_CASE.test(name)) {
2911
3313
  reportFor(
2912
3314
  context,
@@ -2957,8 +3359,8 @@ var localeKeyShapeRule = {
2957
3359
  };
2958
3360
 
2959
3361
  // eslint/rules/shared-style-dedup.ts
2960
- import path31 from "path";
2961
- import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
3362
+ import path32 from "path";
3363
+ import { readFileSync as readFileSync4, statSync as statSync3 } from "fs";
2962
3364
  var CLASS_NAME = /className="(?<classes>[^"]+)"/g;
2963
3365
  var comboCache;
2964
3366
  function combosFor(index) {
@@ -2966,7 +3368,7 @@ function combosFor(index) {
2966
3368
  let fingerprint2 = "";
2967
3369
  for (const file of files) {
2968
3370
  try {
2969
- fingerprint2 += String(statSync2(file).mtimeMs);
3371
+ fingerprint2 += String(statSync3(file).mtimeMs);
2970
3372
  } catch {
2971
3373
  fingerprint2 += "0";
2972
3374
  }
@@ -2976,7 +3378,7 @@ function combosFor(index) {
2976
3378
  }
2977
3379
  const combos = /* @__PURE__ */ new Map();
2978
3380
  for (const file of files) {
2979
- const text = readFileSync3(file, "utf8");
3381
+ const text = readFileSync4(file, "utf8");
2980
3382
  for (const match of text.matchAll(CLASS_NAME)) {
2981
3383
  const classes = (match.groups?.classes ?? "").split(/\s+/).filter(Boolean);
2982
3384
  if (classes.length < 2) continue;
@@ -2998,8 +3400,8 @@ var sharedStyleDedupRule = {
2998
3400
  }
2999
3401
  },
3000
3402
  create(context) {
3001
- const sourceRoot2 = path31.resolve("src");
3002
- const file = path31.resolve(context.filename);
3403
+ const sourceRoot2 = path32.resolve("src");
3404
+ const file = path32.resolve(context.filename);
3003
3405
  const segments = segmentsOf(file, sourceRoot2);
3004
3406
  if (segments.length === 0) return {};
3005
3407
  const index = getProjectIndex(sourceRoot2);
@@ -3203,7 +3605,7 @@ var zodSchemaValidationRule = {
3203
3605
  };
3204
3606
 
3205
3607
  // eslint/rules/source-under-src.ts
3206
- import path32 from "path";
3608
+ import path33 from "path";
3207
3609
  var NON_SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set([
3208
3610
  ".agents",
3209
3611
  ".cache",
@@ -3244,14 +3646,14 @@ var sourceUnderSrcRule = {
3244
3646
  }
3245
3647
  },
3246
3648
  create(context) {
3247
- const filename = path32.resolve(context.filename);
3649
+ const filename = path33.resolve(context.filename);
3248
3650
  if (!MODULE_EXTENSION.test(filename)) return {};
3249
- const relative = path32.relative(process.cwd(), filename).replace(/\\/g, "/");
3651
+ const relative = path33.relative(process.cwd(), filename).replace(/\\/g, "/");
3250
3652
  if (relative === "src" || relative.startsWith("src/")) return {};
3251
3653
  const topLevel = relative.split("/")[0] ?? "";
3252
3654
  if (NON_SOURCE_ROOT_DIRS.has(topLevel)) return {};
3253
3655
  if (!relative.includes("/")) {
3254
- const basename = path32.basename(filename);
3656
+ const basename = path33.basename(filename);
3255
3657
  if (CONFIG_FILE.test(basename) || DECLARATION_FILE.test(basename) || basename.startsWith(".")) return {};
3256
3658
  }
3257
3659
  return {
@@ -3268,7 +3670,7 @@ var sourceUnderSrcRule = {
3268
3670
 
3269
3671
  // eslint/rules/zirka-baseline.ts
3270
3672
  import fs4 from "fs";
3271
- import path33 from "path";
3673
+ import path34 from "path";
3272
3674
  var ESLINT_CONFIG = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
3273
3675
  var PRETTIER_CONFIGS = [
3274
3676
  "prettier.config.mjs",
@@ -3287,10 +3689,10 @@ var zirkaBaselineRule = {
3287
3689
  }
3288
3690
  },
3289
3691
  create(context) {
3290
- const filename = path33.resolve(context.filename);
3291
- const basename = path33.basename(filename);
3692
+ const filename = path34.resolve(context.filename);
3693
+ const basename = path34.basename(filename);
3292
3694
  if (!ESLINT_CONFIG.test(basename)) return {};
3293
- const projectRoot = path33.dirname(filename);
3695
+ const projectRoot = path34.dirname(filename);
3294
3696
  const report3 = (message) => {
3295
3697
  context.report({
3296
3698
  node: context.sourceCode.ast,
@@ -3305,7 +3707,7 @@ var zirkaBaselineRule = {
3305
3707
  'ESLint config must take its configuration from zirka (import { styleguide } from "zirka") instead of restating rules locally.'
3306
3708
  );
3307
3709
  }
3308
- const tsconfigPath = path33.join(projectRoot, "tsconfig.json");
3710
+ const tsconfigPath = path34.join(projectRoot, "tsconfig.json");
3309
3711
  if (!fs4.existsSync(tsconfigPath)) {
3310
3712
  report3('No tsconfig.json found. Create one extending the zirka TypeScript base config ("zirka/typescript").');
3311
3713
  } else {
@@ -3323,13 +3725,13 @@ var zirkaBaselineRule = {
3323
3725
  report3('tsconfig.json must extend the zirka TypeScript base config ("zirka/typescript").');
3324
3726
  }
3325
3727
  }
3326
- const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs4.existsSync(path33.join(projectRoot, name)));
3728
+ const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs4.existsSync(path34.join(projectRoot, name)));
3327
3729
  if (!prettierConfigFile) {
3328
3730
  report3(
3329
3731
  "No prettier config found. Create one that takes its configuration from zirka (styleguide({ prettier: true }).prettierConfig)."
3330
3732
  );
3331
3733
  } else {
3332
- const content = fs4.readFileSync(path33.join(projectRoot, prettierConfigFile), "utf8");
3734
+ const content = fs4.readFileSync(path34.join(projectRoot, prettierConfigFile), "utf8");
3333
3735
  if (!content.includes("zirka")) {
3334
3736
  report3(
3335
3737
  "The prettier config must take its configuration from zirka (styleguide({ prettier: true }).prettierConfig) instead of restating it locally."
@@ -3399,7 +3801,7 @@ var docKindSuffixRule = {
3399
3801
  };
3400
3802
 
3401
3803
  // eslint/rules/documentation/title-matches-file-name.ts
3402
- import path34 from "path";
3804
+ import path35 from "path";
3403
3805
  function toExpectedFileName(title) {
3404
3806
  return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
3405
3807
  }
@@ -3419,7 +3821,7 @@ var titleMatchesFileNameRule = {
3419
3821
  if (!filename.endsWith(".md")) return;
3420
3822
  const title = getTextContent(node).trim();
3421
3823
  const expectedFileName = toExpectedFileName(title);
3422
- const actualFileName = path34.basename(filename);
3824
+ const actualFileName = path35.basename(filename);
3423
3825
  if (!title) {
3424
3826
  context.report({
3425
3827
  node,
@@ -3826,7 +4228,7 @@ var referenceBlockHeadingsRule = {
3826
4228
  };
3827
4229
 
3828
4230
  // eslint/rules/documentation/support-document-placement.ts
3829
- import path35 from "path";
4231
+ import path36 from "path";
3830
4232
  var supportDocumentPlacementRule = {
3831
4233
  meta: {
3832
4234
  type: "problem",
@@ -3840,7 +4242,7 @@ var supportDocumentPlacementRule = {
3840
4242
  root(node) {
3841
4243
  const filename = getFilename(context);
3842
4244
  if (!filename.endsWith(".md")) return;
3843
- const parentFolder = path35.basename(path35.dirname(filename));
4245
+ const parentFolder = path36.basename(path36.dirname(filename));
3844
4246
  if (filename.endsWith("-rule.md") && parentFolder !== "rules") {
3845
4247
  context.report({
3846
4248
  node,
@@ -3883,11 +4285,11 @@ var noTemplatePromptRule = {
3883
4285
  };
3884
4286
 
3885
4287
  // eslint/rules/documentation/guide-folder-entry-point.ts
3886
- import path37 from "path";
4288
+ import path38 from "path";
3887
4289
 
3888
4290
  // eslint/rules/documentation/project-index.ts
3889
- import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
3890
- import path36 from "path";
4291
+ import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
4292
+ import path37 from "path";
3891
4293
  var KIND_BY_SUFFIX = [
3892
4294
  ["-rule.md", "rule"],
3893
4295
  ["-guide.md", "guide"],
@@ -3895,16 +4297,16 @@ var KIND_BY_SUFFIX = [
3895
4297
  ["-policy.md", "policy"]
3896
4298
  ];
3897
4299
  function listMarkdownFiles(dir) {
3898
- return readdirSync2(dir).flatMap((entry) => {
3899
- const entryPath = path36.join(dir, entry);
3900
- if (statSync3(entryPath).isDirectory()) {
4300
+ return readdirSync3(dir).flatMap((entry) => {
4301
+ const entryPath = path37.join(dir, entry);
4302
+ if (statSync4(entryPath).isDirectory()) {
3901
4303
  return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
3902
4304
  }
3903
4305
  return entry.endsWith(".md") ? [entryPath] : [];
3904
4306
  });
3905
4307
  }
3906
4308
  function extractTitle(filePath) {
3907
- const content = readFileSync4(filePath, "utf8");
4309
+ const content = readFileSync5(filePath, "utf8");
3908
4310
  const match = /^# (?<title>.+)$/m.exec(content);
3909
4311
  return match?.groups?.title?.trim() ?? "";
3910
4312
  }
@@ -3914,11 +4316,11 @@ function getProjectDocs(docsRoot) {
3914
4316
  if (cached) return cached;
3915
4317
  const files = listMarkdownFiles(docsRoot);
3916
4318
  const docs = files.sort((a, b) => a.localeCompare(b)).map((filePath) => {
3917
- const fileName = path36.basename(filePath);
4319
+ const fileName = path37.basename(filePath);
3918
4320
  const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
3919
4321
  return {
3920
4322
  filePath,
3921
- doc: path36.relative(docsRoot, filePath).split(path36.sep).join("/"),
4323
+ doc: path37.relative(docsRoot, filePath).split(path37.sep).join("/"),
3922
4324
  fileName,
3923
4325
  kind,
3924
4326
  title: extractTitle(filePath)
@@ -3928,12 +4330,12 @@ function getProjectDocs(docsRoot) {
3928
4330
  return docs;
3929
4331
  }
3930
4332
  function findDocsRoot(filePath) {
3931
- let dir = path36.dirname(filePath);
4333
+ let dir = path37.dirname(filePath);
3932
4334
  for (; ; ) {
3933
- if (path36.basename(dir) === "docs" && statSync3(dir).isDirectory()) {
4335
+ if (path37.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
3934
4336
  return dir;
3935
4337
  }
3936
- const parent = path36.dirname(dir);
4338
+ const parent = path37.dirname(dir);
3937
4339
  if (parent === dir) return void 0;
3938
4340
  dir = parent;
3939
4341
  }
@@ -3957,13 +4359,13 @@ var guideFolderEntryPointRule = {
3957
4359
  if (!docsRoot) return;
3958
4360
  const docs = getProjectDocs(docsRoot);
3959
4361
  const guideFolders = new Set(
3960
- docs.filter((doc) => ["rules", "references"].includes(path37.basename(path37.dirname(doc.filePath)))).map((doc) => path37.dirname(path37.dirname(doc.filePath))).filter((folder) => path37.resolve(folder) !== path37.resolve(docsRoot))
4362
+ docs.filter((doc) => ["rules", "references"].includes(path38.basename(path38.dirname(doc.filePath)))).map((doc) => path38.dirname(path38.dirname(doc.filePath))).filter((folder) => path38.resolve(folder) !== path38.resolve(docsRoot))
3961
4363
  );
3962
- const currentDir = path37.dirname(filename);
4364
+ const currentDir = path38.dirname(filename);
3963
4365
  if (guideFolders.has(currentDir)) {
3964
- const expectedEntryPoint = `${path37.basename(currentDir)}.md`;
4366
+ const expectedEntryPoint = `${path38.basename(currentDir)}.md`;
3965
4367
  const hasEntryPoint = docs.some(
3966
- (doc) => doc.kind === "guide" && path37.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
4368
+ (doc) => doc.kind === "guide" && path38.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
3967
4369
  );
3968
4370
  if (!hasEntryPoint) {
3969
4371
  context.report({
@@ -4187,10 +4589,10 @@ var noNestedHowToRule = {
4187
4589
  };
4188
4590
 
4189
4591
  // eslint/rules/documentation/glossary-term-linking.ts
4190
- import { readFileSync as readFileSync5 } from "fs";
4191
- import path38 from "path";
4592
+ import { readFileSync as readFileSync6 } from "fs";
4593
+ import path39 from "path";
4192
4594
  function extractGlossaryTerms(filePath) {
4193
- const content = readFileSync5(filePath, "utf8");
4595
+ const content = readFileSync6(filePath, "utf8");
4194
4596
  const terms = [];
4195
4597
  const headingPattern = /^## (?<term>.+)$/gm;
4196
4598
  let match;
@@ -4242,9 +4644,9 @@ var glossaryTermLinkingRule = {
4242
4644
  const docsRoot = findDocsRoot(filename);
4243
4645
  if (!docsRoot) return;
4244
4646
  const docs = getProjectDocs(docsRoot);
4245
- const guideDir = path38.dirname(filename);
4647
+ const guideDir = path39.dirname(filename);
4246
4648
  const guideReferences = docs.filter(
4247
- (doc) => doc.kind === "reference" && path38.dirname(doc.filePath) === guideDir
4649
+ (doc) => doc.kind === "reference" && path39.dirname(doc.filePath) === guideDir
4248
4650
  );
4249
4651
  if (guideReferences.length === 0) return;
4250
4652
  const glossaryTerms = [];
@@ -4269,7 +4671,7 @@ var glossaryTermLinkingRule = {
4269
4671
 
4270
4672
  // eslint/rules/documentation/guide-mentions-documents.ts
4271
4673
  import { existsSync } from "fs";
4272
- import path39 from "path";
4674
+ import path40 from "path";
4273
4675
  function visitSteps3(node, check) {
4274
4676
  if (node.type === "list" && node.ordered) {
4275
4677
  for (const child of node.children) check(child);
@@ -4302,12 +4704,12 @@ var guideMentionsDocumentsRule = {
4302
4704
  if (!filename.endsWith("-guide.md")) return;
4303
4705
  const docsRoot = findDocsRoot(filename);
4304
4706
  if (!docsRoot) return;
4305
- const guideDir = path39.dirname(filename);
4306
- if (path39.basename(filename, ".md") !== path39.basename(guideDir)) return;
4707
+ const guideDir = path40.dirname(filename);
4708
+ if (path40.basename(filename, ".md") !== path40.basename(guideDir)) return;
4307
4709
  const docs = getProjectDocs(docsRoot);
4308
4710
  const owned = docs.filter((doc) => {
4309
- const parent = path39.dirname(doc.filePath);
4310
- return parent === path39.join(guideDir, "rules") || parent === path39.join(guideDir, "references");
4711
+ const parent = path40.dirname(doc.filePath);
4712
+ return parent === path40.join(guideDir, "rules") || parent === path40.join(guideDir, "references");
4311
4713
  });
4312
4714
  const allLinks = [];
4313
4715
  collectMarkdownLinks(node, allLinks);
@@ -4334,7 +4736,7 @@ var guideMentionsDocumentsRule = {
4334
4736
  for (const link of allLinks) {
4335
4737
  const target = linkTarget(link.url);
4336
4738
  if (!target.endsWith(".md")) continue;
4337
- const resolved = path39.normalize(path39.join(guideDir, target));
4739
+ const resolved = path40.normalize(path40.join(guideDir, target));
4338
4740
  if (!existsSync(resolved)) {
4339
4741
  context.report({
4340
4742
  node: link,
@@ -4839,6 +5241,83 @@ var globalStylesheetRule = {
4839
5241
  }
4840
5242
  };
4841
5243
 
5244
+ // eslint/rules/tailwind/unused-utility.ts
5245
+ import { readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
5246
+ import path41 from "path";
5247
+ var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".css"];
5248
+ function sourceFiles(dir) {
5249
+ let entries;
5250
+ try {
5251
+ entries = readdirSync4(dir);
5252
+ } catch {
5253
+ return [];
5254
+ }
5255
+ return entries.flatMap((entry) => {
5256
+ if (entry.startsWith(".") || entry === "node_modules") return [];
5257
+ const entryPath = path41.join(dir, entry);
5258
+ let stats;
5259
+ try {
5260
+ stats = statSync5(entryPath);
5261
+ } catch {
5262
+ return [];
5263
+ }
5264
+ if (stats.isDirectory()) return sourceFiles(entryPath);
5265
+ return SOURCE_EXTENSIONS.includes(path41.extname(entry)) ? [entryPath] : [];
5266
+ });
5267
+ }
5268
+ function usagePattern(name) {
5269
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5270
+ return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`);
5271
+ }
5272
+ var unusedUtilityRule = {
5273
+ meta: {
5274
+ schema: [],
5275
+ type: "problem",
5276
+ docs: {
5277
+ description: "Require every custom @utility to be referenced by the repository's source."
5278
+ }
5279
+ },
5280
+ create(context) {
5281
+ const sourceRoot2 = path41.resolve("src");
5282
+ let files;
5283
+ try {
5284
+ if (!statSync5(sourceRoot2).isDirectory()) return {};
5285
+ files = sourceFiles(sourceRoot2);
5286
+ } catch {
5287
+ return {};
5288
+ }
5289
+ const texts = /* @__PURE__ */ new Map();
5290
+ const textOf = (file) => {
5291
+ let text = texts.get(file);
5292
+ if (text === void 0) {
5293
+ try {
5294
+ text = readFileSync7(file, "utf8");
5295
+ } catch {
5296
+ text = "";
5297
+ }
5298
+ texts.set(file, text);
5299
+ }
5300
+ return text;
5301
+ };
5302
+ return {
5303
+ "StyleSheet:exit"(node) {
5304
+ for (const utility of atrulesNamed(node, "utility")) {
5305
+ const name = preludeIdentifiers(utility)[0];
5306
+ if (!name) continue;
5307
+ const pattern = usagePattern(name);
5308
+ const definition = `@utility ${name}`;
5309
+ const used = files.some((file) => pattern.test(textOf(file).replaceAll(definition, "")));
5310
+ if (used) continue;
5311
+ context.report({
5312
+ node: utility,
5313
+ message: `Custom utility "${name}" is never used by the repository's source; remove it or use it.`
5314
+ });
5315
+ }
5316
+ }
5317
+ };
5318
+ }
5319
+ };
5320
+
4842
5321
  // eslint/rules/tailwind/index.ts
4843
5322
  var tailwindRules = {
4844
5323
  "theme-reset": themeResetRule,
@@ -4851,7 +5330,8 @@ var tailwindRules = {
4851
5330
  "surface-utility": surfaceUtilityRule,
4852
5331
  "theme-variable-namespace": themeVariableNamespaceRule,
4853
5332
  "global-css-location": globalCssLocationRule,
4854
- "global-stylesheet": globalStylesheetRule
5333
+ "global-stylesheet": globalStylesheetRule,
5334
+ "unused-utility": unusedUtilityRule
4855
5335
  };
4856
5336
 
4857
5337
  // eslint/rules/package-json/exact-version.ts
@@ -4998,8 +5478,8 @@ var nextPackageJsonRules = {
4998
5478
  };
4999
5479
 
5000
5480
  // eslint/rules/husky/husky-hook.ts
5001
- import { existsSync as existsSync2, readFileSync as readFileSync6 } from "fs";
5002
- import path40 from "path";
5481
+ import { existsSync as existsSync2, readFileSync as readFileSync8 } from "fs";
5482
+ import path42 from "path";
5003
5483
  function memberName4(member) {
5004
5484
  return member.name.type === "String" ? member.name.value : member.name.name;
5005
5485
  }
@@ -5018,7 +5498,7 @@ var huskyHookRule = {
5018
5498
  if (root.type !== "Object") return;
5019
5499
  const scriptName = context.filename;
5020
5500
  if (!scriptName.endsWith("package.json")) return;
5021
- const hookPath = path40.join(process.cwd(), ".husky", "pre-commit");
5501
+ const hookPath = path42.join(process.cwd(), ".husky", "pre-commit");
5022
5502
  if (!existsSync2(hookPath)) {
5023
5503
  context.report({
5024
5504
  node,
@@ -5026,7 +5506,7 @@ var huskyHookRule = {
5026
5506
  });
5027
5507
  return;
5028
5508
  }
5029
- const content = readFileSync6(hookPath, "utf8");
5509
+ const content = readFileSync8(hookPath, "utf8");
5030
5510
  if (!content.includes("lint-staged")) {
5031
5511
  context.report({ node, message: ".husky/pre-commit must run lint-staged." });
5032
5512
  }
@@ -5057,8 +5537,8 @@ var huskyRules = {
5057
5537
  };
5058
5538
 
5059
5539
  // eslint/rules/vulyk/vulyk-docs.ts
5060
- import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
5061
- import path41 from "path";
5540
+ import { existsSync as existsSync3, readFileSync as readFileSync9 } from "fs";
5541
+ import path43 from "path";
5062
5542
  var PASIKA_REPO = "Bredansky/pasika";
5063
5543
  var vulykDocsRule = {
5064
5544
  meta: {
@@ -5072,8 +5552,8 @@ var vulykDocsRule = {
5072
5552
  return {
5073
5553
  Document(node) {
5074
5554
  if (!context.filename.endsWith("package.json")) return;
5075
- const projectRoot = path41.dirname(path41.resolve(context.filename));
5076
- const configPath = path41.join(projectRoot, "vulyk.config.ts");
5555
+ const projectRoot = path43.dirname(path43.resolve(context.filename));
5556
+ const configPath = path43.join(projectRoot, "vulyk.config.ts");
5077
5557
  if (!existsSync3(configPath)) {
5078
5558
  context.report({
5079
5559
  node,
@@ -5081,14 +5561,14 @@ var vulykDocsRule = {
5081
5561
  });
5082
5562
  return;
5083
5563
  }
5084
- const config = readFileSync7(configPath, "utf8");
5564
+ const config = readFileSync9(configPath, "utf8");
5085
5565
  if (!config.includes(PASIKA_REPO)) {
5086
5566
  context.report({
5087
5567
  node,
5088
5568
  message: "vulyk.config.ts must track the framework's docs from the pasika repository."
5089
5569
  });
5090
5570
  }
5091
- const agentsPath = path41.join(projectRoot, "AGENTS.md");
5571
+ const agentsPath = path43.join(projectRoot, "AGENTS.md");
5092
5572
  if (!existsSync3(agentsPath)) {
5093
5573
  context.report({
5094
5574
  node,
@@ -5132,6 +5612,7 @@ var nextjsAppRules = {
5132
5612
  "ui-state": uiStateRule,
5133
5613
  "no-mixed-concerns": noMixedConcernsRule,
5134
5614
  "no-arbitrary-tailwind": noArbitraryTailwindRule,
5615
+ "unknown-utility": unknownUtilityRule,
5135
5616
  "enforce-cn-merge": enforceCnMergeRule,
5136
5617
  "cn-helper": cnHelperRule,
5137
5618
  "enforce-cva-variant-props": enforceCvaVariantPropsRule,