pasika 0.4.3 → 0.5.0

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.
@@ -2,6 +2,7 @@
2
2
  import css from "@eslint/css";
3
3
  import jsonPlugin from "@eslint/json";
4
4
  import markdown from "@eslint/markdown";
5
+ import tsParser from "@typescript-eslint/parser";
5
6
 
6
7
  // eslint/rules/filename-case.ts
7
8
  import path2 from "path";
@@ -228,33 +229,40 @@ function report(context, filename, ext) {
228
229
  }
229
230
 
230
231
  // eslint/rules/import-boundaries.ts
232
+ import path4 from "path";
233
+
234
+ // eslint/rules/project-root.ts
231
235
  import path3 from "path";
232
- var sourceRoot = path3.resolve("src");
236
+ function sourceRootOf(context) {
237
+ return path3.resolve(context.cwd ?? process.cwd(), "src");
238
+ }
239
+
240
+ // eslint/rules/import-boundaries.ts
233
241
  var rootSupportFolders = /* @__PURE__ */ new Set(["config", "constants", "hooks", "locales", "schemas", "types", "utils"]);
234
242
  var moduleExtensions = /* @__PURE__ */ new Set([".cjs", ".cts", ".js", ".jsx", ".mjs", ".mts", ".ts", ".tsx"]);
235
243
  var styleExtensions = /* @__PURE__ */ new Set([".css", ".less", ".sass", ".scss"]);
236
- function resolveSourceImport(filename, importPath) {
244
+ function resolveSourceImport(sourceRoot, filename, importPath) {
237
245
  if (importPath.startsWith("@/")) {
238
- return path3.resolve(sourceRoot, importPath.slice(2));
246
+ return path4.resolve(sourceRoot, importPath.slice(2));
239
247
  }
240
248
  if (importPath.startsWith(".")) {
241
- return path3.resolve(path3.dirname(filename), importPath);
249
+ return path4.resolve(path4.dirname(filename), importPath);
242
250
  }
243
251
  return void 0;
244
252
  }
245
- function sourceSegments(absolutePath) {
246
- const relativePath = path3.relative(sourceRoot, absolutePath);
247
- if (relativePath.startsWith("..") || path3.isAbsolute(relativePath)) {
253
+ function sourceSegments(sourceRoot, absolutePath) {
254
+ const relativePath = path4.relative(sourceRoot, absolutePath);
255
+ if (relativePath.startsWith("..") || path4.isAbsolute(relativePath)) {
248
256
  return void 0;
249
257
  }
250
- return relativePath.split(path3.sep);
258
+ return relativePath.split(path4.sep);
251
259
  }
252
260
  function relativeSpecifier(filename, resolvedPath) {
253
- const relativePath = path3.relative(path3.dirname(filename), resolvedPath).split(path3.sep).join("/");
261
+ const relativePath = path4.relative(path4.dirname(filename), resolvedPath).split(path4.sep).join("/");
254
262
  return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
255
263
  }
256
- function aliasSpecifier(resolvedPath) {
257
- return `@/${(sourceSegments(resolvedPath) ?? []).join("/")}`;
264
+ function aliasSpecifier(sourceRoot, resolvedPath) {
265
+ return `@/${(sourceSegments(sourceRoot, resolvedPath) ?? []).join("/")}`;
258
266
  }
259
267
  function segmentCount(specifier) {
260
268
  return specifier.replace(/^@\//, "").split("/").filter((segment) => segment !== "." && segment !== "").length;
@@ -262,8 +270,8 @@ function segmentCount(specifier) {
262
270
  function describeSegments(count) {
263
271
  return `${String(count)} segment${count === 1 ? "" : "s"}`;
264
272
  }
265
- function prefersRelative(filename, resolvedPath) {
266
- return segmentCount(relativeSpecifier(filename, resolvedPath)) <= segmentCount(aliasSpecifier(resolvedPath));
273
+ function prefersRelative(sourceRoot, filename, resolvedPath) {
274
+ return segmentCount(relativeSpecifier(filename, resolvedPath)) <= segmentCount(aliasSpecifier(sourceRoot, resolvedPath));
267
275
  }
268
276
  var importBoundariesRule = {
269
277
  meta: {
@@ -272,6 +280,7 @@ var importBoundariesRule = {
272
280
  },
273
281
  create(context) {
274
282
  const filename = context.filename;
283
+ const sourceRoot = sourceRootOf(context);
275
284
  if (!filename) {
276
285
  return {};
277
286
  }
@@ -280,18 +289,18 @@ var importBoundariesRule = {
280
289
  return;
281
290
  }
282
291
  const importPath = source.value;
283
- const resolvedPath = resolveSourceImport(filename, importPath);
292
+ const resolvedPath = resolveSourceImport(sourceRoot, filename, importPath);
284
293
  if (!resolvedPath) {
285
294
  return;
286
295
  }
287
- const importer = sourceSegments(filename);
288
- const imported = sourceSegments(resolvedPath);
296
+ const importer = sourceSegments(sourceRoot, filename);
297
+ const imported = sourceSegments(sourceRoot, resolvedPath);
289
298
  if (!importer || !imported || importer.length === 0 || imported.length === 0) {
290
299
  return;
291
300
  }
292
301
  const [importerLayer = "", importerFeature] = importer;
293
302
  const [importedLayer = "", importedFeature] = imported;
294
- const extension = path3.extname(importPath);
303
+ const extension = path4.extname(importPath);
295
304
  const isCodeModule = !extension || moduleExtensions.has(extension);
296
305
  const isAppLocalStyleImport = importerLayer === "app" && importedLayer === "app" && styleExtensions.has(extension);
297
306
  const importedIsRootSupport = rootSupportFolders.has(importedLayer);
@@ -305,21 +314,21 @@ var importBoundariesRule = {
305
314
  return;
306
315
  }
307
316
  const relativeForm = relativeSpecifier(filename, resolvedPath);
308
- const aliasForm = aliasSpecifier(resolvedPath);
317
+ const aliasForm = aliasSpecifier(sourceRoot, resolvedPath);
309
318
  const relativeSegments = segmentCount(relativeForm);
310
319
  const aliasSegments = segmentCount(aliasForm);
311
320
  function describeChoice(preferred, preferredSegments, other, otherSegments) {
312
321
  const tie = preferredSegments === otherSegments ? ", and a tie goes to the relative path" : "";
313
322
  return `Use "${preferred}" (${describeSegments(preferredSegments)}) instead of "${other}" (${describeSegments(otherSegments)})${tie}.`;
314
323
  }
315
- if (prefersRelative(filename, resolvedPath) && importPath.startsWith("@/")) {
324
+ if (prefersRelative(sourceRoot, filename, resolvedPath) && importPath.startsWith("@/")) {
316
325
  context.report({
317
326
  node: source,
318
327
  message: describeChoice(relativeForm, relativeSegments, aliasForm, aliasSegments)
319
328
  });
320
329
  return;
321
330
  }
322
- if (!prefersRelative(filename, resolvedPath) && importPath.startsWith(".")) {
331
+ if (!prefersRelative(sourceRoot, filename, resolvedPath) && importPath.startsWith(".")) {
323
332
  context.report({
324
333
  node: source,
325
334
  message: describeChoice(aliasForm, aliasSegments, relativeForm, relativeSegments)
@@ -448,7 +457,7 @@ var noArbitraryTailwindRule = {
448
457
 
449
458
  // eslint/rules/unknown-utility.ts
450
459
  import { readdirSync, readFileSync, statSync } from "fs";
451
- import path4 from "path";
460
+ import path5 from "path";
452
461
  var PREFIX_NAMESPACES = {
453
462
  bg: ["color"],
454
463
  text: ["color", "text"],
@@ -610,7 +619,19 @@ var DEFAULT_TOKENS = {
610
619
  blur: ["none", "xs", "sm", "md", "lg", "xl", "2xl", "3xl"],
611
620
  animate: ["none", "spin", "ping", "pulse", "bounce"],
612
621
  ease: ["linear", "in", "out", "in-out"],
613
- aspect: ["auto", "video", "square"]
622
+ // Note: aspect's auto/square, radius's none/full, leading's none, blur's
623
+ // none, animate's none, and ease's linear are static built-ins covered by
624
+ // STATIC_VALUE_TOKENS; the remainder are theme-derived.
625
+ aspect: ["video"]
626
+ };
627
+ var STATIC_VALUE_TOKENS = {
628
+ aspect: ["auto", "square"],
629
+ radius: ["none", "full"],
630
+ leading: ["none"],
631
+ blur: ["none"],
632
+ animate: ["none"],
633
+ ease: ["linear", "initial"],
634
+ z: ["auto"]
614
635
  };
615
636
  var DEFAULT_PALETTE_FAMILIES = /* @__PURE__ */ new Set([
616
637
  "red",
@@ -638,6 +659,7 @@ var DEFAULT_PALETTE_FAMILIES = /* @__PURE__ */ new Set([
638
659
  ]);
639
660
  var DEFAULT_PALETTE_SHADES = /* @__PURE__ */ new Set(["50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "950"]);
640
661
  var DEFAULT_COLOR_SPECIALS = /* @__PURE__ */ new Set(["white", "black", "transparent", "current", "inherit"]);
662
+ var STATIC_COLOR_SPECIALS = /* @__PURE__ */ new Set(["transparent", "current", "inherit"]);
641
663
  var NUMERIC_TOKEN_RE = /^-?(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
642
664
  var SIDE_WIDTH_TOKEN_RE = /^(?:x|y|t|r|b|l|s|e)-(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
643
665
  var OFFSET_TOKEN_RE = /^offset-(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
@@ -650,7 +672,7 @@ function stylesheetFiles(dir) {
650
672
  }
651
673
  return entries.flatMap((entry) => {
652
674
  if (entry.startsWith(".") || entry === "node_modules") return [];
653
- const entryPath = path4.join(dir, entry);
675
+ const entryPath = path5.join(dir, entry);
654
676
  let stats;
655
677
  try {
656
678
  stats = statSync(entryPath);
@@ -658,7 +680,7 @@ function stylesheetFiles(dir) {
658
680
  return [];
659
681
  }
660
682
  if (stats.isDirectory()) return stylesheetFiles(entryPath);
661
- return path4.extname(entry) === ".css" ? [entryPath] : [];
683
+ return path5.extname(entry) === ".css" ? [entryPath] : [];
662
684
  });
663
685
  }
664
686
  function themeBlocks(css2) {
@@ -679,10 +701,36 @@ function themeBlocks(css2) {
679
701
  }
680
702
  return blocks;
681
703
  }
704
+ function classSelectors(css2) {
705
+ const blocks = [];
706
+ let index = 0;
707
+ while (index < css2.length) {
708
+ const match = /@utility\s*\{/.exec(css2.slice(index));
709
+ if (!match) break;
710
+ const open = index + match.index + match[0].length - 1;
711
+ let depth = 1;
712
+ let cursor = open + 1;
713
+ for (; cursor < css2.length && depth > 0; cursor++) {
714
+ if (css2[cursor] === "{") depth++;
715
+ else if (css2[cursor] === "}") depth--;
716
+ }
717
+ blocks.push(css2.slice(open + 1, cursor - 1));
718
+ index = cursor;
719
+ }
720
+ let selectors = css2;
721
+ for (const block of blocks) selectors = selectors.replace(block, "");
722
+ const names = [];
723
+ for (const match of selectors.matchAll(/\.(?<className>[a-z][a-z0-9_-]*)(?=\s*[.,:#>{]|$)/gi)) {
724
+ const className = match.groups?.className;
725
+ if (className) names.push(className);
726
+ }
727
+ return names;
728
+ }
682
729
  function readInventory(files) {
683
730
  const utilities = /* @__PURE__ */ new Set();
684
731
  const utilityPrefixes = /* @__PURE__ */ new Set();
685
732
  const themeTokensByNamespace = /* @__PURE__ */ new Map();
733
+ const plainClasses = /* @__PURE__ */ new Set();
686
734
  let defaultsReset = false;
687
735
  for (const file of files) {
688
736
  let css2;
@@ -698,6 +746,7 @@ function readInventory(files) {
698
746
  const dash = utilityName.indexOf("-");
699
747
  if (dash > 0) utilityPrefixes.add(utilityName.slice(0, dash));
700
748
  }
749
+ for (const plainClass of classSelectors(css2)) plainClasses.add(plainClass);
701
750
  for (const block of themeBlocks(css2)) {
702
751
  if (/--\*\s*:\s*initial\b/.test(block)) defaultsReset = true;
703
752
  for (const decl of block.matchAll(/--(?<namespace>[a-z][a-z0-9]*)-(?<token>[a-z0-9][a-z0-9_-]*)\s*:/g)) {
@@ -713,10 +762,11 @@ function readInventory(files) {
713
762
  }
714
763
  }
715
764
  }
716
- return { utilities, utilityPrefixes, themeTokens: themeTokensByNamespace, defaultsReset };
765
+ return { utilities, utilityPrefixes, themeTokens: themeTokensByNamespace, defaultsReset, plainClasses };
717
766
  }
718
767
  function isColorToken(token, inventory) {
719
768
  if (inventory.themeTokens.get("color")?.has(token)) return true;
769
+ if (STATIC_COLOR_SPECIALS.has(token)) return true;
720
770
  if (inventory.defaultsReset) return false;
721
771
  if (DEFAULT_COLOR_SPECIALS.has(token)) return true;
722
772
  const hyphen = token.lastIndexOf("-");
@@ -738,6 +788,7 @@ function isKnown(className, inventory) {
738
788
  if (inventory.utilities.has(utility)) return true;
739
789
  const namespaces = PREFIX_NAMESPACES[prefix];
740
790
  if (!namespaces) {
791
+ if (STATIC_VALUE_TOKENS[prefix]?.includes(token)) return true;
741
792
  const projectTokens = inventory.themeTokens.get(prefix);
742
793
  if (projectTokens) {
743
794
  if (projectTokens.has(token)) return true;
@@ -754,6 +805,7 @@ function isKnown(className, inventory) {
754
805
  continue;
755
806
  }
756
807
  if (inventory.themeTokens.get(namespace)?.has(token)) return true;
808
+ if (STATIC_VALUE_TOKENS[namespace]?.includes(token)) return true;
757
809
  if (!inventory.defaultsReset && DEFAULT_TOKENS[namespace]?.includes(token)) return true;
758
810
  }
759
811
  return false;
@@ -768,11 +820,11 @@ var unknownUtilityRule = {
768
820
  }
769
821
  },
770
822
  create(context) {
771
- const sourceRoot2 = path4.resolve("src");
823
+ const sourceRoot = sourceRootOf(context);
772
824
  let inventory;
773
825
  try {
774
- if (!statSync(sourceRoot2).isDirectory()) return {};
775
- inventory = readInventory(stylesheetFiles(sourceRoot2));
826
+ if (!statSync(sourceRoot).isDirectory()) return {};
827
+ inventory = readInventory(stylesheetFiles(sourceRoot));
776
828
  } catch {
777
829
  return {};
778
830
  }
@@ -781,12 +833,19 @@ var unknownUtilityRule = {
781
833
  for (const candidate of value.split(/\s+/)) {
782
834
  if (!candidate || seen.has(candidate)) continue;
783
835
  seen.add(candidate);
784
- if (!isKnown(candidate, inventory)) {
836
+ if (isKnown(candidate, inventory)) continue;
837
+ const plainBase = candidate.replace(/!+$/, "").split(":").pop() ?? "";
838
+ if (inventory.plainClasses.has(plainBase)) {
785
839
  context.report({
786
840
  node,
787
- message: `Utility class "${candidate}" is not a custom @utility, a theme-generated utility, or a built-in Tailwind utility.`
841
+ message: `Utility class "${candidate}" is defined as a plain CSS selector in a stylesheet, not as an @utility (or @theme variable). Define it with @utility so the framework can own and validate it.`
788
842
  });
843
+ continue;
789
844
  }
845
+ context.report({
846
+ node,
847
+ message: `Utility class "${candidate}" is not a custom @utility, a theme-generated utility, or a built-in Tailwind utility.`
848
+ });
790
849
  }
791
850
  }
792
851
  function checkExpression(node, expression) {
@@ -1046,7 +1105,7 @@ var enforceCvaVariantPropsRule = {
1046
1105
  };
1047
1106
 
1048
1107
  // eslint/rules/enforce-barrel-exports.ts
1049
- import path5 from "path";
1108
+ import path6 from "path";
1050
1109
  import fs from "fs";
1051
1110
  function isPascalCase3(str) {
1052
1111
  return /^[A-Z][A-Za-z0-9]*$/.test(str);
@@ -1066,14 +1125,14 @@ var enforceBarrelExportsRule = {
1066
1125
  create(context) {
1067
1126
  const filename = context.filename;
1068
1127
  if (!filename) return {};
1069
- const baseName = path5.basename(filename);
1128
+ const baseName = path6.basename(filename);
1070
1129
  if (baseName !== "index.ts" && baseName !== "index.cts" && baseName !== "index.mts") return {};
1071
- const dirPath = path5.dirname(filename);
1072
- const folderName = path5.basename(dirPath);
1073
- const parentFolderName = path5.basename(path5.dirname(dirPath));
1130
+ const dirPath = path6.dirname(filename);
1131
+ const folderName = path6.basename(dirPath);
1132
+ const parentFolderName = path6.basename(path6.dirname(dirPath));
1074
1133
  if (SUPPORT_FOLDERS.has(folderName)) return {};
1075
1134
  if (!isPascalCase3(folderName) && !isKebabCase2(folderName)) return {};
1076
- const matchingTsx = fs.existsSync(path5.join(dirPath, `${folderName}.tsx`)) ? folderName : null;
1135
+ const matchingTsx = fs.existsSync(path6.join(dirPath, `${folderName}.tsx`)) ? folderName : null;
1077
1136
  if (!matchingTsx) return {};
1078
1137
  if (!isPascalCase3(parentFolderName) && !isKebabCase2(parentFolderName)) return {};
1079
1138
  const reExportedNames = /* @__PURE__ */ new Set();
@@ -1108,14 +1167,14 @@ var enforceBarrelExportsRule = {
1108
1167
  };
1109
1168
 
1110
1169
  // eslint/rules/component-placement.ts
1111
- import path9 from "path";
1170
+ import path10 from "path";
1112
1171
 
1113
1172
  // eslint/project/index.ts
1114
1173
  import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1115
- import path7 from "path";
1174
+ import path8 from "path";
1116
1175
 
1117
1176
  // eslint/project/parse-module.ts
1118
- import path6 from "path";
1177
+ import path7 from "path";
1119
1178
  import { readFileSync as readFileSync2 } from "fs";
1120
1179
  import ts2 from "typescript";
1121
1180
  var isPascalCase4 = (name) => /^[A-Z][A-Za-z0-9]*$/.test(name);
@@ -1216,7 +1275,7 @@ function parseModule(file) {
1216
1275
  exports.push({ name: statement.name.text, kind: "type", line: lineOf(sourceFile, statement) });
1217
1276
  }
1218
1277
  }
1219
- return { file: path6.resolve(file), imports, exports };
1278
+ return { file: path7.resolve(file), imports, exports };
1220
1279
  }
1221
1280
 
1222
1281
  // eslint/project/index.ts
@@ -1233,7 +1292,7 @@ function listSourceFiles(dir) {
1233
1292
  }
1234
1293
  return entries.flatMap((entry) => {
1235
1294
  if (entry.startsWith(".") || entry === "node_modules") return [];
1236
- const entryPath = path7.join(dir, entry);
1295
+ const entryPath = path8.join(dir, entry);
1237
1296
  let stats;
1238
1297
  try {
1239
1298
  stats = statSync2(entryPath);
@@ -1241,7 +1300,7 @@ function listSourceFiles(dir) {
1241
1300
  return [];
1242
1301
  }
1243
1302
  if (stats.isDirectory()) return listSourceFiles(entryPath);
1244
- return MODULE_EXTENSIONS.includes(path7.extname(entry)) ? [entryPath] : [];
1303
+ return MODULE_EXTENSIONS.includes(path8.extname(entry)) ? [entryPath] : [];
1245
1304
  });
1246
1305
  }
1247
1306
  function fingerprint(files) {
@@ -1254,19 +1313,19 @@ function fingerprint(files) {
1254
1313
  }
1255
1314
  return `${String(files.length)}:${String(total)}`;
1256
1315
  }
1257
- function resolveSpecifier(fromFile, specifier, sourceRoot2) {
1316
+ function resolveSpecifier(fromFile, specifier, sourceRoot) {
1258
1317
  let base;
1259
1318
  if (specifier.startsWith("@/")) {
1260
- base = path7.resolve(sourceRoot2, specifier.slice(2));
1319
+ base = path8.resolve(sourceRoot, specifier.slice(2));
1261
1320
  } else if (specifier.startsWith(".")) {
1262
- base = path7.resolve(path7.dirname(fromFile), specifier);
1321
+ base = path8.resolve(path8.dirname(fromFile), specifier);
1263
1322
  } else {
1264
1323
  return void 0;
1265
1324
  }
1266
1325
  const candidates = [
1267
1326
  base,
1268
1327
  ...MODULE_EXTENSIONS.map((extension) => `${base}${extension}`),
1269
- ...INDEX_BASENAMES.map((name) => path7.join(base, name))
1328
+ ...INDEX_BASENAMES.map((name) => path8.join(base, name))
1270
1329
  ];
1271
1330
  for (const candidate of candidates) {
1272
1331
  try {
@@ -1276,7 +1335,7 @@ function resolveSpecifier(fromFile, specifier, sourceRoot2) {
1276
1335
  }
1277
1336
  return void 0;
1278
1337
  }
1279
- function build(sourceRoot2, files) {
1338
+ function build(sourceRoot, files) {
1280
1339
  const modules = /* @__PURE__ */ new Map();
1281
1340
  const consumers = /* @__PURE__ */ new Map();
1282
1341
  const symbolConsumers = /* @__PURE__ */ new Map();
@@ -1288,7 +1347,7 @@ function build(sourceRoot2, files) {
1288
1347
  }
1289
1348
  for (const [file, module] of modules) {
1290
1349
  for (const moduleImport of module.imports) {
1291
- const target = resolveSpecifier(file, moduleImport.specifier, sourceRoot2);
1350
+ const target = resolveSpecifier(file, moduleImport.specifier, sourceRoot);
1292
1351
  if (!target || !modules.has(target)) continue;
1293
1352
  const fileConsumers = consumers.get(target) ?? /* @__PURE__ */ new Set();
1294
1353
  fileConsumers.add(file);
@@ -1301,39 +1360,39 @@ function build(sourceRoot2, files) {
1301
1360
  }
1302
1361
  }
1303
1362
  }
1304
- return { sourceRoot: sourceRoot2, modules, consumers, symbolConsumers };
1363
+ return { sourceRoot, modules, consumers, symbolConsumers };
1305
1364
  }
1306
1365
  var cache;
1307
- function getProjectIndex(sourceRoot2) {
1366
+ function getProjectIndex(sourceRoot) {
1308
1367
  const now = Date.now();
1309
- if (cache?.index.sourceRoot === sourceRoot2 && now - cache.checkedAt < REVALIDATE_AFTER_MS) {
1368
+ if (cache?.index.sourceRoot === sourceRoot && now - cache.checkedAt < REVALIDATE_AFTER_MS) {
1310
1369
  return cache.index;
1311
1370
  }
1312
1371
  try {
1313
- if (!statSync2(sourceRoot2).isDirectory()) return void 0;
1372
+ if (!statSync2(sourceRoot).isDirectory()) return void 0;
1314
1373
  } catch {
1315
1374
  return void 0;
1316
1375
  }
1317
- const files = listSourceFiles(sourceRoot2).sort((left, right) => left.localeCompare(right));
1376
+ const files = listSourceFiles(sourceRoot).sort((left, right) => left.localeCompare(right));
1318
1377
  const currentFingerprint = fingerprint(files);
1319
- if (cache?.index.sourceRoot === sourceRoot2 && cache.fingerprint === currentFingerprint) {
1378
+ if (cache?.index.sourceRoot === sourceRoot && cache.fingerprint === currentFingerprint) {
1320
1379
  cache.checkedAt = now;
1321
1380
  return cache.index;
1322
1381
  }
1323
- const index = build(sourceRoot2, files);
1382
+ const index = build(sourceRoot, files);
1324
1383
  cache = { index, checkedAt: now, fingerprint: currentFingerprint };
1325
1384
  return index;
1326
1385
  }
1327
1386
 
1328
1387
  // eslint/project/ccf.ts
1329
- import path8 from "path";
1388
+ import path9 from "path";
1330
1389
  var SUPPORT_FOLDERS2 = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
1331
- function segmentsOf(file, sourceRoot2) {
1332
- const relative = path8.relative(sourceRoot2, file);
1333
- return relative.startsWith("..") ? [] : relative.split(path8.sep);
1390
+ function segmentsOf(file, sourceRoot) {
1391
+ const relative = path9.relative(sourceRoot, file);
1392
+ return relative.startsWith("..") ? [] : relative.split(path9.sep);
1334
1393
  }
1335
- function folderSegmentsOf(file, sourceRoot2) {
1336
- return segmentsOf(file, sourceRoot2).slice(0, -1);
1394
+ function folderSegmentsOf(file, sourceRoot) {
1395
+ return segmentsOf(file, sourceRoot).slice(0, -1);
1337
1396
  }
1338
1397
  var isUnderApp = (segments) => segments[0] === "app";
1339
1398
  var isConfigModule = (segments) => segments[0] === "config";
@@ -1376,8 +1435,8 @@ function resolveComponentPlacement(componentFile, index) {
1376
1435
  return { countedConsumers: counted, expectedFolder: shared, reason: "ccf" };
1377
1436
  }
1378
1437
  var formatFolder = (folder) => `src/${folder.join("/")}/`;
1379
- function owningFolderOf(consumer, sourceRoot2) {
1380
- return outOfSupportFolders(folderSegmentsOf(consumer, sourceRoot2));
1438
+ function owningFolderOf(consumer, sourceRoot) {
1439
+ return outOfSupportFolders(folderSegmentsOf(consumer, sourceRoot));
1381
1440
  }
1382
1441
  var configModuleOf = (segments) => isConfigModule(segments) && segments.length >= 3 ? segments[1] : void 0;
1383
1442
  function resolveSupportPlacement(supportFile, supportFolder, index) {
@@ -1407,9 +1466,9 @@ function resolveSupportPlacement(supportFile, supportFolder, index) {
1407
1466
  }
1408
1467
  return { countedConsumers: consumers, expectedFolder: [...shared, supportFolder], reason: "ccf" };
1409
1468
  }
1410
- function describeConsumers(consumers, sourceRoot2) {
1469
+ function describeConsumers(consumers, sourceRoot) {
1411
1470
  const shown = 3;
1412
- const names = consumers.map((consumer) => path8.relative(path8.dirname(sourceRoot2), consumer).split(path8.sep).join("/")).sort((left, right) => left.localeCompare(right));
1471
+ const names = consumers.map((consumer) => path9.relative(path9.dirname(sourceRoot), consumer).split(path9.sep).join("/")).sort((left, right) => left.localeCompare(right));
1413
1472
  if (names.length <= shown) return names.join(", ");
1414
1473
  return `${names.slice(0, shown).join(", ")} and ${String(names.length - shown)} more`;
1415
1474
  }
@@ -1432,16 +1491,16 @@ var componentPlacementRule = {
1432
1491
  create(context) {
1433
1492
  const filename = context.filename;
1434
1493
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
1435
- const sourceRoot2 = path9.resolve("src");
1436
- const index = getProjectIndex(sourceRoot2);
1494
+ const sourceRoot = sourceRootOf(context);
1495
+ const index = getProjectIndex(sourceRoot);
1437
1496
  if (!index) return {};
1438
- const componentFile = path9.resolve(filename);
1439
- const segments = segmentsOf(componentFile, sourceRoot2);
1497
+ const componentFile = path10.resolve(filename);
1498
+ const segments = segmentsOf(componentFile, sourceRoot);
1440
1499
  if (segments.length === 0) return {};
1441
1500
  if (isUnderApp(segments) || isConfigModule(segments)) return {};
1442
1501
  const module = index.modules.get(componentFile);
1443
1502
  if (!module?.exports.some((moduleExport) => moduleExport.kind === "component")) return {};
1444
- const currentFolder = folderSegmentsOf(componentFile, sourceRoot2);
1503
+ const currentFolder = folderSegmentsOf(componentFile, sourceRoot);
1445
1504
  const placement = resolveComponentPlacement(componentFile, index);
1446
1505
  return {
1447
1506
  Program(node) {
@@ -1460,7 +1519,7 @@ var componentPlacementRule = {
1460
1519
  context.report({
1461
1520
  node,
1462
1521
  loc: { line: 1, column: 0 },
1463
- message: `Move this component to ${formatFolder(placement.expectedFolder)} \u2014 ${explanation}. Imported by ${describeConsumers(placement.countedConsumers, sourceRoot2)}. See docs/code-organization-guide/rules/component-placement-rule.md`
1522
+ message: `Move this component to ${formatFolder(placement.expectedFolder)} \u2014 ${explanation}. Imported by ${describeConsumers(placement.countedConsumers, sourceRoot)}. See docs/code-organization-guide/rules/component-placement-rule.md`
1464
1523
  });
1465
1524
  }
1466
1525
  };
@@ -1468,7 +1527,7 @@ var componentPlacementRule = {
1468
1527
  };
1469
1528
 
1470
1529
  // eslint/rules/support-file-placement.ts
1471
- import path10 from "path";
1530
+ import path11 from "path";
1472
1531
  var CONFIG_OWNED_FOLDERS = /* @__PURE__ */ new Set(["types", "constants"]);
1473
1532
  var REASON_TEXT2 = {
1474
1533
  "app-consumer": "a file under src/app/ imports it, so it belongs to the app-wide support folder",
@@ -1487,16 +1546,16 @@ var supportFilePlacementRule = {
1487
1546
  }
1488
1547
  },
1489
1548
  create(context) {
1490
- const sourceRoot2 = path10.resolve("src");
1491
- const supportFile = path10.resolve(context.filename);
1492
- const currentFolder = folderSegmentsOf(supportFile, sourceRoot2);
1549
+ const sourceRoot = sourceRootOf(context);
1550
+ const supportFile = path11.resolve(context.filename);
1551
+ const currentFolder = folderSegmentsOf(supportFile, sourceRoot);
1493
1552
  const supportFolder = currentFolder[currentFolder.length - 1];
1494
1553
  if (supportFolder === void 0 || !SUPPORT_FOLDERS2.has(supportFolder)) return {};
1495
- const index = getProjectIndex(sourceRoot2);
1554
+ const index = getProjectIndex(sourceRoot);
1496
1555
  if (!index) return {};
1497
1556
  const placement = resolveSupportPlacement(supportFile, supportFolder, index);
1498
1557
  if (!placement || sameFolder2(currentFolder, placement.expectedFolder)) return {};
1499
- if (isConfigModule(segmentsOf(supportFile, sourceRoot2)) && CONFIG_OWNED_FOLDERS.has(supportFolder)) {
1558
+ if (isConfigModule(segmentsOf(supportFile, sourceRoot)) && CONFIG_OWNED_FOLDERS.has(supportFolder)) {
1500
1559
  if (placement.reason !== "config-module") return {};
1501
1560
  }
1502
1561
  return {
@@ -1504,7 +1563,7 @@ var supportFilePlacementRule = {
1504
1563
  context.report({
1505
1564
  node,
1506
1565
  loc: { line: 1, column: 0 },
1507
- message: `Move this file to ${formatFolder(placement.expectedFolder)} \u2014 ${REASON_TEXT2[placement.reason] ?? "that is where its consumers place it"}. Imported by ${describeConsumers(placement.countedConsumers, sourceRoot2)}.`
1566
+ message: `Move this file to ${formatFolder(placement.expectedFolder)} \u2014 ${REASON_TEXT2[placement.reason] ?? "that is where its consumers place it"}. Imported by ${describeConsumers(placement.countedConsumers, sourceRoot)}.`
1508
1567
  });
1509
1568
  }
1510
1569
  };
@@ -1513,7 +1572,7 @@ var supportFilePlacementRule = {
1513
1572
 
1514
1573
  // eslint/rules/application-structure.ts
1515
1574
  import fs2 from "fs";
1516
- import path11 from "path";
1575
+ import path12 from "path";
1517
1576
  var MODULE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
1518
1577
  var ROUTING_FILES = /* @__PURE__ */ new Set([
1519
1578
  "default",
@@ -1542,7 +1601,7 @@ function report2(context, message) {
1542
1601
  };
1543
1602
  }
1544
1603
  function isCodeFile(filename) {
1545
- return MODULE_EXTENSIONS2.has(path11.extname(filename));
1604
+ return MODULE_EXTENSIONS2.has(path12.extname(filename));
1546
1605
  }
1547
1606
  function isRootSupportFolder(folder) {
1548
1607
  return SUPPORT_FOLDERS2.has(folder);
@@ -1560,28 +1619,28 @@ function expectedSupportFolder(kinds) {
1560
1619
  }
1561
1620
  return void 0;
1562
1621
  }
1563
- function configModuleRoot(filename, sourceRoot2) {
1564
- const segments = segmentsOf(filename, sourceRoot2);
1622
+ function configModuleRoot(filename, sourceRoot) {
1623
+ const segments = segmentsOf(filename, sourceRoot);
1565
1624
  if (segments[0] !== "config" || segments.length < 3) return void 0;
1566
- return path11.join(sourceRoot2, "config", segments[1] ?? "");
1625
+ return path12.join(sourceRoot, "config", segments[1] ?? "");
1567
1626
  }
1568
1627
  function componentFolderStart(segments) {
1569
1628
  if (segments[0] === "features") return 2;
1570
1629
  if (segments[0] === "compositions" || segments[0] === "shared") return 1;
1571
1630
  return -1;
1572
1631
  }
1573
- function componentFolderViolation(segments, sourceRoot2) {
1632
+ function componentFolderViolation(segments, sourceRoot) {
1574
1633
  const start = componentFolderStart(segments);
1575
1634
  if (start < 0) return void 0;
1576
1635
  for (let depth = segments.length - 2; depth >= start; depth -= 1) {
1577
1636
  const folder = segments[depth];
1578
1637
  if (!folder || SUPPORT_FOLDERS2.has(folder)) continue;
1579
- const folderPath = path11.join(sourceRoot2, ...segments.slice(0, depth + 1));
1638
+ const folderPath = path12.join(sourceRoot, ...segments.slice(0, depth + 1));
1580
1639
  const label = `src/${segments.slice(0, depth + 1).join("/")}/`;
1581
- if (!fs2.existsSync(path11.join(folderPath, `${folder}.tsx`))) {
1640
+ if (!fs2.existsSync(path12.join(folderPath, `${folder}.tsx`))) {
1582
1641
  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.`;
1583
1642
  }
1584
- if (!fs2.existsSync(path11.join(folderPath, "index.ts"))) {
1643
+ if (!fs2.existsSync(path12.join(folderPath, "index.ts"))) {
1585
1644
  return `A component folder must have an index.ts that named-re-exports its component; add index.ts to ${label}.`;
1586
1645
  }
1587
1646
  }
@@ -1596,9 +1655,9 @@ var applicationStructureRule = {
1596
1655
  }
1597
1656
  },
1598
1657
  create(context) {
1599
- const filename = path11.resolve(context.filename);
1600
- const sourceRoot2 = path11.resolve("src");
1601
- const segments = segmentsOf(filename, sourceRoot2);
1658
+ const filename = path12.resolve(context.filename);
1659
+ const sourceRoot = sourceRootOf(context);
1660
+ const segments = segmentsOf(filename, sourceRoot);
1602
1661
  if (segments.length === 0) return {};
1603
1662
  const [topLevel, secondLevel] = segments;
1604
1663
  if (topLevel === void 0) return {};
@@ -1626,43 +1685,43 @@ var applicationStructureRule = {
1626
1685
  "A configuration module must be a src/config/<config-name>/ folder with index.ts as its entry point."
1627
1686
  );
1628
1687
  }
1629
- const moduleRoot = configModuleRoot(filename, sourceRoot2);
1630
- if (moduleRoot && !fs2.existsSync(path11.join(moduleRoot, "index.ts"))) {
1688
+ const moduleRoot = configModuleRoot(filename, sourceRoot);
1689
+ if (moduleRoot && !fs2.existsSync(path12.join(moduleRoot, "index.ts"))) {
1631
1690
  return report2(
1632
1691
  context,
1633
- `Add src/config/${path11.basename(moduleRoot)}/index.ts as the configuration module entry point.`
1692
+ `Add src/config/${path12.basename(moduleRoot)}/index.ts as the configuration module entry point.`
1634
1693
  );
1635
1694
  }
1636
- if (moduleRoot && segments.length === 3 && path11.basename(filename) !== "index.ts") {
1695
+ if (moduleRoot && segments.length === 3 && path12.basename(filename) !== "index.ts") {
1637
1696
  const kinds2 = exportedKinds(filename);
1638
1697
  const expected2 = expectedSupportFolder(kinds2);
1639
1698
  if (expected2 !== void 0) {
1640
1699
  return report2(
1641
1700
  context,
1642
- `Move this configuration support file into src/config/${path11.basename(moduleRoot)}/${expected2}/.`
1701
+ `Move this configuration support file into src/config/${path12.basename(moduleRoot)}/${expected2}/.`
1643
1702
  );
1644
1703
  }
1645
1704
  }
1646
1705
  }
1647
1706
  if (topLevel === "app" && isCodeFile(filename)) {
1648
- const basename = path11.basename(filename, path11.extname(filename));
1649
- const currentFolder2 = path11.basename(path11.dirname(filename));
1707
+ const basename = path12.basename(filename, path12.extname(filename));
1708
+ const currentFolder2 = path12.basename(path12.dirname(filename));
1650
1709
  if (SUPPORT_FOLDERS2.has(currentFolder2)) {
1651
1710
  return report2(
1652
1711
  context,
1653
1712
  "src/app/ may contain routing files and framework assets, but ordinary components and support files must live outside src/app/."
1654
1713
  );
1655
1714
  }
1656
- if (!ROUTING_FILES.has(basename) && path11.extname(filename) !== ".css") {
1715
+ if (!ROUTING_FILES.has(basename) && path12.extname(filename) !== ".css") {
1657
1716
  return report2(
1658
1717
  context,
1659
1718
  "src/app/ may contain routing files and framework assets, but ordinary components and support files must live outside src/app/."
1660
1719
  );
1661
1720
  }
1662
1721
  }
1663
- const currentFolder = path11.basename(path11.dirname(filename));
1722
+ const currentFolder = path12.basename(path12.dirname(filename));
1664
1723
  const kinds = exportedKinds(filename);
1665
- const isConfigModuleRoot = topLevel === "config" && segments.length === 3 && path11.basename(filename) === "index.ts";
1724
+ const isConfigModuleRoot = topLevel === "config" && segments.length === 3 && path12.basename(filename) === "index.ts";
1666
1725
  if (isConfigModuleRoot) return {};
1667
1726
  if (!SUPPORT_FOLDERS2.has(currentFolder)) {
1668
1727
  const expected2 = expectedSupportFolder(kinds);
@@ -1672,14 +1731,14 @@ var applicationStructureRule = {
1672
1731
  `Move this file to a ${expected2}/ folder; ${currentFolder}/ is not a recognized support folder.`
1673
1732
  );
1674
1733
  }
1675
- const violation2 = componentFolderViolation(segments, sourceRoot2);
1734
+ const violation2 = componentFolderViolation(segments, sourceRoot);
1676
1735
  if (violation2 !== void 0) return report2(context, violation2);
1677
1736
  return {};
1678
1737
  }
1679
1738
  if (kinds.has("component")) {
1680
1739
  return report2(
1681
1740
  context,
1682
- `A support folder must not contain a component; move ${path11.basename(filename)} beside ${currentFolder}/.`
1741
+ `A support folder must not contain a component; move ${path12.basename(filename)} beside ${currentFolder}/.`
1683
1742
  );
1684
1743
  }
1685
1744
  const expected = expectedSupportFolder(kinds);
@@ -1689,14 +1748,14 @@ var applicationStructureRule = {
1689
1748
  `Move this file to a ${expected}/ folder; ${currentFolder}/ is reserved for ${expected === "utils" ? "utilities" : expected}.`
1690
1749
  );
1691
1750
  }
1692
- const violation = componentFolderViolation(segments, sourceRoot2);
1751
+ const violation = componentFolderViolation(segments, sourceRoot);
1693
1752
  if (violation !== void 0) return report2(context, violation);
1694
1753
  return {};
1695
1754
  }
1696
1755
  };
1697
1756
 
1698
1757
  // eslint/rules/named-exports.ts
1699
- import path12 from "path";
1758
+ import path13 from "path";
1700
1759
  var FRAMEWORK_DEFAULT_EXPORT_FILES = /* @__PURE__ */ new Set([
1701
1760
  "default",
1702
1761
  "error",
@@ -1708,9 +1767,9 @@ var FRAMEWORK_DEFAULT_EXPORT_FILES = /* @__PURE__ */ new Set([
1708
1767
  "template"
1709
1768
  ]);
1710
1769
  function isFrameworkDefaultExportFile(filename) {
1711
- const normalized = filename.replaceAll(path12.sep, "/");
1770
+ const normalized = filename.replaceAll(path13.sep, "/");
1712
1771
  if (!normalized.includes("/src/app/")) return false;
1713
- const basename = path12.basename(filename, path12.extname(filename));
1772
+ const basename = path13.basename(filename, path13.extname(filename));
1714
1773
  return FRAMEWORK_DEFAULT_EXPORT_FILES.has(basename);
1715
1774
  }
1716
1775
  var namedExportsRule = {
@@ -1735,7 +1794,7 @@ var namedExportsRule = {
1735
1794
  };
1736
1795
 
1737
1796
  // eslint/rules/data-testid-case.ts
1738
- import path13 from "path";
1797
+ import path14 from "path";
1739
1798
  var NEXT_ROUTING_FILES2 = /* @__PURE__ */ new Set([
1740
1799
  "page",
1741
1800
  "layout",
@@ -1759,9 +1818,9 @@ var dataTestIdCaseRule = {
1759
1818
  }
1760
1819
  },
1761
1820
  create(context) {
1762
- const filename = path13.resolve(context.filename);
1821
+ const filename = path14.resolve(context.filename);
1763
1822
  if (!filename.endsWith(".tsx")) return {};
1764
- const base = path13.basename(filename, path13.extname(filename));
1823
+ const base = path14.basename(filename, path14.extname(filename));
1765
1824
  if (NEXT_ROUTING_FILES2.has(base)) return {};
1766
1825
  const text = context.sourceCode.text;
1767
1826
  const components = parseComponentInfo(text, filename);
@@ -1803,7 +1862,7 @@ function toKebabCase(value) {
1803
1862
 
1804
1863
  // eslint/rules/support-folder-shape.ts
1805
1864
  import fs3 from "fs";
1806
- import path14 from "path";
1865
+ import path15 from "path";
1807
1866
  var SUPPORT_FOLDERS3 = /* @__PURE__ */ new Set(["constants", "types", "schemas"]);
1808
1867
  var INDEX_NAMES = /* @__PURE__ */ new Set(["index.ts", "index.tsx", "index.mts", "index.cts"]);
1809
1868
  var supportFolderShapeRule = {
@@ -1815,12 +1874,12 @@ var supportFolderShapeRule = {
1815
1874
  }
1816
1875
  },
1817
1876
  create(context) {
1818
- const filename = path14.resolve(context.filename);
1819
- const baseName = path14.basename(filename);
1877
+ const filename = path15.resolve(context.filename);
1878
+ const baseName = path15.basename(filename);
1820
1879
  if (!INDEX_NAMES.has(baseName)) return {};
1821
- const folder = path14.basename(path14.dirname(filename));
1880
+ const folder = path15.basename(path15.dirname(filename));
1822
1881
  if (!SUPPORT_FOLDERS3.has(folder)) return {};
1823
- const directory = path14.dirname(filename);
1882
+ const directory = path15.dirname(filename);
1824
1883
  let entries;
1825
1884
  try {
1826
1885
  entries = fs3.readdirSync(directory);
@@ -1838,7 +1897,7 @@ var supportFolderShapeRule = {
1838
1897
  const exportPattern = /export\s+(?:\{[^}]*\}|\*[^;]*)\s+from\s+["'](?<specifier>\.[^"']+)["']/g;
1839
1898
  for (const match of source.matchAll(exportPattern)) {
1840
1899
  const specifier = match.groups?.specifier;
1841
- if (specifier) exportedFiles.add(path14.basename(specifier));
1900
+ if (specifier) exportedFiles.add(path15.basename(specifier));
1842
1901
  }
1843
1902
  const missing = siblingModules.filter((entry) => {
1844
1903
  const stem = entry.replace(/\.(?:[cm]?tsx?|jsx?)$/, "");
@@ -1855,7 +1914,7 @@ var supportFolderShapeRule = {
1855
1914
  };
1856
1915
 
1857
1916
  // eslint/rules/import-through-index.ts
1858
- import path15 from "path";
1917
+ import path16 from "path";
1859
1918
  var importThroughIndexRule = {
1860
1919
  meta: {
1861
1920
  schema: [],
@@ -1865,19 +1924,19 @@ var importThroughIndexRule = {
1865
1924
  }
1866
1925
  },
1867
1926
  create(context) {
1868
- const filename = path15.resolve(context.filename);
1869
- const sourceRoot2 = sourceRootOf(filename);
1927
+ const filename = path16.resolve(context.filename);
1928
+ const sourceRoot = sourceRootOf2(context, filename);
1870
1929
  return {
1871
1930
  Program(node) {
1872
1931
  for (const specifier of importSpecifiers(context.sourceCode.text)) {
1873
- const target = resolveSpecifier(filename, specifier, sourceRoot2);
1932
+ const target = resolveSpecifier(filename, specifier, sourceRoot);
1874
1933
  if (!target) continue;
1875
- const targetSegments = segmentsOf(target, sourceRoot2);
1934
+ const targetSegments = segmentsOf(target, sourceRoot);
1876
1935
  const supportFolderIndex = targetSegments.findIndex(
1877
1936
  (segment) => ["constants", "types", "schemas"].includes(segment)
1878
1937
  );
1879
1938
  const supportFolder = supportFolderIndex >= 0 ? targetSegments[supportFolderIndex] : void 0;
1880
- if (!supportFolder || path15.basename(target).startsWith("index.")) continue;
1939
+ if (!supportFolder || path16.basename(target).startsWith("index.")) continue;
1881
1940
  const folderIndex = targetSegments.slice(0, supportFolderIndex + 1);
1882
1941
  const expected = `@/${folderIndex.join("/")}`;
1883
1942
  context.report({
@@ -1898,14 +1957,15 @@ function importSpecifiers(source) {
1898
1957
  }
1899
1958
  return specifiers;
1900
1959
  }
1901
- function sourceRootOf(filename) {
1902
- const marker = `${path15.sep}src${path15.sep}`;
1960
+ function sourceRootOf2(context, filename) {
1961
+ const marker = `${path16.sep}src${path16.sep}`;
1903
1962
  const srcIndex = filename.lastIndexOf(marker);
1904
- return srcIndex >= 0 ? filename.slice(0, srcIndex + marker.length - 1) : path15.resolve("src");
1963
+ if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
1964
+ return path16.resolve(context.cwd ?? process.cwd(), "src");
1905
1965
  }
1906
1966
 
1907
1967
  // eslint/rules/util-file-name.ts
1908
- import path16 from "path";
1968
+ import path17 from "path";
1909
1969
  function toKebabCase2(value) {
1910
1970
  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();
1911
1971
  }
@@ -1918,7 +1978,7 @@ var utilFileNameRule = {
1918
1978
  }
1919
1979
  },
1920
1980
  create(context) {
1921
- const filename = path16.resolve(context.filename);
1981
+ const filename = path17.resolve(context.filename);
1922
1982
  const segments = filename.replace(/\\/g, "/").split("/");
1923
1983
  if (!segments.includes("utils")) return {};
1924
1984
  let module;
@@ -1932,13 +1992,13 @@ var utilFileNameRule = {
1932
1992
  const functionName = functions[0]?.name;
1933
1993
  if (!functionName) return {};
1934
1994
  const expected = toKebabCase2(functionName);
1935
- const actual = path16.basename(filename, path16.extname(filename));
1995
+ const actual = path17.basename(filename, path17.extname(filename));
1936
1996
  if (!expected || actual === expected) return {};
1937
1997
  return {
1938
1998
  Program(node) {
1939
1999
  context.report({
1940
2000
  node,
1941
- message: `A utility file exporting ${functionName} must be named ${expected}.${path16.extname(filename).slice(1)}.`
2001
+ message: `A utility file exporting ${functionName} must be named ${expected}.${path17.extname(filename).slice(1)}.`
1942
2002
  });
1943
2003
  }
1944
2004
  };
@@ -1946,7 +2006,7 @@ var utilFileNameRule = {
1946
2006
  };
1947
2007
 
1948
2008
  // eslint/rules/no-util-barrel.ts
1949
- import path17 from "path";
2009
+ import path18 from "path";
1950
2010
  var noUtilBarrelRule = {
1951
2011
  meta: {
1952
2012
  schema: [],
@@ -1956,16 +2016,16 @@ var noUtilBarrelRule = {
1956
2016
  }
1957
2017
  },
1958
2018
  create(context) {
1959
- const filename = path17.resolve(context.filename);
1960
- const sourceRoot2 = sourceRootOf2(filename);
2019
+ const filename = path18.resolve(context.filename);
2020
+ const sourceRoot = sourceRootOf3(context, filename);
1961
2021
  return {
1962
2022
  Program(node) {
1963
2023
  for (const specifier of importSpecifiers2(context.sourceCode.text)) {
1964
- const target = resolveSpecifier(filename, specifier, sourceRoot2);
2024
+ const target = resolveSpecifier(filename, specifier, sourceRoot);
1965
2025
  if (!target) continue;
1966
2026
  const segments = target.replace(/\\/g, "/").split("/");
1967
2027
  const utilsIndex = segments.lastIndexOf("utils");
1968
- if (utilsIndex < 0 || !path17.basename(target).startsWith("index.")) continue;
2028
+ if (utilsIndex < 0 || !path18.basename(target).startsWith("index.")) continue;
1969
2029
  context.report({
1970
2030
  node,
1971
2031
  message: `Import utilities directly instead of through "${specifier}". See docs/code-organization-guide/rules/utilities-rule.md`
@@ -1984,10 +2044,11 @@ function importSpecifiers2(source) {
1984
2044
  }
1985
2045
  return specifiers;
1986
2046
  }
1987
- function sourceRootOf2(filename) {
1988
- const marker = `${path17.sep}src${path17.sep}`;
2047
+ function sourceRootOf3(context, filename) {
2048
+ const marker = `${path18.sep}src${path18.sep}`;
1989
2049
  const srcIndex = filename.lastIndexOf(marker);
1990
- return srcIndex >= 0 ? filename.slice(0, srcIndex + marker.length - 1) : path17.resolve("src");
2050
+ if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
2051
+ return path18.resolve(context.cwd ?? process.cwd(), "src");
1991
2052
  }
1992
2053
 
1993
2054
  // eslint/rules/jsx-hygiene.ts
@@ -2363,12 +2424,12 @@ var cvaBooleanVariantsRule = {
2363
2424
  };
2364
2425
 
2365
2426
  // eslint/rules/cross-feature-import.ts
2366
- import path18 from "path";
2427
+ import path19 from "path";
2367
2428
  var FEATURES_SEGMENT = "features";
2368
- function featureNameOf(resolvedPath, sourceRoot2) {
2369
- const relative = path18.relative(sourceRoot2, resolvedPath);
2429
+ function featureNameOf(resolvedPath, sourceRoot) {
2430
+ const relative = path19.relative(sourceRoot, resolvedPath);
2370
2431
  if (relative.startsWith("..")) return void 0;
2371
- const segments = relative.split(path18.sep);
2432
+ const segments = relative.split(path19.sep);
2372
2433
  if (segments[0] !== FEATURES_SEGMENT || segments.length < 2) return void 0;
2373
2434
  return segments[1];
2374
2435
  }
@@ -2383,10 +2444,10 @@ var crossFeatureImportRule = {
2383
2444
  create(context) {
2384
2445
  const filename = context.filename;
2385
2446
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
2386
- const sourceRoot2 = path18.resolve("src");
2387
- const fileRelative = path18.relative(sourceRoot2, filename);
2447
+ const sourceRoot = sourceRootOf(context);
2448
+ const fileRelative = path19.relative(sourceRoot, filename);
2388
2449
  if (fileRelative.startsWith("..")) return {};
2389
- const fileSegments = fileRelative.split(path18.sep);
2450
+ const fileSegments = fileRelative.split(path19.sep);
2390
2451
  const isInCompositions = fileSegments[0] === "compositions";
2391
2452
  const isInApp = fileSegments[0] === "app";
2392
2453
  const isConfig = fileSegments[0] === "config";
@@ -2400,12 +2461,12 @@ var crossFeatureImportRule = {
2400
2461
  if (typeof source.value !== "string") return;
2401
2462
  let resolved;
2402
2463
  if (source.value.startsWith("@/")) {
2403
- resolved = path18.resolve(sourceRoot2, source.value.slice(2));
2464
+ resolved = path19.resolve(sourceRoot, source.value.slice(2));
2404
2465
  } else if (source.value.startsWith(".")) {
2405
- resolved = path18.resolve(path18.dirname(filename), source.value);
2466
+ resolved = path19.resolve(path19.dirname(filename), source.value);
2406
2467
  }
2407
2468
  if (!resolved) return;
2408
- const feature = featureNameOf(resolved, sourceRoot2);
2469
+ const feature = featureNameOf(resolved, sourceRoot);
2409
2470
  if (feature) importedFeatures.add(feature);
2410
2471
  if (importedFeatures.size >= 2) {
2411
2472
  alreadyReported = true;
@@ -2421,7 +2482,7 @@ var crossFeatureImportRule = {
2421
2482
  };
2422
2483
 
2423
2484
  // eslint/rules/pure-function-extract.ts
2424
- import path19 from "path";
2485
+ import path20 from "path";
2425
2486
  function isComponentLikeName(name) {
2426
2487
  return /^[A-Z]/.test(name);
2427
2488
  }
@@ -2449,10 +2510,10 @@ var pureFunctionExtractRule = {
2449
2510
  create(context) {
2450
2511
  const filename = context.filename;
2451
2512
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
2452
- const sourceRoot2 = path19.resolve("src");
2453
- const relative = path19.relative(sourceRoot2, filename);
2513
+ const sourceRoot = sourceRootOf(context);
2514
+ const relative = path20.relative(sourceRoot, filename);
2454
2515
  if (relative.startsWith("..")) return {};
2455
- const segments = relative.split(path19.sep);
2516
+ const segments = relative.split(path20.sep);
2456
2517
  if (segments[0] === "utils") return {};
2457
2518
  if (segments[0] === "app") return {};
2458
2519
  const supportFolders = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
@@ -2492,7 +2553,7 @@ var pureFunctionExtractRule = {
2492
2553
  };
2493
2554
 
2494
2555
  // eslint/rules/hook-complexity.ts
2495
- import path20 from "path";
2556
+ import path21 from "path";
2496
2557
  import ts4 from "typescript";
2497
2558
  var REACT_HOOKS = /* @__PURE__ */ new Set([
2498
2559
  "useState",
@@ -2544,10 +2605,10 @@ var hookComplexityRule = {
2544
2605
  },
2545
2606
  create(context) {
2546
2607
  const filename = context.filename;
2547
- const sourceRoot2 = path20.resolve("src");
2548
- const relative = path20.relative(sourceRoot2, filename);
2608
+ const sourceRoot = sourceRootOf(context);
2609
+ const relative = path21.relative(sourceRoot, filename);
2549
2610
  if (relative.startsWith("..")) return {};
2550
- const segments = relative.split(path20.sep);
2611
+ const segments = relative.split(path21.sep);
2551
2612
  const sourceText = context.sourceCode.text;
2552
2613
  function checkHook(node, name, body, exported) {
2553
2614
  if (!exported) return;
@@ -2589,9 +2650,9 @@ var hookComplexityRule = {
2589
2650
  };
2590
2651
 
2591
2652
  // eslint/rules/locale-dotted-path.ts
2592
- import path21 from "path";
2653
+ import path22 from "path";
2593
2654
  function isInLocalesDir(filename) {
2594
- const segments = path21.resolve(filename).split(path21.sep);
2655
+ const segments = path22.resolve(filename).split(path22.sep);
2595
2656
  const srcIdx = segments.lastIndexOf("src");
2596
2657
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
2597
2658
  }
@@ -2640,9 +2701,9 @@ var localeDottedPathRule = {
2640
2701
  };
2641
2702
 
2642
2703
  // eslint/rules/locales-location.ts
2643
- import path22 from "path";
2704
+ import path23 from "path";
2644
2705
  function isLocalesFile(filename) {
2645
- const segments = path22.resolve(filename).split(path22.sep);
2706
+ const segments = path23.resolve(filename).split(path23.sep);
2646
2707
  const srcIdx = segments.lastIndexOf("src");
2647
2708
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
2648
2709
  }
@@ -2661,7 +2722,7 @@ var localesLocationRule = {
2661
2722
  create(context) {
2662
2723
  if (isLocalesFile(context.filename)) return {};
2663
2724
  const filename = context.filename;
2664
- const segments = path22.resolve(filename).split(path22.sep);
2725
+ const segments = path23.resolve(filename).split(path23.sep);
2665
2726
  const srcIdx = segments.lastIndexOf("src");
2666
2727
  if (srcIdx === -1) return {};
2667
2728
  const folder = segments[srcIdx + 1];
@@ -2683,7 +2744,7 @@ var localesLocationRule = {
2683
2744
  };
2684
2745
 
2685
2746
  // eslint/rules/hook-extraction.ts
2686
- import path23 from "path";
2747
+ import path24 from "path";
2687
2748
  var hookExtractionRule = {
2688
2749
  meta: {
2689
2750
  schema: [],
@@ -2693,15 +2754,15 @@ var hookExtractionRule = {
2693
2754
  }
2694
2755
  },
2695
2756
  create(context) {
2696
- const sourceRoot2 = path23.resolve("src");
2697
- const file = path23.resolve(context.filename);
2698
- const segments = segmentsOf(file, sourceRoot2);
2757
+ const sourceRoot = sourceRootOf(context);
2758
+ const file = path24.resolve(context.filename);
2759
+ const segments = segmentsOf(file, sourceRoot);
2699
2760
  if (segments.length === 0) return {};
2700
- const index = getProjectIndex(sourceRoot2);
2761
+ const index = getProjectIndex(sourceRoot);
2701
2762
  if (!index) return {};
2702
2763
  const module = index.modules.get(file);
2703
2764
  if (!module) return {};
2704
- const folder = folderSegmentsOf(file, sourceRoot2);
2765
+ const folder = folderSegmentsOf(file, sourceRoot);
2705
2766
  const alreadyInHooksFolder = folder.length > 0 && folder[folder.length - 1] === "hooks";
2706
2767
  const reusedHooks = module.exports.filter((exp) => exp.kind === "hook" && !alreadyInHooksFolder).map((exp) => ({ exp, consumers: index.symbolConsumers.get(symbolKey(file, exp.name)) })).filter(({ consumers }) => (consumers?.size ?? 0) >= 2);
2707
2768
  if (reusedHooks.length === 0) return {};
@@ -2720,7 +2781,7 @@ var hookExtractionRule = {
2720
2781
  };
2721
2782
 
2722
2783
  // eslint/rules/value-extraction.ts
2723
- import path24 from "path";
2784
+ import path25 from "path";
2724
2785
  var valueExtractionRule = {
2725
2786
  meta: {
2726
2787
  schema: [],
@@ -2730,21 +2791,21 @@ var valueExtractionRule = {
2730
2791
  }
2731
2792
  },
2732
2793
  create(context) {
2733
- const sourceRoot2 = path24.resolve("src");
2734
- const file = path24.resolve(context.filename);
2735
- const segments = segmentsOf(file, sourceRoot2);
2794
+ const sourceRoot = sourceRootOf(context);
2795
+ const file = path25.resolve(context.filename);
2796
+ const segments = segmentsOf(file, sourceRoot);
2736
2797
  if (segments.length === 0 || segments[0] !== "app") return {};
2737
- const index = getProjectIndex(sourceRoot2);
2798
+ const index = getProjectIndex(sourceRoot);
2738
2799
  if (!index) return {};
2739
2800
  const consumers = [...index.consumers.get(file) ?? []];
2740
- const outsideApp = consumers.filter((consumer) => segmentsOf(consumer, sourceRoot2)[0] !== "app");
2801
+ const outsideApp = consumers.filter((consumer) => segmentsOf(consumer, sourceRoot)[0] !== "app");
2741
2802
  if (outsideApp.length === 0) return {};
2742
2803
  return {
2743
2804
  Program(node) {
2744
2805
  context.report({
2745
2806
  node,
2746
2807
  loc: { line: 1, column: 0 },
2747
- message: `This file in src/app/ is imported by ${describeConsumers(outsideApp, sourceRoot2)} outside src/app/. Extract the value to a shared or feature folder so it can be imported independently. See docs/code-organization-guide/rules/constants-rule.md`
2808
+ message: `This file in src/app/ is imported by ${describeConsumers(outsideApp, sourceRoot)} outside src/app/. Extract the value to a shared or feature folder so it can be imported independently. See docs/code-organization-guide/rules/constants-rule.md`
2748
2809
  });
2749
2810
  }
2750
2811
  };
@@ -2752,7 +2813,7 @@ var valueExtractionRule = {
2752
2813
  };
2753
2814
 
2754
2815
  // eslint/rules/config-extraction.ts
2755
- import path25 from "path";
2816
+ import path26 from "path";
2756
2817
  var configExtractionRule = {
2757
2818
  meta: {
2758
2819
  schema: [],
@@ -2762,12 +2823,12 @@ var configExtractionRule = {
2762
2823
  }
2763
2824
  },
2764
2825
  create(context) {
2765
- const sourceRoot2 = path25.resolve("src");
2766
- const file = path25.resolve(context.filename);
2767
- const segments = segmentsOf(file, sourceRoot2);
2826
+ const sourceRoot = sourceRootOf(context);
2827
+ const file = path26.resolve(context.filename);
2828
+ const segments = segmentsOf(file, sourceRoot);
2768
2829
  if (segments.length < 3 || segments[0] !== "config") return {};
2769
2830
  if (SUPPORT_FOLDERS2.has(segments[2] ?? "")) return {};
2770
- const index = getProjectIndex(sourceRoot2);
2831
+ const index = getProjectIndex(sourceRoot);
2771
2832
  if (!index) return {};
2772
2833
  const module = index.modules.get(file);
2773
2834
  if (!module) return {};
@@ -2776,11 +2837,11 @@ var configExtractionRule = {
2776
2837
  const findings = [];
2777
2838
  for (const exp of suspects) {
2778
2839
  const consumers = [...index.symbolConsumers.get(symbolKey(file, exp.name)) ?? []];
2779
- const outsideConfig = consumers.filter((consumer) => segmentsOf(consumer, sourceRoot2)[0] !== "config");
2840
+ const outsideConfig = consumers.filter((consumer) => segmentsOf(consumer, sourceRoot)[0] !== "config");
2780
2841
  if (outsideConfig.length > 0) {
2781
2842
  findings.push({
2782
2843
  line: exp.line,
2783
- message: `${exp.kind} "${exp.name}" in a configuration module is imported by ${describeConsumers(outsideConfig, sourceRoot2)} outside src/config/. Move it to the matching root support folder. See docs/code-organization-guide/rules/configuration-rule.md`
2844
+ message: `${exp.kind} "${exp.name}" in a configuration module is imported by ${describeConsumers(outsideConfig, sourceRoot)} outside src/config/. Move it to the matching root support folder. See docs/code-organization-guide/rules/configuration-rule.md`
2784
2845
  });
2785
2846
  } else if (consumers.length > 0) {
2786
2847
  findings.push({
@@ -2801,7 +2862,7 @@ var configExtractionRule = {
2801
2862
  };
2802
2863
 
2803
2864
  // eslint/rules/component-nesting.ts
2804
- import path26 from "path";
2865
+ import path27 from "path";
2805
2866
  var componentNestingRule = {
2806
2867
  meta: {
2807
2868
  schema: [],
@@ -2811,18 +2872,18 @@ var componentNestingRule = {
2811
2872
  }
2812
2873
  },
2813
2874
  create(context) {
2814
- const sourceRoot2 = path26.resolve("src");
2815
- const file = path26.resolve(context.filename);
2816
- const segments = segmentsOf(file, sourceRoot2);
2875
+ const sourceRoot = sourceRootOf(context);
2876
+ const file = path27.resolve(context.filename);
2877
+ const segments = segmentsOf(file, sourceRoot);
2817
2878
  if (segments.length !== 4 || segments[0] !== "features") return {};
2818
- const index = getProjectIndex(sourceRoot2);
2879
+ const index = getProjectIndex(sourceRoot);
2819
2880
  if (!index) return {};
2820
2881
  const module = index.modules.get(file);
2821
2882
  if (!module?.exports.some((exp) => exp.kind === "component")) return {};
2822
2883
  const folder = segments.slice(0, 3);
2823
2884
  const hasChildComponent = [...index.modules.values()].some((candidate) => {
2824
2885
  if (candidate.file === file) return false;
2825
- const candidateSegments = segmentsOf(candidate.file, sourceRoot2);
2886
+ const candidateSegments = segmentsOf(candidate.file, sourceRoot);
2826
2887
  if (candidateSegments.length !== 4) return false;
2827
2888
  if (candidateSegments[0] !== folder[0] || candidateSegments[1] !== folder[1] || candidateSegments[2] !== folder[2]) {
2828
2889
  return false;
@@ -2845,7 +2906,7 @@ var componentNestingRule = {
2845
2906
  };
2846
2907
 
2847
2908
  // eslint/rules/stay-flat.ts
2848
- import path27 from "path";
2909
+ import path28 from "path";
2849
2910
  var stayFlatRule = {
2850
2911
  meta: {
2851
2912
  schema: [],
@@ -2855,11 +2916,11 @@ var stayFlatRule = {
2855
2916
  }
2856
2917
  },
2857
2918
  create(context) {
2858
- const sourceRoot2 = path27.resolve("src");
2859
- const file = path27.resolve(context.filename);
2860
- const segments = segmentsOf(file, sourceRoot2);
2919
+ const sourceRoot = sourceRootOf(context);
2920
+ const file = path28.resolve(context.filename);
2921
+ const segments = segmentsOf(file, sourceRoot);
2861
2922
  if (segments.length !== 3 || segments[0] !== "features") return {};
2862
- const index = getProjectIndex(sourceRoot2);
2923
+ const index = getProjectIndex(sourceRoot);
2863
2924
  if (!index) return {};
2864
2925
  const module = index.modules.get(file);
2865
2926
  if (!module?.exports.some((exp) => exp.kind === "component")) return {};
@@ -2867,7 +2928,7 @@ var stayFlatRule = {
2867
2928
  if (!featureName) return {};
2868
2929
  const exclusiveChildren = [...index.modules.values()].filter((candidate) => {
2869
2930
  if (candidate.file === file) return false;
2870
- const candidateSegments = segmentsOf(candidate.file, sourceRoot2);
2931
+ const candidateSegments = segmentsOf(candidate.file, sourceRoot);
2871
2932
  if (candidateSegments.length !== 3) return false;
2872
2933
  if (candidateSegments[0] !== "features" || candidateSegments[1] !== featureName) return false;
2873
2934
  if (candidateSegments[2]?.startsWith("index.")) return false;
@@ -2876,7 +2937,7 @@ var stayFlatRule = {
2876
2937
  if (consumers.size === 0) return false;
2877
2938
  return [...consumers].every((consumer) => {
2878
2939
  if (consumer === file) return true;
2879
- const consumerSegments = segmentsOf(consumer, sourceRoot2);
2940
+ const consumerSegments = segmentsOf(consumer, sourceRoot);
2880
2941
  if (consumerSegments[0] !== "features" || consumerSegments[1] !== featureName) return false;
2881
2942
  return !(index.modules.get(consumer)?.exports.some((exp) => exp.kind === "component") ?? false);
2882
2943
  });
@@ -2896,7 +2957,7 @@ var stayFlatRule = {
2896
2957
  };
2897
2958
 
2898
2959
  // eslint/rules/type-extraction.ts
2899
- import path28 from "path";
2960
+ import path29 from "path";
2900
2961
  var typeExtractionRule = {
2901
2962
  meta: {
2902
2963
  schema: [],
@@ -2906,11 +2967,11 @@ var typeExtractionRule = {
2906
2967
  }
2907
2968
  },
2908
2969
  create(context) {
2909
- const sourceRoot2 = path28.resolve("src");
2910
- const file = path28.resolve(context.filename);
2911
- const segments = segmentsOf(file, sourceRoot2);
2970
+ const sourceRoot = sourceRootOf(context);
2971
+ const file = path29.resolve(context.filename);
2972
+ const segments = segmentsOf(file, sourceRoot);
2912
2973
  if (segments.length === 0) return {};
2913
- const index = getProjectIndex(sourceRoot2);
2974
+ const index = getProjectIndex(sourceRoot);
2914
2975
  if (!index) return {};
2915
2976
  const module = index.modules.get(file);
2916
2977
  if (!module) return {};
@@ -2927,7 +2988,7 @@ var typeExtractionRule = {
2927
2988
  if (independent2.length === 0) continue;
2928
2989
  findings.push({
2929
2990
  line: exp.line,
2930
- message: `${exp.kind} "${exp.name}" is imported by ${describeConsumers(independent2, sourceRoot2)} without the component "${componentExport.name}" that defines it. Extract it to a types/ or schemas/ folder. See docs/code-organization-guide/rules/types-and-schemas-rule.md`
2991
+ message: `${exp.kind} "${exp.name}" is imported by ${describeConsumers(independent2, sourceRoot)} without the component "${componentExport.name}" that defines it. Extract it to a types/ or schemas/ folder. See docs/code-organization-guide/rules/types-and-schemas-rule.md`
2931
2992
  });
2932
2993
  continue;
2933
2994
  }
@@ -2938,7 +2999,7 @@ var typeExtractionRule = {
2938
2999
  if (independent.length === 0) continue;
2939
3000
  findings.push({
2940
3001
  line: exp.line,
2941
- message: `${exp.kind} "${exp.name}" is imported by ${describeConsumers(independent, sourceRoot2)} without using the code in this file. Extract it to a types/ or schemas/ folder. See docs/code-organization-guide/rules/types-and-schemas-rule.md`
3002
+ message: `${exp.kind} "${exp.name}" is imported by ${describeConsumers(independent, sourceRoot)} without using the code in this file. Extract it to a types/ or schemas/ folder. See docs/code-organization-guide/rules/types-and-schemas-rule.md`
2942
3003
  });
2943
3004
  }
2944
3005
  if (findings.length === 0) return {};
@@ -2953,7 +3014,7 @@ var typeExtractionRule = {
2953
3014
  };
2954
3015
 
2955
3016
  // eslint/rules/locale-placement.ts
2956
- import path29 from "path";
3017
+ import path30 from "path";
2957
3018
  import { readFileSync as readFileSync3 } from "fs";
2958
3019
  import ts5 from "typescript";
2959
3020
  var LOCALE_ACCESS = /\blocales\.(?<key>[A-Za-z_$][\w$]*)/g;
@@ -3000,14 +3061,14 @@ var localePlacementRule = {
3000
3061
  }
3001
3062
  },
3002
3063
  create(context) {
3003
- const sourceRoot2 = path29.resolve("src");
3004
- const file = path29.resolve(context.filename);
3005
- const segments = segmentsOf(file, sourceRoot2);
3064
+ const sourceRoot = sourceRootOf(context);
3065
+ const file = path30.resolve(context.filename);
3066
+ const segments = segmentsOf(file, sourceRoot);
3006
3067
  if (segments.length === 0) return {};
3007
- const index = getProjectIndex(sourceRoot2);
3068
+ const index = getProjectIndex(sourceRoot);
3008
3069
  if (!index) return {};
3009
3070
  const localesFile = [...index.modules.keys()].find((candidate) => {
3010
- const candidateSegments = segmentsOf(candidate, sourceRoot2);
3071
+ const candidateSegments = segmentsOf(candidate, sourceRoot);
3011
3072
  return candidateSegments.length === 2 && candidateSegments[0] === "locales" && candidateSegments[1]?.startsWith("index.");
3012
3073
  });
3013
3074
  if (!localesFile || file !== localesFile) return {};
@@ -3019,10 +3080,10 @@ var localePlacementRule = {
3019
3080
  for (const [candidateFile, module] of index.modules) {
3020
3081
  if (candidateFile === localesFile) continue;
3021
3082
  const importsLocales = module.imports.some(
3022
- (moduleImport) => resolveSpecifier(candidateFile, moduleImport.specifier, sourceRoot2) === localesFile
3083
+ (moduleImport) => resolveSpecifier(candidateFile, moduleImport.specifier, sourceRoot) === localesFile
3023
3084
  );
3024
3085
  if (!importsLocales) continue;
3025
- const candidateSegments = segmentsOf(candidateFile, sourceRoot2);
3086
+ const candidateSegments = segmentsOf(candidateFile, sourceRoot);
3026
3087
  for (const match of readFileSync3(candidateFile, "utf8").matchAll(LOCALE_ACCESS)) {
3027
3088
  const key = match.groups?.key;
3028
3089
  if (key === void 0) continue;
@@ -3047,7 +3108,7 @@ var localePlacementRule = {
3047
3108
  if (current?.kind === "nested") {
3048
3109
  findings.push({
3049
3110
  line: current.line,
3050
- message: `Locale "${key}" is read by ${describeConsumers([...readers], sourceRoot2)} and must live at the top level of locales. See docs/code-organization-guide/rules/locales-rule.md`
3111
+ message: `Locale "${key}" is read by ${describeConsumers([...readers], sourceRoot)} and must live at the top level of locales. See docs/code-organization-guide/rules/locales-rule.md`
3051
3112
  });
3052
3113
  }
3053
3114
  continue;
@@ -3079,7 +3140,7 @@ var localePlacementRule = {
3079
3140
  };
3080
3141
 
3081
3142
  // eslint/rules/sole-state-owner.ts
3082
- import path30 from "path";
3143
+ import path31 from "path";
3083
3144
  import ts6 from "typescript";
3084
3145
  function findStateHooks(node) {
3085
3146
  const hooks = [];
@@ -3159,7 +3220,7 @@ var soleStateOwnerRule = {
3159
3220
  }
3160
3221
  },
3161
3222
  create(context) {
3162
- const filename = path30.resolve(context.filename);
3223
+ const filename = path31.resolve(context.filename);
3163
3224
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
3164
3225
  const text = context.sourceCode.text;
3165
3226
  const components = parseComponentInfo(text, filename);
@@ -3238,7 +3299,7 @@ function usesOutsideJsx(declaration, hook, children) {
3238
3299
  }
3239
3300
 
3240
3301
  // eslint/rules/locale-key-shape.ts
3241
- import path31 from "path";
3302
+ import path32 from "path";
3242
3303
  var MAX_KEY_LENGTH = 30;
3243
3304
  var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
3244
3305
  "Button",
@@ -3288,7 +3349,7 @@ var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
3288
3349
  var CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
3289
3350
  var ENGLISH = /^[A-Za-z0-9_]*$/;
3290
3351
  function isLocalesFile2(filename) {
3291
- const segments = path31.resolve(filename).split(path31.sep);
3352
+ const segments = path32.resolve(filename).split(path32.sep);
3292
3353
  const srcIdx = segments.lastIndexOf("src");
3293
3354
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
3294
3355
  }
@@ -3359,7 +3420,7 @@ var localeKeyShapeRule = {
3359
3420
  };
3360
3421
 
3361
3422
  // eslint/rules/shared-style-dedup.ts
3362
- import path32 from "path";
3423
+ import path33 from "path";
3363
3424
  import { readFileSync as readFileSync4, statSync as statSync3 } from "fs";
3364
3425
  var CLASS_NAME = /className="(?<classes>[^"]+)"/g;
3365
3426
  var comboCache;
@@ -3400,11 +3461,11 @@ var sharedStyleDedupRule = {
3400
3461
  }
3401
3462
  },
3402
3463
  create(context) {
3403
- const sourceRoot2 = path32.resolve("src");
3404
- const file = path32.resolve(context.filename);
3405
- const segments = segmentsOf(file, sourceRoot2);
3464
+ const sourceRoot = sourceRootOf(context);
3465
+ const file = path33.resolve(context.filename);
3466
+ const segments = segmentsOf(file, sourceRoot);
3406
3467
  if (segments.length === 0) return {};
3407
- const index = getProjectIndex(sourceRoot2);
3468
+ const index = getProjectIndex(sourceRoot);
3408
3469
  if (!index) return {};
3409
3470
  const combos = combosFor(index);
3410
3471
  const sharedHere = [...combos.entries()].filter(([, users]) => users.size >= 2 && users.has(file)).filter(([, users]) => [...users].sort((left, right) => left.localeCompare(right))[0] === file);
@@ -3605,7 +3666,7 @@ var zodSchemaValidationRule = {
3605
3666
  };
3606
3667
 
3607
3668
  // eslint/rules/source-under-src.ts
3608
- import path33 from "path";
3669
+ import path34 from "path";
3609
3670
  var NON_SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set([
3610
3671
  ".agents",
3611
3672
  ".cache",
@@ -3646,14 +3707,14 @@ var sourceUnderSrcRule = {
3646
3707
  }
3647
3708
  },
3648
3709
  create(context) {
3649
- const filename = path33.resolve(context.filename);
3710
+ const filename = path34.resolve(context.filename);
3650
3711
  if (!MODULE_EXTENSION.test(filename)) return {};
3651
- const relative = path33.relative(process.cwd(), filename).replace(/\\/g, "/");
3712
+ const relative = path34.relative(context.cwd, filename).replace(/\\/g, "/");
3652
3713
  if (relative === "src" || relative.startsWith("src/")) return {};
3653
3714
  const topLevel = relative.split("/")[0] ?? "";
3654
3715
  if (NON_SOURCE_ROOT_DIRS.has(topLevel)) return {};
3655
3716
  if (!relative.includes("/")) {
3656
- const basename = path33.basename(filename);
3717
+ const basename = path34.basename(filename);
3657
3718
  if (CONFIG_FILE.test(basename) || DECLARATION_FILE.test(basename) || basename.startsWith(".")) return {};
3658
3719
  }
3659
3720
  return {
@@ -3670,7 +3731,7 @@ var sourceUnderSrcRule = {
3670
3731
 
3671
3732
  // eslint/rules/zirka-baseline.ts
3672
3733
  import fs4 from "fs";
3673
- import path34 from "path";
3734
+ import path35 from "path";
3674
3735
  var ESLINT_CONFIG = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
3675
3736
  var PRETTIER_CONFIGS = [
3676
3737
  "prettier.config.mjs",
@@ -3689,10 +3750,10 @@ var zirkaBaselineRule = {
3689
3750
  }
3690
3751
  },
3691
3752
  create(context) {
3692
- const filename = path34.resolve(context.filename);
3693
- const basename = path34.basename(filename);
3753
+ const filename = path35.resolve(context.filename);
3754
+ const basename = path35.basename(filename);
3694
3755
  if (!ESLINT_CONFIG.test(basename)) return {};
3695
- const projectRoot = path34.dirname(filename);
3756
+ const projectRoot = path35.dirname(filename);
3696
3757
  const report3 = (message) => {
3697
3758
  context.report({
3698
3759
  node: context.sourceCode.ast,
@@ -3707,7 +3768,7 @@ var zirkaBaselineRule = {
3707
3768
  'ESLint config must take its configuration from zirka (import { styleguide } from "zirka") instead of restating rules locally.'
3708
3769
  );
3709
3770
  }
3710
- const tsconfigPath = path34.join(projectRoot, "tsconfig.json");
3771
+ const tsconfigPath = path35.join(projectRoot, "tsconfig.json");
3711
3772
  if (!fs4.existsSync(tsconfigPath)) {
3712
3773
  report3('No tsconfig.json found. Create one extending the zirka TypeScript base config ("zirka/typescript").');
3713
3774
  } else {
@@ -3725,13 +3786,13 @@ var zirkaBaselineRule = {
3725
3786
  report3('tsconfig.json must extend the zirka TypeScript base config ("zirka/typescript").');
3726
3787
  }
3727
3788
  }
3728
- const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs4.existsSync(path34.join(projectRoot, name)));
3789
+ const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs4.existsSync(path35.join(projectRoot, name)));
3729
3790
  if (!prettierConfigFile) {
3730
3791
  report3(
3731
3792
  "No prettier config found. Create one that takes its configuration from zirka (styleguide({ prettier: true }).prettierConfig)."
3732
3793
  );
3733
3794
  } else {
3734
- const content = fs4.readFileSync(path34.join(projectRoot, prettierConfigFile), "utf8");
3795
+ const content = fs4.readFileSync(path35.join(projectRoot, prettierConfigFile), "utf8");
3735
3796
  if (!content.includes("zirka")) {
3736
3797
  report3(
3737
3798
  "The prettier config must take its configuration from zirka (styleguide({ prettier: true }).prettierConfig) instead of restating it locally."
@@ -3801,7 +3862,7 @@ var docKindSuffixRule = {
3801
3862
  };
3802
3863
 
3803
3864
  // eslint/rules/documentation/title-matches-file-name.ts
3804
- import path35 from "path";
3865
+ import path36 from "path";
3805
3866
  function toExpectedFileName(title) {
3806
3867
  return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
3807
3868
  }
@@ -3821,7 +3882,7 @@ var titleMatchesFileNameRule = {
3821
3882
  if (!filename.endsWith(".md")) return;
3822
3883
  const title = getTextContent(node).trim();
3823
3884
  const expectedFileName = toExpectedFileName(title);
3824
- const actualFileName = path35.basename(filename);
3885
+ const actualFileName = path36.basename(filename);
3825
3886
  if (!title) {
3826
3887
  context.report({
3827
3888
  node,
@@ -4228,7 +4289,7 @@ var referenceBlockHeadingsRule = {
4228
4289
  };
4229
4290
 
4230
4291
  // eslint/rules/documentation/support-document-placement.ts
4231
- import path36 from "path";
4292
+ import path37 from "path";
4232
4293
  var supportDocumentPlacementRule = {
4233
4294
  meta: {
4234
4295
  type: "problem",
@@ -4242,7 +4303,7 @@ var supportDocumentPlacementRule = {
4242
4303
  root(node) {
4243
4304
  const filename = getFilename(context);
4244
4305
  if (!filename.endsWith(".md")) return;
4245
- const parentFolder = path36.basename(path36.dirname(filename));
4306
+ const parentFolder = path37.basename(path37.dirname(filename));
4246
4307
  if (filename.endsWith("-rule.md") && parentFolder !== "rules") {
4247
4308
  context.report({
4248
4309
  node,
@@ -4285,11 +4346,11 @@ var noTemplatePromptRule = {
4285
4346
  };
4286
4347
 
4287
4348
  // eslint/rules/documentation/guide-folder-entry-point.ts
4288
- import path38 from "path";
4349
+ import path39 from "path";
4289
4350
 
4290
4351
  // eslint/rules/documentation/project-index.ts
4291
4352
  import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
4292
- import path37 from "path";
4353
+ import path38 from "path";
4293
4354
  var KIND_BY_SUFFIX = [
4294
4355
  ["-rule.md", "rule"],
4295
4356
  ["-guide.md", "guide"],
@@ -4298,7 +4359,7 @@ var KIND_BY_SUFFIX = [
4298
4359
  ];
4299
4360
  function listMarkdownFiles(dir) {
4300
4361
  return readdirSync3(dir).flatMap((entry) => {
4301
- const entryPath = path37.join(dir, entry);
4362
+ const entryPath = path38.join(dir, entry);
4302
4363
  if (statSync4(entryPath).isDirectory()) {
4303
4364
  return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
4304
4365
  }
@@ -4316,11 +4377,11 @@ function getProjectDocs(docsRoot) {
4316
4377
  if (cached) return cached;
4317
4378
  const files = listMarkdownFiles(docsRoot);
4318
4379
  const docs = files.sort((a, b) => a.localeCompare(b)).map((filePath) => {
4319
- const fileName = path37.basename(filePath);
4380
+ const fileName = path38.basename(filePath);
4320
4381
  const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
4321
4382
  return {
4322
4383
  filePath,
4323
- doc: path37.relative(docsRoot, filePath).split(path37.sep).join("/"),
4384
+ doc: path38.relative(docsRoot, filePath).split(path38.sep).join("/"),
4324
4385
  fileName,
4325
4386
  kind,
4326
4387
  title: extractTitle(filePath)
@@ -4330,12 +4391,12 @@ function getProjectDocs(docsRoot) {
4330
4391
  return docs;
4331
4392
  }
4332
4393
  function findDocsRoot(filePath) {
4333
- let dir = path37.dirname(filePath);
4394
+ let dir = path38.dirname(filePath);
4334
4395
  for (; ; ) {
4335
- if (path37.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
4396
+ if (path38.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
4336
4397
  return dir;
4337
4398
  }
4338
- const parent = path37.dirname(dir);
4399
+ const parent = path38.dirname(dir);
4339
4400
  if (parent === dir) return void 0;
4340
4401
  dir = parent;
4341
4402
  }
@@ -4359,13 +4420,13 @@ var guideFolderEntryPointRule = {
4359
4420
  if (!docsRoot) return;
4360
4421
  const docs = getProjectDocs(docsRoot);
4361
4422
  const guideFolders = new Set(
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))
4423
+ docs.filter((doc) => ["rules", "references"].includes(path39.basename(path39.dirname(doc.filePath)))).map((doc) => path39.dirname(path39.dirname(doc.filePath))).filter((folder) => path39.resolve(folder) !== path39.resolve(docsRoot))
4363
4424
  );
4364
- const currentDir = path38.dirname(filename);
4425
+ const currentDir = path39.dirname(filename);
4365
4426
  if (guideFolders.has(currentDir)) {
4366
- const expectedEntryPoint = `${path38.basename(currentDir)}.md`;
4427
+ const expectedEntryPoint = `${path39.basename(currentDir)}.md`;
4367
4428
  const hasEntryPoint = docs.some(
4368
- (doc) => doc.kind === "guide" && path38.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
4429
+ (doc) => doc.kind === "guide" && path39.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
4369
4430
  );
4370
4431
  if (!hasEntryPoint) {
4371
4432
  context.report({
@@ -4590,7 +4651,7 @@ var noNestedHowToRule = {
4590
4651
 
4591
4652
  // eslint/rules/documentation/glossary-term-linking.ts
4592
4653
  import { readFileSync as readFileSync6 } from "fs";
4593
- import path39 from "path";
4654
+ import path40 from "path";
4594
4655
  function extractGlossaryTerms(filePath) {
4595
4656
  const content = readFileSync6(filePath, "utf8");
4596
4657
  const terms = [];
@@ -4644,9 +4705,9 @@ var glossaryTermLinkingRule = {
4644
4705
  const docsRoot = findDocsRoot(filename);
4645
4706
  if (!docsRoot) return;
4646
4707
  const docs = getProjectDocs(docsRoot);
4647
- const guideDir = path39.dirname(filename);
4708
+ const guideDir = path40.dirname(filename);
4648
4709
  const guideReferences = docs.filter(
4649
- (doc) => doc.kind === "reference" && path39.dirname(doc.filePath) === guideDir
4710
+ (doc) => doc.kind === "reference" && path40.dirname(doc.filePath) === guideDir
4650
4711
  );
4651
4712
  if (guideReferences.length === 0) return;
4652
4713
  const glossaryTerms = [];
@@ -4671,7 +4732,7 @@ var glossaryTermLinkingRule = {
4671
4732
 
4672
4733
  // eslint/rules/documentation/guide-mentions-documents.ts
4673
4734
  import { existsSync } from "fs";
4674
- import path40 from "path";
4735
+ import path41 from "path";
4675
4736
  function visitSteps3(node, check) {
4676
4737
  if (node.type === "list" && node.ordered) {
4677
4738
  for (const child of node.children) check(child);
@@ -4704,12 +4765,12 @@ var guideMentionsDocumentsRule = {
4704
4765
  if (!filename.endsWith("-guide.md")) return;
4705
4766
  const docsRoot = findDocsRoot(filename);
4706
4767
  if (!docsRoot) return;
4707
- const guideDir = path40.dirname(filename);
4708
- if (path40.basename(filename, ".md") !== path40.basename(guideDir)) return;
4768
+ const guideDir = path41.dirname(filename);
4769
+ if (path41.basename(filename, ".md") !== path41.basename(guideDir)) return;
4709
4770
  const docs = getProjectDocs(docsRoot);
4710
4771
  const owned = docs.filter((doc) => {
4711
- const parent = path40.dirname(doc.filePath);
4712
- return parent === path40.join(guideDir, "rules") || parent === path40.join(guideDir, "references");
4772
+ const parent = path41.dirname(doc.filePath);
4773
+ return parent === path41.join(guideDir, "rules") || parent === path41.join(guideDir, "references");
4713
4774
  });
4714
4775
  const allLinks = [];
4715
4776
  collectMarkdownLinks(node, allLinks);
@@ -4736,7 +4797,7 @@ var guideMentionsDocumentsRule = {
4736
4797
  for (const link of allLinks) {
4737
4798
  const target = linkTarget(link.url);
4738
4799
  if (!target.endsWith(".md")) continue;
4739
- const resolved = path40.normalize(path40.join(guideDir, target));
4800
+ const resolved = path41.normalize(path41.join(guideDir, target));
4740
4801
  if (!existsSync(resolved)) {
4741
4802
  context.report({
4742
4803
  node: link,
@@ -5177,32 +5238,170 @@ var themeVariableNamespaceRule = {
5177
5238
  }
5178
5239
  };
5179
5240
 
5180
- // eslint/rules/tailwind/global-css-location.ts
5181
- var globalCssLocationRule = {
5241
+ // eslint/rules/tailwind/css-entry-point.ts
5242
+ import { statSync as statSync6 } from "fs";
5243
+ import path44 from "path";
5244
+
5245
+ // eslint/rules/tailwind/source-files.ts
5246
+ import { readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
5247
+ import path42 from "path";
5248
+ var CSS_EXTENSIONS = [".css"];
5249
+ var MODULE_EXTENSIONS3 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
5250
+ var SOURCE_EXTENSIONS = [...MODULE_EXTENSIONS3, ...CSS_EXTENSIONS];
5251
+ function findFiles(dir, extensions) {
5252
+ let entries;
5253
+ try {
5254
+ entries = readdirSync4(dir);
5255
+ } catch {
5256
+ return [];
5257
+ }
5258
+ return entries.flatMap((entry) => {
5259
+ if (entry.startsWith(".") || entry === "node_modules") return [];
5260
+ const entryPath = path42.join(dir, entry);
5261
+ let stats;
5262
+ try {
5263
+ stats = statSync5(entryPath);
5264
+ } catch {
5265
+ return [];
5266
+ }
5267
+ if (stats.isDirectory()) return findFiles(entryPath, extensions);
5268
+ return extensions.includes(path42.extname(entry)) ? [entryPath] : [];
5269
+ });
5270
+ }
5271
+ function cachedTextReader() {
5272
+ const texts = /* @__PURE__ */ new Map();
5273
+ return (file) => {
5274
+ let text = texts.get(file);
5275
+ if (text === void 0) {
5276
+ try {
5277
+ text = readFileSync7(file, "utf8");
5278
+ } catch {
5279
+ text = "";
5280
+ }
5281
+ texts.set(file, text);
5282
+ }
5283
+ return text;
5284
+ };
5285
+ }
5286
+ function escapeRegExp(text) {
5287
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5288
+ }
5289
+
5290
+ // eslint/rules/tailwind/stylesheet-graph.ts
5291
+ import path43 from "path";
5292
+ function registersTailwind(text) {
5293
+ return /@import\s+(?:url\(\s*)?["']tailwindcss["']\s*\)?/i.test(text);
5294
+ }
5295
+ function importedSpecifiers(text) {
5296
+ return [...text.matchAll(/@import\s+(?:url\(\s*)?["'](?<spec>[^"']+)["']/gi)].map(
5297
+ (match) => match.groups?.spec ?? ""
5298
+ );
5299
+ }
5300
+ function moduleImports(text, fileName) {
5301
+ const escaped = fileName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5302
+ return new RegExp(`(?:import|require)\\s*\\(?\\s*["'][^"']*${escaped}["']`, "i").test(text);
5303
+ }
5304
+ function resolveSpecifier2(fromFile, spec, sourceRoot) {
5305
+ if (spec.startsWith("/")) return path43.resolve(spec);
5306
+ if (spec.startsWith("./") || spec.startsWith("../")) return path43.resolve(path43.dirname(fromFile), spec);
5307
+ if (spec.startsWith("@/")) return path43.resolve(sourceRoot, spec.slice(2));
5308
+ return void 0;
5309
+ }
5310
+ function buildStylesheetGraph(options) {
5311
+ const { cssFiles, sourceRoot, textOf } = options;
5312
+ const cssSet = new Set(cssFiles.map((file) => path43.normalize(file)));
5313
+ const globals = cssFiles.filter((file) => registersTailwind(textOf(file)));
5314
+ const reachable = /* @__PURE__ */ new Set();
5315
+ const queue = [...globals];
5316
+ for (const global of globals) reachable.add(path43.normalize(global));
5317
+ while (queue.length > 0) {
5318
+ const from = queue.shift();
5319
+ if (!from) continue;
5320
+ for (const spec of importedSpecifiers(textOf(from))) {
5321
+ const target = resolveSpecifier2(from, spec, sourceRoot);
5322
+ if (!target) continue;
5323
+ const normalized = path43.normalize(target);
5324
+ if (cssSet.has(normalized) && !reachable.has(normalized)) {
5325
+ reachable.add(normalized);
5326
+ queue.push(normalized);
5327
+ }
5328
+ }
5329
+ }
5330
+ const directChildren = /* @__PURE__ */ new Set();
5331
+ for (const global of globals) {
5332
+ for (const spec of importedSpecifiers(textOf(global))) {
5333
+ const target = resolveSpecifier2(global, spec, sourceRoot);
5334
+ if (!target) continue;
5335
+ const normalized = path43.normalize(target);
5336
+ if (cssSet.has(normalized)) directChildren.add(normalized);
5337
+ }
5338
+ }
5339
+ return { globals, reachable, directChildren };
5340
+ }
5341
+
5342
+ // eslint/rules/tailwind/css-entry-point.ts
5343
+ var cssEntryPointRule = {
5182
5344
  meta: {
5183
5345
  schema: [],
5184
5346
  type: "problem",
5185
5347
  docs: {
5186
- description: "Require project global CSS to live only in the entry point stylesheet."
5348
+ description: "Require one global entry point; project CSS only in a stylesheet it imports directly."
5187
5349
  }
5188
5350
  },
5189
5351
  create(context) {
5352
+ const sourceRoot = sourceRootOf(context);
5353
+ let cssFiles;
5354
+ let moduleFiles;
5355
+ try {
5356
+ if (!statSync6(sourceRoot).isDirectory()) return {};
5357
+ cssFiles = findFiles(sourceRoot, CSS_EXTENSIONS);
5358
+ moduleFiles = findFiles(sourceRoot, MODULE_EXTENSIONS3);
5359
+ } catch {
5360
+ return {};
5361
+ }
5362
+ const textOf = cachedTextReader();
5363
+ const graph = buildStylesheetGraph({ cssFiles, sourceRoot, textOf });
5364
+ const { globals, reachable, directChildren } = graph;
5190
5365
  return {
5191
5366
  "StyleSheet:exit"(node) {
5192
- const registersTailwind = node.children.some(
5193
- (child) => child.type === "Atrule" && child.name === "import" && JSON.stringify(child.prelude).includes("tailwindcss")
5194
- );
5195
- if (registersTailwind) return;
5367
+ if (globals.length === 0) return;
5368
+ const current = path44.normalize(path44.resolve(context.filename));
5369
+ if (globals.includes(current)) {
5370
+ if (globals.length > 1) {
5371
+ context.report({
5372
+ node,
5373
+ message: "Only one stylesheet may register Tailwind as the global entry point."
5374
+ });
5375
+ return;
5376
+ }
5377
+ const basename = path44.basename(current);
5378
+ const importCount = moduleFiles.filter((modulePath) => moduleImports(textOf(modulePath), basename)).length;
5379
+ if (importCount !== 1) {
5380
+ context.report({
5381
+ node,
5382
+ message: `The global stylesheet entry point must be imported by exactly one module (the root layout), but it is imported by ${String(importCount)} module(s).`
5383
+ });
5384
+ }
5385
+ return;
5386
+ }
5196
5387
  const hasProjectCss = node.children.some((child) => {
5197
5388
  if (child.type === "Atrule" && child.name === "import") return false;
5198
5389
  if (child.type === "Comment") return false;
5199
5390
  return true;
5200
5391
  });
5201
- if (!hasProjectCss) return;
5202
- context.report({
5203
- node,
5204
- message: "Global CSS must live in the global stylesheet entry point that registers Tailwind, not in this file."
5205
- });
5392
+ if (hasProjectCss && !directChildren.has(current)) {
5393
+ context.report({
5394
+ node,
5395
+ message: "Project CSS may only live in a stylesheet the global entry point imports directly; route it through a direct import or into the entry point."
5396
+ });
5397
+ return;
5398
+ }
5399
+ if (!reachable.has(current)) {
5400
+ context.report({
5401
+ node,
5402
+ message: "This stylesheet must be imported by the global stylesheet entry point via @import, so CSS arrives through one door."
5403
+ });
5404
+ }
5206
5405
  }
5207
5406
  };
5208
5407
  }
@@ -5220,10 +5419,10 @@ var globalStylesheetRule = {
5220
5419
  create(context) {
5221
5420
  return {
5222
5421
  "StyleSheet:exit"(node) {
5223
- const registersTailwind = node.children.some(
5422
+ const registersTailwind2 = node.children.some(
5224
5423
  (child) => child.type === "Atrule" && child.name === "import" && JSON.stringify(child.prelude).includes("tailwindcss")
5225
5424
  );
5226
- if (!registersTailwind) {
5425
+ if (!registersTailwind2) {
5227
5426
  const hasProjectCss = node.children.some((child) => {
5228
5427
  if (child.type === "Atrule" && child.name === "import") return false;
5229
5428
  if (child.type === "Comment") return false;
@@ -5242,32 +5441,9 @@ var globalStylesheetRule = {
5242
5441
  };
5243
5442
 
5244
5443
  // 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
- }
5444
+ import { statSync as statSync7 } from "fs";
5268
5445
  function usagePattern(name) {
5269
- const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5270
- return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`);
5446
+ return new RegExp(`(?<![\\w-])${escapeRegExp(name)}(?![\\w-])`);
5271
5447
  }
5272
5448
  var unusedUtilityRule = {
5273
5449
  meta: {
@@ -5278,27 +5454,15 @@ var unusedUtilityRule = {
5278
5454
  }
5279
5455
  },
5280
5456
  create(context) {
5281
- const sourceRoot2 = path41.resolve("src");
5457
+ const sourceRoot = sourceRootOf(context);
5282
5458
  let files;
5283
5459
  try {
5284
- if (!statSync5(sourceRoot2).isDirectory()) return {};
5285
- files = sourceFiles(sourceRoot2);
5460
+ if (!statSync7(sourceRoot).isDirectory()) return {};
5461
+ files = findFiles(sourceRoot, SOURCE_EXTENSIONS);
5286
5462
  } catch {
5287
5463
  return {};
5288
5464
  }
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
- };
5465
+ const textOf = cachedTextReader();
5302
5466
  return {
5303
5467
  "StyleSheet:exit"(node) {
5304
5468
  for (const utility of atrulesNamed(node, "utility")) {
@@ -5329,7 +5493,7 @@ var tailwindRules = {
5329
5493
  "custom-utility-apply": customUtilityApplyRule,
5330
5494
  "surface-utility": surfaceUtilityRule,
5331
5495
  "theme-variable-namespace": themeVariableNamespaceRule,
5332
- "global-css-location": globalCssLocationRule,
5496
+ "css-entry-point": cssEntryPointRule,
5333
5497
  "global-stylesheet": globalStylesheetRule,
5334
5498
  "unused-utility": unusedUtilityRule
5335
5499
  };
@@ -5473,13 +5637,13 @@ var repoPackageJsonRules = {
5473
5637
  "no-vulyk-dependency": noVulykDependencyRule,
5474
5638
  "exact-version": exactVersionRule
5475
5639
  };
5476
- var nextPackageJsonRules = {
5640
+ var nextjsPackageJsonRules = {
5477
5641
  "nextjs-stack": nextjsStackRule
5478
5642
  };
5479
5643
 
5480
5644
  // eslint/rules/husky/husky-hook.ts
5481
5645
  import { existsSync as existsSync2, readFileSync as readFileSync8 } from "fs";
5482
- import path42 from "path";
5646
+ import path45 from "path";
5483
5647
  function memberName4(member) {
5484
5648
  return member.name.type === "String" ? member.name.value : member.name.name;
5485
5649
  }
@@ -5498,7 +5662,7 @@ var huskyHookRule = {
5498
5662
  if (root.type !== "Object") return;
5499
5663
  const scriptName = context.filename;
5500
5664
  if (!scriptName.endsWith("package.json")) return;
5501
- const hookPath = path42.join(process.cwd(), ".husky", "pre-commit");
5665
+ const hookPath = path45.join(context.cwd, ".husky", "pre-commit");
5502
5666
  if (!existsSync2(hookPath)) {
5503
5667
  context.report({
5504
5668
  node,
@@ -5538,7 +5702,7 @@ var huskyRules = {
5538
5702
 
5539
5703
  // eslint/rules/vulyk/vulyk-docs.ts
5540
5704
  import { existsSync as existsSync3, readFileSync as readFileSync9 } from "fs";
5541
- import path43 from "path";
5705
+ import path46 from "path";
5542
5706
  var PASIKA_REPO = "Bredansky/pasika";
5543
5707
  var vulykDocsRule = {
5544
5708
  meta: {
@@ -5552,8 +5716,8 @@ var vulykDocsRule = {
5552
5716
  return {
5553
5717
  Document(node) {
5554
5718
  if (!context.filename.endsWith("package.json")) return;
5555
- const projectRoot = path43.dirname(path43.resolve(context.filename));
5556
- const configPath = path43.join(projectRoot, "vulyk.config.ts");
5719
+ const projectRoot = path46.dirname(path46.resolve(context.filename));
5720
+ const configPath = path46.join(projectRoot, "vulyk.config.ts");
5557
5721
  if (!existsSync3(configPath)) {
5558
5722
  context.report({
5559
5723
  node,
@@ -5568,7 +5732,7 @@ var vulykDocsRule = {
5568
5732
  message: "vulyk.config.ts must track the framework's docs from the pasika repository."
5569
5733
  });
5570
5734
  }
5571
- const agentsPath = path43.join(projectRoot, "AGENTS.md");
5735
+ const agentsPath = path46.join(projectRoot, "AGENTS.md");
5572
5736
  if (!existsSync3(agentsPath)) {
5573
5737
  context.report({
5574
5738
  node,
@@ -5586,7 +5750,8 @@ var vulykRules = {
5586
5750
  };
5587
5751
 
5588
5752
  // eslint/index.ts
5589
- var typescriptAppRules = {
5753
+ var nextjsAppRules = {
5754
+ // Framework-agnostic TypeScript rules.
5590
5755
  "filename-case": filenameCaseRule,
5591
5756
  "import-boundaries": importBoundariesRule,
5592
5757
  "named-exports": namedExportsRule,
@@ -5601,9 +5766,8 @@ var typescriptAppRules = {
5601
5766
  "type-extraction": typeExtractionRule,
5602
5767
  "zod-schema-validation": zodSchemaValidationRule,
5603
5768
  "source-under-src": sourceUnderSrcRule,
5604
- "zirka-baseline": zirkaBaselineRule
5605
- };
5606
- var nextjsAppRules = {
5769
+ "zirka-baseline": zirkaBaselineRule,
5770
+ // Next.js/React application rules.
5607
5771
  "component-placement": componentPlacementRule,
5608
5772
  "application-structure": applicationStructureRule,
5609
5773
  "data-testid-case": dataTestIdCaseRule,
@@ -5632,58 +5796,56 @@ var nextjsAppRules = {
5632
5796
  "shared-style-dedup": sharedStyleDedupRule,
5633
5797
  "repeated-structure": repeatedStructureRule
5634
5798
  };
5635
- var pasikaRules = {
5636
- ...typescriptAppRules,
5637
- ...nextjsAppRules
5638
- };
5639
5799
  var pasikaPlugin = {
5640
5800
  rules: {
5641
- ...pasikaRules,
5801
+ // The shared plugin must register every rule the preset blocks reference,
5802
+ // so all rule sets live here.
5803
+ ...nextjsAppRules,
5642
5804
  ...documentationRules,
5643
5805
  ...tailwindRules,
5644
5806
  ...repoPackageJsonRules,
5645
- ...nextPackageJsonRules,
5807
+ ...nextjsPackageJsonRules,
5646
5808
  ...huskyRules,
5647
5809
  ...vulykRules
5648
5810
  }
5649
5811
  };
5650
- var jsonLanguagePlugin = { languages: { json: jsonPlugin.languages.json } };
5812
+ var jsonLanguage = { languages: { json: jsonPlugin.languages.json } };
5651
5813
  function ruleIds(rules2) {
5652
5814
  return Object.keys(rules2).map((name) => `pasika/${name}`);
5653
5815
  }
5654
- var typescriptAppRuleIds = ruleIds(typescriptAppRules);
5655
5816
  var nextjsAppRuleIds = ruleIds(nextjsAppRules);
5656
- var pasikaRuleIds = ruleIds(pasikaRules);
5657
5817
  var documentationRuleIds = ruleIds(documentationRules);
5658
5818
  var tailwindRuleIds = ruleIds(tailwindRules);
5659
5819
  var repoPackageJsonRuleIds = ruleIds(repoPackageJsonRules);
5660
- var nextPackageJsonRuleIds = ruleIds(nextPackageJsonRules);
5820
+ var nextjsPackageJsonRuleIds = ruleIds(nextjsPackageJsonRules);
5661
5821
  var huskyRuleIds = ruleIds(huskyRules);
5662
5822
  var vulykRuleIds = ruleIds(vulykRules);
5663
5823
  var allPasikaRuleIds = [
5664
- ...pasikaRuleIds,
5824
+ ...nextjsAppRuleIds,
5665
5825
  ...documentationRuleIds,
5666
5826
  ...tailwindRuleIds,
5667
5827
  ...repoPackageJsonRuleIds,
5668
- ...nextPackageJsonRuleIds,
5828
+ ...nextjsPackageJsonRuleIds,
5669
5829
  ...huskyRuleIds,
5670
5830
  ...vulykRuleIds
5671
5831
  ];
5672
- var typescriptAppBlock = {
5673
- files: ["src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}"],
5674
- plugins: {
5675
- pasika: pasikaPlugin
5676
- },
5677
- rules: Object.fromEntries(typescriptAppRuleIds.map((id) => [id, "error"]))
5832
+ var typescriptAppLanguageOptions = {
5833
+ parser: tsParser,
5834
+ parserOptions: {
5835
+ ecmaVersion: "latest",
5836
+ sourceType: "module",
5837
+ ecmaFeatures: { jsx: true }
5838
+ }
5678
5839
  };
5679
- var nextjsAppBlock = {
5840
+ var nextjsAppConfig = {
5680
5841
  files: ["src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}"],
5842
+ languageOptions: typescriptAppLanguageOptions,
5681
5843
  plugins: {
5682
5844
  pasika: pasikaPlugin
5683
5845
  },
5684
5846
  rules: Object.fromEntries(nextjsAppRuleIds.map((id) => [id, "error"]))
5685
5847
  };
5686
- var tailwindGlobalsBlock = {
5848
+ var tailwindStructureRules = {
5687
5849
  files: ["src/**/globals.css"],
5688
5850
  plugins: {
5689
5851
  css,
@@ -5692,10 +5854,10 @@ var tailwindGlobalsBlock = {
5692
5854
  language: "css/css",
5693
5855
  languageOptions: { tolerant: true },
5694
5856
  rules: Object.fromEntries(
5695
- tailwindRuleIds.filter((id) => id !== "pasika/global-css-location").map((id) => [id, "error"])
5857
+ tailwindRuleIds.filter((id) => id !== "pasika/css-entry-point").map((id) => [id, "error"])
5696
5858
  )
5697
5859
  };
5698
- var tailwindAnyCssBlock = {
5860
+ var tailwindImportGraph = {
5699
5861
  files: ["src/**/*.css"],
5700
5862
  plugins: {
5701
5863
  css,
@@ -5703,32 +5865,32 @@ var tailwindAnyCssBlock = {
5703
5865
  },
5704
5866
  language: "css/css",
5705
5867
  languageOptions: { tolerant: true },
5706
- rules: { "pasika/global-css-location": "error" }
5868
+ rules: { "pasika/css-entry-point": "error" }
5707
5869
  };
5708
- var repoManifestBlock = {
5870
+ var typescriptAppPackageJsonConfig = {
5709
5871
  files: ["package.json"],
5710
5872
  plugins: {
5711
- json: jsonLanguagePlugin,
5873
+ json: jsonLanguage,
5712
5874
  pasika: pasikaPlugin
5713
5875
  },
5714
5876
  language: "json/json",
5715
5877
  rules: Object.fromEntries([...repoPackageJsonRuleIds, ...huskyRuleIds, ...vulykRuleIds].map((id) => [id, "error"]))
5716
5878
  };
5717
- var nextManifestBlock = {
5879
+ var nextjsAppPackageJsonConfig = {
5718
5880
  files: ["package.json"],
5719
5881
  plugins: {
5720
- json: jsonLanguagePlugin,
5882
+ json: jsonLanguage,
5721
5883
  pasika: pasikaPlugin
5722
5884
  },
5723
5885
  language: "json/json",
5724
- rules: Object.fromEntries(nextPackageJsonRuleIds.map((id) => [id, "error"]))
5886
+ rules: Object.fromEntries(nextjsPackageJsonRuleIds.map((id) => [id, "error"]))
5725
5887
  };
5726
- var zirkaBlock = {
5888
+ var zirkaConfig = {
5727
5889
  files: ["eslint.config.{ts,mts,cts,js,mjs,cjs}"],
5728
5890
  plugins: { pasika: pasikaPlugin },
5729
5891
  rules: { "pasika/zirka-baseline": "error" }
5730
5892
  };
5731
- var docsBlock = {
5893
+ var documentationConfig = {
5732
5894
  files: ["docs/**/*.md"],
5733
5895
  ignores: ["**/_*/**"],
5734
5896
  plugins: {
@@ -5738,22 +5900,25 @@ var docsBlock = {
5738
5900
  language: "markdown/gfm",
5739
5901
  rules: Object.fromEntries(documentationRuleIds.map((id) => [id, "error"]))
5740
5902
  };
5741
- var typescriptApp = [repoManifestBlock, zirkaBlock, typescriptAppBlock, docsBlock];
5903
+ var typescriptApp = [
5904
+ typescriptAppPackageJsonConfig,
5905
+ zirkaConfig,
5906
+ documentationConfig
5907
+ ];
5742
5908
  var nextjsApp = [
5743
5909
  ...typescriptApp,
5744
- nextManifestBlock,
5745
- nextjsAppBlock,
5746
- tailwindGlobalsBlock,
5747
- tailwindAnyCssBlock
5910
+ nextjsAppPackageJsonConfig,
5911
+ nextjsAppConfig,
5912
+ tailwindStructureRules,
5913
+ tailwindImportGraph
5748
5914
  ];
5749
5915
  export {
5750
5916
  allPasikaRuleIds,
5751
5917
  documentationRules,
5752
5918
  huskyRules,
5753
- nextPackageJsonRules,
5754
5919
  nextjsApp,
5920
+ nextjsPackageJsonRules,
5755
5921
  pasikaPlugin,
5756
- pasikaRules,
5757
5922
  repoPackageJsonRules,
5758
5923
  tailwindRules,
5759
5924
  typescriptApp,