pasika 0.4.3 → 0.5.1

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";
@@ -91,15 +92,17 @@ function jsxTagName(element) {
91
92
  return ts.isIdentifier(tagName2) ? tagName2.text : void 0;
92
93
  }
93
94
  function rootFromExpression(expression) {
94
- if (ts.isJsxSelfClosingElement(expression)) {
95
- const tagName2 = jsxTagName(expression);
95
+ let unwrapped = expression;
96
+ while (ts.isParenthesizedExpression(unwrapped)) unwrapped = unwrapped.expression;
97
+ if (ts.isJsxSelfClosingElement(unwrapped)) {
98
+ const tagName2 = jsxTagName(unwrapped);
96
99
  if (!tagName2?.startsWith(tagName2[0]?.toLowerCase() ?? "")) return void 0;
97
- return { tagName: tagName2, attributes: [...expression.attributes.properties] };
100
+ return { tagName: tagName2, attributes: [...unwrapped.attributes.properties] };
98
101
  }
99
- if (ts.isJsxElement(expression)) {
100
- const tagName2 = jsxTagName(expression);
102
+ if (ts.isJsxElement(unwrapped)) {
103
+ const tagName2 = jsxTagName(unwrapped);
101
104
  if (!tagName2?.startsWith(tagName2[0]?.toLowerCase() ?? "")) return void 0;
102
- return { tagName: tagName2, attributes: [...expression.openingElement.attributes.properties] };
105
+ return { tagName: tagName2, attributes: [...unwrapped.openingElement.attributes.properties] };
103
106
  }
104
107
  return void 0;
105
108
  }
@@ -140,12 +143,21 @@ var NEXT_ROUTING_FILES = /* @__PURE__ */ new Set([
140
143
  "layout",
141
144
  "loading",
142
145
  "error",
146
+ "global-error",
143
147
  "not-found",
144
148
  "route",
145
149
  "template",
146
150
  "default",
147
151
  "middleware",
148
- "instrumentation"
152
+ "instrumentation",
153
+ // File conventions that Next.js requires to keep their exact kebab-case names
154
+ "apple-icon",
155
+ "icon",
156
+ "manifest",
157
+ "opengraph-image",
158
+ "robots",
159
+ "sitemap",
160
+ "twitter-image"
149
161
  ]);
150
162
  var COMPOUND_SUFFIXES = /* @__PURE__ */ new Set(["example", "test", "spec", "stories"]);
151
163
  function isKebabCase(str) {
@@ -228,33 +240,40 @@ function report(context, filename, ext) {
228
240
  }
229
241
 
230
242
  // eslint/rules/import-boundaries.ts
243
+ import path4 from "path";
244
+
245
+ // eslint/rules/project-root.ts
231
246
  import path3 from "path";
232
- var sourceRoot = path3.resolve("src");
247
+ function sourceRootOf(context) {
248
+ return path3.resolve(context.cwd ?? process.cwd(), "src");
249
+ }
250
+
251
+ // eslint/rules/import-boundaries.ts
233
252
  var rootSupportFolders = /* @__PURE__ */ new Set(["config", "constants", "hooks", "locales", "schemas", "types", "utils"]);
234
253
  var moduleExtensions = /* @__PURE__ */ new Set([".cjs", ".cts", ".js", ".jsx", ".mjs", ".mts", ".ts", ".tsx"]);
235
254
  var styleExtensions = /* @__PURE__ */ new Set([".css", ".less", ".sass", ".scss"]);
236
- function resolveSourceImport(filename, importPath) {
255
+ function resolveSourceImport(sourceRoot, filename, importPath) {
237
256
  if (importPath.startsWith("@/")) {
238
- return path3.resolve(sourceRoot, importPath.slice(2));
257
+ return path4.resolve(sourceRoot, importPath.slice(2));
239
258
  }
240
259
  if (importPath.startsWith(".")) {
241
- return path3.resolve(path3.dirname(filename), importPath);
260
+ return path4.resolve(path4.dirname(filename), importPath);
242
261
  }
243
262
  return void 0;
244
263
  }
245
- function sourceSegments(absolutePath) {
246
- const relativePath = path3.relative(sourceRoot, absolutePath);
247
- if (relativePath.startsWith("..") || path3.isAbsolute(relativePath)) {
264
+ function sourceSegments(sourceRoot, absolutePath) {
265
+ const relativePath = path4.relative(sourceRoot, absolutePath);
266
+ if (relativePath.startsWith("..") || path4.isAbsolute(relativePath)) {
248
267
  return void 0;
249
268
  }
250
- return relativePath.split(path3.sep);
269
+ return relativePath.split(path4.sep);
251
270
  }
252
271
  function relativeSpecifier(filename, resolvedPath) {
253
- const relativePath = path3.relative(path3.dirname(filename), resolvedPath).split(path3.sep).join("/");
272
+ const relativePath = path4.relative(path4.dirname(filename), resolvedPath).split(path4.sep).join("/");
254
273
  return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
255
274
  }
256
- function aliasSpecifier(resolvedPath) {
257
- return `@/${(sourceSegments(resolvedPath) ?? []).join("/")}`;
275
+ function aliasSpecifier(sourceRoot, resolvedPath) {
276
+ return `@/${(sourceSegments(sourceRoot, resolvedPath) ?? []).join("/")}`;
258
277
  }
259
278
  function segmentCount(specifier) {
260
279
  return specifier.replace(/^@\//, "").split("/").filter((segment) => segment !== "." && segment !== "").length;
@@ -262,8 +281,8 @@ function segmentCount(specifier) {
262
281
  function describeSegments(count) {
263
282
  return `${String(count)} segment${count === 1 ? "" : "s"}`;
264
283
  }
265
- function prefersRelative(filename, resolvedPath) {
266
- return segmentCount(relativeSpecifier(filename, resolvedPath)) <= segmentCount(aliasSpecifier(resolvedPath));
284
+ function prefersRelative(sourceRoot, filename, resolvedPath) {
285
+ return segmentCount(relativeSpecifier(filename, resolvedPath)) <= segmentCount(aliasSpecifier(sourceRoot, resolvedPath));
267
286
  }
268
287
  var importBoundariesRule = {
269
288
  meta: {
@@ -272,6 +291,7 @@ var importBoundariesRule = {
272
291
  },
273
292
  create(context) {
274
293
  const filename = context.filename;
294
+ const sourceRoot = sourceRootOf(context);
275
295
  if (!filename) {
276
296
  return {};
277
297
  }
@@ -280,18 +300,18 @@ var importBoundariesRule = {
280
300
  return;
281
301
  }
282
302
  const importPath = source.value;
283
- const resolvedPath = resolveSourceImport(filename, importPath);
303
+ const resolvedPath = resolveSourceImport(sourceRoot, filename, importPath);
284
304
  if (!resolvedPath) {
285
305
  return;
286
306
  }
287
- const importer = sourceSegments(filename);
288
- const imported = sourceSegments(resolvedPath);
307
+ const importer = sourceSegments(sourceRoot, filename);
308
+ const imported = sourceSegments(sourceRoot, resolvedPath);
289
309
  if (!importer || !imported || importer.length === 0 || imported.length === 0) {
290
310
  return;
291
311
  }
292
312
  const [importerLayer = "", importerFeature] = importer;
293
313
  const [importedLayer = "", importedFeature] = imported;
294
- const extension = path3.extname(importPath);
314
+ const extension = path4.extname(importPath);
295
315
  const isCodeModule = !extension || moduleExtensions.has(extension);
296
316
  const isAppLocalStyleImport = importerLayer === "app" && importedLayer === "app" && styleExtensions.has(extension);
297
317
  const importedIsRootSupport = rootSupportFolders.has(importedLayer);
@@ -305,21 +325,21 @@ var importBoundariesRule = {
305
325
  return;
306
326
  }
307
327
  const relativeForm = relativeSpecifier(filename, resolvedPath);
308
- const aliasForm = aliasSpecifier(resolvedPath);
328
+ const aliasForm = aliasSpecifier(sourceRoot, resolvedPath);
309
329
  const relativeSegments = segmentCount(relativeForm);
310
330
  const aliasSegments = segmentCount(aliasForm);
311
331
  function describeChoice(preferred, preferredSegments, other, otherSegments) {
312
332
  const tie = preferredSegments === otherSegments ? ", and a tie goes to the relative path" : "";
313
333
  return `Use "${preferred}" (${describeSegments(preferredSegments)}) instead of "${other}" (${describeSegments(otherSegments)})${tie}.`;
314
334
  }
315
- if (prefersRelative(filename, resolvedPath) && importPath.startsWith("@/")) {
335
+ if (prefersRelative(sourceRoot, filename, resolvedPath) && importPath.startsWith("@/")) {
316
336
  context.report({
317
337
  node: source,
318
338
  message: describeChoice(relativeForm, relativeSegments, aliasForm, aliasSegments)
319
339
  });
320
340
  return;
321
341
  }
322
- if (!prefersRelative(filename, resolvedPath) && importPath.startsWith(".")) {
342
+ if (!prefersRelative(sourceRoot, filename, resolvedPath) && importPath.startsWith(".")) {
323
343
  context.report({
324
344
  node: source,
325
345
  message: describeChoice(aliasForm, aliasSegments, relativeForm, relativeSegments)
@@ -448,7 +468,7 @@ var noArbitraryTailwindRule = {
448
468
 
449
469
  // eslint/rules/unknown-utility.ts
450
470
  import { readdirSync, readFileSync, statSync } from "fs";
451
- import path4 from "path";
471
+ import path5 from "path";
452
472
  var PREFIX_NAMESPACES = {
453
473
  bg: ["color"],
454
474
  text: ["color", "text"],
@@ -610,7 +630,19 @@ var DEFAULT_TOKENS = {
610
630
  blur: ["none", "xs", "sm", "md", "lg", "xl", "2xl", "3xl"],
611
631
  animate: ["none", "spin", "ping", "pulse", "bounce"],
612
632
  ease: ["linear", "in", "out", "in-out"],
613
- aspect: ["auto", "video", "square"]
633
+ // Note: aspect's auto/square, radius's none/full, leading's none, blur's
634
+ // none, animate's none, and ease's linear are static built-ins covered by
635
+ // STATIC_VALUE_TOKENS; the remainder are theme-derived.
636
+ aspect: ["video"]
637
+ };
638
+ var STATIC_VALUE_TOKENS = {
639
+ aspect: ["auto", "square"],
640
+ radius: ["none", "full"],
641
+ leading: ["none"],
642
+ blur: ["none"],
643
+ animate: ["none"],
644
+ ease: ["linear", "initial"],
645
+ z: ["auto"]
614
646
  };
615
647
  var DEFAULT_PALETTE_FAMILIES = /* @__PURE__ */ new Set([
616
648
  "red",
@@ -638,6 +670,7 @@ var DEFAULT_PALETTE_FAMILIES = /* @__PURE__ */ new Set([
638
670
  ]);
639
671
  var DEFAULT_PALETTE_SHADES = /* @__PURE__ */ new Set(["50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "950"]);
640
672
  var DEFAULT_COLOR_SPECIALS = /* @__PURE__ */ new Set(["white", "black", "transparent", "current", "inherit"]);
673
+ var STATIC_COLOR_SPECIALS = /* @__PURE__ */ new Set(["transparent", "current", "inherit"]);
641
674
  var NUMERIC_TOKEN_RE = /^-?(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
642
675
  var SIDE_WIDTH_TOKEN_RE = /^(?:x|y|t|r|b|l|s|e)-(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
643
676
  var OFFSET_TOKEN_RE = /^offset-(?:\d+\.?\d*|\.\d+)(?:%|px|rem|em)?$/;
@@ -650,7 +683,7 @@ function stylesheetFiles(dir) {
650
683
  }
651
684
  return entries.flatMap((entry) => {
652
685
  if (entry.startsWith(".") || entry === "node_modules") return [];
653
- const entryPath = path4.join(dir, entry);
686
+ const entryPath = path5.join(dir, entry);
654
687
  let stats;
655
688
  try {
656
689
  stats = statSync(entryPath);
@@ -658,7 +691,7 @@ function stylesheetFiles(dir) {
658
691
  return [];
659
692
  }
660
693
  if (stats.isDirectory()) return stylesheetFiles(entryPath);
661
- return path4.extname(entry) === ".css" ? [entryPath] : [];
694
+ return path5.extname(entry) === ".css" ? [entryPath] : [];
662
695
  });
663
696
  }
664
697
  function themeBlocks(css2) {
@@ -679,10 +712,36 @@ function themeBlocks(css2) {
679
712
  }
680
713
  return blocks;
681
714
  }
715
+ function classSelectors(css2) {
716
+ const blocks = [];
717
+ let index = 0;
718
+ while (index < css2.length) {
719
+ const match = /@utility\s*\{/.exec(css2.slice(index));
720
+ if (!match) break;
721
+ const open = index + match.index + match[0].length - 1;
722
+ let depth = 1;
723
+ let cursor = open + 1;
724
+ for (; cursor < css2.length && depth > 0; cursor++) {
725
+ if (css2[cursor] === "{") depth++;
726
+ else if (css2[cursor] === "}") depth--;
727
+ }
728
+ blocks.push(css2.slice(open + 1, cursor - 1));
729
+ index = cursor;
730
+ }
731
+ let selectors = css2;
732
+ for (const block of blocks) selectors = selectors.replace(block, "");
733
+ const names = [];
734
+ for (const match of selectors.matchAll(/\.(?<className>[a-z][a-z0-9_-]*)(?=\s*[.,:#>{]|$)/gi)) {
735
+ const className = match.groups?.className;
736
+ if (className) names.push(className);
737
+ }
738
+ return names;
739
+ }
682
740
  function readInventory(files) {
683
741
  const utilities = /* @__PURE__ */ new Set();
684
742
  const utilityPrefixes = /* @__PURE__ */ new Set();
685
743
  const themeTokensByNamespace = /* @__PURE__ */ new Map();
744
+ const plainClasses = /* @__PURE__ */ new Set();
686
745
  let defaultsReset = false;
687
746
  for (const file of files) {
688
747
  let css2;
@@ -698,6 +757,7 @@ function readInventory(files) {
698
757
  const dash = utilityName.indexOf("-");
699
758
  if (dash > 0) utilityPrefixes.add(utilityName.slice(0, dash));
700
759
  }
760
+ for (const plainClass of classSelectors(css2)) plainClasses.add(plainClass);
701
761
  for (const block of themeBlocks(css2)) {
702
762
  if (/--\*\s*:\s*initial\b/.test(block)) defaultsReset = true;
703
763
  for (const decl of block.matchAll(/--(?<namespace>[a-z][a-z0-9]*)-(?<token>[a-z0-9][a-z0-9_-]*)\s*:/g)) {
@@ -713,10 +773,11 @@ function readInventory(files) {
713
773
  }
714
774
  }
715
775
  }
716
- return { utilities, utilityPrefixes, themeTokens: themeTokensByNamespace, defaultsReset };
776
+ return { utilities, utilityPrefixes, themeTokens: themeTokensByNamespace, defaultsReset, plainClasses };
717
777
  }
718
778
  function isColorToken(token, inventory) {
719
779
  if (inventory.themeTokens.get("color")?.has(token)) return true;
780
+ if (STATIC_COLOR_SPECIALS.has(token)) return true;
720
781
  if (inventory.defaultsReset) return false;
721
782
  if (DEFAULT_COLOR_SPECIALS.has(token)) return true;
722
783
  const hyphen = token.lastIndexOf("-");
@@ -738,6 +799,7 @@ function isKnown(className, inventory) {
738
799
  if (inventory.utilities.has(utility)) return true;
739
800
  const namespaces = PREFIX_NAMESPACES[prefix];
740
801
  if (!namespaces) {
802
+ if (STATIC_VALUE_TOKENS[prefix]?.includes(token)) return true;
741
803
  const projectTokens = inventory.themeTokens.get(prefix);
742
804
  if (projectTokens) {
743
805
  if (projectTokens.has(token)) return true;
@@ -754,6 +816,7 @@ function isKnown(className, inventory) {
754
816
  continue;
755
817
  }
756
818
  if (inventory.themeTokens.get(namespace)?.has(token)) return true;
819
+ if (STATIC_VALUE_TOKENS[namespace]?.includes(token)) return true;
757
820
  if (!inventory.defaultsReset && DEFAULT_TOKENS[namespace]?.includes(token)) return true;
758
821
  }
759
822
  return false;
@@ -768,11 +831,11 @@ var unknownUtilityRule = {
768
831
  }
769
832
  },
770
833
  create(context) {
771
- const sourceRoot2 = path4.resolve("src");
834
+ const sourceRoot = sourceRootOf(context);
772
835
  let inventory;
773
836
  try {
774
- if (!statSync(sourceRoot2).isDirectory()) return {};
775
- inventory = readInventory(stylesheetFiles(sourceRoot2));
837
+ if (!statSync(sourceRoot).isDirectory()) return {};
838
+ inventory = readInventory(stylesheetFiles(sourceRoot));
776
839
  } catch {
777
840
  return {};
778
841
  }
@@ -781,12 +844,19 @@ var unknownUtilityRule = {
781
844
  for (const candidate of value.split(/\s+/)) {
782
845
  if (!candidate || seen.has(candidate)) continue;
783
846
  seen.add(candidate);
784
- if (!isKnown(candidate, inventory)) {
847
+ if (isKnown(candidate, inventory)) continue;
848
+ const plainBase = candidate.replace(/!+$/, "").split(":").pop() ?? "";
849
+ if (inventory.plainClasses.has(plainBase)) {
785
850
  context.report({
786
851
  node,
787
- message: `Utility class "${candidate}" is not a custom @utility, a theme-generated utility, or a built-in Tailwind utility.`
852
+ 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
853
  });
854
+ continue;
789
855
  }
856
+ context.report({
857
+ node,
858
+ message: `Utility class "${candidate}" is not a custom @utility, a theme-generated utility, or a built-in Tailwind utility.`
859
+ });
790
860
  }
791
861
  }
792
862
  function checkExpression(node, expression) {
@@ -1046,7 +1116,7 @@ var enforceCvaVariantPropsRule = {
1046
1116
  };
1047
1117
 
1048
1118
  // eslint/rules/enforce-barrel-exports.ts
1049
- import path5 from "path";
1119
+ import path6 from "path";
1050
1120
  import fs from "fs";
1051
1121
  function isPascalCase3(str) {
1052
1122
  return /^[A-Z][A-Za-z0-9]*$/.test(str);
@@ -1066,14 +1136,14 @@ var enforceBarrelExportsRule = {
1066
1136
  create(context) {
1067
1137
  const filename = context.filename;
1068
1138
  if (!filename) return {};
1069
- const baseName = path5.basename(filename);
1139
+ const baseName = path6.basename(filename);
1070
1140
  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));
1141
+ const dirPath = path6.dirname(filename);
1142
+ const folderName = path6.basename(dirPath);
1143
+ const parentFolderName = path6.basename(path6.dirname(dirPath));
1074
1144
  if (SUPPORT_FOLDERS.has(folderName)) return {};
1075
1145
  if (!isPascalCase3(folderName) && !isKebabCase2(folderName)) return {};
1076
- const matchingTsx = fs.existsSync(path5.join(dirPath, `${folderName}.tsx`)) ? folderName : null;
1146
+ const matchingTsx = fs.existsSync(path6.join(dirPath, `${folderName}.tsx`)) ? folderName : null;
1077
1147
  if (!matchingTsx) return {};
1078
1148
  if (!isPascalCase3(parentFolderName) && !isKebabCase2(parentFolderName)) return {};
1079
1149
  const reExportedNames = /* @__PURE__ */ new Set();
@@ -1108,14 +1178,14 @@ var enforceBarrelExportsRule = {
1108
1178
  };
1109
1179
 
1110
1180
  // eslint/rules/component-placement.ts
1111
- import path9 from "path";
1181
+ import path10 from "path";
1112
1182
 
1113
1183
  // eslint/project/index.ts
1114
1184
  import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
1115
- import path7 from "path";
1185
+ import path8 from "path";
1116
1186
 
1117
1187
  // eslint/project/parse-module.ts
1118
- import path6 from "path";
1188
+ import path7 from "path";
1119
1189
  import { readFileSync as readFileSync2 } from "fs";
1120
1190
  import ts2 from "typescript";
1121
1191
  var isPascalCase4 = (name) => /^[A-Z][A-Za-z0-9]*$/.test(name);
@@ -1216,7 +1286,7 @@ function parseModule(file) {
1216
1286
  exports.push({ name: statement.name.text, kind: "type", line: lineOf(sourceFile, statement) });
1217
1287
  }
1218
1288
  }
1219
- return { file: path6.resolve(file), imports, exports };
1289
+ return { file: path7.resolve(file), imports, exports };
1220
1290
  }
1221
1291
 
1222
1292
  // eslint/project/index.ts
@@ -1233,7 +1303,7 @@ function listSourceFiles(dir) {
1233
1303
  }
1234
1304
  return entries.flatMap((entry) => {
1235
1305
  if (entry.startsWith(".") || entry === "node_modules") return [];
1236
- const entryPath = path7.join(dir, entry);
1306
+ const entryPath = path8.join(dir, entry);
1237
1307
  let stats;
1238
1308
  try {
1239
1309
  stats = statSync2(entryPath);
@@ -1241,7 +1311,7 @@ function listSourceFiles(dir) {
1241
1311
  return [];
1242
1312
  }
1243
1313
  if (stats.isDirectory()) return listSourceFiles(entryPath);
1244
- return MODULE_EXTENSIONS.includes(path7.extname(entry)) ? [entryPath] : [];
1314
+ return MODULE_EXTENSIONS.includes(path8.extname(entry)) ? [entryPath] : [];
1245
1315
  });
1246
1316
  }
1247
1317
  function fingerprint(files) {
@@ -1254,19 +1324,19 @@ function fingerprint(files) {
1254
1324
  }
1255
1325
  return `${String(files.length)}:${String(total)}`;
1256
1326
  }
1257
- function resolveSpecifier(fromFile, specifier, sourceRoot2) {
1327
+ function resolveSpecifier(fromFile, specifier, sourceRoot) {
1258
1328
  let base;
1259
1329
  if (specifier.startsWith("@/")) {
1260
- base = path7.resolve(sourceRoot2, specifier.slice(2));
1330
+ base = path8.resolve(sourceRoot, specifier.slice(2));
1261
1331
  } else if (specifier.startsWith(".")) {
1262
- base = path7.resolve(path7.dirname(fromFile), specifier);
1332
+ base = path8.resolve(path8.dirname(fromFile), specifier);
1263
1333
  } else {
1264
1334
  return void 0;
1265
1335
  }
1266
1336
  const candidates = [
1267
1337
  base,
1268
1338
  ...MODULE_EXTENSIONS.map((extension) => `${base}${extension}`),
1269
- ...INDEX_BASENAMES.map((name) => path7.join(base, name))
1339
+ ...INDEX_BASENAMES.map((name) => path8.join(base, name))
1270
1340
  ];
1271
1341
  for (const candidate of candidates) {
1272
1342
  try {
@@ -1276,7 +1346,7 @@ function resolveSpecifier(fromFile, specifier, sourceRoot2) {
1276
1346
  }
1277
1347
  return void 0;
1278
1348
  }
1279
- function build(sourceRoot2, files) {
1349
+ function build(sourceRoot, files) {
1280
1350
  const modules = /* @__PURE__ */ new Map();
1281
1351
  const consumers = /* @__PURE__ */ new Map();
1282
1352
  const symbolConsumers = /* @__PURE__ */ new Map();
@@ -1288,7 +1358,7 @@ function build(sourceRoot2, files) {
1288
1358
  }
1289
1359
  for (const [file, module] of modules) {
1290
1360
  for (const moduleImport of module.imports) {
1291
- const target = resolveSpecifier(file, moduleImport.specifier, sourceRoot2);
1361
+ const target = resolveSpecifier(file, moduleImport.specifier, sourceRoot);
1292
1362
  if (!target || !modules.has(target)) continue;
1293
1363
  const fileConsumers = consumers.get(target) ?? /* @__PURE__ */ new Set();
1294
1364
  fileConsumers.add(file);
@@ -1301,39 +1371,39 @@ function build(sourceRoot2, files) {
1301
1371
  }
1302
1372
  }
1303
1373
  }
1304
- return { sourceRoot: sourceRoot2, modules, consumers, symbolConsumers };
1374
+ return { sourceRoot, modules, consumers, symbolConsumers };
1305
1375
  }
1306
1376
  var cache;
1307
- function getProjectIndex(sourceRoot2) {
1377
+ function getProjectIndex(sourceRoot) {
1308
1378
  const now = Date.now();
1309
- if (cache?.index.sourceRoot === sourceRoot2 && now - cache.checkedAt < REVALIDATE_AFTER_MS) {
1379
+ if (cache?.index.sourceRoot === sourceRoot && now - cache.checkedAt < REVALIDATE_AFTER_MS) {
1310
1380
  return cache.index;
1311
1381
  }
1312
1382
  try {
1313
- if (!statSync2(sourceRoot2).isDirectory()) return void 0;
1383
+ if (!statSync2(sourceRoot).isDirectory()) return void 0;
1314
1384
  } catch {
1315
1385
  return void 0;
1316
1386
  }
1317
- const files = listSourceFiles(sourceRoot2).sort((left, right) => left.localeCompare(right));
1387
+ const files = listSourceFiles(sourceRoot).sort((left, right) => left.localeCompare(right));
1318
1388
  const currentFingerprint = fingerprint(files);
1319
- if (cache?.index.sourceRoot === sourceRoot2 && cache.fingerprint === currentFingerprint) {
1389
+ if (cache?.index.sourceRoot === sourceRoot && cache.fingerprint === currentFingerprint) {
1320
1390
  cache.checkedAt = now;
1321
1391
  return cache.index;
1322
1392
  }
1323
- const index = build(sourceRoot2, files);
1393
+ const index = build(sourceRoot, files);
1324
1394
  cache = { index, checkedAt: now, fingerprint: currentFingerprint };
1325
1395
  return index;
1326
1396
  }
1327
1397
 
1328
1398
  // eslint/project/ccf.ts
1329
- import path8 from "path";
1399
+ import path9 from "path";
1330
1400
  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);
1401
+ function segmentsOf(file, sourceRoot) {
1402
+ const relative = path9.relative(sourceRoot, file);
1403
+ return relative.startsWith("..") ? [] : relative.split(path9.sep);
1334
1404
  }
1335
- function folderSegmentsOf(file, sourceRoot2) {
1336
- return segmentsOf(file, sourceRoot2).slice(0, -1);
1405
+ function folderSegmentsOf(file, sourceRoot) {
1406
+ return segmentsOf(file, sourceRoot).slice(0, -1);
1337
1407
  }
1338
1408
  var isUnderApp = (segments) => segments[0] === "app";
1339
1409
  var isConfigModule = (segments) => segments[0] === "config";
@@ -1376,8 +1446,8 @@ function resolveComponentPlacement(componentFile, index) {
1376
1446
  return { countedConsumers: counted, expectedFolder: shared, reason: "ccf" };
1377
1447
  }
1378
1448
  var formatFolder = (folder) => `src/${folder.join("/")}/`;
1379
- function owningFolderOf(consumer, sourceRoot2) {
1380
- return outOfSupportFolders(folderSegmentsOf(consumer, sourceRoot2));
1449
+ function owningFolderOf(consumer, sourceRoot) {
1450
+ return outOfSupportFolders(folderSegmentsOf(consumer, sourceRoot));
1381
1451
  }
1382
1452
  var configModuleOf = (segments) => isConfigModule(segments) && segments.length >= 3 ? segments[1] : void 0;
1383
1453
  function resolveSupportPlacement(supportFile, supportFolder, index) {
@@ -1407,9 +1477,9 @@ function resolveSupportPlacement(supportFile, supportFolder, index) {
1407
1477
  }
1408
1478
  return { countedConsumers: consumers, expectedFolder: [...shared, supportFolder], reason: "ccf" };
1409
1479
  }
1410
- function describeConsumers(consumers, sourceRoot2) {
1480
+ function describeConsumers(consumers, sourceRoot) {
1411
1481
  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));
1482
+ const names = consumers.map((consumer) => path9.relative(path9.dirname(sourceRoot), consumer).split(path9.sep).join("/")).sort((left, right) => left.localeCompare(right));
1413
1483
  if (names.length <= shown) return names.join(", ");
1414
1484
  return `${names.slice(0, shown).join(", ")} and ${String(names.length - shown)} more`;
1415
1485
  }
@@ -1432,16 +1502,16 @@ var componentPlacementRule = {
1432
1502
  create(context) {
1433
1503
  const filename = context.filename;
1434
1504
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
1435
- const sourceRoot2 = path9.resolve("src");
1436
- const index = getProjectIndex(sourceRoot2);
1505
+ const sourceRoot = sourceRootOf(context);
1506
+ const index = getProjectIndex(sourceRoot);
1437
1507
  if (!index) return {};
1438
- const componentFile = path9.resolve(filename);
1439
- const segments = segmentsOf(componentFile, sourceRoot2);
1508
+ const componentFile = path10.resolve(filename);
1509
+ const segments = segmentsOf(componentFile, sourceRoot);
1440
1510
  if (segments.length === 0) return {};
1441
1511
  if (isUnderApp(segments) || isConfigModule(segments)) return {};
1442
1512
  const module = index.modules.get(componentFile);
1443
1513
  if (!module?.exports.some((moduleExport) => moduleExport.kind === "component")) return {};
1444
- const currentFolder = folderSegmentsOf(componentFile, sourceRoot2);
1514
+ const currentFolder = folderSegmentsOf(componentFile, sourceRoot);
1445
1515
  const placement = resolveComponentPlacement(componentFile, index);
1446
1516
  return {
1447
1517
  Program(node) {
@@ -1460,7 +1530,7 @@ var componentPlacementRule = {
1460
1530
  context.report({
1461
1531
  node,
1462
1532
  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`
1533
+ 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
1534
  });
1465
1535
  }
1466
1536
  };
@@ -1468,7 +1538,7 @@ var componentPlacementRule = {
1468
1538
  };
1469
1539
 
1470
1540
  // eslint/rules/support-file-placement.ts
1471
- import path10 from "path";
1541
+ import path11 from "path";
1472
1542
  var CONFIG_OWNED_FOLDERS = /* @__PURE__ */ new Set(["types", "constants"]);
1473
1543
  var REASON_TEXT2 = {
1474
1544
  "app-consumer": "a file under src/app/ imports it, so it belongs to the app-wide support folder",
@@ -1487,16 +1557,16 @@ var supportFilePlacementRule = {
1487
1557
  }
1488
1558
  },
1489
1559
  create(context) {
1490
- const sourceRoot2 = path10.resolve("src");
1491
- const supportFile = path10.resolve(context.filename);
1492
- const currentFolder = folderSegmentsOf(supportFile, sourceRoot2);
1560
+ const sourceRoot = sourceRootOf(context);
1561
+ const supportFile = path11.resolve(context.filename);
1562
+ const currentFolder = folderSegmentsOf(supportFile, sourceRoot);
1493
1563
  const supportFolder = currentFolder[currentFolder.length - 1];
1494
1564
  if (supportFolder === void 0 || !SUPPORT_FOLDERS2.has(supportFolder)) return {};
1495
- const index = getProjectIndex(sourceRoot2);
1565
+ const index = getProjectIndex(sourceRoot);
1496
1566
  if (!index) return {};
1497
1567
  const placement = resolveSupportPlacement(supportFile, supportFolder, index);
1498
1568
  if (!placement || sameFolder2(currentFolder, placement.expectedFolder)) return {};
1499
- if (isConfigModule(segmentsOf(supportFile, sourceRoot2)) && CONFIG_OWNED_FOLDERS.has(supportFolder)) {
1569
+ if (isConfigModule(segmentsOf(supportFile, sourceRoot)) && CONFIG_OWNED_FOLDERS.has(supportFolder)) {
1500
1570
  if (placement.reason !== "config-module") return {};
1501
1571
  }
1502
1572
  return {
@@ -1504,7 +1574,7 @@ var supportFilePlacementRule = {
1504
1574
  context.report({
1505
1575
  node,
1506
1576
  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)}.`
1577
+ 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
1578
  });
1509
1579
  }
1510
1580
  };
@@ -1513,7 +1583,7 @@ var supportFilePlacementRule = {
1513
1583
 
1514
1584
  // eslint/rules/application-structure.ts
1515
1585
  import fs2 from "fs";
1516
- import path11 from "path";
1586
+ import path12 from "path";
1517
1587
  var MODULE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
1518
1588
  var ROUTING_FILES = /* @__PURE__ */ new Set([
1519
1589
  "default",
@@ -1542,7 +1612,7 @@ function report2(context, message) {
1542
1612
  };
1543
1613
  }
1544
1614
  function isCodeFile(filename) {
1545
- return MODULE_EXTENSIONS2.has(path11.extname(filename));
1615
+ return MODULE_EXTENSIONS2.has(path12.extname(filename));
1546
1616
  }
1547
1617
  function isRootSupportFolder(folder) {
1548
1618
  return SUPPORT_FOLDERS2.has(folder);
@@ -1560,28 +1630,28 @@ function expectedSupportFolder(kinds) {
1560
1630
  }
1561
1631
  return void 0;
1562
1632
  }
1563
- function configModuleRoot(filename, sourceRoot2) {
1564
- const segments = segmentsOf(filename, sourceRoot2);
1633
+ function configModuleRoot(filename, sourceRoot) {
1634
+ const segments = segmentsOf(filename, sourceRoot);
1565
1635
  if (segments[0] !== "config" || segments.length < 3) return void 0;
1566
- return path11.join(sourceRoot2, "config", segments[1] ?? "");
1636
+ return path12.join(sourceRoot, "config", segments[1] ?? "");
1567
1637
  }
1568
1638
  function componentFolderStart(segments) {
1569
1639
  if (segments[0] === "features") return 2;
1570
1640
  if (segments[0] === "compositions" || segments[0] === "shared") return 1;
1571
1641
  return -1;
1572
1642
  }
1573
- function componentFolderViolation(segments, sourceRoot2) {
1643
+ function componentFolderViolation(segments, sourceRoot) {
1574
1644
  const start = componentFolderStart(segments);
1575
1645
  if (start < 0) return void 0;
1576
1646
  for (let depth = segments.length - 2; depth >= start; depth -= 1) {
1577
1647
  const folder = segments[depth];
1578
1648
  if (!folder || SUPPORT_FOLDERS2.has(folder)) continue;
1579
- const folderPath = path11.join(sourceRoot2, ...segments.slice(0, depth + 1));
1649
+ const folderPath = path12.join(sourceRoot, ...segments.slice(0, depth + 1));
1580
1650
  const label = `src/${segments.slice(0, depth + 1).join("/")}/`;
1581
- if (!fs2.existsSync(path11.join(folderPath, `${folder}.tsx`))) {
1651
+ if (!fs2.existsSync(path12.join(folderPath, `${folder}.tsx`))) {
1582
1652
  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
1653
  }
1584
- if (!fs2.existsSync(path11.join(folderPath, "index.ts"))) {
1654
+ if (!fs2.existsSync(path12.join(folderPath, "index.ts"))) {
1585
1655
  return `A component folder must have an index.ts that named-re-exports its component; add index.ts to ${label}.`;
1586
1656
  }
1587
1657
  }
@@ -1596,9 +1666,9 @@ var applicationStructureRule = {
1596
1666
  }
1597
1667
  },
1598
1668
  create(context) {
1599
- const filename = path11.resolve(context.filename);
1600
- const sourceRoot2 = path11.resolve("src");
1601
- const segments = segmentsOf(filename, sourceRoot2);
1669
+ const filename = path12.resolve(context.filename);
1670
+ const sourceRoot = sourceRootOf(context);
1671
+ const segments = segmentsOf(filename, sourceRoot);
1602
1672
  if (segments.length === 0) return {};
1603
1673
  const [topLevel, secondLevel] = segments;
1604
1674
  if (topLevel === void 0) return {};
@@ -1626,43 +1696,43 @@ var applicationStructureRule = {
1626
1696
  "A configuration module must be a src/config/<config-name>/ folder with index.ts as its entry point."
1627
1697
  );
1628
1698
  }
1629
- const moduleRoot = configModuleRoot(filename, sourceRoot2);
1630
- if (moduleRoot && !fs2.existsSync(path11.join(moduleRoot, "index.ts"))) {
1699
+ const moduleRoot = configModuleRoot(filename, sourceRoot);
1700
+ if (moduleRoot && !fs2.existsSync(path12.join(moduleRoot, "index.ts"))) {
1631
1701
  return report2(
1632
1702
  context,
1633
- `Add src/config/${path11.basename(moduleRoot)}/index.ts as the configuration module entry point.`
1703
+ `Add src/config/${path12.basename(moduleRoot)}/index.ts as the configuration module entry point.`
1634
1704
  );
1635
1705
  }
1636
- if (moduleRoot && segments.length === 3 && path11.basename(filename) !== "index.ts") {
1706
+ if (moduleRoot && segments.length === 3 && path12.basename(filename) !== "index.ts") {
1637
1707
  const kinds2 = exportedKinds(filename);
1638
1708
  const expected2 = expectedSupportFolder(kinds2);
1639
1709
  if (expected2 !== void 0) {
1640
1710
  return report2(
1641
1711
  context,
1642
- `Move this configuration support file into src/config/${path11.basename(moduleRoot)}/${expected2}/.`
1712
+ `Move this configuration support file into src/config/${path12.basename(moduleRoot)}/${expected2}/.`
1643
1713
  );
1644
1714
  }
1645
1715
  }
1646
1716
  }
1647
1717
  if (topLevel === "app" && isCodeFile(filename)) {
1648
- const basename = path11.basename(filename, path11.extname(filename));
1649
- const currentFolder2 = path11.basename(path11.dirname(filename));
1718
+ const basename = path12.basename(filename, path12.extname(filename));
1719
+ const currentFolder2 = path12.basename(path12.dirname(filename));
1650
1720
  if (SUPPORT_FOLDERS2.has(currentFolder2)) {
1651
1721
  return report2(
1652
1722
  context,
1653
1723
  "src/app/ may contain routing files and framework assets, but ordinary components and support files must live outside src/app/."
1654
1724
  );
1655
1725
  }
1656
- if (!ROUTING_FILES.has(basename) && path11.extname(filename) !== ".css") {
1726
+ if (!ROUTING_FILES.has(basename) && path12.extname(filename) !== ".css") {
1657
1727
  return report2(
1658
1728
  context,
1659
1729
  "src/app/ may contain routing files and framework assets, but ordinary components and support files must live outside src/app/."
1660
1730
  );
1661
1731
  }
1662
1732
  }
1663
- const currentFolder = path11.basename(path11.dirname(filename));
1733
+ const currentFolder = path12.basename(path12.dirname(filename));
1664
1734
  const kinds = exportedKinds(filename);
1665
- const isConfigModuleRoot = topLevel === "config" && segments.length === 3 && path11.basename(filename) === "index.ts";
1735
+ const isConfigModuleRoot = topLevel === "config" && segments.length === 3 && path12.basename(filename) === "index.ts";
1666
1736
  if (isConfigModuleRoot) return {};
1667
1737
  if (!SUPPORT_FOLDERS2.has(currentFolder)) {
1668
1738
  const expected2 = expectedSupportFolder(kinds);
@@ -1672,14 +1742,14 @@ var applicationStructureRule = {
1672
1742
  `Move this file to a ${expected2}/ folder; ${currentFolder}/ is not a recognized support folder.`
1673
1743
  );
1674
1744
  }
1675
- const violation2 = componentFolderViolation(segments, sourceRoot2);
1745
+ const violation2 = componentFolderViolation(segments, sourceRoot);
1676
1746
  if (violation2 !== void 0) return report2(context, violation2);
1677
1747
  return {};
1678
1748
  }
1679
1749
  if (kinds.has("component")) {
1680
1750
  return report2(
1681
1751
  context,
1682
- `A support folder must not contain a component; move ${path11.basename(filename)} beside ${currentFolder}/.`
1752
+ `A support folder must not contain a component; move ${path12.basename(filename)} beside ${currentFolder}/.`
1683
1753
  );
1684
1754
  }
1685
1755
  const expected = expectedSupportFolder(kinds);
@@ -1689,28 +1759,38 @@ var applicationStructureRule = {
1689
1759
  `Move this file to a ${expected}/ folder; ${currentFolder}/ is reserved for ${expected === "utils" ? "utilities" : expected}.`
1690
1760
  );
1691
1761
  }
1692
- const violation = componentFolderViolation(segments, sourceRoot2);
1762
+ const violation = componentFolderViolation(segments, sourceRoot);
1693
1763
  if (violation !== void 0) return report2(context, violation);
1694
1764
  return {};
1695
1765
  }
1696
1766
  };
1697
1767
 
1698
1768
  // eslint/rules/named-exports.ts
1699
- import path12 from "path";
1769
+ import path13 from "path";
1700
1770
  var FRAMEWORK_DEFAULT_EXPORT_FILES = /* @__PURE__ */ new Set([
1771
+ // App Router routing files
1701
1772
  "default",
1702
1773
  "error",
1774
+ "global-error",
1703
1775
  "layout",
1704
1776
  "loading",
1705
1777
  "not-found",
1706
1778
  "page",
1707
1779
  "route",
1708
- "template"
1780
+ "template",
1781
+ // File conventions (metadata, icons) that must default-export their handler
1782
+ "apple-icon",
1783
+ "icon",
1784
+ "manifest",
1785
+ "opengraph-image",
1786
+ "robots",
1787
+ "sitemap",
1788
+ "twitter-image"
1709
1789
  ]);
1710
1790
  function isFrameworkDefaultExportFile(filename) {
1711
- const normalized = filename.replaceAll(path12.sep, "/");
1791
+ const normalized = filename.replaceAll(path13.sep, "/");
1712
1792
  if (!normalized.includes("/src/app/")) return false;
1713
- const basename = path12.basename(filename, path12.extname(filename));
1793
+ const basename = path13.basename(filename, path13.extname(filename));
1714
1794
  return FRAMEWORK_DEFAULT_EXPORT_FILES.has(basename);
1715
1795
  }
1716
1796
  var namedExportsRule = {
@@ -1735,7 +1815,7 @@ var namedExportsRule = {
1735
1815
  };
1736
1816
 
1737
1817
  // eslint/rules/data-testid-case.ts
1738
- import path13 from "path";
1818
+ import path14 from "path";
1739
1819
  var NEXT_ROUTING_FILES2 = /* @__PURE__ */ new Set([
1740
1820
  "page",
1741
1821
  "layout",
@@ -1759,9 +1839,9 @@ var dataTestIdCaseRule = {
1759
1839
  }
1760
1840
  },
1761
1841
  create(context) {
1762
- const filename = path13.resolve(context.filename);
1842
+ const filename = path14.resolve(context.filename);
1763
1843
  if (!filename.endsWith(".tsx")) return {};
1764
- const base = path13.basename(filename, path13.extname(filename));
1844
+ const base = path14.basename(filename, path14.extname(filename));
1765
1845
  if (NEXT_ROUTING_FILES2.has(base)) return {};
1766
1846
  const text = context.sourceCode.text;
1767
1847
  const components = parseComponentInfo(text, filename);
@@ -1803,7 +1883,7 @@ function toKebabCase(value) {
1803
1883
 
1804
1884
  // eslint/rules/support-folder-shape.ts
1805
1885
  import fs3 from "fs";
1806
- import path14 from "path";
1886
+ import path15 from "path";
1807
1887
  var SUPPORT_FOLDERS3 = /* @__PURE__ */ new Set(["constants", "types", "schemas"]);
1808
1888
  var INDEX_NAMES = /* @__PURE__ */ new Set(["index.ts", "index.tsx", "index.mts", "index.cts"]);
1809
1889
  var supportFolderShapeRule = {
@@ -1815,12 +1895,12 @@ var supportFolderShapeRule = {
1815
1895
  }
1816
1896
  },
1817
1897
  create(context) {
1818
- const filename = path14.resolve(context.filename);
1819
- const baseName = path14.basename(filename);
1898
+ const filename = path15.resolve(context.filename);
1899
+ const baseName = path15.basename(filename);
1820
1900
  if (!INDEX_NAMES.has(baseName)) return {};
1821
- const folder = path14.basename(path14.dirname(filename));
1901
+ const folder = path15.basename(path15.dirname(filename));
1822
1902
  if (!SUPPORT_FOLDERS3.has(folder)) return {};
1823
- const directory = path14.dirname(filename);
1903
+ const directory = path15.dirname(filename);
1824
1904
  let entries;
1825
1905
  try {
1826
1906
  entries = fs3.readdirSync(directory);
@@ -1838,7 +1918,7 @@ var supportFolderShapeRule = {
1838
1918
  const exportPattern = /export\s+(?:\{[^}]*\}|\*[^;]*)\s+from\s+["'](?<specifier>\.[^"']+)["']/g;
1839
1919
  for (const match of source.matchAll(exportPattern)) {
1840
1920
  const specifier = match.groups?.specifier;
1841
- if (specifier) exportedFiles.add(path14.basename(specifier));
1921
+ if (specifier) exportedFiles.add(path15.basename(specifier));
1842
1922
  }
1843
1923
  const missing = siblingModules.filter((entry) => {
1844
1924
  const stem = entry.replace(/\.(?:[cm]?tsx?|jsx?)$/, "");
@@ -1855,7 +1935,7 @@ var supportFolderShapeRule = {
1855
1935
  };
1856
1936
 
1857
1937
  // eslint/rules/import-through-index.ts
1858
- import path15 from "path";
1938
+ import path16 from "path";
1859
1939
  var importThroughIndexRule = {
1860
1940
  meta: {
1861
1941
  schema: [],
@@ -1865,19 +1945,19 @@ var importThroughIndexRule = {
1865
1945
  }
1866
1946
  },
1867
1947
  create(context) {
1868
- const filename = path15.resolve(context.filename);
1869
- const sourceRoot2 = sourceRootOf(filename);
1948
+ const filename = path16.resolve(context.filename);
1949
+ const sourceRoot = sourceRootOf2(context, filename);
1870
1950
  return {
1871
1951
  Program(node) {
1872
1952
  for (const specifier of importSpecifiers(context.sourceCode.text)) {
1873
- const target = resolveSpecifier(filename, specifier, sourceRoot2);
1953
+ const target = resolveSpecifier(filename, specifier, sourceRoot);
1874
1954
  if (!target) continue;
1875
- const targetSegments = segmentsOf(target, sourceRoot2);
1955
+ const targetSegments = segmentsOf(target, sourceRoot);
1876
1956
  const supportFolderIndex = targetSegments.findIndex(
1877
1957
  (segment) => ["constants", "types", "schemas"].includes(segment)
1878
1958
  );
1879
1959
  const supportFolder = supportFolderIndex >= 0 ? targetSegments[supportFolderIndex] : void 0;
1880
- if (!supportFolder || path15.basename(target).startsWith("index.")) continue;
1960
+ if (!supportFolder || path16.basename(target).startsWith("index.")) continue;
1881
1961
  const folderIndex = targetSegments.slice(0, supportFolderIndex + 1);
1882
1962
  const expected = `@/${folderIndex.join("/")}`;
1883
1963
  context.report({
@@ -1898,14 +1978,15 @@ function importSpecifiers(source) {
1898
1978
  }
1899
1979
  return specifiers;
1900
1980
  }
1901
- function sourceRootOf(filename) {
1902
- const marker = `${path15.sep}src${path15.sep}`;
1981
+ function sourceRootOf2(context, filename) {
1982
+ const marker = `${path16.sep}src${path16.sep}`;
1903
1983
  const srcIndex = filename.lastIndexOf(marker);
1904
- return srcIndex >= 0 ? filename.slice(0, srcIndex + marker.length - 1) : path15.resolve("src");
1984
+ if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
1985
+ return path16.resolve(context.cwd ?? process.cwd(), "src");
1905
1986
  }
1906
1987
 
1907
1988
  // eslint/rules/util-file-name.ts
1908
- import path16 from "path";
1989
+ import path17 from "path";
1909
1990
  function toKebabCase2(value) {
1910
1991
  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
1992
  }
@@ -1918,7 +1999,7 @@ var utilFileNameRule = {
1918
1999
  }
1919
2000
  },
1920
2001
  create(context) {
1921
- const filename = path16.resolve(context.filename);
2002
+ const filename = path17.resolve(context.filename);
1922
2003
  const segments = filename.replace(/\\/g, "/").split("/");
1923
2004
  if (!segments.includes("utils")) return {};
1924
2005
  let module;
@@ -1932,13 +2013,13 @@ var utilFileNameRule = {
1932
2013
  const functionName = functions[0]?.name;
1933
2014
  if (!functionName) return {};
1934
2015
  const expected = toKebabCase2(functionName);
1935
- const actual = path16.basename(filename, path16.extname(filename));
2016
+ const actual = path17.basename(filename, path17.extname(filename));
1936
2017
  if (!expected || actual === expected) return {};
1937
2018
  return {
1938
2019
  Program(node) {
1939
2020
  context.report({
1940
2021
  node,
1941
- message: `A utility file exporting ${functionName} must be named ${expected}.${path16.extname(filename).slice(1)}.`
2022
+ message: `A utility file exporting ${functionName} must be named ${expected}.${path17.extname(filename).slice(1)}.`
1942
2023
  });
1943
2024
  }
1944
2025
  };
@@ -1946,7 +2027,7 @@ var utilFileNameRule = {
1946
2027
  };
1947
2028
 
1948
2029
  // eslint/rules/no-util-barrel.ts
1949
- import path17 from "path";
2030
+ import path18 from "path";
1950
2031
  var noUtilBarrelRule = {
1951
2032
  meta: {
1952
2033
  schema: [],
@@ -1956,16 +2037,16 @@ var noUtilBarrelRule = {
1956
2037
  }
1957
2038
  },
1958
2039
  create(context) {
1959
- const filename = path17.resolve(context.filename);
1960
- const sourceRoot2 = sourceRootOf2(filename);
2040
+ const filename = path18.resolve(context.filename);
2041
+ const sourceRoot = sourceRootOf3(context, filename);
1961
2042
  return {
1962
2043
  Program(node) {
1963
2044
  for (const specifier of importSpecifiers2(context.sourceCode.text)) {
1964
- const target = resolveSpecifier(filename, specifier, sourceRoot2);
2045
+ const target = resolveSpecifier(filename, specifier, sourceRoot);
1965
2046
  if (!target) continue;
1966
2047
  const segments = target.replace(/\\/g, "/").split("/");
1967
2048
  const utilsIndex = segments.lastIndexOf("utils");
1968
- if (utilsIndex < 0 || !path17.basename(target).startsWith("index.")) continue;
2049
+ if (utilsIndex < 0 || !path18.basename(target).startsWith("index.")) continue;
1969
2050
  context.report({
1970
2051
  node,
1971
2052
  message: `Import utilities directly instead of through "${specifier}". See docs/code-organization-guide/rules/utilities-rule.md`
@@ -1984,10 +2065,11 @@ function importSpecifiers2(source) {
1984
2065
  }
1985
2066
  return specifiers;
1986
2067
  }
1987
- function sourceRootOf2(filename) {
1988
- const marker = `${path17.sep}src${path17.sep}`;
2068
+ function sourceRootOf3(context, filename) {
2069
+ const marker = `${path18.sep}src${path18.sep}`;
1989
2070
  const srcIndex = filename.lastIndexOf(marker);
1990
- return srcIndex >= 0 ? filename.slice(0, srcIndex + marker.length - 1) : path17.resolve("src");
2071
+ if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
2072
+ return path18.resolve(context.cwd ?? process.cwd(), "src");
1991
2073
  }
1992
2074
 
1993
2075
  // eslint/rules/jsx-hygiene.ts
@@ -2363,12 +2445,12 @@ var cvaBooleanVariantsRule = {
2363
2445
  };
2364
2446
 
2365
2447
  // eslint/rules/cross-feature-import.ts
2366
- import path18 from "path";
2448
+ import path19 from "path";
2367
2449
  var FEATURES_SEGMENT = "features";
2368
- function featureNameOf(resolvedPath, sourceRoot2) {
2369
- const relative = path18.relative(sourceRoot2, resolvedPath);
2450
+ function featureNameOf(resolvedPath, sourceRoot) {
2451
+ const relative = path19.relative(sourceRoot, resolvedPath);
2370
2452
  if (relative.startsWith("..")) return void 0;
2371
- const segments = relative.split(path18.sep);
2453
+ const segments = relative.split(path19.sep);
2372
2454
  if (segments[0] !== FEATURES_SEGMENT || segments.length < 2) return void 0;
2373
2455
  return segments[1];
2374
2456
  }
@@ -2383,10 +2465,10 @@ var crossFeatureImportRule = {
2383
2465
  create(context) {
2384
2466
  const filename = context.filename;
2385
2467
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
2386
- const sourceRoot2 = path18.resolve("src");
2387
- const fileRelative = path18.relative(sourceRoot2, filename);
2468
+ const sourceRoot = sourceRootOf(context);
2469
+ const fileRelative = path19.relative(sourceRoot, filename);
2388
2470
  if (fileRelative.startsWith("..")) return {};
2389
- const fileSegments = fileRelative.split(path18.sep);
2471
+ const fileSegments = fileRelative.split(path19.sep);
2390
2472
  const isInCompositions = fileSegments[0] === "compositions";
2391
2473
  const isInApp = fileSegments[0] === "app";
2392
2474
  const isConfig = fileSegments[0] === "config";
@@ -2400,12 +2482,12 @@ var crossFeatureImportRule = {
2400
2482
  if (typeof source.value !== "string") return;
2401
2483
  let resolved;
2402
2484
  if (source.value.startsWith("@/")) {
2403
- resolved = path18.resolve(sourceRoot2, source.value.slice(2));
2485
+ resolved = path19.resolve(sourceRoot, source.value.slice(2));
2404
2486
  } else if (source.value.startsWith(".")) {
2405
- resolved = path18.resolve(path18.dirname(filename), source.value);
2487
+ resolved = path19.resolve(path19.dirname(filename), source.value);
2406
2488
  }
2407
2489
  if (!resolved) return;
2408
- const feature = featureNameOf(resolved, sourceRoot2);
2490
+ const feature = featureNameOf(resolved, sourceRoot);
2409
2491
  if (feature) importedFeatures.add(feature);
2410
2492
  if (importedFeatures.size >= 2) {
2411
2493
  alreadyReported = true;
@@ -2421,7 +2503,7 @@ var crossFeatureImportRule = {
2421
2503
  };
2422
2504
 
2423
2505
  // eslint/rules/pure-function-extract.ts
2424
- import path19 from "path";
2506
+ import path20 from "path";
2425
2507
  function isComponentLikeName(name) {
2426
2508
  return /^[A-Z]/.test(name);
2427
2509
  }
@@ -2449,10 +2531,10 @@ var pureFunctionExtractRule = {
2449
2531
  create(context) {
2450
2532
  const filename = context.filename;
2451
2533
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
2452
- const sourceRoot2 = path19.resolve("src");
2453
- const relative = path19.relative(sourceRoot2, filename);
2534
+ const sourceRoot = sourceRootOf(context);
2535
+ const relative = path20.relative(sourceRoot, filename);
2454
2536
  if (relative.startsWith("..")) return {};
2455
- const segments = relative.split(path19.sep);
2537
+ const segments = relative.split(path20.sep);
2456
2538
  if (segments[0] === "utils") return {};
2457
2539
  if (segments[0] === "app") return {};
2458
2540
  const supportFolders = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
@@ -2492,7 +2574,7 @@ var pureFunctionExtractRule = {
2492
2574
  };
2493
2575
 
2494
2576
  // eslint/rules/hook-complexity.ts
2495
- import path20 from "path";
2577
+ import path21 from "path";
2496
2578
  import ts4 from "typescript";
2497
2579
  var REACT_HOOKS = /* @__PURE__ */ new Set([
2498
2580
  "useState",
@@ -2544,10 +2626,10 @@ var hookComplexityRule = {
2544
2626
  },
2545
2627
  create(context) {
2546
2628
  const filename = context.filename;
2547
- const sourceRoot2 = path20.resolve("src");
2548
- const relative = path20.relative(sourceRoot2, filename);
2629
+ const sourceRoot = sourceRootOf(context);
2630
+ const relative = path21.relative(sourceRoot, filename);
2549
2631
  if (relative.startsWith("..")) return {};
2550
- const segments = relative.split(path20.sep);
2632
+ const segments = relative.split(path21.sep);
2551
2633
  const sourceText = context.sourceCode.text;
2552
2634
  function checkHook(node, name, body, exported) {
2553
2635
  if (!exported) return;
@@ -2589,9 +2671,9 @@ var hookComplexityRule = {
2589
2671
  };
2590
2672
 
2591
2673
  // eslint/rules/locale-dotted-path.ts
2592
- import path21 from "path";
2674
+ import path22 from "path";
2593
2675
  function isInLocalesDir(filename) {
2594
- const segments = path21.resolve(filename).split(path21.sep);
2676
+ const segments = path22.resolve(filename).split(path22.sep);
2595
2677
  const srcIdx = segments.lastIndexOf("src");
2596
2678
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
2597
2679
  }
@@ -2640,9 +2722,9 @@ var localeDottedPathRule = {
2640
2722
  };
2641
2723
 
2642
2724
  // eslint/rules/locales-location.ts
2643
- import path22 from "path";
2725
+ import path23 from "path";
2644
2726
  function isLocalesFile(filename) {
2645
- const segments = path22.resolve(filename).split(path22.sep);
2727
+ const segments = path23.resolve(filename).split(path23.sep);
2646
2728
  const srcIdx = segments.lastIndexOf("src");
2647
2729
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
2648
2730
  }
@@ -2661,7 +2743,7 @@ var localesLocationRule = {
2661
2743
  create(context) {
2662
2744
  if (isLocalesFile(context.filename)) return {};
2663
2745
  const filename = context.filename;
2664
- const segments = path22.resolve(filename).split(path22.sep);
2746
+ const segments = path23.resolve(filename).split(path23.sep);
2665
2747
  const srcIdx = segments.lastIndexOf("src");
2666
2748
  if (srcIdx === -1) return {};
2667
2749
  const folder = segments[srcIdx + 1];
@@ -2683,7 +2765,7 @@ var localesLocationRule = {
2683
2765
  };
2684
2766
 
2685
2767
  // eslint/rules/hook-extraction.ts
2686
- import path23 from "path";
2768
+ import path24 from "path";
2687
2769
  var hookExtractionRule = {
2688
2770
  meta: {
2689
2771
  schema: [],
@@ -2693,15 +2775,15 @@ var hookExtractionRule = {
2693
2775
  }
2694
2776
  },
2695
2777
  create(context) {
2696
- const sourceRoot2 = path23.resolve("src");
2697
- const file = path23.resolve(context.filename);
2698
- const segments = segmentsOf(file, sourceRoot2);
2778
+ const sourceRoot = sourceRootOf(context);
2779
+ const file = path24.resolve(context.filename);
2780
+ const segments = segmentsOf(file, sourceRoot);
2699
2781
  if (segments.length === 0) return {};
2700
- const index = getProjectIndex(sourceRoot2);
2782
+ const index = getProjectIndex(sourceRoot);
2701
2783
  if (!index) return {};
2702
2784
  const module = index.modules.get(file);
2703
2785
  if (!module) return {};
2704
- const folder = folderSegmentsOf(file, sourceRoot2);
2786
+ const folder = folderSegmentsOf(file, sourceRoot);
2705
2787
  const alreadyInHooksFolder = folder.length > 0 && folder[folder.length - 1] === "hooks";
2706
2788
  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
2789
  if (reusedHooks.length === 0) return {};
@@ -2720,7 +2802,7 @@ var hookExtractionRule = {
2720
2802
  };
2721
2803
 
2722
2804
  // eslint/rules/value-extraction.ts
2723
- import path24 from "path";
2805
+ import path25 from "path";
2724
2806
  var valueExtractionRule = {
2725
2807
  meta: {
2726
2808
  schema: [],
@@ -2730,21 +2812,21 @@ var valueExtractionRule = {
2730
2812
  }
2731
2813
  },
2732
2814
  create(context) {
2733
- const sourceRoot2 = path24.resolve("src");
2734
- const file = path24.resolve(context.filename);
2735
- const segments = segmentsOf(file, sourceRoot2);
2815
+ const sourceRoot = sourceRootOf(context);
2816
+ const file = path25.resolve(context.filename);
2817
+ const segments = segmentsOf(file, sourceRoot);
2736
2818
  if (segments.length === 0 || segments[0] !== "app") return {};
2737
- const index = getProjectIndex(sourceRoot2);
2819
+ const index = getProjectIndex(sourceRoot);
2738
2820
  if (!index) return {};
2739
2821
  const consumers = [...index.consumers.get(file) ?? []];
2740
- const outsideApp = consumers.filter((consumer) => segmentsOf(consumer, sourceRoot2)[0] !== "app");
2822
+ const outsideApp = consumers.filter((consumer) => segmentsOf(consumer, sourceRoot)[0] !== "app");
2741
2823
  if (outsideApp.length === 0) return {};
2742
2824
  return {
2743
2825
  Program(node) {
2744
2826
  context.report({
2745
2827
  node,
2746
2828
  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`
2829
+ 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
2830
  });
2749
2831
  }
2750
2832
  };
@@ -2752,7 +2834,7 @@ var valueExtractionRule = {
2752
2834
  };
2753
2835
 
2754
2836
  // eslint/rules/config-extraction.ts
2755
- import path25 from "path";
2837
+ import path26 from "path";
2756
2838
  var configExtractionRule = {
2757
2839
  meta: {
2758
2840
  schema: [],
@@ -2762,12 +2844,12 @@ var configExtractionRule = {
2762
2844
  }
2763
2845
  },
2764
2846
  create(context) {
2765
- const sourceRoot2 = path25.resolve("src");
2766
- const file = path25.resolve(context.filename);
2767
- const segments = segmentsOf(file, sourceRoot2);
2847
+ const sourceRoot = sourceRootOf(context);
2848
+ const file = path26.resolve(context.filename);
2849
+ const segments = segmentsOf(file, sourceRoot);
2768
2850
  if (segments.length < 3 || segments[0] !== "config") return {};
2769
2851
  if (SUPPORT_FOLDERS2.has(segments[2] ?? "")) return {};
2770
- const index = getProjectIndex(sourceRoot2);
2852
+ const index = getProjectIndex(sourceRoot);
2771
2853
  if (!index) return {};
2772
2854
  const module = index.modules.get(file);
2773
2855
  if (!module) return {};
@@ -2776,11 +2858,11 @@ var configExtractionRule = {
2776
2858
  const findings = [];
2777
2859
  for (const exp of suspects) {
2778
2860
  const consumers = [...index.symbolConsumers.get(symbolKey(file, exp.name)) ?? []];
2779
- const outsideConfig = consumers.filter((consumer) => segmentsOf(consumer, sourceRoot2)[0] !== "config");
2861
+ const outsideConfig = consumers.filter((consumer) => segmentsOf(consumer, sourceRoot)[0] !== "config");
2780
2862
  if (outsideConfig.length > 0) {
2781
2863
  findings.push({
2782
2864
  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`
2865
+ 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
2866
  });
2785
2867
  } else if (consumers.length > 0) {
2786
2868
  findings.push({
@@ -2801,7 +2883,7 @@ var configExtractionRule = {
2801
2883
  };
2802
2884
 
2803
2885
  // eslint/rules/component-nesting.ts
2804
- import path26 from "path";
2886
+ import path27 from "path";
2805
2887
  var componentNestingRule = {
2806
2888
  meta: {
2807
2889
  schema: [],
@@ -2811,18 +2893,18 @@ var componentNestingRule = {
2811
2893
  }
2812
2894
  },
2813
2895
  create(context) {
2814
- const sourceRoot2 = path26.resolve("src");
2815
- const file = path26.resolve(context.filename);
2816
- const segments = segmentsOf(file, sourceRoot2);
2896
+ const sourceRoot = sourceRootOf(context);
2897
+ const file = path27.resolve(context.filename);
2898
+ const segments = segmentsOf(file, sourceRoot);
2817
2899
  if (segments.length !== 4 || segments[0] !== "features") return {};
2818
- const index = getProjectIndex(sourceRoot2);
2900
+ const index = getProjectIndex(sourceRoot);
2819
2901
  if (!index) return {};
2820
2902
  const module = index.modules.get(file);
2821
2903
  if (!module?.exports.some((exp) => exp.kind === "component")) return {};
2822
2904
  const folder = segments.slice(0, 3);
2823
2905
  const hasChildComponent = [...index.modules.values()].some((candidate) => {
2824
2906
  if (candidate.file === file) return false;
2825
- const candidateSegments = segmentsOf(candidate.file, sourceRoot2);
2907
+ const candidateSegments = segmentsOf(candidate.file, sourceRoot);
2826
2908
  if (candidateSegments.length !== 4) return false;
2827
2909
  if (candidateSegments[0] !== folder[0] || candidateSegments[1] !== folder[1] || candidateSegments[2] !== folder[2]) {
2828
2910
  return false;
@@ -2845,7 +2927,7 @@ var componentNestingRule = {
2845
2927
  };
2846
2928
 
2847
2929
  // eslint/rules/stay-flat.ts
2848
- import path27 from "path";
2930
+ import path28 from "path";
2849
2931
  var stayFlatRule = {
2850
2932
  meta: {
2851
2933
  schema: [],
@@ -2855,11 +2937,11 @@ var stayFlatRule = {
2855
2937
  }
2856
2938
  },
2857
2939
  create(context) {
2858
- const sourceRoot2 = path27.resolve("src");
2859
- const file = path27.resolve(context.filename);
2860
- const segments = segmentsOf(file, sourceRoot2);
2940
+ const sourceRoot = sourceRootOf(context);
2941
+ const file = path28.resolve(context.filename);
2942
+ const segments = segmentsOf(file, sourceRoot);
2861
2943
  if (segments.length !== 3 || segments[0] !== "features") return {};
2862
- const index = getProjectIndex(sourceRoot2);
2944
+ const index = getProjectIndex(sourceRoot);
2863
2945
  if (!index) return {};
2864
2946
  const module = index.modules.get(file);
2865
2947
  if (!module?.exports.some((exp) => exp.kind === "component")) return {};
@@ -2867,7 +2949,7 @@ var stayFlatRule = {
2867
2949
  if (!featureName) return {};
2868
2950
  const exclusiveChildren = [...index.modules.values()].filter((candidate) => {
2869
2951
  if (candidate.file === file) return false;
2870
- const candidateSegments = segmentsOf(candidate.file, sourceRoot2);
2952
+ const candidateSegments = segmentsOf(candidate.file, sourceRoot);
2871
2953
  if (candidateSegments.length !== 3) return false;
2872
2954
  if (candidateSegments[0] !== "features" || candidateSegments[1] !== featureName) return false;
2873
2955
  if (candidateSegments[2]?.startsWith("index.")) return false;
@@ -2876,7 +2958,7 @@ var stayFlatRule = {
2876
2958
  if (consumers.size === 0) return false;
2877
2959
  return [...consumers].every((consumer) => {
2878
2960
  if (consumer === file) return true;
2879
- const consumerSegments = segmentsOf(consumer, sourceRoot2);
2961
+ const consumerSegments = segmentsOf(consumer, sourceRoot);
2880
2962
  if (consumerSegments[0] !== "features" || consumerSegments[1] !== featureName) return false;
2881
2963
  return !(index.modules.get(consumer)?.exports.some((exp) => exp.kind === "component") ?? false);
2882
2964
  });
@@ -2896,7 +2978,7 @@ var stayFlatRule = {
2896
2978
  };
2897
2979
 
2898
2980
  // eslint/rules/type-extraction.ts
2899
- import path28 from "path";
2981
+ import path29 from "path";
2900
2982
  var typeExtractionRule = {
2901
2983
  meta: {
2902
2984
  schema: [],
@@ -2906,11 +2988,11 @@ var typeExtractionRule = {
2906
2988
  }
2907
2989
  },
2908
2990
  create(context) {
2909
- const sourceRoot2 = path28.resolve("src");
2910
- const file = path28.resolve(context.filename);
2911
- const segments = segmentsOf(file, sourceRoot2);
2991
+ const sourceRoot = sourceRootOf(context);
2992
+ const file = path29.resolve(context.filename);
2993
+ const segments = segmentsOf(file, sourceRoot);
2912
2994
  if (segments.length === 0) return {};
2913
- const index = getProjectIndex(sourceRoot2);
2995
+ const index = getProjectIndex(sourceRoot);
2914
2996
  if (!index) return {};
2915
2997
  const module = index.modules.get(file);
2916
2998
  if (!module) return {};
@@ -2927,7 +3009,7 @@ var typeExtractionRule = {
2927
3009
  if (independent2.length === 0) continue;
2928
3010
  findings.push({
2929
3011
  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`
3012
+ 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
3013
  });
2932
3014
  continue;
2933
3015
  }
@@ -2938,7 +3020,7 @@ var typeExtractionRule = {
2938
3020
  if (independent.length === 0) continue;
2939
3021
  findings.push({
2940
3022
  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`
3023
+ 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
3024
  });
2943
3025
  }
2944
3026
  if (findings.length === 0) return {};
@@ -2953,7 +3035,7 @@ var typeExtractionRule = {
2953
3035
  };
2954
3036
 
2955
3037
  // eslint/rules/locale-placement.ts
2956
- import path29 from "path";
3038
+ import path30 from "path";
2957
3039
  import { readFileSync as readFileSync3 } from "fs";
2958
3040
  import ts5 from "typescript";
2959
3041
  var LOCALE_ACCESS = /\blocales\.(?<key>[A-Za-z_$][\w$]*)/g;
@@ -3000,14 +3082,14 @@ var localePlacementRule = {
3000
3082
  }
3001
3083
  },
3002
3084
  create(context) {
3003
- const sourceRoot2 = path29.resolve("src");
3004
- const file = path29.resolve(context.filename);
3005
- const segments = segmentsOf(file, sourceRoot2);
3085
+ const sourceRoot = sourceRootOf(context);
3086
+ const file = path30.resolve(context.filename);
3087
+ const segments = segmentsOf(file, sourceRoot);
3006
3088
  if (segments.length === 0) return {};
3007
- const index = getProjectIndex(sourceRoot2);
3089
+ const index = getProjectIndex(sourceRoot);
3008
3090
  if (!index) return {};
3009
3091
  const localesFile = [...index.modules.keys()].find((candidate) => {
3010
- const candidateSegments = segmentsOf(candidate, sourceRoot2);
3092
+ const candidateSegments = segmentsOf(candidate, sourceRoot);
3011
3093
  return candidateSegments.length === 2 && candidateSegments[0] === "locales" && candidateSegments[1]?.startsWith("index.");
3012
3094
  });
3013
3095
  if (!localesFile || file !== localesFile) return {};
@@ -3019,10 +3101,10 @@ var localePlacementRule = {
3019
3101
  for (const [candidateFile, module] of index.modules) {
3020
3102
  if (candidateFile === localesFile) continue;
3021
3103
  const importsLocales = module.imports.some(
3022
- (moduleImport) => resolveSpecifier(candidateFile, moduleImport.specifier, sourceRoot2) === localesFile
3104
+ (moduleImport) => resolveSpecifier(candidateFile, moduleImport.specifier, sourceRoot) === localesFile
3023
3105
  );
3024
3106
  if (!importsLocales) continue;
3025
- const candidateSegments = segmentsOf(candidateFile, sourceRoot2);
3107
+ const candidateSegments = segmentsOf(candidateFile, sourceRoot);
3026
3108
  for (const match of readFileSync3(candidateFile, "utf8").matchAll(LOCALE_ACCESS)) {
3027
3109
  const key = match.groups?.key;
3028
3110
  if (key === void 0) continue;
@@ -3047,7 +3129,7 @@ var localePlacementRule = {
3047
3129
  if (current?.kind === "nested") {
3048
3130
  findings.push({
3049
3131
  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`
3132
+ 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
3133
  });
3052
3134
  }
3053
3135
  continue;
@@ -3079,7 +3161,7 @@ var localePlacementRule = {
3079
3161
  };
3080
3162
 
3081
3163
  // eslint/rules/sole-state-owner.ts
3082
- import path30 from "path";
3164
+ import path31 from "path";
3083
3165
  import ts6 from "typescript";
3084
3166
  function findStateHooks(node) {
3085
3167
  const hooks = [];
@@ -3159,7 +3241,7 @@ var soleStateOwnerRule = {
3159
3241
  }
3160
3242
  },
3161
3243
  create(context) {
3162
- const filename = path30.resolve(context.filename);
3244
+ const filename = path31.resolve(context.filename);
3163
3245
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
3164
3246
  const text = context.sourceCode.text;
3165
3247
  const components = parseComponentInfo(text, filename);
@@ -3238,7 +3320,7 @@ function usesOutsideJsx(declaration, hook, children) {
3238
3320
  }
3239
3321
 
3240
3322
  // eslint/rules/locale-key-shape.ts
3241
- import path31 from "path";
3323
+ import path32 from "path";
3242
3324
  var MAX_KEY_LENGTH = 30;
3243
3325
  var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
3244
3326
  "Button",
@@ -3288,7 +3370,7 @@ var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
3288
3370
  var CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
3289
3371
  var ENGLISH = /^[A-Za-z0-9_]*$/;
3290
3372
  function isLocalesFile2(filename) {
3291
- const segments = path31.resolve(filename).split(path31.sep);
3373
+ const segments = path32.resolve(filename).split(path32.sep);
3292
3374
  const srcIdx = segments.lastIndexOf("src");
3293
3375
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
3294
3376
  }
@@ -3359,7 +3441,7 @@ var localeKeyShapeRule = {
3359
3441
  };
3360
3442
 
3361
3443
  // eslint/rules/shared-style-dedup.ts
3362
- import path32 from "path";
3444
+ import path33 from "path";
3363
3445
  import { readFileSync as readFileSync4, statSync as statSync3 } from "fs";
3364
3446
  var CLASS_NAME = /className="(?<classes>[^"]+)"/g;
3365
3447
  var comboCache;
@@ -3400,11 +3482,11 @@ var sharedStyleDedupRule = {
3400
3482
  }
3401
3483
  },
3402
3484
  create(context) {
3403
- const sourceRoot2 = path32.resolve("src");
3404
- const file = path32.resolve(context.filename);
3405
- const segments = segmentsOf(file, sourceRoot2);
3485
+ const sourceRoot = sourceRootOf(context);
3486
+ const file = path33.resolve(context.filename);
3487
+ const segments = segmentsOf(file, sourceRoot);
3406
3488
  if (segments.length === 0) return {};
3407
- const index = getProjectIndex(sourceRoot2);
3489
+ const index = getProjectIndex(sourceRoot);
3408
3490
  if (!index) return {};
3409
3491
  const combos = combosFor(index);
3410
3492
  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 +3687,7 @@ var zodSchemaValidationRule = {
3605
3687
  };
3606
3688
 
3607
3689
  // eslint/rules/source-under-src.ts
3608
- import path33 from "path";
3690
+ import path34 from "path";
3609
3691
  var NON_SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set([
3610
3692
  ".agents",
3611
3693
  ".cache",
@@ -3646,14 +3728,14 @@ var sourceUnderSrcRule = {
3646
3728
  }
3647
3729
  },
3648
3730
  create(context) {
3649
- const filename = path33.resolve(context.filename);
3731
+ const filename = path34.resolve(context.filename);
3650
3732
  if (!MODULE_EXTENSION.test(filename)) return {};
3651
- const relative = path33.relative(process.cwd(), filename).replace(/\\/g, "/");
3733
+ const relative = path34.relative(context.cwd, filename).replace(/\\/g, "/");
3652
3734
  if (relative === "src" || relative.startsWith("src/")) return {};
3653
3735
  const topLevel = relative.split("/")[0] ?? "";
3654
3736
  if (NON_SOURCE_ROOT_DIRS.has(topLevel)) return {};
3655
3737
  if (!relative.includes("/")) {
3656
- const basename = path33.basename(filename);
3738
+ const basename = path34.basename(filename);
3657
3739
  if (CONFIG_FILE.test(basename) || DECLARATION_FILE.test(basename) || basename.startsWith(".")) return {};
3658
3740
  }
3659
3741
  return {
@@ -3670,7 +3752,7 @@ var sourceUnderSrcRule = {
3670
3752
 
3671
3753
  // eslint/rules/zirka-baseline.ts
3672
3754
  import fs4 from "fs";
3673
- import path34 from "path";
3755
+ import path35 from "path";
3674
3756
  var ESLINT_CONFIG = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
3675
3757
  var PRETTIER_CONFIGS = [
3676
3758
  "prettier.config.mjs",
@@ -3689,10 +3771,10 @@ var zirkaBaselineRule = {
3689
3771
  }
3690
3772
  },
3691
3773
  create(context) {
3692
- const filename = path34.resolve(context.filename);
3693
- const basename = path34.basename(filename);
3774
+ const filename = path35.resolve(context.filename);
3775
+ const basename = path35.basename(filename);
3694
3776
  if (!ESLINT_CONFIG.test(basename)) return {};
3695
- const projectRoot = path34.dirname(filename);
3777
+ const projectRoot = path35.dirname(filename);
3696
3778
  const report3 = (message) => {
3697
3779
  context.report({
3698
3780
  node: context.sourceCode.ast,
@@ -3707,7 +3789,7 @@ var zirkaBaselineRule = {
3707
3789
  'ESLint config must take its configuration from zirka (import { styleguide } from "zirka") instead of restating rules locally.'
3708
3790
  );
3709
3791
  }
3710
- const tsconfigPath = path34.join(projectRoot, "tsconfig.json");
3792
+ const tsconfigPath = path35.join(projectRoot, "tsconfig.json");
3711
3793
  if (!fs4.existsSync(tsconfigPath)) {
3712
3794
  report3('No tsconfig.json found. Create one extending the zirka TypeScript base config ("zirka/typescript").');
3713
3795
  } else {
@@ -3725,13 +3807,13 @@ var zirkaBaselineRule = {
3725
3807
  report3('tsconfig.json must extend the zirka TypeScript base config ("zirka/typescript").');
3726
3808
  }
3727
3809
  }
3728
- const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs4.existsSync(path34.join(projectRoot, name)));
3810
+ const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs4.existsSync(path35.join(projectRoot, name)));
3729
3811
  if (!prettierConfigFile) {
3730
3812
  report3(
3731
3813
  "No prettier config found. Create one that takes its configuration from zirka (styleguide({ prettier: true }).prettierConfig)."
3732
3814
  );
3733
3815
  } else {
3734
- const content = fs4.readFileSync(path34.join(projectRoot, prettierConfigFile), "utf8");
3816
+ const content = fs4.readFileSync(path35.join(projectRoot, prettierConfigFile), "utf8");
3735
3817
  if (!content.includes("zirka")) {
3736
3818
  report3(
3737
3819
  "The prettier config must take its configuration from zirka (styleguide({ prettier: true }).prettierConfig) instead of restating it locally."
@@ -3801,7 +3883,7 @@ var docKindSuffixRule = {
3801
3883
  };
3802
3884
 
3803
3885
  // eslint/rules/documentation/title-matches-file-name.ts
3804
- import path35 from "path";
3886
+ import path36 from "path";
3805
3887
  function toExpectedFileName(title) {
3806
3888
  return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
3807
3889
  }
@@ -3821,7 +3903,7 @@ var titleMatchesFileNameRule = {
3821
3903
  if (!filename.endsWith(".md")) return;
3822
3904
  const title = getTextContent(node).trim();
3823
3905
  const expectedFileName = toExpectedFileName(title);
3824
- const actualFileName = path35.basename(filename);
3906
+ const actualFileName = path36.basename(filename);
3825
3907
  if (!title) {
3826
3908
  context.report({
3827
3909
  node,
@@ -4228,7 +4310,7 @@ var referenceBlockHeadingsRule = {
4228
4310
  };
4229
4311
 
4230
4312
  // eslint/rules/documentation/support-document-placement.ts
4231
- import path36 from "path";
4313
+ import path37 from "path";
4232
4314
  var supportDocumentPlacementRule = {
4233
4315
  meta: {
4234
4316
  type: "problem",
@@ -4242,7 +4324,7 @@ var supportDocumentPlacementRule = {
4242
4324
  root(node) {
4243
4325
  const filename = getFilename(context);
4244
4326
  if (!filename.endsWith(".md")) return;
4245
- const parentFolder = path36.basename(path36.dirname(filename));
4327
+ const parentFolder = path37.basename(path37.dirname(filename));
4246
4328
  if (filename.endsWith("-rule.md") && parentFolder !== "rules") {
4247
4329
  context.report({
4248
4330
  node,
@@ -4285,11 +4367,11 @@ var noTemplatePromptRule = {
4285
4367
  };
4286
4368
 
4287
4369
  // eslint/rules/documentation/guide-folder-entry-point.ts
4288
- import path38 from "path";
4370
+ import path39 from "path";
4289
4371
 
4290
4372
  // eslint/rules/documentation/project-index.ts
4291
4373
  import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
4292
- import path37 from "path";
4374
+ import path38 from "path";
4293
4375
  var KIND_BY_SUFFIX = [
4294
4376
  ["-rule.md", "rule"],
4295
4377
  ["-guide.md", "guide"],
@@ -4298,7 +4380,7 @@ var KIND_BY_SUFFIX = [
4298
4380
  ];
4299
4381
  function listMarkdownFiles(dir) {
4300
4382
  return readdirSync3(dir).flatMap((entry) => {
4301
- const entryPath = path37.join(dir, entry);
4383
+ const entryPath = path38.join(dir, entry);
4302
4384
  if (statSync4(entryPath).isDirectory()) {
4303
4385
  return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
4304
4386
  }
@@ -4316,11 +4398,11 @@ function getProjectDocs(docsRoot) {
4316
4398
  if (cached) return cached;
4317
4399
  const files = listMarkdownFiles(docsRoot);
4318
4400
  const docs = files.sort((a, b) => a.localeCompare(b)).map((filePath) => {
4319
- const fileName = path37.basename(filePath);
4401
+ const fileName = path38.basename(filePath);
4320
4402
  const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
4321
4403
  return {
4322
4404
  filePath,
4323
- doc: path37.relative(docsRoot, filePath).split(path37.sep).join("/"),
4405
+ doc: path38.relative(docsRoot, filePath).split(path38.sep).join("/"),
4324
4406
  fileName,
4325
4407
  kind,
4326
4408
  title: extractTitle(filePath)
@@ -4330,12 +4412,12 @@ function getProjectDocs(docsRoot) {
4330
4412
  return docs;
4331
4413
  }
4332
4414
  function findDocsRoot(filePath) {
4333
- let dir = path37.dirname(filePath);
4415
+ let dir = path38.dirname(filePath);
4334
4416
  for (; ; ) {
4335
- if (path37.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
4417
+ if (path38.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
4336
4418
  return dir;
4337
4419
  }
4338
- const parent = path37.dirname(dir);
4420
+ const parent = path38.dirname(dir);
4339
4421
  if (parent === dir) return void 0;
4340
4422
  dir = parent;
4341
4423
  }
@@ -4359,13 +4441,13 @@ var guideFolderEntryPointRule = {
4359
4441
  if (!docsRoot) return;
4360
4442
  const docs = getProjectDocs(docsRoot);
4361
4443
  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))
4444
+ 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
4445
  );
4364
- const currentDir = path38.dirname(filename);
4446
+ const currentDir = path39.dirname(filename);
4365
4447
  if (guideFolders.has(currentDir)) {
4366
- const expectedEntryPoint = `${path38.basename(currentDir)}.md`;
4448
+ const expectedEntryPoint = `${path39.basename(currentDir)}.md`;
4367
4449
  const hasEntryPoint = docs.some(
4368
- (doc) => doc.kind === "guide" && path38.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
4450
+ (doc) => doc.kind === "guide" && path39.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
4369
4451
  );
4370
4452
  if (!hasEntryPoint) {
4371
4453
  context.report({
@@ -4590,7 +4672,7 @@ var noNestedHowToRule = {
4590
4672
 
4591
4673
  // eslint/rules/documentation/glossary-term-linking.ts
4592
4674
  import { readFileSync as readFileSync6 } from "fs";
4593
- import path39 from "path";
4675
+ import path40 from "path";
4594
4676
  function extractGlossaryTerms(filePath) {
4595
4677
  const content = readFileSync6(filePath, "utf8");
4596
4678
  const terms = [];
@@ -4644,9 +4726,9 @@ var glossaryTermLinkingRule = {
4644
4726
  const docsRoot = findDocsRoot(filename);
4645
4727
  if (!docsRoot) return;
4646
4728
  const docs = getProjectDocs(docsRoot);
4647
- const guideDir = path39.dirname(filename);
4729
+ const guideDir = path40.dirname(filename);
4648
4730
  const guideReferences = docs.filter(
4649
- (doc) => doc.kind === "reference" && path39.dirname(doc.filePath) === guideDir
4731
+ (doc) => doc.kind === "reference" && path40.dirname(doc.filePath) === guideDir
4650
4732
  );
4651
4733
  if (guideReferences.length === 0) return;
4652
4734
  const glossaryTerms = [];
@@ -4671,7 +4753,7 @@ var glossaryTermLinkingRule = {
4671
4753
 
4672
4754
  // eslint/rules/documentation/guide-mentions-documents.ts
4673
4755
  import { existsSync } from "fs";
4674
- import path40 from "path";
4756
+ import path41 from "path";
4675
4757
  function visitSteps3(node, check) {
4676
4758
  if (node.type === "list" && node.ordered) {
4677
4759
  for (const child of node.children) check(child);
@@ -4704,12 +4786,12 @@ var guideMentionsDocumentsRule = {
4704
4786
  if (!filename.endsWith("-guide.md")) return;
4705
4787
  const docsRoot = findDocsRoot(filename);
4706
4788
  if (!docsRoot) return;
4707
- const guideDir = path40.dirname(filename);
4708
- if (path40.basename(filename, ".md") !== path40.basename(guideDir)) return;
4789
+ const guideDir = path41.dirname(filename);
4790
+ if (path41.basename(filename, ".md") !== path41.basename(guideDir)) return;
4709
4791
  const docs = getProjectDocs(docsRoot);
4710
4792
  const owned = docs.filter((doc) => {
4711
- const parent = path40.dirname(doc.filePath);
4712
- return parent === path40.join(guideDir, "rules") || parent === path40.join(guideDir, "references");
4793
+ const parent = path41.dirname(doc.filePath);
4794
+ return parent === path41.join(guideDir, "rules") || parent === path41.join(guideDir, "references");
4713
4795
  });
4714
4796
  const allLinks = [];
4715
4797
  collectMarkdownLinks(node, allLinks);
@@ -4736,7 +4818,7 @@ var guideMentionsDocumentsRule = {
4736
4818
  for (const link of allLinks) {
4737
4819
  const target = linkTarget(link.url);
4738
4820
  if (!target.endsWith(".md")) continue;
4739
- const resolved = path40.normalize(path40.join(guideDir, target));
4821
+ const resolved = path41.normalize(path41.join(guideDir, target));
4740
4822
  if (!existsSync(resolved)) {
4741
4823
  context.report({
4742
4824
  node: link,
@@ -5177,32 +5259,170 @@ var themeVariableNamespaceRule = {
5177
5259
  }
5178
5260
  };
5179
5261
 
5180
- // eslint/rules/tailwind/global-css-location.ts
5181
- var globalCssLocationRule = {
5262
+ // eslint/rules/tailwind/css-entry-point.ts
5263
+ import { statSync as statSync6 } from "fs";
5264
+ import path44 from "path";
5265
+
5266
+ // eslint/rules/tailwind/source-files.ts
5267
+ import { readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
5268
+ import path42 from "path";
5269
+ var CSS_EXTENSIONS = [".css"];
5270
+ var MODULE_EXTENSIONS3 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
5271
+ var SOURCE_EXTENSIONS = [...MODULE_EXTENSIONS3, ...CSS_EXTENSIONS];
5272
+ function findFiles(dir, extensions) {
5273
+ let entries;
5274
+ try {
5275
+ entries = readdirSync4(dir);
5276
+ } catch {
5277
+ return [];
5278
+ }
5279
+ return entries.flatMap((entry) => {
5280
+ if (entry.startsWith(".") || entry === "node_modules") return [];
5281
+ const entryPath = path42.join(dir, entry);
5282
+ let stats;
5283
+ try {
5284
+ stats = statSync5(entryPath);
5285
+ } catch {
5286
+ return [];
5287
+ }
5288
+ if (stats.isDirectory()) return findFiles(entryPath, extensions);
5289
+ return extensions.includes(path42.extname(entry)) ? [entryPath] : [];
5290
+ });
5291
+ }
5292
+ function cachedTextReader() {
5293
+ const texts = /* @__PURE__ */ new Map();
5294
+ return (file) => {
5295
+ let text = texts.get(file);
5296
+ if (text === void 0) {
5297
+ try {
5298
+ text = readFileSync7(file, "utf8");
5299
+ } catch {
5300
+ text = "";
5301
+ }
5302
+ texts.set(file, text);
5303
+ }
5304
+ return text;
5305
+ };
5306
+ }
5307
+ function escapeRegExp(text) {
5308
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5309
+ }
5310
+
5311
+ // eslint/rules/tailwind/stylesheet-graph.ts
5312
+ import path43 from "path";
5313
+ function registersTailwind(text) {
5314
+ return /@import\s+(?:url\(\s*)?["']tailwindcss["']\s*\)?/i.test(text);
5315
+ }
5316
+ function importedSpecifiers(text) {
5317
+ return [...text.matchAll(/@import\s+(?:url\(\s*)?["'](?<spec>[^"']+)["']/gi)].map(
5318
+ (match) => match.groups?.spec ?? ""
5319
+ );
5320
+ }
5321
+ function moduleImports(text, fileName) {
5322
+ const escaped = fileName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5323
+ return new RegExp(`(?:import|require)\\s*\\(?\\s*["'][^"']*${escaped}["']`, "i").test(text);
5324
+ }
5325
+ function resolveSpecifier2(fromFile, spec, sourceRoot) {
5326
+ if (spec.startsWith("/")) return path43.resolve(spec);
5327
+ if (spec.startsWith("./") || spec.startsWith("../")) return path43.resolve(path43.dirname(fromFile), spec);
5328
+ if (spec.startsWith("@/")) return path43.resolve(sourceRoot, spec.slice(2));
5329
+ return void 0;
5330
+ }
5331
+ function buildStylesheetGraph(options) {
5332
+ const { cssFiles, sourceRoot, textOf } = options;
5333
+ const cssSet = new Set(cssFiles.map((file) => path43.normalize(file)));
5334
+ const globals = cssFiles.filter((file) => registersTailwind(textOf(file)));
5335
+ const reachable = /* @__PURE__ */ new Set();
5336
+ const queue = [...globals];
5337
+ for (const global of globals) reachable.add(path43.normalize(global));
5338
+ while (queue.length > 0) {
5339
+ const from = queue.shift();
5340
+ if (!from) continue;
5341
+ for (const spec of importedSpecifiers(textOf(from))) {
5342
+ const target = resolveSpecifier2(from, spec, sourceRoot);
5343
+ if (!target) continue;
5344
+ const normalized = path43.normalize(target);
5345
+ if (cssSet.has(normalized) && !reachable.has(normalized)) {
5346
+ reachable.add(normalized);
5347
+ queue.push(normalized);
5348
+ }
5349
+ }
5350
+ }
5351
+ const directChildren = /* @__PURE__ */ new Set();
5352
+ for (const global of globals) {
5353
+ for (const spec of importedSpecifiers(textOf(global))) {
5354
+ const target = resolveSpecifier2(global, spec, sourceRoot);
5355
+ if (!target) continue;
5356
+ const normalized = path43.normalize(target);
5357
+ if (cssSet.has(normalized)) directChildren.add(normalized);
5358
+ }
5359
+ }
5360
+ return { globals, reachable, directChildren };
5361
+ }
5362
+
5363
+ // eslint/rules/tailwind/css-entry-point.ts
5364
+ var cssEntryPointRule = {
5182
5365
  meta: {
5183
5366
  schema: [],
5184
5367
  type: "problem",
5185
5368
  docs: {
5186
- description: "Require project global CSS to live only in the entry point stylesheet."
5369
+ description: "Require one global entry point; project CSS only in a stylesheet it imports directly."
5187
5370
  }
5188
5371
  },
5189
5372
  create(context) {
5373
+ const sourceRoot = sourceRootOf(context);
5374
+ let cssFiles;
5375
+ let moduleFiles;
5376
+ try {
5377
+ if (!statSync6(sourceRoot).isDirectory()) return {};
5378
+ cssFiles = findFiles(sourceRoot, CSS_EXTENSIONS);
5379
+ moduleFiles = findFiles(sourceRoot, MODULE_EXTENSIONS3);
5380
+ } catch {
5381
+ return {};
5382
+ }
5383
+ const textOf = cachedTextReader();
5384
+ const graph = buildStylesheetGraph({ cssFiles, sourceRoot, textOf });
5385
+ const { globals, reachable, directChildren } = graph;
5190
5386
  return {
5191
5387
  "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;
5388
+ if (globals.length === 0) return;
5389
+ const current = path44.normalize(path44.resolve(context.filename));
5390
+ if (globals.includes(current)) {
5391
+ if (globals.length > 1) {
5392
+ context.report({
5393
+ node,
5394
+ message: "Only one stylesheet may register Tailwind as the global entry point."
5395
+ });
5396
+ return;
5397
+ }
5398
+ const basename = path44.basename(current);
5399
+ const importCount = moduleFiles.filter((modulePath) => moduleImports(textOf(modulePath), basename)).length;
5400
+ if (importCount !== 1) {
5401
+ context.report({
5402
+ node,
5403
+ 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).`
5404
+ });
5405
+ }
5406
+ return;
5407
+ }
5196
5408
  const hasProjectCss = node.children.some((child) => {
5197
5409
  if (child.type === "Atrule" && child.name === "import") return false;
5198
5410
  if (child.type === "Comment") return false;
5199
5411
  return true;
5200
5412
  });
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
- });
5413
+ if (hasProjectCss && !directChildren.has(current)) {
5414
+ context.report({
5415
+ node,
5416
+ 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."
5417
+ });
5418
+ return;
5419
+ }
5420
+ if (!reachable.has(current)) {
5421
+ context.report({
5422
+ node,
5423
+ message: "This stylesheet must be imported by the global stylesheet entry point via @import, so CSS arrives through one door."
5424
+ });
5425
+ }
5206
5426
  }
5207
5427
  };
5208
5428
  }
@@ -5220,10 +5440,10 @@ var globalStylesheetRule = {
5220
5440
  create(context) {
5221
5441
  return {
5222
5442
  "StyleSheet:exit"(node) {
5223
- const registersTailwind = node.children.some(
5443
+ const registersTailwind2 = node.children.some(
5224
5444
  (child) => child.type === "Atrule" && child.name === "import" && JSON.stringify(child.prelude).includes("tailwindcss")
5225
5445
  );
5226
- if (!registersTailwind) {
5446
+ if (!registersTailwind2) {
5227
5447
  const hasProjectCss = node.children.some((child) => {
5228
5448
  if (child.type === "Atrule" && child.name === "import") return false;
5229
5449
  if (child.type === "Comment") return false;
@@ -5242,32 +5462,9 @@ var globalStylesheetRule = {
5242
5462
  };
5243
5463
 
5244
5464
  // 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
- }
5465
+ import { statSync as statSync7 } from "fs";
5268
5466
  function usagePattern(name) {
5269
- const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5270
- return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`);
5467
+ return new RegExp(`(?<![\\w-])${escapeRegExp(name)}(?![\\w-])`);
5271
5468
  }
5272
5469
  var unusedUtilityRule = {
5273
5470
  meta: {
@@ -5278,27 +5475,15 @@ var unusedUtilityRule = {
5278
5475
  }
5279
5476
  },
5280
5477
  create(context) {
5281
- const sourceRoot2 = path41.resolve("src");
5478
+ const sourceRoot = sourceRootOf(context);
5282
5479
  let files;
5283
5480
  try {
5284
- if (!statSync5(sourceRoot2).isDirectory()) return {};
5285
- files = sourceFiles(sourceRoot2);
5481
+ if (!statSync7(sourceRoot).isDirectory()) return {};
5482
+ files = findFiles(sourceRoot, SOURCE_EXTENSIONS);
5286
5483
  } catch {
5287
5484
  return {};
5288
5485
  }
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
- };
5486
+ const textOf = cachedTextReader();
5302
5487
  return {
5303
5488
  "StyleSheet:exit"(node) {
5304
5489
  for (const utility of atrulesNamed(node, "utility")) {
@@ -5329,7 +5514,7 @@ var tailwindRules = {
5329
5514
  "custom-utility-apply": customUtilityApplyRule,
5330
5515
  "surface-utility": surfaceUtilityRule,
5331
5516
  "theme-variable-namespace": themeVariableNamespaceRule,
5332
- "global-css-location": globalCssLocationRule,
5517
+ "css-entry-point": cssEntryPointRule,
5333
5518
  "global-stylesheet": globalStylesheetRule,
5334
5519
  "unused-utility": unusedUtilityRule
5335
5520
  };
@@ -5473,13 +5658,13 @@ var repoPackageJsonRules = {
5473
5658
  "no-vulyk-dependency": noVulykDependencyRule,
5474
5659
  "exact-version": exactVersionRule
5475
5660
  };
5476
- var nextPackageJsonRules = {
5661
+ var nextjsPackageJsonRules = {
5477
5662
  "nextjs-stack": nextjsStackRule
5478
5663
  };
5479
5664
 
5480
5665
  // eslint/rules/husky/husky-hook.ts
5481
5666
  import { existsSync as existsSync2, readFileSync as readFileSync8 } from "fs";
5482
- import path42 from "path";
5667
+ import path45 from "path";
5483
5668
  function memberName4(member) {
5484
5669
  return member.name.type === "String" ? member.name.value : member.name.name;
5485
5670
  }
@@ -5498,7 +5683,7 @@ var huskyHookRule = {
5498
5683
  if (root.type !== "Object") return;
5499
5684
  const scriptName = context.filename;
5500
5685
  if (!scriptName.endsWith("package.json")) return;
5501
- const hookPath = path42.join(process.cwd(), ".husky", "pre-commit");
5686
+ const hookPath = path45.join(context.cwd, ".husky", "pre-commit");
5502
5687
  if (!existsSync2(hookPath)) {
5503
5688
  context.report({
5504
5689
  node,
@@ -5538,7 +5723,7 @@ var huskyRules = {
5538
5723
 
5539
5724
  // eslint/rules/vulyk/vulyk-docs.ts
5540
5725
  import { existsSync as existsSync3, readFileSync as readFileSync9 } from "fs";
5541
- import path43 from "path";
5726
+ import path46 from "path";
5542
5727
  var PASIKA_REPO = "Bredansky/pasika";
5543
5728
  var vulykDocsRule = {
5544
5729
  meta: {
@@ -5552,8 +5737,8 @@ var vulykDocsRule = {
5552
5737
  return {
5553
5738
  Document(node) {
5554
5739
  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");
5740
+ const projectRoot = path46.dirname(path46.resolve(context.filename));
5741
+ const configPath = path46.join(projectRoot, "vulyk.config.ts");
5557
5742
  if (!existsSync3(configPath)) {
5558
5743
  context.report({
5559
5744
  node,
@@ -5568,7 +5753,7 @@ var vulykDocsRule = {
5568
5753
  message: "vulyk.config.ts must track the framework's docs from the pasika repository."
5569
5754
  });
5570
5755
  }
5571
- const agentsPath = path43.join(projectRoot, "AGENTS.md");
5756
+ const agentsPath = path46.join(projectRoot, "AGENTS.md");
5572
5757
  if (!existsSync3(agentsPath)) {
5573
5758
  context.report({
5574
5759
  node,
@@ -5586,7 +5771,8 @@ var vulykRules = {
5586
5771
  };
5587
5772
 
5588
5773
  // eslint/index.ts
5589
- var typescriptAppRules = {
5774
+ var nextjsAppRules = {
5775
+ // Framework-agnostic TypeScript rules.
5590
5776
  "filename-case": filenameCaseRule,
5591
5777
  "import-boundaries": importBoundariesRule,
5592
5778
  "named-exports": namedExportsRule,
@@ -5601,9 +5787,8 @@ var typescriptAppRules = {
5601
5787
  "type-extraction": typeExtractionRule,
5602
5788
  "zod-schema-validation": zodSchemaValidationRule,
5603
5789
  "source-under-src": sourceUnderSrcRule,
5604
- "zirka-baseline": zirkaBaselineRule
5605
- };
5606
- var nextjsAppRules = {
5790
+ "zirka-baseline": zirkaBaselineRule,
5791
+ // Next.js/React application rules.
5607
5792
  "component-placement": componentPlacementRule,
5608
5793
  "application-structure": applicationStructureRule,
5609
5794
  "data-testid-case": dataTestIdCaseRule,
@@ -5632,58 +5817,56 @@ var nextjsAppRules = {
5632
5817
  "shared-style-dedup": sharedStyleDedupRule,
5633
5818
  "repeated-structure": repeatedStructureRule
5634
5819
  };
5635
- var pasikaRules = {
5636
- ...typescriptAppRules,
5637
- ...nextjsAppRules
5638
- };
5639
5820
  var pasikaPlugin = {
5640
5821
  rules: {
5641
- ...pasikaRules,
5822
+ // The shared plugin must register every rule the preset blocks reference,
5823
+ // so all rule sets live here.
5824
+ ...nextjsAppRules,
5642
5825
  ...documentationRules,
5643
5826
  ...tailwindRules,
5644
5827
  ...repoPackageJsonRules,
5645
- ...nextPackageJsonRules,
5828
+ ...nextjsPackageJsonRules,
5646
5829
  ...huskyRules,
5647
5830
  ...vulykRules
5648
5831
  }
5649
5832
  };
5650
- var jsonLanguagePlugin = { languages: { json: jsonPlugin.languages.json } };
5833
+ var jsonLanguage = { languages: { json: jsonPlugin.languages.json } };
5651
5834
  function ruleIds(rules2) {
5652
5835
  return Object.keys(rules2).map((name) => `pasika/${name}`);
5653
5836
  }
5654
- var typescriptAppRuleIds = ruleIds(typescriptAppRules);
5655
5837
  var nextjsAppRuleIds = ruleIds(nextjsAppRules);
5656
- var pasikaRuleIds = ruleIds(pasikaRules);
5657
5838
  var documentationRuleIds = ruleIds(documentationRules);
5658
5839
  var tailwindRuleIds = ruleIds(tailwindRules);
5659
5840
  var repoPackageJsonRuleIds = ruleIds(repoPackageJsonRules);
5660
- var nextPackageJsonRuleIds = ruleIds(nextPackageJsonRules);
5841
+ var nextjsPackageJsonRuleIds = ruleIds(nextjsPackageJsonRules);
5661
5842
  var huskyRuleIds = ruleIds(huskyRules);
5662
5843
  var vulykRuleIds = ruleIds(vulykRules);
5663
5844
  var allPasikaRuleIds = [
5664
- ...pasikaRuleIds,
5845
+ ...nextjsAppRuleIds,
5665
5846
  ...documentationRuleIds,
5666
5847
  ...tailwindRuleIds,
5667
5848
  ...repoPackageJsonRuleIds,
5668
- ...nextPackageJsonRuleIds,
5849
+ ...nextjsPackageJsonRuleIds,
5669
5850
  ...huskyRuleIds,
5670
5851
  ...vulykRuleIds
5671
5852
  ];
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"]))
5853
+ var typescriptAppLanguageOptions = {
5854
+ parser: tsParser,
5855
+ parserOptions: {
5856
+ ecmaVersion: "latest",
5857
+ sourceType: "module",
5858
+ ecmaFeatures: { jsx: true }
5859
+ }
5678
5860
  };
5679
- var nextjsAppBlock = {
5861
+ var nextjsAppConfig = {
5680
5862
  files: ["src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}"],
5863
+ languageOptions: typescriptAppLanguageOptions,
5681
5864
  plugins: {
5682
5865
  pasika: pasikaPlugin
5683
5866
  },
5684
5867
  rules: Object.fromEntries(nextjsAppRuleIds.map((id) => [id, "error"]))
5685
5868
  };
5686
- var tailwindGlobalsBlock = {
5869
+ var tailwindStructureRules = {
5687
5870
  files: ["src/**/globals.css"],
5688
5871
  plugins: {
5689
5872
  css,
@@ -5692,10 +5875,10 @@ var tailwindGlobalsBlock = {
5692
5875
  language: "css/css",
5693
5876
  languageOptions: { tolerant: true },
5694
5877
  rules: Object.fromEntries(
5695
- tailwindRuleIds.filter((id) => id !== "pasika/global-css-location").map((id) => [id, "error"])
5878
+ tailwindRuleIds.filter((id) => id !== "pasika/css-entry-point").map((id) => [id, "error"])
5696
5879
  )
5697
5880
  };
5698
- var tailwindAnyCssBlock = {
5881
+ var tailwindImportGraph = {
5699
5882
  files: ["src/**/*.css"],
5700
5883
  plugins: {
5701
5884
  css,
@@ -5703,32 +5886,32 @@ var tailwindAnyCssBlock = {
5703
5886
  },
5704
5887
  language: "css/css",
5705
5888
  languageOptions: { tolerant: true },
5706
- rules: { "pasika/global-css-location": "error" }
5889
+ rules: { "pasika/css-entry-point": "error" }
5707
5890
  };
5708
- var repoManifestBlock = {
5891
+ var typescriptAppPackageJsonConfig = {
5709
5892
  files: ["package.json"],
5710
5893
  plugins: {
5711
- json: jsonLanguagePlugin,
5894
+ json: jsonLanguage,
5712
5895
  pasika: pasikaPlugin
5713
5896
  },
5714
5897
  language: "json/json",
5715
5898
  rules: Object.fromEntries([...repoPackageJsonRuleIds, ...huskyRuleIds, ...vulykRuleIds].map((id) => [id, "error"]))
5716
5899
  };
5717
- var nextManifestBlock = {
5900
+ var nextjsAppPackageJsonConfig = {
5718
5901
  files: ["package.json"],
5719
5902
  plugins: {
5720
- json: jsonLanguagePlugin,
5903
+ json: jsonLanguage,
5721
5904
  pasika: pasikaPlugin
5722
5905
  },
5723
5906
  language: "json/json",
5724
- rules: Object.fromEntries(nextPackageJsonRuleIds.map((id) => [id, "error"]))
5907
+ rules: Object.fromEntries(nextjsPackageJsonRuleIds.map((id) => [id, "error"]))
5725
5908
  };
5726
- var zirkaBlock = {
5909
+ var zirkaConfig = {
5727
5910
  files: ["eslint.config.{ts,mts,cts,js,mjs,cjs}"],
5728
5911
  plugins: { pasika: pasikaPlugin },
5729
5912
  rules: { "pasika/zirka-baseline": "error" }
5730
5913
  };
5731
- var docsBlock = {
5914
+ var documentationConfig = {
5732
5915
  files: ["docs/**/*.md"],
5733
5916
  ignores: ["**/_*/**"],
5734
5917
  plugins: {
@@ -5738,22 +5921,25 @@ var docsBlock = {
5738
5921
  language: "markdown/gfm",
5739
5922
  rules: Object.fromEntries(documentationRuleIds.map((id) => [id, "error"]))
5740
5923
  };
5741
- var typescriptApp = [repoManifestBlock, zirkaBlock, typescriptAppBlock, docsBlock];
5924
+ var typescriptApp = [
5925
+ typescriptAppPackageJsonConfig,
5926
+ zirkaConfig,
5927
+ documentationConfig
5928
+ ];
5742
5929
  var nextjsApp = [
5743
5930
  ...typescriptApp,
5744
- nextManifestBlock,
5745
- nextjsAppBlock,
5746
- tailwindGlobalsBlock,
5747
- tailwindAnyCssBlock
5931
+ nextjsAppPackageJsonConfig,
5932
+ nextjsAppConfig,
5933
+ tailwindStructureRules,
5934
+ tailwindImportGraph
5748
5935
  ];
5749
5936
  export {
5750
5937
  allPasikaRuleIds,
5751
5938
  documentationRules,
5752
5939
  huskyRules,
5753
- nextPackageJsonRules,
5754
5940
  nextjsApp,
5941
+ nextjsPackageJsonRules,
5755
5942
  pasikaPlugin,
5756
- pasikaRules,
5757
5943
  repoPackageJsonRules,
5758
5944
  tailwindRules,
5759
5945
  typescriptApp,