pasika 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1079,13 +1079,13 @@ function classifyFunction(name, isTsx, hasJsx) {
1079
1079
  return "function";
1080
1080
  }
1081
1081
  function classifyValue(name, initializer, isTsx) {
1082
- const isFunctionLike = initializer !== void 0 && (ts2.isArrowFunction(initializer) || ts2.isFunctionExpression(initializer) || ts2.isFunctionDeclaration(initializer));
1082
+ const isFunctionLike2 = initializer !== void 0 && (ts2.isArrowFunction(initializer) || ts2.isFunctionExpression(initializer) || ts2.isFunctionDeclaration(initializer));
1083
1083
  if (isHookName(name)) return "hook";
1084
1084
  if (isSchemaName(name)) return "schema";
1085
- if (isTsx && isPascalCase3(name) && (initializer === void 0 || returnsJsx(initializer) || isFunctionLike)) {
1085
+ if (isTsx && isPascalCase3(name) && (initializer === void 0 || returnsJsx(initializer) || isFunctionLike2)) {
1086
1086
  return "component";
1087
1087
  }
1088
- if (isFunctionLike) return "function";
1088
+ if (isFunctionLike2) return "function";
1089
1089
  return "constant";
1090
1090
  }
1091
1091
  function parseModule(file) {
@@ -1121,7 +1121,11 @@ function parseModule(file) {
1121
1121
  }
1122
1122
  if (statement.exportClause && ts2.isNamedExports(statement.exportClause)) {
1123
1123
  for (const element of statement.exportClause.elements) {
1124
- exports.push({ name: element.name.text, kind: "other", line: lineOf(sourceFile, element) });
1124
+ exports.push({
1125
+ name: element.name.text,
1126
+ kind: "other",
1127
+ line: lineOf(sourceFile, element)
1128
+ });
1125
1129
  }
1126
1130
  }
1127
1131
  continue;
@@ -1149,7 +1153,11 @@ function parseModule(file) {
1149
1153
  continue;
1150
1154
  }
1151
1155
  if (ts2.isTypeAliasDeclaration(statement) || ts2.isInterfaceDeclaration(statement)) {
1152
- exports.push({ name: statement.name.text, kind: "type", line: lineOf(sourceFile, statement) });
1156
+ exports.push({
1157
+ name: statement.name.text,
1158
+ kind: "type",
1159
+ line: lineOf(sourceFile, statement)
1160
+ });
1153
1161
  }
1154
1162
  }
1155
1163
  return { file: path6.resolve(file), imports, exports };
@@ -1382,41 +1390,81 @@ var enforceCnMergeRule = {
1382
1390
  };
1383
1391
 
1384
1392
  // eslint/rules/cn-helper.ts
1385
- function isIdentifier(node, name) {
1393
+ import path9 from "path";
1394
+ var HELPER = "cn";
1395
+ var DOC = "docs/pasika-adoption-guide/rules/cn-helper-rule.md";
1396
+ var ESLINT_CONFIG = /^eslint\.config\.(?:cjs|cts|js|mjs|mts|ts)$/;
1397
+ function isNamed(node, name) {
1386
1398
  return node?.type === "Identifier" && node.name === name;
1387
1399
  }
1400
+ function isCallTo(node, callee) {
1401
+ return node?.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === callee;
1402
+ }
1403
+ function isComposition(node) {
1404
+ if (!isCallTo(node, "twMerge")) return false;
1405
+ const first = node.arguments[0];
1406
+ return first !== void 0 && first.type !== "SpreadElement" && isCallTo(first, "clsx");
1407
+ }
1408
+ function returnedExpression(body) {
1409
+ if (!body) return void 0;
1410
+ if (body.type !== "BlockStatement") return body;
1411
+ for (const statement of body.body) {
1412
+ if (statement.type === "ReturnStatement") return statement.argument ?? void 0;
1413
+ }
1414
+ return void 0;
1415
+ }
1416
+ function sourceRootFor(context) {
1417
+ return path9.join(path9.dirname(path9.resolve(context.filename)), "src");
1418
+ }
1419
+ function definesHelper(context, name) {
1420
+ const index = getProjectIndex(sourceRootFor(context));
1421
+ if (!index) return true;
1422
+ for (const parsed of index.modules.values()) {
1423
+ if (parsed.exports.some((exported) => exported.name === name)) return true;
1424
+ }
1425
+ return false;
1426
+ }
1388
1427
  var cnHelperRule = {
1389
1428
  meta: {
1390
1429
  schema: [],
1391
1430
  type: "problem",
1392
1431
  docs: {
1393
- description: "Require the cn helper to merge clsx and tailwind-merge."
1432
+ description: `Require a repository to define a ${HELPER} helper that returns twMerge(clsx(...)).`
1394
1433
  }
1395
1434
  },
1396
1435
  create(context) {
1436
+ if (ESLINT_CONFIG.test(path9.basename(context.filename))) {
1437
+ return {
1438
+ Program() {
1439
+ if (definesHelper(context, HELPER)) return;
1440
+ context.report({
1441
+ node: context.sourceCode.ast,
1442
+ loc: { line: 1, column: 0 },
1443
+ message: `A repository must define a ${HELPER} helper. See ${DOC}`
1444
+ });
1445
+ }
1446
+ };
1447
+ }
1448
+ const check = (body, reportNode) => {
1449
+ if (isComposition(returnedExpression(body))) return;
1450
+ context.report({
1451
+ node: reportNode,
1452
+ message: `${HELPER} must return twMerge(clsx(...)). See ${DOC}`
1453
+ });
1454
+ };
1397
1455
  return {
1398
1456
  FunctionDeclaration(node) {
1399
- if (isIdentifier(node.id, "cn")) checkBody(context, node.body);
1457
+ if (isNamed(node.id, HELPER)) check(node.body, node);
1400
1458
  },
1401
1459
  VariableDeclarator(node) {
1402
- if (node.init?.type === "ArrowFunctionExpression" && isIdentifier(node.id, "cn")) {
1403
- checkBody(context, node.init.body);
1404
- }
1460
+ if (!isNamed(node.id.type === "Identifier" ? node.id : null, HELPER)) return;
1461
+ const init = node.init;
1462
+ if (init?.type !== "ArrowFunctionExpression" && init?.type !== "FunctionExpression") return;
1463
+ check(init.body, node);
1405
1464
  }
1406
1465
  };
1407
1466
  }
1408
1467
  };
1409
- function checkBody(context, body) {
1410
- const source = context.sourceCode.getText(body);
1411
- const usesClsx = /\bclsx\b/.test(source);
1412
- const usesTwMerge = /\btwMerge\b|\btailwind-merge\b/.test(source);
1413
- if (!usesClsx || !usesTwMerge) {
1414
- context.report({
1415
- node: body,
1416
- message: "cn must be built from clsx and tailwind-merge (e.g. cn = twMerge(clsx(inputs)))."
1417
- });
1418
- }
1419
- }
1420
1468
 
1421
1469
  // eslint/rules/enforce-cva-variant-props.ts
1422
1470
  var enforceCvaVariantPropsRule = {
@@ -1482,7 +1530,7 @@ var enforceCvaVariantPropsRule = {
1482
1530
  };
1483
1531
 
1484
1532
  // eslint/rules/enforce-barrel-exports.ts
1485
- import path9 from "path";
1533
+ import path10 from "path";
1486
1534
  import fs from "fs";
1487
1535
  function isPascalCase4(str) {
1488
1536
  return /^[A-Z][A-Za-z0-9]*$/.test(str);
@@ -1492,7 +1540,7 @@ function isKebabCase2(str) {
1492
1540
  }
1493
1541
  var SUPPORT_FOLDERS = /* @__PURE__ */ new Set(["types", "schemas", "hooks", "constants", "utils", "config", "locales"]);
1494
1542
  function parentComponentName(dirPath, folderName) {
1495
- const componentFile = path9.join(dirPath, `${folderName}.tsx`);
1543
+ const componentFile = path10.join(dirPath, `${folderName}.tsx`);
1496
1544
  if (!fs.existsSync(componentFile)) return void 0;
1497
1545
  try {
1498
1546
  const exports = parseModule(componentFile).exports;
@@ -1512,11 +1560,11 @@ var enforceBarrelExportsRule = {
1512
1560
  create(context) {
1513
1561
  const filename = context.filename;
1514
1562
  if (!filename) return {};
1515
- const baseName = path9.basename(filename);
1563
+ const baseName = path10.basename(filename);
1516
1564
  if (baseName !== "index.ts" && baseName !== "index.cts" && baseName !== "index.mts") return {};
1517
- const dirPath = path9.dirname(filename);
1518
- const folderName = path9.basename(dirPath);
1519
- const parentFolderName = path9.basename(path9.dirname(dirPath));
1565
+ const dirPath = path10.dirname(filename);
1566
+ const folderName = path10.basename(dirPath);
1567
+ const parentFolderName = path10.basename(path10.dirname(dirPath));
1520
1568
  if (SUPPORT_FOLDERS.has(folderName)) return {};
1521
1569
  if (!isPascalCase4(folderName) && !isKebabCase2(folderName)) return {};
1522
1570
  const parentName = parentComponentName(dirPath, folderName);
@@ -1555,14 +1603,14 @@ var enforceBarrelExportsRule = {
1555
1603
 
1556
1604
  // eslint/rules/component-placement.ts
1557
1605
  import fs2 from "fs";
1558
- import path11 from "path";
1606
+ import path12 from "path";
1559
1607
 
1560
1608
  // eslint/project/ccf.ts
1561
- import path10 from "path";
1609
+ import path11 from "path";
1562
1610
  var SUPPORT_FOLDERS2 = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
1563
1611
  function segmentsOf(file, sourceRoot) {
1564
- const relative = path10.relative(sourceRoot, file);
1565
- return relative.startsWith("..") ? [] : relative.split(path10.sep);
1612
+ const relative = path11.relative(sourceRoot, file);
1613
+ return relative.startsWith("..") ? [] : relative.split(path11.sep);
1566
1614
  }
1567
1615
  function folderSegmentsOf(file, sourceRoot) {
1568
1616
  return segmentsOf(file, sourceRoot).slice(0, -1);
@@ -1641,7 +1689,7 @@ function resolveSupportPlacement(supportFile, supportFolder, index) {
1641
1689
  }
1642
1690
  function describeConsumers(consumers, sourceRoot) {
1643
1691
  const shown = 3;
1644
- const names = consumers.map((consumer) => path10.relative(path10.dirname(sourceRoot), consumer).split(path10.sep).join("/")).sort((left, right) => left.localeCompare(right));
1692
+ const names = consumers.map((consumer) => path11.relative(path11.dirname(sourceRoot), consumer).split(path11.sep).join("/")).sort((left, right) => left.localeCompare(right));
1645
1693
  if (names.length <= shown) return names.join(", ");
1646
1694
  return `${names.slice(0, shown).join(", ")} and ${String(names.length - shown)} more`;
1647
1695
  }
@@ -1658,7 +1706,7 @@ function isNestedInside(expectedFolder, currentFolder, componentFile) {
1658
1706
  if (!sameFolder(currentFolder.slice(0, -1), expectedFolder)) return false;
1659
1707
  const folderName = currentFolder[currentFolder.length - 1];
1660
1708
  if (!folderName) return false;
1661
- return fs2.existsSync(path11.join(path11.dirname(componentFile), `${folderName}.tsx`));
1709
+ return fs2.existsSync(path12.join(path12.dirname(componentFile), `${folderName}.tsx`));
1662
1710
  }
1663
1711
  var componentPlacementRule = {
1664
1712
  meta: {
@@ -1674,7 +1722,7 @@ var componentPlacementRule = {
1674
1722
  const sourceRoot = sourceRootOf(context);
1675
1723
  const index = getProjectIndex(sourceRoot);
1676
1724
  if (!index) return {};
1677
- const componentFile = path11.resolve(filename);
1725
+ const componentFile = path12.resolve(filename);
1678
1726
  const segments = segmentsOf(componentFile, sourceRoot);
1679
1727
  if (segments.length === 0) return {};
1680
1728
  if (isUnderApp(segments) || isConfigModule(segments)) return {};
@@ -1708,7 +1756,7 @@ var componentPlacementRule = {
1708
1756
  };
1709
1757
 
1710
1758
  // eslint/rules/support-file-placement.ts
1711
- import path12 from "path";
1759
+ import path13 from "path";
1712
1760
  var CONFIG_OWNED_FOLDERS = /* @__PURE__ */ new Set(["types", "constants"]);
1713
1761
  var REASON_TEXT2 = {
1714
1762
  "app-consumer": "a file under src/app/ imports it, so it belongs to the app-wide support folder",
@@ -1728,7 +1776,7 @@ var supportFilePlacementRule = {
1728
1776
  },
1729
1777
  create(context) {
1730
1778
  const sourceRoot = sourceRootOf(context);
1731
- const supportFile = path12.resolve(context.filename);
1779
+ const supportFile = path13.resolve(context.filename);
1732
1780
  const currentFolder = folderSegmentsOf(supportFile, sourceRoot);
1733
1781
  const supportFolder = currentFolder[currentFolder.length - 1];
1734
1782
  if (supportFolder === void 0 || !SUPPORT_FOLDERS2.has(supportFolder)) return {};
@@ -1753,7 +1801,7 @@ var supportFilePlacementRule = {
1753
1801
 
1754
1802
  // eslint/rules/application-structure.ts
1755
1803
  import fs3 from "fs";
1756
- import path13 from "path";
1804
+ import path14 from "path";
1757
1805
  var MODULE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
1758
1806
  var ROUTING_FILES = /* @__PURE__ */ new Set([
1759
1807
  "default",
@@ -1791,7 +1839,7 @@ function report2(context, message) {
1791
1839
  };
1792
1840
  }
1793
1841
  function isCodeFile(filename) {
1794
- return MODULE_EXTENSIONS2.has(path13.extname(filename));
1842
+ return MODULE_EXTENSIONS2.has(path14.extname(filename));
1795
1843
  }
1796
1844
  function isRootSupportFolder(folder) {
1797
1845
  return SUPPORT_FOLDERS2.has(folder);
@@ -1812,7 +1860,7 @@ function expectedSupportFolder(kinds) {
1812
1860
  function configModuleRoot(filename, sourceRoot) {
1813
1861
  const segments = segmentsOf(filename, sourceRoot);
1814
1862
  if (segments[0] !== "config" || segments.length < 3) return void 0;
1815
- return path13.join(sourceRoot, "config", segments[1] ?? "");
1863
+ return path14.join(sourceRoot, "config", segments[1] ?? "");
1816
1864
  }
1817
1865
  function componentFolderStart(segments) {
1818
1866
  if (segments[0] === "features") return 2;
@@ -1825,12 +1873,12 @@ function componentFolderViolation(segments, sourceRoot) {
1825
1873
  for (let depth = segments.length - 2; depth >= start; depth -= 1) {
1826
1874
  const folder = segments[depth];
1827
1875
  if (!folder || SUPPORT_FOLDERS2.has(folder)) continue;
1828
- const folderPath = path13.join(sourceRoot, ...segments.slice(0, depth + 1));
1876
+ const folderPath = path14.join(sourceRoot, ...segments.slice(0, depth + 1));
1829
1877
  const label = `src/${segments.slice(0, depth + 1).join("/")}/`;
1830
- if (!fs3.existsSync(path13.join(folderPath, `${folder}.tsx`))) {
1878
+ if (!fs3.existsSync(path14.join(folderPath, `${folder}.tsx`))) {
1831
1879
  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.`;
1832
1880
  }
1833
- if (!fs3.existsSync(path13.join(folderPath, "index.ts"))) {
1881
+ if (!fs3.existsSync(path14.join(folderPath, "index.ts"))) {
1834
1882
  return `A component folder must have an index.ts that named-re-exports its component; add index.ts to ${label}.`;
1835
1883
  }
1836
1884
  }
@@ -1845,7 +1893,7 @@ var applicationStructureRule = {
1845
1893
  }
1846
1894
  },
1847
1895
  create(context) {
1848
- const filename = path13.resolve(context.filename);
1896
+ const filename = path14.resolve(context.filename);
1849
1897
  const sourceRoot = sourceRootOf(context);
1850
1898
  const segments = segmentsOf(filename, sourceRoot);
1851
1899
  if (segments.length === 0) return {};
@@ -1876,43 +1924,43 @@ var applicationStructureRule = {
1876
1924
  );
1877
1925
  }
1878
1926
  const moduleRoot = configModuleRoot(filename, sourceRoot);
1879
- if (moduleRoot && !fs3.existsSync(path13.join(moduleRoot, "index.ts"))) {
1927
+ if (moduleRoot && !fs3.existsSync(path14.join(moduleRoot, "index.ts"))) {
1880
1928
  return report2(
1881
1929
  context,
1882
- `Add src/config/${path13.basename(moduleRoot)}/index.ts as the configuration module entry point.`
1930
+ `Add src/config/${path14.basename(moduleRoot)}/index.ts as the configuration module entry point.`
1883
1931
  );
1884
1932
  }
1885
- if (moduleRoot && segments.length === 3 && path13.basename(filename) !== "index.ts") {
1933
+ if (moduleRoot && segments.length === 3 && path14.basename(filename) !== "index.ts") {
1886
1934
  const kinds2 = exportedKinds(filename);
1887
1935
  const expected2 = expectedSupportFolder(kinds2);
1888
1936
  if (expected2 !== void 0) {
1889
1937
  return report2(
1890
1938
  context,
1891
- `Move this configuration support file into src/config/${path13.basename(moduleRoot)}/${expected2}/.`
1939
+ `Move this configuration support file into src/config/${path14.basename(moduleRoot)}/${expected2}/.`
1892
1940
  );
1893
1941
  }
1894
1942
  }
1895
1943
  }
1896
1944
  if (topLevel === "app" && isCodeFile(filename)) {
1897
- const basename = path13.basename(filename, path13.extname(filename));
1898
- const currentFolder2 = path13.basename(path13.dirname(filename));
1945
+ const basename = path14.basename(filename, path14.extname(filename));
1946
+ const currentFolder2 = path14.basename(path14.dirname(filename));
1899
1947
  if (SUPPORT_FOLDERS2.has(currentFolder2)) {
1900
1948
  return report2(
1901
1949
  context,
1902
1950
  "src/app/ may contain routing files and framework assets, but ordinary components and support files must live outside src/app/."
1903
1951
  );
1904
1952
  }
1905
- if (!ROUTING_FILES.has(basename) && path13.extname(filename) !== ".css") {
1953
+ if (!ROUTING_FILES.has(basename) && path14.extname(filename) !== ".css") {
1906
1954
  return report2(
1907
1955
  context,
1908
1956
  "src/app/ may contain routing files and framework assets, but ordinary components and support files must live outside src/app/."
1909
1957
  );
1910
1958
  }
1911
- if (ROUTING_FILES.has(basename) || path13.extname(filename) === ".css") return {};
1959
+ if (ROUTING_FILES.has(basename) || path14.extname(filename) === ".css") return {};
1912
1960
  }
1913
- const currentFolder = path13.basename(path13.dirname(filename));
1961
+ const currentFolder = path14.basename(path14.dirname(filename));
1914
1962
  const kinds = exportedKinds(filename);
1915
- const isConfigModuleRoot = topLevel === "config" && segments.length === 3 && path13.basename(filename) === "index.ts";
1963
+ const isConfigModuleRoot = topLevel === "config" && segments.length === 3 && path14.basename(filename) === "index.ts";
1916
1964
  if (isConfigModuleRoot) return {};
1917
1965
  if (!SUPPORT_FOLDERS2.has(currentFolder)) {
1918
1966
  const expected2 = expectedSupportFolder(kinds);
@@ -1929,7 +1977,7 @@ var applicationStructureRule = {
1929
1977
  if (kinds.has("component")) {
1930
1978
  return report2(
1931
1979
  context,
1932
- `A support folder must not contain a component; move ${path13.basename(filename)} beside ${currentFolder}/.`
1980
+ `A support folder must not contain a component; move ${path14.basename(filename)} beside ${currentFolder}/.`
1933
1981
  );
1934
1982
  }
1935
1983
  const expected = expectedSupportFolder(kinds);
@@ -1946,7 +1994,7 @@ var applicationStructureRule = {
1946
1994
  };
1947
1995
 
1948
1996
  // eslint/rules/named-exports.ts
1949
- import path14 from "path";
1997
+ import path15 from "path";
1950
1998
  var FRAMEWORK_DEFAULT_EXPORT_FILES = /* @__PURE__ */ new Set([
1951
1999
  // App Router routing files
1952
2000
  "default",
@@ -1968,9 +2016,9 @@ var FRAMEWORK_DEFAULT_EXPORT_FILES = /* @__PURE__ */ new Set([
1968
2016
  "twitter-image"
1969
2017
  ]);
1970
2018
  function isFrameworkDefaultExportFile(filename) {
1971
- const normalized = filename.replaceAll(path14.sep, "/");
2019
+ const normalized = filename.replaceAll(path15.sep, "/");
1972
2020
  if (!normalized.includes("/src/app/")) return false;
1973
- const basename = path14.basename(filename, path14.extname(filename));
2021
+ const basename = path15.basename(filename, path15.extname(filename));
1974
2022
  return FRAMEWORK_DEFAULT_EXPORT_FILES.has(basename);
1975
2023
  }
1976
2024
  var namedExportsRule = {
@@ -1995,7 +2043,7 @@ var namedExportsRule = {
1995
2043
  };
1996
2044
 
1997
2045
  // eslint/rules/data-testid-case.ts
1998
- import path15 from "path";
2046
+ import path16 from "path";
1999
2047
  var NEXT_ROUTING_FILES2 = /* @__PURE__ */ new Set([
2000
2048
  "default",
2001
2049
  "error",
@@ -2028,9 +2076,9 @@ var dataTestIdCaseRule = {
2028
2076
  }
2029
2077
  },
2030
2078
  create(context) {
2031
- const filename = path15.resolve(context.filename);
2079
+ const filename = path16.resolve(context.filename);
2032
2080
  if (!filename.endsWith(".tsx")) return {};
2033
- const base = path15.basename(filename, path15.extname(filename));
2081
+ const base = path16.basename(filename, path16.extname(filename));
2034
2082
  if (NEXT_ROUTING_FILES2.has(base)) return {};
2035
2083
  const text = context.sourceCode.text;
2036
2084
  const components = parseComponentInfo(text, filename);
@@ -2072,7 +2120,7 @@ function toKebabCase(value) {
2072
2120
 
2073
2121
  // eslint/rules/support-folder-shape.ts
2074
2122
  import fs4 from "fs";
2075
- import path16 from "path";
2123
+ import path17 from "path";
2076
2124
  var SUPPORT_FOLDERS3 = /* @__PURE__ */ new Set(["constants", "types", "schemas"]);
2077
2125
  var INDEX_NAMES = /* @__PURE__ */ new Set(["index.ts", "index.tsx", "index.mts", "index.cts"]);
2078
2126
  var supportFolderShapeRule = {
@@ -2084,12 +2132,12 @@ var supportFolderShapeRule = {
2084
2132
  }
2085
2133
  },
2086
2134
  create(context) {
2087
- const filename = path16.resolve(context.filename);
2088
- const baseName = path16.basename(filename);
2135
+ const filename = path17.resolve(context.filename);
2136
+ const baseName = path17.basename(filename);
2089
2137
  if (!INDEX_NAMES.has(baseName)) return {};
2090
- const folder = path16.basename(path16.dirname(filename));
2138
+ const folder = path17.basename(path17.dirname(filename));
2091
2139
  if (!SUPPORT_FOLDERS3.has(folder)) return {};
2092
- const directory = path16.dirname(filename);
2140
+ const directory = path17.dirname(filename);
2093
2141
  let entries;
2094
2142
  try {
2095
2143
  entries = fs4.readdirSync(directory);
@@ -2106,7 +2154,7 @@ var supportFolderShapeRule = {
2106
2154
  const exportPattern = /export\s+(?:\{[^}]*\}|\*[^;]*)\s+from\s+["'](?<specifier>\.[^"']+)["']/g;
2107
2155
  for (const match of source.matchAll(exportPattern)) {
2108
2156
  const specifier = match.groups?.specifier;
2109
- if (specifier) exportedFiles.add(path16.basename(specifier));
2157
+ if (specifier) exportedFiles.add(path17.basename(specifier));
2110
2158
  }
2111
2159
  const hasAnyReExport = exportedFiles.size > 0;
2112
2160
  const guide = `docs/next-codebase-guide/rules/${folder === "constants" ? "constants" : "types-and-schemas"}-rule.md`;
@@ -2133,7 +2181,7 @@ var supportFolderShapeRule = {
2133
2181
  };
2134
2182
 
2135
2183
  // eslint/rules/constant-casing.ts
2136
- import path17 from "path";
2184
+ import path18 from "path";
2137
2185
  var NEXTJS_ROUTE_HANDLER_NAMES = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2138
2186
  function isScreamingSnakeCase(name) {
2139
2187
  return /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/.test(name);
@@ -2147,9 +2195,9 @@ var constantCasingRule = {
2147
2195
  }
2148
2196
  },
2149
2197
  create(context) {
2150
- const filename = path17.resolve(context.filename);
2198
+ const filename = path18.resolve(context.filename);
2151
2199
  const sourceRoot = sourceRootOf(context);
2152
- if (!filename.startsWith(sourceRoot + path17.sep)) return {};
2200
+ if (!filename.startsWith(sourceRoot + path18.sep)) return {};
2153
2201
  return {
2154
2202
  VariableDeclarator(node) {
2155
2203
  if (node.id.type !== "Identifier") return;
@@ -2171,7 +2219,7 @@ var constantCasingRule = {
2171
2219
  };
2172
2220
 
2173
2221
  // eslint/rules/prefer-enum.ts
2174
- import path18 from "path";
2222
+ import path19 from "path";
2175
2223
  function isConstAssertion(node) {
2176
2224
  const typeAnnotation = node.typeAnnotation;
2177
2225
  return typeAnnotation?.type === "TSTypeReference" && typeAnnotation.typeName?.type === "Identifier" && typeAnnotation.typeName.name === "const";
@@ -2190,9 +2238,9 @@ var preferEnumRule = {
2190
2238
  }
2191
2239
  },
2192
2240
  create(context) {
2193
- const filename = path18.resolve(context.filename);
2241
+ const filename = path19.resolve(context.filename);
2194
2242
  const sourceRoot = sourceRootOf(context);
2195
- if (!filename.startsWith(sourceRoot + path18.sep)) return {};
2243
+ if (!filename.startsWith(sourceRoot + path19.sep)) return {};
2196
2244
  return {
2197
2245
  TSAsExpression(node) {
2198
2246
  if (!isConstAssertion(node)) return;
@@ -2210,7 +2258,7 @@ var preferEnumRule = {
2210
2258
  };
2211
2259
 
2212
2260
  // eslint/rules/import-through-index.ts
2213
- import path19 from "path";
2261
+ import path20 from "path";
2214
2262
  var importThroughIndexRule = {
2215
2263
  meta: {
2216
2264
  schema: [],
@@ -2220,7 +2268,7 @@ var importThroughIndexRule = {
2220
2268
  }
2221
2269
  },
2222
2270
  create(context) {
2223
- const filename = path19.resolve(context.filename);
2271
+ const filename = path20.resolve(context.filename);
2224
2272
  const sourceRoot = sourceRootOf2(context, filename);
2225
2273
  return {
2226
2274
  Program(node) {
@@ -2232,7 +2280,7 @@ var importThroughIndexRule = {
2232
2280
  (segment) => ["constants", "types", "schemas"].includes(segment)
2233
2281
  );
2234
2282
  const supportFolder = supportFolderIndex >= 0 ? targetSegments[supportFolderIndex] : void 0;
2235
- if (!supportFolder || path19.basename(target).startsWith("index.")) continue;
2283
+ if (!supportFolder || path20.basename(target).startsWith("index.")) continue;
2236
2284
  const folderIndex = targetSegments.slice(0, supportFolderIndex + 1);
2237
2285
  const expected = `@/${folderIndex.join("/")}`;
2238
2286
  context.report({
@@ -2254,14 +2302,14 @@ function importSpecifiers(source) {
2254
2302
  return specifiers;
2255
2303
  }
2256
2304
  function sourceRootOf2(context, filename) {
2257
- const marker = `${path19.sep}src${path19.sep}`;
2305
+ const marker = `${path20.sep}src${path20.sep}`;
2258
2306
  const srcIndex = filename.lastIndexOf(marker);
2259
2307
  if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
2260
- return path19.resolve(context.cwd ?? process.cwd(), "src");
2308
+ return path20.resolve(context.cwd ?? process.cwd(), "src");
2261
2309
  }
2262
2310
 
2263
2311
  // eslint/rules/util-file-name.ts
2264
- import path20 from "path";
2312
+ import path21 from "path";
2265
2313
  function toKebabCase2(value) {
2266
2314
  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();
2267
2315
  }
@@ -2274,7 +2322,7 @@ var utilFileNameRule = {
2274
2322
  }
2275
2323
  },
2276
2324
  create(context) {
2277
- const filename = path20.resolve(context.filename);
2325
+ const filename = path21.resolve(context.filename);
2278
2326
  const segments = filename.replace(/\\/g, "/").split("/");
2279
2327
  if (!segments.includes("utils")) return {};
2280
2328
  let module;
@@ -2288,13 +2336,13 @@ var utilFileNameRule = {
2288
2336
  const functionName = functions[0]?.name;
2289
2337
  if (!functionName) return {};
2290
2338
  const expected = toKebabCase2(functionName);
2291
- const actual = path20.basename(filename, path20.extname(filename));
2339
+ const actual = path21.basename(filename, path21.extname(filename));
2292
2340
  if (!expected || actual === expected) return {};
2293
2341
  return {
2294
2342
  Program(node) {
2295
2343
  context.report({
2296
2344
  node,
2297
- message: `A utility file exporting ${functionName} must be named ${expected}.${path20.extname(filename).slice(1)}.`
2345
+ message: `A utility file exporting ${functionName} must be named ${expected}.${path21.extname(filename).slice(1)}.`
2298
2346
  });
2299
2347
  }
2300
2348
  };
@@ -2302,7 +2350,7 @@ var utilFileNameRule = {
2302
2350
  };
2303
2351
 
2304
2352
  // eslint/rules/no-util-barrel.ts
2305
- import path21 from "path";
2353
+ import path22 from "path";
2306
2354
  var noUtilBarrelRule = {
2307
2355
  meta: {
2308
2356
  schema: [],
@@ -2312,7 +2360,7 @@ var noUtilBarrelRule = {
2312
2360
  }
2313
2361
  },
2314
2362
  create(context) {
2315
- const filename = path21.resolve(context.filename);
2363
+ const filename = path22.resolve(context.filename);
2316
2364
  const sourceRoot = sourceRootOf3(context, filename);
2317
2365
  return {
2318
2366
  Program(node) {
@@ -2321,7 +2369,7 @@ var noUtilBarrelRule = {
2321
2369
  if (!target) continue;
2322
2370
  const segments = target.replace(/\\/g, "/").split("/");
2323
2371
  const utilsIndex = segments.lastIndexOf("utils");
2324
- if (utilsIndex < 0 || !path21.basename(target).startsWith("index.")) continue;
2372
+ if (utilsIndex < 0 || !path22.basename(target).startsWith("index.")) continue;
2325
2373
  context.report({
2326
2374
  node,
2327
2375
  message: `Import utilities directly instead of through "${specifier}". See docs/next-codebase-guide/rules/utilities-rule.md`
@@ -2341,10 +2389,10 @@ function importSpecifiers2(source) {
2341
2389
  return specifiers;
2342
2390
  }
2343
2391
  function sourceRootOf3(context, filename) {
2344
- const marker = `${path21.sep}src${path21.sep}`;
2392
+ const marker = `${path22.sep}src${path22.sep}`;
2345
2393
  const srcIndex = filename.lastIndexOf(marker);
2346
2394
  if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
2347
- return path21.resolve(context.cwd ?? process.cwd(), "src");
2395
+ return path22.resolve(context.cwd ?? process.cwd(), "src");
2348
2396
  }
2349
2397
 
2350
2398
  // eslint/rules/jsx-hygiene.ts
@@ -2437,7 +2485,7 @@ var jsxHygieneRule = {
2437
2485
  };
2438
2486
 
2439
2487
  // eslint/rules/interactive-component.ts
2440
- import path22 from "path";
2488
+ import path23 from "path";
2441
2489
  var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
2442
2490
  "a",
2443
2491
  "button",
@@ -2592,8 +2640,8 @@ var interactiveComponentRule = {
2592
2640
  },
2593
2641
  create(context) {
2594
2642
  if (!context.filename.endsWith(".tsx") && !context.filename.endsWith(".jsx")) return {};
2595
- const filename = path22.resolve(context.filename);
2596
- const base = path22.basename(filename, path22.extname(filename));
2643
+ const filename = path23.resolve(context.filename);
2644
+ const base = path23.basename(filename, path23.extname(filename));
2597
2645
  if (NEXT_ROUTING_FILES3.has(base)) return {};
2598
2646
  return {
2599
2647
  JSXElement(node) {
@@ -2844,12 +2892,12 @@ var cvaBooleanVariantsRule = {
2844
2892
  };
2845
2893
 
2846
2894
  // eslint/rules/cross-feature-import.ts
2847
- import path23 from "path";
2895
+ import path24 from "path";
2848
2896
  var FEATURES_SEGMENT = "features";
2849
2897
  function featureNameOf(resolvedPath, sourceRoot) {
2850
- const relative = path23.relative(sourceRoot, resolvedPath);
2898
+ const relative = path24.relative(sourceRoot, resolvedPath);
2851
2899
  if (relative.startsWith("..")) return void 0;
2852
- const segments = relative.split(path23.sep);
2900
+ const segments = relative.split(path24.sep);
2853
2901
  if (segments[0] !== FEATURES_SEGMENT || segments.length < 2) return void 0;
2854
2902
  return segments[1];
2855
2903
  }
@@ -2865,9 +2913,9 @@ var crossFeatureImportRule = {
2865
2913
  const filename = context.filename;
2866
2914
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
2867
2915
  const sourceRoot = sourceRootOf(context);
2868
- const fileRelative = path23.relative(sourceRoot, filename);
2916
+ const fileRelative = path24.relative(sourceRoot, filename);
2869
2917
  if (fileRelative.startsWith("..")) return {};
2870
- const fileSegments = fileRelative.split(path23.sep);
2918
+ const fileSegments = fileRelative.split(path24.sep);
2871
2919
  const isInCompositions = fileSegments[0] === "compositions";
2872
2920
  const isInApp = fileSegments[0] === "app";
2873
2921
  const isConfig = fileSegments[0] === "config";
@@ -2881,9 +2929,9 @@ var crossFeatureImportRule = {
2881
2929
  if (typeof source.value !== "string") return;
2882
2930
  let resolved;
2883
2931
  if (source.value.startsWith("@/")) {
2884
- resolved = path23.resolve(sourceRoot, source.value.slice(2));
2932
+ resolved = path24.resolve(sourceRoot, source.value.slice(2));
2885
2933
  } else if (source.value.startsWith(".")) {
2886
- resolved = path23.resolve(path23.dirname(filename), source.value);
2934
+ resolved = path24.resolve(path24.dirname(filename), source.value);
2887
2935
  }
2888
2936
  if (!resolved) return;
2889
2937
  const feature = featureNameOf(resolved, sourceRoot);
@@ -2902,7 +2950,7 @@ var crossFeatureImportRule = {
2902
2950
  };
2903
2951
 
2904
2952
  // eslint/rules/pure-function-extract.ts
2905
- import path24 from "path";
2953
+ import path25 from "path";
2906
2954
  var ROUTE_HANDLER_EXPORT_NAMES = /* @__PURE__ */ new Set([
2907
2955
  "GET",
2908
2956
  "POST",
@@ -2946,12 +2994,12 @@ var pureFunctionExtractRule = {
2946
2994
  },
2947
2995
  create(context) {
2948
2996
  const filename = context.filename;
2949
- const isRouteFile = path24.basename(filename) === "route.ts";
2997
+ const isRouteFile = path25.basename(filename) === "route.ts";
2950
2998
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx") && !isRouteFile) return {};
2951
2999
  const sourceRoot = sourceRootOf(context);
2952
- const relative = path24.relative(sourceRoot, filename);
3000
+ const relative = path25.relative(sourceRoot, filename);
2953
3001
  if (relative.startsWith("..")) return {};
2954
- const segments = relative.split(path24.sep);
3002
+ const segments = relative.split(path25.sep);
2955
3003
  if (segments[0] === "utils") return {};
2956
3004
  if (segments[0] === "app" && !isRouteFile) return {};
2957
3005
  const supportFolders = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
@@ -2976,30 +3024,532 @@ var pureFunctionExtractRule = {
2976
3024
  report3(node, name);
2977
3025
  },
2978
3026
  VariableDeclarator(node) {
2979
- if (node.id.type !== "Identifier") return;
2980
- const name = node.id.name;
2981
- if (!name) return;
2982
- const container = node.parent.parent;
2983
- const exported = container?.type === "ExportNamedDeclaration";
2984
- const moduleLevel = exported || container?.type === "Program";
2985
- if (!moduleLevel) return;
2986
- if (!isRouteFile && !exported) return;
2987
- if (isRouteFile && ROUTE_HANDLER_EXPORT_NAMES.has(name)) return;
2988
- if (isComponentLikeName(name) || isHookName2(name)) return;
3027
+ if (node.id.type !== "Identifier") return;
3028
+ const name = node.id.name;
3029
+ if (!name) return;
3030
+ const container = node.parent.parent;
3031
+ const exported = container?.type === "ExportNamedDeclaration";
3032
+ const moduleLevel = exported || container?.type === "Program";
3033
+ if (!moduleLevel) return;
3034
+ if (!isRouteFile && !exported) return;
3035
+ if (isRouteFile && ROUTE_HANDLER_EXPORT_NAMES.has(name)) return;
3036
+ if (isComponentLikeName(name) || isHookName2(name)) return;
3037
+ const init = node.init;
3038
+ if (!init || init.type !== "ArrowFunctionExpression" && init.type !== "FunctionExpression") {
3039
+ return;
3040
+ }
3041
+ if (init.body.type === "BlockStatement" && hasHookUsage(init.body)) return;
3042
+ report3(node, name);
3043
+ }
3044
+ };
3045
+ }
3046
+ };
3047
+
3048
+ // eslint/rules/root-support-placement.ts
3049
+ import path26 from "path";
3050
+ function isSupportFolder(value) {
3051
+ return value === "utils" || value === "types" || value === "schemas" || value === "constants";
3052
+ }
3053
+ var TYPES_AND_SCHEMAS_DOC = "docs/next-codebase-guide/rules/types-and-schemas-rule.md";
3054
+ var KIND_FOR_SUPPORT_FOLDER = {
3055
+ utils: "function",
3056
+ types: "type",
3057
+ schemas: "schema",
3058
+ constants: "constant"
3059
+ };
3060
+ var LABEL_FOR_SUPPORT_FOLDER = {
3061
+ utils: "Function",
3062
+ types: "Type",
3063
+ schemas: "Schema",
3064
+ constants: "Constant"
3065
+ };
3066
+ var DOC_FOR_SUPPORT_FOLDER = {
3067
+ utils: "docs/next-codebase-guide/rules/utilities-rule.md",
3068
+ types: TYPES_AND_SCHEMAS_DOC,
3069
+ schemas: TYPES_AND_SCHEMAS_DOC,
3070
+ constants: "docs/next-codebase-guide/rules/constants-rule.md"
3071
+ };
3072
+ var rootSupportPlacementRule = {
3073
+ meta: {
3074
+ schema: [],
3075
+ type: "problem",
3076
+ docs: {
3077
+ description: "Require a zero-consumer support-folder export to live under a feature folder, not root or elsewhere."
3078
+ }
3079
+ },
3080
+ create(context) {
3081
+ const sourceRoot = sourceRootOf(context);
3082
+ const file = path26.resolve(context.filename);
3083
+ const segments = segmentsOf(file, sourceRoot);
3084
+ const folderSegments = folderSegmentsOf(file, sourceRoot);
3085
+ const supportFolder = folderSegments[folderSegments.length - 1];
3086
+ if (!isSupportFolder(supportFolder)) return {};
3087
+ const expectedKind = KIND_FOR_SUPPORT_FOLDER[supportFolder];
3088
+ const doc = DOC_FOR_SUPPORT_FOLDER[supportFolder];
3089
+ const label = LABEL_FOR_SUPPORT_FOLDER[supportFolder];
3090
+ const isRoot = segments[0] === supportFolder;
3091
+ const isUnderFeature = segments[0] === "features";
3092
+ if (isUnderFeature) return {};
3093
+ const index = getProjectIndex(sourceRoot);
3094
+ if (!index) return {};
3095
+ const module = index.modules.get(file);
3096
+ if (!module) return {};
3097
+ const findings = [];
3098
+ for (const exp of module.exports) {
3099
+ if (exp.kind !== expectedKind) continue;
3100
+ const consumers = [...index.symbolConsumers.get(symbolKey(file, exp.name)) ?? []];
3101
+ if (consumers.length === 0) continue;
3102
+ const real = consumers.filter((consumer) => {
3103
+ const consumerSegments = segmentsOf(consumer, sourceRoot);
3104
+ return !isUnderApp(consumerSegments) && !isConfigModule(consumerSegments);
3105
+ });
3106
+ if (real.length > 0) continue;
3107
+ const where = isRoot ? `root src/${supportFolder}/` : `src/${folderSegments.join("/")}/`;
3108
+ findings.push({
3109
+ line: exp.line,
3110
+ message: `${label} "${exp.name}" has no consumer outside src/app/ or a configuration module, so it has not earned ${where}; move it into the feature it represents (src/features/<feature>/${supportFolder}/). If no existing feature applies, introduce a new feature folder. See ${doc}`
3111
+ });
3112
+ }
3113
+ if (findings.length === 0) return {};
3114
+ return {
3115
+ Program(node) {
3116
+ for (const finding of findings) {
3117
+ context.report({ node, loc: { line: finding.line, column: 0 }, message: finding.message });
3118
+ }
3119
+ }
3120
+ };
3121
+ }
3122
+ };
3123
+
3124
+ // eslint/rules/route-handler-shape.ts
3125
+ import path27 from "path";
3126
+ import ts4 from "typescript";
3127
+ var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
3128
+ var REQUIRED_BOUNDARY = "withResponse";
3129
+ function isFunctionLike(node) {
3130
+ return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression";
3131
+ }
3132
+ function findHandler(node) {
3133
+ if (isFunctionLike(node)) return node;
3134
+ if (node.type === "CallExpression") {
3135
+ for (const arg of node.arguments) {
3136
+ if (arg.type === "SpreadElement") continue;
3137
+ const found = findHandler(arg);
3138
+ if (found) return found;
3139
+ }
3140
+ }
3141
+ return void 0;
3142
+ }
3143
+ function isLoop(node) {
3144
+ return ts4.isForStatement(node) || ts4.isForOfStatement(node) || ts4.isForInStatement(node) || ts4.isWhileStatement(node) || ts4.isDoStatement(node);
3145
+ }
3146
+ function isFunctionBoundary(node) {
3147
+ return ts4.isFunctionExpression(node) || ts4.isArrowFunction(node) || ts4.isFunctionDeclaration(node);
3148
+ }
3149
+ function analyzeHandlerBody(body, sourceText) {
3150
+ const start = body.range?.[0] ?? 0;
3151
+ const end = body.range?.[1] ?? sourceText.length;
3152
+ const sourceFile = ts4.createSourceFile(
3153
+ "route.ts",
3154
+ sourceText.slice(start, end),
3155
+ ts4.ScriptTarget.Latest,
3156
+ true,
3157
+ ts4.ScriptKind.TS
3158
+ );
3159
+ const kinds = /* @__PURE__ */ new Set();
3160
+ const calledNames = /* @__PURE__ */ new Set();
3161
+ const visit = (node) => {
3162
+ if (ts4.isTryStatement(node)) kinds.add("try");
3163
+ if (isLoop(node)) kinds.add("loop");
3164
+ if (ts4.isIfStatement(node)) kinds.add("if");
3165
+ if (ts4.isCallExpression(node) && ts4.isIdentifier(node.expression)) calledNames.add(node.expression.text);
3166
+ if (isFunctionBoundary(node)) return;
3167
+ ts4.forEachChild(node, visit);
3168
+ };
3169
+ visit(sourceFile);
3170
+ return { kinds, calledNames };
3171
+ }
3172
+ var CONTROL_FLOW_MESSAGES = {
3173
+ try: "a try statement",
3174
+ loop: "a loop",
3175
+ if: "an if statement"
3176
+ };
3177
+ function isFunctionLikeInit(node) {
3178
+ return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression";
3179
+ }
3180
+ function collectLocallyDeclaredNames(programBody) {
3181
+ const names = /* @__PURE__ */ new Set();
3182
+ for (const statement of programBody) {
3183
+ const declaration = statement.type === "ExportNamedDeclaration" && statement.declaration ? statement.declaration : statement;
3184
+ if (declaration.type === "FunctionDeclaration") {
3185
+ names.add(declaration.id.name);
3186
+ }
3187
+ if (declaration.type === "VariableDeclaration") {
3188
+ for (const declarator of declaration.declarations) {
3189
+ if (declarator.id.type === "Identifier" && isFunctionLikeInit(declarator.init)) {
3190
+ names.add(declarator.id.name);
3191
+ }
3192
+ }
3193
+ }
3194
+ }
3195
+ return names;
3196
+ }
3197
+ var routeHandlerShapeRule = {
3198
+ meta: {
3199
+ schema: [],
3200
+ type: "problem",
3201
+ docs: {
3202
+ description: "Require a route.ts handler to be wrapped in withResponse, with no try, loop, or if of its own, and every function it calls imported rather than declared in route.ts."
3203
+ }
3204
+ },
3205
+ create(context) {
3206
+ if (path27.basename(context.filename) !== "route.ts") return {};
3207
+ const sourceText = context.sourceCode.text;
3208
+ function reportUnwrapped(node, name) {
3209
+ context.report({
3210
+ node,
3211
+ message: `Handler "${name}" must be wrapped in withResponse. See docs/next-codebase-guide/rules/route-handler-rule.md`
3212
+ });
3213
+ }
3214
+ function checkExport(node, name, init) {
3215
+ if (init.type === "Identifier") return;
3216
+ const boundary = init.type === "CallExpression" && init.callee.type === "Identifier" ? init.callee.name : void 0;
3217
+ if (boundary !== REQUIRED_BOUNDARY) {
3218
+ reportUnwrapped(node, name);
3219
+ return;
3220
+ }
3221
+ const handler = findHandler(init);
3222
+ if (!handler?.body) return;
3223
+ const { kinds, calledNames } = analyzeHandlerBody(handler.body, sourceText);
3224
+ for (const kind of kinds) {
3225
+ context.report({
3226
+ node,
3227
+ message: `Handler "${name}" contains ${CONTROL_FLOW_MESSAGES[kind]} of its own; delegate that to a function it calls. See docs/next-codebase-guide/rules/route-handler-rule.md`
3228
+ });
3229
+ }
3230
+ const localNames = collectLocallyDeclaredNames(context.sourceCode.ast.body);
3231
+ for (const calledName of calledNames) {
3232
+ if (localNames.has(calledName)) {
3233
+ context.report({
3234
+ node,
3235
+ message: `Handler "${name}" calls "${calledName}", which is declared in route.ts instead of imported. See docs/next-codebase-guide/rules/route-handler-rule.md`
3236
+ });
3237
+ }
3238
+ }
3239
+ }
3240
+ return {
3241
+ FunctionDeclaration(node) {
3242
+ const exported = node.parent?.type === "ExportNamedDeclaration";
3243
+ const name = node.id?.name;
3244
+ if (!exported || !name || !HTTP_METHODS.has(name) || !node.body) return;
3245
+ reportUnwrapped(node, name);
3246
+ },
3247
+ VariableDeclarator(node) {
3248
+ if (node.id.type !== "Identifier") return;
3249
+ const name = node.id.name;
3250
+ const exported = node.parent.parent?.type === "ExportNamedDeclaration";
3251
+ if (!exported || !HTTP_METHODS.has(name) || !node.init) return;
3252
+ checkExport(node, name, node.init);
3253
+ }
3254
+ };
3255
+ }
3256
+ };
3257
+
3258
+ // eslint/rules/http-error-usage.ts
3259
+ import { readFileSync as readFileSync4 } from "fs";
3260
+ import path28 from "path";
3261
+ import ts5 from "typescript";
3262
+ var HTTP_ERROR = "HttpError";
3263
+ var ROUTE_FILE = "route.ts";
3264
+ var DOC2 = "docs/next-codebase-guide/rules/route-handler-rule.md";
3265
+ var HERITAGE_DEPTH = 5;
3266
+ var HANDS_ERROR_BACK = `A delegated module must throw the HttpError it reports a failure with, not return it. See ${DOC2}`;
3267
+ var delegatedModuleMessage = (name) => `A delegated module must report a failure by throwing an HttpError, not "${name}". See ${DOC2}`;
3268
+ function constructedErrorName(node) {
3269
+ if (node === void 0) return void 0;
3270
+ if (node.type === "NewExpression") {
3271
+ return node.callee.type === "Identifier" ? node.callee.name : void 0;
3272
+ }
3273
+ if (node.type === "AwaitExpression") return constructedErrorName(node.argument);
3274
+ if (node.type === "ConditionalExpression") {
3275
+ return constructedErrorName(node.consequent) ?? constructedErrorName(node.alternate);
3276
+ }
3277
+ if (node.type === "LogicalExpression") {
3278
+ return constructedErrorName(node.left) ?? constructedErrorName(node.right);
3279
+ }
3280
+ if (node.type !== "CallExpression") return void 0;
3281
+ for (const argument of node.arguments) {
3282
+ const found = constructedErrorName(argument);
3283
+ if (found) return found;
3284
+ }
3285
+ return void 0;
3286
+ }
3287
+ function parentClassOf(file, name) {
3288
+ let text;
3289
+ try {
3290
+ text = readFileSync4(file, "utf8");
3291
+ } catch {
3292
+ return void 0;
3293
+ }
3294
+ const sourceFile = ts5.createSourceFile(file, text, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
3295
+ for (const statement of sourceFile.statements) {
3296
+ if (!ts5.isClassDeclaration(statement) || statement.name?.text !== name) continue;
3297
+ const heritage = statement.heritageClauses?.find((clause) => clause.token === ts5.SyntaxKind.ExtendsKeyword);
3298
+ const parent = heritage?.types[0]?.expression;
3299
+ return parent && ts5.isIdentifier(parent) ? parent.text : void 0;
3300
+ }
3301
+ return void 0;
3302
+ }
3303
+ function importedFrom(file, name, sourceRoot, index) {
3304
+ const module = index.modules.get(path28.resolve(file));
3305
+ const moduleImport = module?.imports.find((entry) => entry.names.includes(name));
3306
+ if (!moduleImport) return void 0;
3307
+ return resolveSpecifier(file, moduleImport.specifier, sourceRoot);
3308
+ }
3309
+ function isHttpErrorClass(file, name, sourceRoot, index) {
3310
+ let currentFile = path28.resolve(file);
3311
+ let currentName = name;
3312
+ for (let depth = 0; depth <= HERITAGE_DEPTH; depth += 1) {
3313
+ if (currentName === HTTP_ERROR) return true;
3314
+ const parent = parentClassOf(currentFile, currentName);
3315
+ if (parent) {
3316
+ currentName = parent;
3317
+ continue;
3318
+ }
3319
+ const imported = importedFrom(currentFile, currentName, sourceRoot, index);
3320
+ if (!imported) return false;
3321
+ currentFile = imported;
3322
+ }
3323
+ return false;
3324
+ }
3325
+ var pipelineCache;
3326
+ function pipelineFiles(sourceRoot) {
3327
+ const index = getProjectIndex(sourceRoot);
3328
+ if (!index) return void 0;
3329
+ if (pipelineCache?.index === index) return pipelineCache.files;
3330
+ const files = /* @__PURE__ */ new Set();
3331
+ const queue = [...index.modules.keys()].filter((file) => path28.basename(file) === ROUTE_FILE);
3332
+ while (queue.length > 0) {
3333
+ const file = queue.pop();
3334
+ if (file === void 0 || files.has(file)) continue;
3335
+ files.add(file);
3336
+ for (const moduleImport of index.modules.get(file)?.imports ?? []) {
3337
+ const target = resolveSpecifier(file, moduleImport.specifier, index.sourceRoot);
3338
+ if (target && index.modules.has(target) && !files.has(target)) queue.push(target);
3339
+ }
3340
+ }
3341
+ pipelineCache = { index, files };
3342
+ return files;
3343
+ }
3344
+ var httpErrorUsageRule = {
3345
+ meta: {
3346
+ schema: [],
3347
+ type: "problem",
3348
+ docs: {
3349
+ description: "Require a delegated module to report a failure by throwing an HttpError, never another error type and never a returned value."
3350
+ }
3351
+ },
3352
+ create(context) {
3353
+ const sourceRoot = sourceRootOf(context);
3354
+ const file = path28.resolve(context.filename);
3355
+ let delegated;
3356
+ function inPipeline() {
3357
+ delegated ??= pipelineFiles(sourceRoot)?.has(file) ?? false;
3358
+ return delegated;
3359
+ }
3360
+ return {
3361
+ ReturnStatement(node) {
3362
+ if (node.argument === null) return;
3363
+ if (!inPipeline()) return;
3364
+ if (constructedErrorName(node.argument) !== HTTP_ERROR) return;
3365
+ context.report({ node, message: HANDS_ERROR_BACK });
3366
+ },
3367
+ ArrowFunctionExpression(node) {
3368
+ if (node.body.type === "BlockStatement") return;
3369
+ if (!inPipeline()) return;
3370
+ if (constructedErrorName(node.body) !== HTTP_ERROR) return;
3371
+ context.report({ node, message: HANDS_ERROR_BACK });
3372
+ },
3373
+ ThrowStatement(node) {
3374
+ const thrown = node.argument;
3375
+ if (thrown.type !== "NewExpression" || thrown.callee.type !== "Identifier") return;
3376
+ const name = thrown.callee.name;
3377
+ if (name === HTTP_ERROR) return;
3378
+ const index = getProjectIndex(sourceRoot);
3379
+ if (!index || !inPipeline()) return;
3380
+ if (isHttpErrorClass(file, name, sourceRoot, index)) return;
3381
+ context.report({ node, message: delegatedModuleMessage(name) });
3382
+ }
3383
+ };
3384
+ }
3385
+ };
3386
+
3387
+ // eslint/rules/with-response-helper.ts
3388
+ import path29 from "path";
3389
+ import ts6 from "typescript";
3390
+ var HELPER2 = "withResponse";
3391
+ var DOC3 = "docs/pasika-adoption-guide/rules/with-response-helper-rule.md";
3392
+ var ESLINT_CONFIG2 = /^eslint\.config\.(?:cjs|cts|js|mjs|mts|ts)$/;
3393
+ var BEAT_KEYS = [
3394
+ "awaitsHandler",
3395
+ "validatesWithSchema",
3396
+ "checksHttpError",
3397
+ "answersWithNullData",
3398
+ "usesErrorStatus",
3399
+ "rethrows"
3400
+ ];
3401
+ var BEAT_MESSAGES = {
3402
+ awaitsHandler: "must await the handler",
3403
+ validatesWithSchema: "must validate the handler's returned data through the response schema",
3404
+ checksHttpError: "must check the caught error with instanceof HttpError",
3405
+ answersWithNullData: "must answer a thrown HttpError with { data: null, message }",
3406
+ usesErrorStatus: "must answer at the caught error's status",
3407
+ rethrows: "must rethrow a caught error that is not an HttpError"
3408
+ };
3409
+ function isNamed2(node, name) {
3410
+ return node?.type === "Identifier" && node.name === name;
3411
+ }
3412
+ function sourceRootFor2(context) {
3413
+ return path29.join(path29.dirname(path29.resolve(context.filename)), "src");
3414
+ }
3415
+ function definesHelper2(context, name) {
3416
+ const index = getProjectIndex(sourceRootFor2(context));
3417
+ if (!index) return true;
3418
+ for (const parsed of index.modules.values()) {
3419
+ if (parsed.exports.some((exported) => exported.name === name)) return true;
3420
+ }
3421
+ return false;
3422
+ }
3423
+ function contains(node, check) {
3424
+ let found = false;
3425
+ const visit = (current) => {
3426
+ if (found) return;
3427
+ if (check(current)) {
3428
+ found = true;
3429
+ return;
3430
+ }
3431
+ ts6.forEachChild(current, visit);
3432
+ };
3433
+ visit(node);
3434
+ return found;
3435
+ }
3436
+ function findTryStatement(node) {
3437
+ let found;
3438
+ const visit = (current) => {
3439
+ if (found) return;
3440
+ if (ts6.isTryStatement(current)) {
3441
+ found = current;
3442
+ return;
3443
+ }
3444
+ ts6.forEachChild(current, visit);
3445
+ };
3446
+ visit(node);
3447
+ return found;
3448
+ }
3449
+ function awaitsHandlerCall(handlerName) {
3450
+ return (node) => ts6.isAwaitExpression(node) && ts6.isCallExpression(node.expression) && ts6.isIdentifier(node.expression.expression) && node.expression.expression.text === handlerName;
3451
+ }
3452
+ function parsesThroughSchema(schemaName) {
3453
+ return (node) => ts6.isCallExpression(node) && ts6.isPropertyAccessExpression(node.expression) && ts6.isIdentifier(node.expression.expression) && node.expression.expression.text === schemaName && (node.expression.name.text === "parse" || node.expression.name.text === "safeParse");
3454
+ }
3455
+ function checksHttpError(node) {
3456
+ return ts6.isBinaryExpression(node) && node.operatorToken.kind === ts6.SyntaxKind.InstanceOfKeyword && ts6.isIdentifier(node.right) && node.right.text === "HttpError";
3457
+ }
3458
+ function answersWithNullData(node) {
3459
+ if (!ts6.isCallExpression(node) || !ts6.isPropertyAccessExpression(node.expression) || node.expression.name.text !== "json") {
3460
+ return false;
3461
+ }
3462
+ return node.arguments.some((argument) => {
3463
+ if (!ts6.isObjectLiteralExpression(argument)) return false;
3464
+ return argument.properties.some((property) => {
3465
+ if (!ts6.isPropertyAssignment(property)) return false;
3466
+ const name = ts6.isIdentifier(property.name) ? property.name.text : void 0;
3467
+ return name === "data" && property.initializer.kind === ts6.SyntaxKind.NullKeyword;
3468
+ });
3469
+ });
3470
+ }
3471
+ function readsErrorStatus(errorName) {
3472
+ return (node) => ts6.isPropertyAccessExpression(node) && node.name.text === "status" && ts6.isIdentifier(node.expression) && node.expression.text === errorName;
3473
+ }
3474
+ function collectBeats(text, schemaName, handlerName) {
3475
+ const beats = {
3476
+ awaitsHandler: false,
3477
+ validatesWithSchema: false,
3478
+ checksHttpError: false,
3479
+ answersWithNullData: false,
3480
+ usesErrorStatus: false,
3481
+ rethrows: false
3482
+ };
3483
+ const sourceFile = ts6.createSourceFile("with-response.ts", text, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TS);
3484
+ const tryStatement = findTryStatement(sourceFile);
3485
+ if (!tryStatement) return beats;
3486
+ beats.awaitsHandler = contains(tryStatement.tryBlock, awaitsHandlerCall(handlerName));
3487
+ beats.validatesWithSchema = contains(tryStatement.tryBlock, parsesThroughSchema(schemaName));
3488
+ const catchClause = tryStatement.catchClause;
3489
+ if (!catchClause) return beats;
3490
+ const errorName = catchClause.variableDeclaration?.name;
3491
+ const caughtName = errorName && ts6.isIdentifier(errorName) ? errorName.text : void 0;
3492
+ beats.checksHttpError = contains(catchClause.block, checksHttpError);
3493
+ beats.answersWithNullData = contains(catchClause.block, answersWithNullData);
3494
+ beats.usesErrorStatus = contains(catchClause.block, readsErrorStatus(caughtName));
3495
+ beats.rethrows = contains(catchClause.block, ts6.isThrowStatement);
3496
+ return beats;
3497
+ }
3498
+ function parameterNames(node) {
3499
+ if (node.type !== "FunctionDeclaration" && node.type !== "FunctionExpression" && node.type !== "ArrowFunctionExpression") {
3500
+ return [];
3501
+ }
3502
+ return node.params.map((parameter) => parameter.type === "Identifier" ? parameter.name : void 0);
3503
+ }
3504
+ var withResponseHelperRule = {
3505
+ meta: {
3506
+ schema: [],
3507
+ type: "problem",
3508
+ docs: {
3509
+ description: `Require a repository to define a ${HELPER2} helper that validates the handler's data and maps a thrown HttpError to a response.`
3510
+ }
3511
+ },
3512
+ create(context) {
3513
+ if (ESLINT_CONFIG2.test(path29.basename(context.filename))) {
3514
+ return {
3515
+ Program() {
3516
+ if (definesHelper2(context, HELPER2)) return;
3517
+ context.report({
3518
+ node: context.sourceCode.ast,
3519
+ loc: { line: 1, column: 0 },
3520
+ message: `A repository must define a ${HELPER2} helper. See ${DOC3}`
3521
+ });
3522
+ }
3523
+ };
3524
+ }
3525
+ const check = (node, parameters) => {
3526
+ const [start, end] = node.range ?? [0, context.sourceCode.text.length];
3527
+ const beats = collectBeats(context.sourceCode.text.slice(start, end), parameters[0], parameters[1]);
3528
+ for (const beat of BEAT_KEYS) {
3529
+ if (beats[beat]) continue;
3530
+ context.report({
3531
+ node,
3532
+ message: `${HELPER2} ${BEAT_MESSAGES[beat]}. See ${DOC3}`
3533
+ });
3534
+ }
3535
+ };
3536
+ return {
3537
+ FunctionDeclaration(node) {
3538
+ if (isNamed2(node.id, HELPER2)) check(node, parameterNames(node));
3539
+ },
3540
+ VariableDeclarator(node) {
3541
+ if (!isNamed2(node.id.type === "Identifier" ? node.id : null, HELPER2)) return;
2989
3542
  const init = node.init;
2990
- if (!init || init.type !== "ArrowFunctionExpression" && init.type !== "FunctionExpression") {
2991
- return;
2992
- }
2993
- if (init.body.type === "BlockStatement" && hasHookUsage(init.body)) return;
2994
- report3(node, name);
3543
+ if (init?.type !== "ArrowFunctionExpression" && init?.type !== "FunctionExpression") return;
3544
+ check(init, parameterNames(init));
2995
3545
  }
2996
3546
  };
2997
3547
  }
2998
3548
  };
2999
3549
 
3000
3550
  // eslint/rules/hook-complexity.ts
3001
- import path25 from "path";
3002
- import ts4 from "typescript";
3551
+ import path30 from "path";
3552
+ import ts7 from "typescript";
3003
3553
  var REACT_HOOKS = /* @__PURE__ */ new Set([
3004
3554
  "useState",
3005
3555
  "useEffect",
@@ -3017,28 +3567,67 @@ var REACT_HOOKS = /* @__PURE__ */ new Set([
3017
3567
  "useSyncExternalStore",
3018
3568
  "useInsertionEffect"
3019
3569
  ]);
3570
+ var SUBSCRIPTION_METHODS = /* @__PURE__ */ new Set(["on", "off", "addEventListener", "removeEventListener"]);
3571
+ var STORAGE_OBJECTS = /* @__PURE__ */ new Set(["localStorage", "sessionStorage", "indexedDB"]);
3572
+ var DOM_METHODS = /* @__PURE__ */ new Set(["focus", "blur", "scrollIntoView", "click"]);
3573
+ var DOM_PROPERTIES = /* @__PURE__ */ new Set(["classList"]);
3574
+ var DOM_CONSTRUCTORS = /* @__PURE__ */ new Set(["MutationObserver", "ResizeObserver", "IntersectionObserver"]);
3575
+ var LIFECYCLE_METHODS = /* @__PURE__ */ new Set(["load", "destroy", "dispose", "close", "cleanup", "unmount"]);
3020
3576
  function isHookName3(name) {
3021
3577
  return /^use[A-Z]/.test(name);
3022
3578
  }
3023
- function countImperativeCategories(body, sourceText) {
3579
+ function calledMethodName(node) {
3580
+ return ts7.isPropertyAccessExpression(node.expression) ? node.expression.name.text : void 0;
3581
+ }
3582
+ function calledOnObjectName(node) {
3583
+ if (!ts7.isPropertyAccessExpression(node.expression)) return void 0;
3584
+ const object = node.expression.expression;
3585
+ return ts7.isIdentifier(object) ? object.text : void 0;
3586
+ }
3587
+ function calledHookName(node) {
3588
+ return ts7.isCallExpression(node) && ts7.isIdentifier(node.expression) && REACT_HOOKS.has(node.expression.text) ? node.expression.text : void 0;
3589
+ }
3590
+ function sideEffectCategoryOf(node) {
3591
+ if (ts7.isAwaitExpression(node)) return "externalIO";
3592
+ if (ts7.isNewExpression(node) && ts7.isIdentifier(node.expression) && DOM_CONSTRUCTORS.has(node.expression.text)) {
3593
+ return "domManipulation";
3594
+ }
3595
+ if (ts7.isPropertyAccessExpression(node) && DOM_PROPERTIES.has(node.name.text)) {
3596
+ return "domManipulation";
3597
+ }
3598
+ if (ts7.isCallExpression(node)) {
3599
+ if (ts7.isIdentifier(node.expression) && node.expression.text === "fetch") return "externalIO";
3600
+ const method = calledMethodName(node);
3601
+ if (method && SUBSCRIPTION_METHODS.has(method)) return "subscription";
3602
+ if (method && LIFECYCLE_METHODS.has(method)) return "lifecycle";
3603
+ if (method && DOM_METHODS.has(method)) return "domManipulation";
3604
+ const object = calledOnObjectName(node);
3605
+ if (object && STORAGE_OBJECTS.has(object)) return "externalIO";
3606
+ }
3607
+ return void 0;
3608
+ }
3609
+ function computeExtractionScore(body, sourceText) {
3024
3610
  const start = body.range?.[0] ?? 0;
3025
3611
  const end = body.range?.[1] ?? sourceText.length;
3026
- const sourceFile = ts4.createSourceFile(
3612
+ const sourceFile = ts7.createSourceFile(
3027
3613
  "hook.ts",
3028
3614
  sourceText.slice(start, end),
3029
- ts4.ScriptTarget.Latest,
3615
+ ts7.ScriptTarget.Latest,
3030
3616
  true,
3031
- ts4.ScriptKind.TS
3617
+ ts7.ScriptKind.TS
3032
3618
  );
3033
- const categories = /* @__PURE__ */ new Set();
3619
+ const hookNames = /* @__PURE__ */ new Set();
3620
+ const sideEffectCategories = /* @__PURE__ */ new Set();
3034
3621
  const visit = (node) => {
3035
- if (ts4.isCallExpression(node) && ts4.isIdentifier(node.expression) && REACT_HOOKS.has(node.expression.text)) {
3036
- categories.add(node.expression.text);
3037
- }
3038
- ts4.forEachChild(node, visit);
3622
+ const hookName = calledHookName(node);
3623
+ if (hookName) hookNames.add(hookName);
3624
+ const sideEffect = sideEffectCategoryOf(node);
3625
+ if (sideEffect) sideEffectCategories.add(sideEffect);
3626
+ ts7.forEachChild(node, visit);
3039
3627
  };
3040
3628
  visit(sourceFile);
3041
- return categories.size;
3629
+ const hookDiversityPoint = hookNames.size >= 2 ? 1 : 0;
3630
+ return hookDiversityPoint + sideEffectCategories.size;
3042
3631
  }
3043
3632
  var hookComplexityRule = {
3044
3633
  meta: {
@@ -3051,26 +3640,26 @@ var hookComplexityRule = {
3051
3640
  create(context) {
3052
3641
  const filename = context.filename;
3053
3642
  const sourceRoot = sourceRootOf(context);
3054
- const relative = path25.relative(sourceRoot, filename);
3643
+ const relative = path30.relative(sourceRoot, filename);
3055
3644
  if (relative.startsWith("..")) return {};
3056
- const segments = relative.split(path25.sep);
3645
+ const segments = relative.split(path30.sep);
3057
3646
  const sourceText = context.sourceCode.text;
3058
3647
  function checkHook(node, name, body, exported) {
3059
3648
  if (!exported) return;
3060
3649
  if (!name || !isHookName3(name)) return;
3061
3650
  if (!body) return;
3062
- const imperativeCount = countImperativeCategories(body, sourceText);
3651
+ const score = computeExtractionScore(body, sourceText);
3063
3652
  const parentFolder = segments.length >= 2 ? segments[segments.length - 2] : void 0;
3064
3653
  const inSupportFolder = parentFolder === "hooks";
3065
- if (imperativeCount >= 2 && !inSupportFolder) {
3654
+ if (score >= 2 && !inSupportFolder) {
3066
3655
  context.report({
3067
3656
  node,
3068
- message: `Hook "${name}" has ${String(imperativeCount)} imperative categories and must be extracted to a hooks/ folder. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3657
+ message: `Hook "${name}" has an extraction score of ${String(score)} and must be extracted to a hooks/ folder. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3069
3658
  });
3070
- } else if (imperativeCount < 2 && inSupportFolder) {
3659
+ } else if (score < 2 && inSupportFolder) {
3071
3660
  context.report({
3072
3661
  node,
3073
- message: `Hook "${name}" has fewer than two imperative categories and must stay inline in its consumer file. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3662
+ message: `Hook "${name}" has an extraction score below two and must stay inline in its consumer file. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3074
3663
  });
3075
3664
  }
3076
3665
  }
@@ -3095,9 +3684,9 @@ var hookComplexityRule = {
3095
3684
  };
3096
3685
 
3097
3686
  // eslint/rules/locale-dotted-path.ts
3098
- import path26 from "path";
3687
+ import path31 from "path";
3099
3688
  function isInLocalesDir(filename) {
3100
- const segments = path26.resolve(filename).split(path26.sep);
3689
+ const segments = path31.resolve(filename).split(path31.sep);
3101
3690
  const srcIdx = segments.lastIndexOf("src");
3102
3691
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
3103
3692
  }
@@ -3146,9 +3735,9 @@ var localeDottedPathRule = {
3146
3735
  };
3147
3736
 
3148
3737
  // eslint/rules/locales-location.ts
3149
- import path27 from "path";
3738
+ import path32 from "path";
3150
3739
  function isLocalesFile(filename) {
3151
- const segments = path27.resolve(filename).split(path27.sep);
3740
+ const segments = path32.resolve(filename).split(path32.sep);
3152
3741
  const srcIdx = segments.lastIndexOf("src");
3153
3742
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
3154
3743
  }
@@ -3173,7 +3762,7 @@ var localesLocationRule = {
3173
3762
  create(context) {
3174
3763
  if (isLocalesFile(context.filename) || isTestFile(context.filename)) return {};
3175
3764
  const filename = context.filename;
3176
- const segments = path27.resolve(filename).split(path27.sep);
3765
+ const segments = path32.resolve(filename).split(path32.sep);
3177
3766
  const srcIdx = segments.lastIndexOf("src");
3178
3767
  if (srcIdx === -1) return {};
3179
3768
  const folder = segments[srcIdx + 1];
@@ -3197,7 +3786,7 @@ var localesLocationRule = {
3197
3786
  };
3198
3787
 
3199
3788
  // eslint/rules/hook-extraction.ts
3200
- import path28 from "path";
3789
+ import path33 from "path";
3201
3790
  var hookExtractionRule = {
3202
3791
  meta: {
3203
3792
  schema: [],
@@ -3208,7 +3797,7 @@ var hookExtractionRule = {
3208
3797
  },
3209
3798
  create(context) {
3210
3799
  const sourceRoot = sourceRootOf(context);
3211
- const file = path28.resolve(context.filename);
3800
+ const file = path33.resolve(context.filename);
3212
3801
  const segments = segmentsOf(file, sourceRoot);
3213
3802
  if (segments.length === 0) return {};
3214
3803
  const index = getProjectIndex(sourceRoot);
@@ -3234,7 +3823,7 @@ var hookExtractionRule = {
3234
3823
  };
3235
3824
 
3236
3825
  // eslint/rules/value-extraction.ts
3237
- import path29 from "path";
3826
+ import path34 from "path";
3238
3827
  var valueExtractionRule = {
3239
3828
  meta: {
3240
3829
  schema: [],
@@ -3245,7 +3834,7 @@ var valueExtractionRule = {
3245
3834
  },
3246
3835
  create(context) {
3247
3836
  const sourceRoot = sourceRootOf(context);
3248
- const file = path29.resolve(context.filename);
3837
+ const file = path34.resolve(context.filename);
3249
3838
  const segments = segmentsOf(file, sourceRoot);
3250
3839
  if (segments.length === 0 || segments[0] !== "app") return {};
3251
3840
  const index = getProjectIndex(sourceRoot);
@@ -3266,7 +3855,7 @@ var valueExtractionRule = {
3266
3855
  };
3267
3856
 
3268
3857
  // eslint/rules/config-extraction.ts
3269
- import path30 from "path";
3858
+ import path35 from "path";
3270
3859
  var configExtractionRule = {
3271
3860
  meta: {
3272
3861
  schema: [],
@@ -3277,7 +3866,7 @@ var configExtractionRule = {
3277
3866
  },
3278
3867
  create(context) {
3279
3868
  const sourceRoot = sourceRootOf(context);
3280
- const file = path30.resolve(context.filename);
3869
+ const file = path35.resolve(context.filename);
3281
3870
  const segments = segmentsOf(file, sourceRoot);
3282
3871
  if (segments.length < 3 || segments[0] !== "config") return {};
3283
3872
  if (SUPPORT_FOLDERS2.has(segments[2] ?? "")) return {};
@@ -3315,7 +3904,7 @@ var configExtractionRule = {
3315
3904
  };
3316
3905
 
3317
3906
  // eslint/rules/component-nesting.ts
3318
- import path31 from "path";
3907
+ import path36 from "path";
3319
3908
  var componentNestingRule = {
3320
3909
  meta: {
3321
3910
  schema: [],
@@ -3326,7 +3915,7 @@ var componentNestingRule = {
3326
3915
  },
3327
3916
  create(context) {
3328
3917
  const sourceRoot = sourceRootOf(context);
3329
- const file = path31.resolve(context.filename);
3918
+ const file = path36.resolve(context.filename);
3330
3919
  const segments = segmentsOf(file, sourceRoot);
3331
3920
  if (segments.length !== 4 || segments[0] !== "features") return {};
3332
3921
  const index = getProjectIndex(sourceRoot);
@@ -3359,7 +3948,7 @@ var componentNestingRule = {
3359
3948
  };
3360
3949
 
3361
3950
  // eslint/rules/stay-flat.ts
3362
- import path32 from "path";
3951
+ import path37 from "path";
3363
3952
  var stayFlatRule = {
3364
3953
  meta: {
3365
3954
  schema: [],
@@ -3370,7 +3959,7 @@ var stayFlatRule = {
3370
3959
  },
3371
3960
  create(context) {
3372
3961
  const sourceRoot = sourceRootOf(context);
3373
- const file = path32.resolve(context.filename);
3962
+ const file = path37.resolve(context.filename);
3374
3963
  const segments = segmentsOf(file, sourceRoot);
3375
3964
  if (segments.length !== 3 || segments[0] !== "features") return {};
3376
3965
  const index = getProjectIndex(sourceRoot);
@@ -3410,7 +3999,7 @@ var stayFlatRule = {
3410
3999
  };
3411
4000
 
3412
4001
  // eslint/rules/type-extraction.ts
3413
- import path33 from "path";
4002
+ import path38 from "path";
3414
4003
  var typeExtractionRule = {
3415
4004
  meta: {
3416
4005
  schema: [],
@@ -3421,7 +4010,7 @@ var typeExtractionRule = {
3421
4010
  },
3422
4011
  create(context) {
3423
4012
  const sourceRoot = sourceRootOf(context);
3424
- const file = path33.resolve(context.filename);
4013
+ const file = path38.resolve(context.filename);
3425
4014
  const segments = segmentsOf(file, sourceRoot);
3426
4015
  if (segments.length === 0) return {};
3427
4016
  const index = getProjectIndex(sourceRoot);
@@ -3467,9 +4056,9 @@ var typeExtractionRule = {
3467
4056
  };
3468
4057
 
3469
4058
  // eslint/rules/locale-placement.ts
3470
- import path34 from "path";
3471
- import { readFileSync as readFileSync4 } from "fs";
3472
- import ts5 from "typescript";
4059
+ import path39 from "path";
4060
+ import { readFileSync as readFileSync5 } from "fs";
4061
+ import ts8 from "typescript";
3473
4062
  var LOCALE_ACCESS = /\blocales\.(?<key>[A-Za-z_$][\w$]*)/g;
3474
4063
  var camelCase = (name) => name.replace(/-[a-z]/g, (match) => match.slice(1).toUpperCase());
3475
4064
  var FORCED_TOP_LEVEL = /* @__PURE__ */ new Set(["app", "shared", "compositions", "config"]);
@@ -3477,26 +4066,26 @@ function forcesTopLevel(segments) {
3477
4066
  return FORCED_TOP_LEVEL.has(segments[0] ?? "") || SUPPORT_FOLDERS2.has(segments[0] ?? "");
3478
4067
  }
3479
4068
  function localePlacement(text) {
3480
- const sourceFile = ts5.createSourceFile("locales.ts", text, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TS);
4069
+ const sourceFile = ts8.createSourceFile("locales.ts", text, ts8.ScriptTarget.Latest, true, ts8.ScriptKind.TS);
3481
4070
  for (const statement of sourceFile.statements) {
3482
- if (!ts5.isVariableStatement(statement)) continue;
3483
- const isExported2 = (ts5.getModifiers(statement) ?? []).some(
3484
- (modifier) => modifier.kind === ts5.SyntaxKind.ExportKeyword
4071
+ if (!ts8.isVariableStatement(statement)) continue;
4072
+ const isExported2 = (ts8.getModifiers(statement) ?? []).some(
4073
+ (modifier) => modifier.kind === ts8.SyntaxKind.ExportKeyword
3485
4074
  );
3486
4075
  if (!isExported2) continue;
3487
4076
  for (const declaration of statement.declarationList.declarations) {
3488
- if (!ts5.isIdentifier(declaration.name) || declaration.name.text !== "locales") continue;
3489
- if (!declaration.initializer || !ts5.isObjectLiteralExpression(declaration.initializer)) continue;
4077
+ if (!ts8.isIdentifier(declaration.name) || declaration.name.text !== "locales") continue;
4078
+ if (!declaration.initializer || !ts8.isObjectLiteralExpression(declaration.initializer)) continue;
3490
4079
  const placement = /* @__PURE__ */ new Map();
3491
4080
  for (const property of declaration.initializer.properties) {
3492
- if (!ts5.isPropertyAssignment(property)) continue;
4081
+ if (!ts8.isPropertyAssignment(property)) continue;
3493
4082
  let name;
3494
- if (ts5.isIdentifier(property.name)) name = property.name.text;
3495
- else if (ts5.isStringLiteral(property.name)) name = property.name.text;
4083
+ if (ts8.isIdentifier(property.name)) name = property.name.text;
4084
+ else if (ts8.isStringLiteral(property.name)) name = property.name.text;
3496
4085
  if (name === void 0) continue;
3497
4086
  const line = sourceFile.getLineAndCharacterOfPosition(property.getStart(sourceFile)).line + 1;
3498
4087
  placement.set(name, {
3499
- kind: ts5.isObjectLiteralExpression(property.initializer) ? "nested" : "top",
4088
+ kind: ts8.isObjectLiteralExpression(property.initializer) ? "nested" : "top",
3500
4089
  line
3501
4090
  });
3502
4091
  }
@@ -3515,7 +4104,7 @@ var localePlacementRule = {
3515
4104
  },
3516
4105
  create(context) {
3517
4106
  const sourceRoot = sourceRootOf(context);
3518
- const file = path34.resolve(context.filename);
4107
+ const file = path39.resolve(context.filename);
3519
4108
  const segments = segmentsOf(file, sourceRoot);
3520
4109
  if (segments.length === 0) return {};
3521
4110
  const index = getProjectIndex(sourceRoot);
@@ -3525,7 +4114,7 @@ var localePlacementRule = {
3525
4114
  return candidateSegments.length === 2 && candidateSegments[0] === "locales" && candidateSegments[1]?.startsWith("index.");
3526
4115
  });
3527
4116
  if (!localesFile || file !== localesFile) return {};
3528
- const placement = localePlacement(readFileSync4(localesFile, "utf8"));
4117
+ const placement = localePlacement(readFileSync5(localesFile, "utf8"));
3529
4118
  if (!placement) return {};
3530
4119
  const keyReaders = /* @__PURE__ */ new Map();
3531
4120
  const keyFeatures = /* @__PURE__ */ new Map();
@@ -3537,7 +4126,7 @@ var localePlacementRule = {
3537
4126
  );
3538
4127
  if (!importsLocales) continue;
3539
4128
  const candidateSegments = segmentsOf(candidateFile, sourceRoot);
3540
- for (const match of readFileSync4(candidateFile, "utf8").matchAll(LOCALE_ACCESS)) {
4129
+ for (const match of readFileSync5(candidateFile, "utf8").matchAll(LOCALE_ACCESS)) {
3541
4130
  const key = match.groups?.key;
3542
4131
  if (key === void 0) continue;
3543
4132
  const readers = keyReaders.get(key) ?? /* @__PURE__ */ new Set();
@@ -3593,39 +4182,39 @@ var localePlacementRule = {
3593
4182
  };
3594
4183
 
3595
4184
  // eslint/rules/sole-state-owner.ts
3596
- import path35 from "path";
3597
- import ts6 from "typescript";
4185
+ import path40 from "path";
4186
+ import ts9 from "typescript";
3598
4187
  function findStateHooks(node) {
3599
4188
  const hooks = [];
3600
4189
  const visit = (child) => {
3601
- if (ts6.isCallExpression(child) && ts6.isIdentifier(child.expression) && child.expression.text === "useState") {
3602
- if (ts6.isVariableDeclaration(child.parent)) {
4190
+ if (ts9.isCallExpression(child) && ts9.isIdentifier(child.expression) && child.expression.text === "useState") {
4191
+ if (ts9.isVariableDeclaration(child.parent)) {
3603
4192
  const { name } = child.parent;
3604
- if (ts6.isArrayBindingPattern(name) && name.elements.length >= 2) {
4193
+ if (ts9.isArrayBindingPattern(name) && name.elements.length >= 2) {
3605
4194
  const value = name.elements[0];
3606
4195
  const updater = name.elements[1];
3607
- if (value && updater && ts6.isBindingElement(value) && ts6.isBindingElement(updater)) {
4196
+ if (value && updater && ts9.isBindingElement(value) && ts9.isBindingElement(updater)) {
3608
4197
  const valueName = value.name;
3609
4198
  const updaterName = updater.name;
3610
- if (ts6.isIdentifier(valueName) && ts6.isIdentifier(updaterName)) {
4199
+ if (ts9.isIdentifier(valueName) && ts9.isIdentifier(updaterName)) {
3611
4200
  hooks.push({ value: valueName.text, updater: updaterName.text });
3612
4201
  }
3613
4202
  }
3614
4203
  }
3615
4204
  }
3616
4205
  }
3617
- ts6.forEachChild(child, visit);
4206
+ ts9.forEachChild(child, visit);
3618
4207
  };
3619
4208
  visit(node);
3620
4209
  return hooks;
3621
4210
  }
3622
4211
  function isHookUsage(node, hook) {
3623
- if (ts6.isCallExpression(node) && ts6.isIdentifier(node.expression) && node.expression.text === hook.updater) {
4212
+ if (ts9.isCallExpression(node) && ts9.isIdentifier(node.expression) && node.expression.text === hook.updater) {
3624
4213
  return "updater";
3625
4214
  }
3626
- if (ts6.isIdentifier(node) && node.text === hook.value) {
4215
+ if (ts9.isIdentifier(node) && node.text === hook.value) {
3627
4216
  const parent = node.parent;
3628
- if (ts6.isBindingElement(parent) || ts6.isPropertyAccessExpression(parent) || ts6.isShorthandPropertyAssignment(parent)) {
4217
+ if (ts9.isBindingElement(parent) || ts9.isPropertyAccessExpression(parent) || ts9.isShorthandPropertyAssignment(parent)) {
3629
4218
  return void 0;
3630
4219
  }
3631
4220
  return "value";
@@ -3635,16 +4224,16 @@ function isHookUsage(node, hook) {
3635
4224
  function topLevelJsxChildren(initial) {
3636
4225
  if (!initial) return void 0;
3637
4226
  let expression = initial;
3638
- while (ts6.isParenthesizedExpression(expression)) expression = expression.expression;
3639
- if (ts6.isJsxFragment(expression)) {
4227
+ while (ts9.isParenthesizedExpression(expression)) expression = expression.expression;
4228
+ if (ts9.isJsxFragment(expression)) {
3640
4229
  const children = expression.children.filter(
3641
- (c) => !ts6.isJsxText(c) && !ts6.isJsxSpreadAttribute(c)
4230
+ (c) => !ts9.isJsxText(c) && !ts9.isJsxSpreadAttribute(c)
3642
4231
  );
3643
4232
  return { root: expression, children };
3644
4233
  }
3645
- if (ts6.isJsxElement(expression)) {
4234
+ if (ts9.isJsxElement(expression)) {
3646
4235
  const children = expression.children.filter(
3647
- (c) => !ts6.isJsxText(c) && !ts6.isJsxSpreadAttribute(c)
4236
+ (c) => !ts9.isJsxText(c) && !ts9.isJsxSpreadAttribute(c)
3648
4237
  );
3649
4238
  return { root: expression, children };
3650
4239
  }
@@ -3658,8 +4247,8 @@ function collectUsesIn(child, hook) {
3658
4247
  positions.push(node);
3659
4248
  count += 1;
3660
4249
  }
3661
- if (ts6.isFunctionDeclaration(node) || ts6.isClassDeclaration(node)) return;
3662
- ts6.forEachChild(node, visit);
4250
+ if (ts9.isFunctionDeclaration(node) || ts9.isClassDeclaration(node)) return;
4251
+ ts9.forEachChild(node, visit);
3663
4252
  };
3664
4253
  visit(child);
3665
4254
  return { positions, count };
@@ -3673,7 +4262,7 @@ var soleStateOwnerRule = {
3673
4262
  }
3674
4263
  },
3675
4264
  create(context) {
3676
- const filename = path35.resolve(context.filename);
4265
+ const filename = path40.resolve(context.filename);
3677
4266
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
3678
4267
  const text = context.sourceCode.text;
3679
4268
  const components = parseComponentInfo(text, filename);
@@ -3698,18 +4287,18 @@ var soleStateOwnerRule = {
3698
4287
  }
3699
4288
  };
3700
4289
  function analyzeSoleOwner(declaration, hook) {
3701
- const body = ts6.isFunctionDeclaration(declaration) ? declaration.body : void 0;
3702
- if (!body || !ts6.isBlock(body)) return void 0;
4290
+ const body = ts9.isFunctionDeclaration(declaration) ? declaration.body : void 0;
4291
+ if (!body || !ts9.isBlock(body)) return void 0;
3703
4292
  const returns = [];
3704
4293
  const visit = (node) => {
3705
- if (node !== body && (ts6.isFunctionLike(node) || ts6.isClassLike(node))) return;
3706
- if (ts6.isReturnStatement(node)) returns.push(node);
3707
- ts6.forEachChild(node, visit);
4294
+ if (node !== body && (ts9.isFunctionLike(node) || ts9.isClassLike(node))) return;
4295
+ if (ts9.isReturnStatement(node)) returns.push(node);
4296
+ ts9.forEachChild(node, visit);
3708
4297
  };
3709
4298
  visit(body);
3710
4299
  if (returns.length !== 1) return void 0;
3711
4300
  const single = returns[0];
3712
- if (!single?.expression || !ts6.isExpression(single.expression)) return void 0;
4301
+ if (!single?.expression || !ts9.isExpression(single.expression)) return void 0;
3713
4302
  const returnExpression = single.expression;
3714
4303
  const root = topLevelJsxChildren(returnExpression);
3715
4304
  if (!root || root.children.length === 0) return void 0;
@@ -3732,11 +4321,11 @@ function usesOutsideJsx(declaration, hook, children) {
3732
4321
  let outside = false;
3733
4322
  const visit = (node) => {
3734
4323
  if (outside) return;
3735
- if (node !== declaration && (ts6.isFunctionDeclaration(node) || ts6.isClassDeclaration(node))) return;
4324
+ if (node !== declaration && (ts9.isFunctionDeclaration(node) || ts9.isClassDeclaration(node))) return;
3736
4325
  if (isHookUsage(node, hook)) {
3737
4326
  let current = node;
3738
4327
  let isInJsxChild = false;
3739
- while (!ts6.isSourceFile(current)) {
4328
+ while (!ts9.isSourceFile(current)) {
3740
4329
  if (children.includes(current)) {
3741
4330
  isInJsxChild = true;
3742
4331
  break;
@@ -3745,14 +4334,14 @@ function usesOutsideJsx(declaration, hook, children) {
3745
4334
  }
3746
4335
  if (!isInJsxChild) outside = true;
3747
4336
  }
3748
- ts6.forEachChild(node, visit);
4337
+ ts9.forEachChild(node, visit);
3749
4338
  };
3750
4339
  visit(declaration);
3751
4340
  return outside;
3752
4341
  }
3753
4342
 
3754
4343
  // eslint/rules/locale-key-shape.ts
3755
- import path36 from "path";
4344
+ import path41 from "path";
3756
4345
  var MAX_KEY_LENGTH = 30;
3757
4346
  var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
3758
4347
  "Button",
@@ -3802,7 +4391,7 @@ var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
3802
4391
  var CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
3803
4392
  var ENGLISH = /^[A-Za-z0-9_]*$/;
3804
4393
  function isLocalesFile2(filename) {
3805
- const segments = path36.resolve(filename).split(path36.sep);
4394
+ const segments = path41.resolve(filename).split(path41.sep);
3806
4395
  const srcIdx = segments.lastIndexOf("src");
3807
4396
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
3808
4397
  }
@@ -3873,8 +4462,8 @@ var localeKeyShapeRule = {
3873
4462
  };
3874
4463
 
3875
4464
  // eslint/rules/shared-style-dedup.ts
3876
- import path37 from "path";
3877
- import { readFileSync as readFileSync5, statSync as statSync3 } from "fs";
4465
+ import path42 from "path";
4466
+ import { readFileSync as readFileSync6, statSync as statSync3 } from "fs";
3878
4467
  var CLASS_NAME = /className="(?<classes>[^"]+)"/g;
3879
4468
  var comboCache;
3880
4469
  function combosFor(index) {
@@ -3888,7 +4477,7 @@ function combosFor(index) {
3888
4477
  }
3889
4478
  const combos = /* @__PURE__ */ new Map();
3890
4479
  for (const file of files) {
3891
- const text = readFileSync5(file, "utf8");
4480
+ const text = readFileSync6(file, "utf8");
3892
4481
  for (const match of text.matchAll(CLASS_NAME)) {
3893
4482
  const classes = (match.groups?.classes ?? "").split(/\s+/).filter(Boolean);
3894
4483
  if (classes.length < 2) continue;
@@ -3911,7 +4500,7 @@ var sharedStyleDedupRule = {
3911
4500
  },
3912
4501
  create(context) {
3913
4502
  const sourceRoot = sourceRootOf(context);
3914
- const file = path37.resolve(context.filename);
4503
+ const file = path42.resolve(context.filename);
3915
4504
  const segments = segmentsOf(file, sourceRoot);
3916
4505
  if (segments.length === 0) return {};
3917
4506
  const index = getProjectIndex(sourceRoot);
@@ -4115,7 +4704,7 @@ var zodSchemaValidationRule = {
4115
4704
  };
4116
4705
 
4117
4706
  // eslint/rules/schema-casing.ts
4118
- import path38 from "path";
4707
+ import path43 from "path";
4119
4708
  function isCamelCase(name) {
4120
4709
  return /^[a-z][a-zA-Z0-9]*$/.test(name);
4121
4710
  }
@@ -4143,9 +4732,9 @@ var schemaCasingRule = {
4143
4732
  }
4144
4733
  },
4145
4734
  create(context) {
4146
- const filename = path38.resolve(context.filename);
4735
+ const filename = path43.resolve(context.filename);
4147
4736
  const sourceRoot = sourceRootOf(context);
4148
- if (!filename.startsWith(sourceRoot + path38.sep)) return {};
4737
+ if (!filename.startsWith(sourceRoot + path43.sep)) return {};
4149
4738
  let zodLocalName;
4150
4739
  return {
4151
4740
  ImportDeclaration(node) {
@@ -4177,7 +4766,7 @@ var schemaCasingRule = {
4177
4766
  };
4178
4767
 
4179
4768
  // eslint/rules/component-casing.ts
4180
- import path39 from "path";
4769
+ import path44 from "path";
4181
4770
  function isPascalCase6(name) {
4182
4771
  return /^[A-Z][A-Za-z0-9]*$/.test(name);
4183
4772
  }
@@ -4190,10 +4779,10 @@ var componentCasingRule = {
4190
4779
  }
4191
4780
  },
4192
4781
  create(context) {
4193
- const filename = path39.resolve(context.filename);
4782
+ const filename = path44.resolve(context.filename);
4194
4783
  const sourceRoot = sourceRootOf(context);
4195
- if (!filename.startsWith(sourceRoot + path39.sep)) return {};
4196
- if (path39.extname(filename) !== ".tsx") return {};
4784
+ if (!filename.startsWith(sourceRoot + path44.sep)) return {};
4785
+ if (path44.extname(filename) !== ".tsx") return {};
4197
4786
  return {
4198
4787
  Program(node) {
4199
4788
  for (const declaration of findJsxReturningDeclarations(context.sourceCode.text, filename)) {
@@ -4210,7 +4799,7 @@ var componentCasingRule = {
4210
4799
  };
4211
4800
 
4212
4801
  // eslint/rules/source-under-src.ts
4213
- import path40 from "path";
4802
+ import path45 from "path";
4214
4803
  var NON_SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set([
4215
4804
  ".agents",
4216
4805
  ".cache",
@@ -4251,14 +4840,14 @@ var sourceUnderSrcRule = {
4251
4840
  }
4252
4841
  },
4253
4842
  create(context) {
4254
- const filename = path40.resolve(context.filename);
4843
+ const filename = path45.resolve(context.filename);
4255
4844
  if (!MODULE_EXTENSION.test(filename)) return {};
4256
- const relative = path40.relative(context.cwd, filename).replace(/\\/g, "/");
4845
+ const relative = path45.relative(context.cwd, filename).replace(/\\/g, "/");
4257
4846
  if (relative === "src" || relative.startsWith("src/")) return {};
4258
4847
  const topLevel = relative.split("/")[0] ?? "";
4259
4848
  if (NON_SOURCE_ROOT_DIRS.has(topLevel)) return {};
4260
4849
  if (!relative.includes("/")) {
4261
- const basename = path40.basename(filename);
4850
+ const basename = path45.basename(filename);
4262
4851
  if (CONFIG_FILE.test(basename) || DECLARATION_FILE.test(basename) || basename.startsWith(".")) return {};
4263
4852
  }
4264
4853
  return {
@@ -4275,8 +4864,8 @@ var sourceUnderSrcRule = {
4275
4864
 
4276
4865
  // eslint/rules/zirka-baseline.ts
4277
4866
  import fs5 from "fs";
4278
- import path41 from "path";
4279
- var ESLINT_CONFIG = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
4867
+ import path46 from "path";
4868
+ var ESLINT_CONFIG3 = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
4280
4869
  var PRETTIER_CONFIGS = [
4281
4870
  "prettier.config.mjs",
4282
4871
  "prettier.config.cjs",
@@ -4294,10 +4883,10 @@ var zirkaBaselineRule = {
4294
4883
  }
4295
4884
  },
4296
4885
  create(context) {
4297
- const filename = path41.resolve(context.filename);
4298
- const basename = path41.basename(filename);
4299
- if (!ESLINT_CONFIG.test(basename)) return {};
4300
- const projectRoot = path41.dirname(filename);
4886
+ const filename = path46.resolve(context.filename);
4887
+ const basename = path46.basename(filename);
4888
+ if (!ESLINT_CONFIG3.test(basename)) return {};
4889
+ const projectRoot = path46.dirname(filename);
4301
4890
  const report3 = (message) => {
4302
4891
  context.report({
4303
4892
  node: context.sourceCode.ast,
@@ -4312,7 +4901,7 @@ var zirkaBaselineRule = {
4312
4901
  'ESLint config must take its configuration from zirka (import { styleguide } from "zirka") instead of restating rules locally.'
4313
4902
  );
4314
4903
  }
4315
- const tsconfigPath = path41.join(projectRoot, "tsconfig.json");
4904
+ const tsconfigPath = path46.join(projectRoot, "tsconfig.json");
4316
4905
  if (!fs5.existsSync(tsconfigPath)) {
4317
4906
  report3('No tsconfig.json found. Create one extending the zirka TypeScript base config ("zirka/typescript").');
4318
4907
  } else {
@@ -4330,13 +4919,13 @@ var zirkaBaselineRule = {
4330
4919
  report3('tsconfig.json must extend the zirka TypeScript base config ("zirka/typescript").');
4331
4920
  }
4332
4921
  }
4333
- const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs5.existsSync(path41.join(projectRoot, name)));
4922
+ const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs5.existsSync(path46.join(projectRoot, name)));
4334
4923
  if (!prettierConfigFile) {
4335
4924
  report3(
4336
4925
  "No prettier config found. Create one that takes its configuration from zirka (styleguide({ prettier: true }).prettierConfig)."
4337
4926
  );
4338
4927
  } else {
4339
- const content = fs5.readFileSync(path41.join(projectRoot, prettierConfigFile), "utf8");
4928
+ const content = fs5.readFileSync(path46.join(projectRoot, prettierConfigFile), "utf8");
4340
4929
  if (!content.includes("zirka")) {
4341
4930
  report3(
4342
4931
  "The prettier config must take its configuration from zirka (styleguide({ prettier: true }).prettierConfig) instead of restating it locally."
@@ -4378,6 +4967,15 @@ function getTextContent(node) {
4378
4967
  function getLine(node) {
4379
4968
  return node.position?.start.line ?? 0;
4380
4969
  }
4970
+ function linkTarget(url) {
4971
+ return url.split("#")[0] ?? url;
4972
+ }
4973
+ function isDocLink(url) {
4974
+ return linkTarget(url).endsWith(".md");
4975
+ }
4976
+ function headingAnchor(text) {
4977
+ return text.trim().toLowerCase().replaceAll(/[^\p{L}\p{N}\s-]/gu, "").replaceAll(/\s+/g, "-");
4978
+ }
4381
4979
 
4382
4980
  // eslint/rules/documentation/doc-kind-suffix.ts
4383
4981
  var docKindSuffixRule = {
@@ -4406,7 +5004,7 @@ var docKindSuffixRule = {
4406
5004
  };
4407
5005
 
4408
5006
  // eslint/rules/documentation/title-matches-file-name.ts
4409
- import path42 from "path";
5007
+ import path47 from "path";
4410
5008
  function toExpectedFileName(title) {
4411
5009
  return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
4412
5010
  }
@@ -4426,7 +5024,7 @@ var titleMatchesFileNameRule = {
4426
5024
  if (!filename.endsWith(".md")) return;
4427
5025
  const title = getTextContent(node).trim();
4428
5026
  const expectedFileName = toExpectedFileName(title);
4429
- const actualFileName = path42.basename(filename);
5027
+ const actualFileName = path47.basename(filename);
4430
5028
  if (!title) {
4431
5029
  context.report({
4432
5030
  node,
@@ -4581,7 +5179,7 @@ var guideStepSingleSentenceRule = {
4581
5179
 
4582
5180
  // eslint/rules/documentation/guide-step-single-link.ts
4583
5181
  function countDocLinks(node) {
4584
- if (node.type === "link" && node.url.endsWith(".md")) return 1;
5182
+ if (node.type === "link" && isDocLink(node.url)) return 1;
4585
5183
  if ("children" in node) {
4586
5184
  return node.children.reduce((sum, child) => sum + countDocLinks(child), 0);
4587
5185
  }
@@ -4799,7 +5397,7 @@ var noCrossDocumentLinkRule = {
4799
5397
  const filename = getFilename(context);
4800
5398
  const kind = linkedKind(filename);
4801
5399
  if (!kind) return;
4802
- if (node.url.endsWith(".md")) {
5400
+ if (isDocLink(node.url)) {
4803
5401
  context.report({
4804
5402
  node,
4805
5403
  message: `${kind} links another document: ${node.url}`
@@ -4867,22 +5465,46 @@ var referenceBlockHeadingsRule = {
4867
5465
  }
4868
5466
  };
4869
5467
 
5468
+ // eslint/rules/documentation/reference-max-heading-depth.ts
5469
+ var referenceMaxHeadingDepthRule = {
5470
+ meta: {
5471
+ type: "problem",
5472
+ docs: {
5473
+ description: "Reference max heading depth rule.",
5474
+ recommended: true
5475
+ }
5476
+ },
5477
+ create(context) {
5478
+ return {
5479
+ heading(node) {
5480
+ const filename = getFilename(context);
5481
+ if (!filename.endsWith("-reference.md")) return;
5482
+ if (node.depth <= 2) return;
5483
+ context.report({
5484
+ node,
5485
+ message: "reference heading is deeper than level 2; flatten it into the section it would nest under"
5486
+ });
5487
+ }
5488
+ };
5489
+ }
5490
+ };
5491
+
4870
5492
  // eslint/rules/documentation/support-document-placement.ts
4871
5493
  import { existsSync as existsSync2 } from "fs";
4872
- import path43 from "path";
5494
+ import path48 from "path";
4873
5495
  function checkPlacement(filename, kind) {
4874
- const parentFolder = path43.basename(path43.dirname(filename));
5496
+ const parentFolder = path48.basename(path48.dirname(filename));
4875
5497
  const expectedParent = `${kind}s`;
4876
5498
  if (parentFolder !== expectedParent) {
4877
5499
  return `${kind} lives in "${parentFolder}/" instead of "${expectedParent}/"`;
4878
5500
  }
4879
- const guideFolderPath = path43.dirname(path43.dirname(filename));
4880
- const guideFolder = path43.basename(guideFolderPath);
5501
+ const guideFolderPath = path48.dirname(path48.dirname(filename));
5502
+ const guideFolder = path48.basename(guideFolderPath);
4881
5503
  if (!guideFolder.endsWith("-guide")) {
4882
5504
  return `${kind} owner folder "${guideFolder}/" does not use the "*-guide/" suffix`;
4883
5505
  }
4884
5506
  const entryPoint = `${guideFolder}.md`;
4885
- if (!existsSync2(path43.join(guideFolderPath, entryPoint))) {
5507
+ if (!existsSync2(path48.join(guideFolderPath, entryPoint))) {
4886
5508
  return `${kind} owner folder "${guideFolder}/" has no "${entryPoint}" entry point`;
4887
5509
  }
4888
5510
  return void 0;
@@ -4941,11 +5563,11 @@ var noTemplatePromptRule = {
4941
5563
  };
4942
5564
 
4943
5565
  // eslint/rules/documentation/guide-folder-entry-point.ts
4944
- import path45 from "path";
5566
+ import path50 from "path";
4945
5567
 
4946
5568
  // eslint/rules/documentation/project-index.ts
4947
- import { readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
4948
- import path44 from "path";
5569
+ import { readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
5570
+ import path49 from "path";
4949
5571
  var KIND_BY_SUFFIX = [
4950
5572
  ["-rule.md", "rule"],
4951
5573
  ["-guide.md", "guide"],
@@ -4954,7 +5576,7 @@ var KIND_BY_SUFFIX = [
4954
5576
  ];
4955
5577
  function listMarkdownFiles(dir) {
4956
5578
  return readdirSync3(dir).flatMap((entry) => {
4957
- const entryPath = path44.join(dir, entry);
5579
+ const entryPath = path49.join(dir, entry);
4958
5580
  if (statSync4(entryPath).isDirectory()) {
4959
5581
  return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
4960
5582
  }
@@ -4962,7 +5584,7 @@ function listMarkdownFiles(dir) {
4962
5584
  });
4963
5585
  }
4964
5586
  function extractTitle(filePath) {
4965
- const content = readFileSync6(filePath, "utf8");
5587
+ const content = readFileSync7(filePath, "utf8");
4966
5588
  const match = /^# (?<title>.+)$/m.exec(content);
4967
5589
  return match?.groups?.title?.trim() ?? "";
4968
5590
  }
@@ -4972,11 +5594,11 @@ function getProjectDocs(docsRoot) {
4972
5594
  if (cached) return cached;
4973
5595
  const files = listMarkdownFiles(docsRoot);
4974
5596
  const docs = files.sort((a, b) => a.localeCompare(b)).map((filePath) => {
4975
- const fileName = path44.basename(filePath);
5597
+ const fileName = path49.basename(filePath);
4976
5598
  const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
4977
5599
  return {
4978
5600
  filePath,
4979
- doc: path44.relative(docsRoot, filePath).split(path44.sep).join("/"),
5601
+ doc: path49.relative(docsRoot, filePath).split(path49.sep).join("/"),
4980
5602
  fileName,
4981
5603
  kind,
4982
5604
  title: extractTitle(filePath)
@@ -4986,12 +5608,12 @@ function getProjectDocs(docsRoot) {
4986
5608
  return docs;
4987
5609
  }
4988
5610
  function findDocsRoot(filePath) {
4989
- let dir = path44.dirname(filePath);
5611
+ let dir = path49.dirname(filePath);
4990
5612
  for (; ; ) {
4991
- if (path44.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
5613
+ if (path49.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
4992
5614
  return dir;
4993
5615
  }
4994
- const parent = path44.dirname(dir);
5616
+ const parent = path49.dirname(dir);
4995
5617
  if (parent === dir) return void 0;
4996
5618
  dir = parent;
4997
5619
  }
@@ -5015,13 +5637,13 @@ var guideFolderEntryPointRule = {
5015
5637
  if (!docsRoot) return;
5016
5638
  const docs = getProjectDocs(docsRoot);
5017
5639
  const guideFolders = new Set(
5018
- docs.filter((doc) => ["rules", "references"].includes(path45.basename(path45.dirname(doc.filePath)))).map((doc) => path45.dirname(path45.dirname(doc.filePath))).filter((folder) => path45.resolve(folder) !== path45.resolve(docsRoot))
5640
+ docs.filter((doc) => ["rules", "references"].includes(path50.basename(path50.dirname(doc.filePath)))).map((doc) => path50.dirname(path50.dirname(doc.filePath))).filter((folder) => path50.resolve(folder) !== path50.resolve(docsRoot))
5019
5641
  );
5020
- const currentDir = path45.dirname(filename);
5642
+ const currentDir = path50.dirname(filename);
5021
5643
  if (guideFolders.has(currentDir)) {
5022
- const expectedEntryPoint = `${path45.basename(currentDir)}.md`;
5644
+ const expectedEntryPoint = `${path50.basename(currentDir)}.md`;
5023
5645
  const hasEntryPoint = docs.some(
5024
- (doc) => doc.kind === "guide" && path45.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
5646
+ (doc) => doc.kind === "guide" && path50.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
5025
5647
  );
5026
5648
  if (!hasEntryPoint) {
5027
5649
  context.report({
@@ -5165,6 +5787,8 @@ var policySubjectHeadingsRule = {
5165
5787
  };
5166
5788
 
5167
5789
  // eslint/rules/documentation/guide-link-anchors.ts
5790
+ import { existsSync as existsSync3, readFileSync as readFileSync8 } from "fs";
5791
+ import path51 from "path";
5168
5792
  function visitSteps2(node, check) {
5169
5793
  if (node.type === "list" && node.ordered) {
5170
5794
  for (const child of node.children) check(child);
@@ -5174,17 +5798,52 @@ function visitSteps2(node, check) {
5174
5798
  }
5175
5799
  }
5176
5800
  function collectGuideLinks(node) {
5177
- if (node.type === "link" && node.url.endsWith("-guide.md")) return [node];
5801
+ if (node.type === "link" && linkTarget(node.url).endsWith("-guide.md")) return [node];
5178
5802
  if ("children" in node) {
5179
5803
  return node.children.flatMap(collectGuideLinks);
5180
5804
  }
5181
5805
  return [];
5182
5806
  }
5807
+ function collectLinks(node, out) {
5808
+ if (node.type === "link") out.push(node);
5809
+ if ("children" in node) {
5810
+ for (const child of node.children) collectLinks(child, out);
5811
+ }
5812
+ }
5813
+ var anchorsByFile = /* @__PURE__ */ new Map();
5814
+ function documentAnchors(filePath) {
5815
+ const cached = anchorsByFile.get(filePath);
5816
+ if (cached) return cached;
5817
+ const anchors = /* @__PURE__ */ new Set();
5818
+ anchorsByFile.set(filePath, anchors);
5819
+ let content;
5820
+ try {
5821
+ content = readFileSync8(filePath, "utf8");
5822
+ } catch {
5823
+ return anchors;
5824
+ }
5825
+ const seen = /* @__PURE__ */ new Map();
5826
+ let fenced = false;
5827
+ for (const line of content.split("\n")) {
5828
+ if (/^\s*(?:```|~~~)/.test(line)) {
5829
+ fenced = !fenced;
5830
+ continue;
5831
+ }
5832
+ if (fenced) continue;
5833
+ const heading = /^#{1,6}\s+(?<text>.+)$/.exec(line)?.groups?.text;
5834
+ if (!heading) continue;
5835
+ const base = headingAnchor(heading);
5836
+ const count = seen.get(base) ?? 0;
5837
+ seen.set(base, count + 1);
5838
+ anchors.add(count === 0 ? base : `${base}-${String(count)}`);
5839
+ }
5840
+ return anchors;
5841
+ }
5183
5842
  var guideLinkAnchorsRule = {
5184
5843
  meta: {
5185
5844
  type: "problem",
5186
5845
  docs: {
5187
- description: "A step that links another Guide must link directly to a How To section.",
5846
+ description: "A step that links another Guide must link directly to a How To section, and a link that carries an anchor must point at a heading the linked document has.",
5188
5847
  recommended: true
5189
5848
  }
5190
5849
  },
@@ -5203,6 +5862,19 @@ var guideLinkAnchorsRule = {
5203
5862
  }
5204
5863
  }
5205
5864
  });
5865
+ const links = [];
5866
+ collectLinks(node, links);
5867
+ for (const link of links) {
5868
+ const [target = "", fragment = ""] = link.url.split("#");
5869
+ if (!fragment || !isDocLink(link.url)) continue;
5870
+ const targetPath = path51.resolve(path51.dirname(filename), target);
5871
+ if (!existsSync3(targetPath)) continue;
5872
+ if (documentAnchors(targetPath).has(fragment.toLowerCase())) continue;
5873
+ context.report({
5874
+ node: link,
5875
+ message: `link ${link.url} points at a heading the document does not have`
5876
+ });
5877
+ }
5206
5878
  }
5207
5879
  };
5208
5880
  }
@@ -5245,41 +5917,82 @@ var noNestedHowToRule = {
5245
5917
  };
5246
5918
 
5247
5919
  // eslint/rules/documentation/glossary-term-linking.ts
5248
- import { readFileSync as readFileSync7 } from "fs";
5249
- import path46 from "path";
5920
+ import { readFileSync as readFileSync9 } from "fs";
5921
+ import path52 from "path";
5922
+ function isGlossary(filePath) {
5923
+ return path52.basename(filePath).includes("glossary");
5924
+ }
5925
+ function termNames(cell) {
5926
+ const abbreviation = /\((?<abbreviation>[^()]+)\)/.exec(cell)?.groups?.abbreviation?.trim();
5927
+ return abbreviation ? [cell, abbreviation] : [cell];
5928
+ }
5929
+ function extractTableTerms(content) {
5930
+ const terms = [];
5931
+ const rowPattern = /^\|(?<cell>[^|]*)\|/gm;
5932
+ for (const match of content.matchAll(rowPattern)) {
5933
+ const cell = match.groups?.cell?.trim() ?? "";
5934
+ if (!cell || /^:?-+:?$/.test(cell) || cell.toLowerCase() === "term") continue;
5935
+ terms.push({ term: cell, names: termNames(cell) });
5936
+ }
5937
+ return terms;
5938
+ }
5250
5939
  function extractGlossaryTerms(filePath) {
5251
- const content = readFileSync7(filePath, "utf8");
5940
+ if (!isGlossary(filePath)) return [];
5941
+ const content = readFileSync9(filePath, "utf8");
5942
+ const tableTerms = extractTableTerms(content);
5943
+ if (tableTerms.length > 0) return tableTerms;
5252
5944
  const terms = [];
5253
5945
  const headingPattern = /^## (?<term>.+)$/gm;
5254
- let match;
5255
- while ((match = headingPattern.exec(content)) !== null) {
5946
+ for (const match of content.matchAll(headingPattern)) {
5256
5947
  const term = match.groups?.term?.trim();
5257
- if (term) terms.push(term);
5948
+ if (term) terms.push({ term, names: termNames(term) });
5258
5949
  }
5259
5950
  return terms;
5260
5951
  }
5261
- function collectSteps(node) {
5262
- const texts = [];
5263
- const firstStepLinks = [];
5264
- if (node.type === "list" && node.ordered) {
5265
- for (const item of node.children) {
5266
- texts.push(getTextContent(item));
5267
- if (firstStepLinks.length === 0) {
5268
- collectDocLinks(item, firstStepLinks);
5269
- }
5952
+ function extractBlockHeadings(filePath) {
5953
+ const headings = [];
5954
+ let fenced = false;
5955
+ for (const line of readFileSync9(filePath, "utf8").split("\n")) {
5956
+ if (/^\s*(?:```|~~~)/.test(line)) {
5957
+ fenced = !fenced;
5958
+ continue;
5270
5959
  }
5960
+ if (fenced) continue;
5961
+ const heading = /^##\s+(?<text>.+)$/.exec(line)?.groups?.text;
5962
+ if (heading) headings.push(heading.trim());
5271
5963
  }
5272
- if ("children" in node) {
5273
- for (const child of node.children) {
5274
- const nested = collectSteps(child);
5275
- texts.push(...nested.texts);
5276
- if (firstStepLinks.length === 0) firstStepLinks.push(...nested.firstStepLinks);
5277
- }
5964
+ return headings;
5965
+ }
5966
+ function normalize(text) {
5967
+ return text.replaceAll("`", "").toLowerCase();
5968
+ }
5969
+ function collectSections(node) {
5970
+ const sections = [];
5971
+ const children = node.children;
5972
+ for (const [index, child] of children.entries()) {
5973
+ if (child.type !== "heading" || child.depth !== 2) continue;
5974
+ const title = getTextContent(child).trim();
5975
+ if (!/^How To \S/.test(title)) continue;
5976
+ const end = children.findIndex(
5977
+ (sibling, siblingIndex) => siblingIndex > index && sibling.type === "heading" && sibling.depth === 2
5978
+ );
5979
+ const section = { heading: child, title, stepTexts: [], firstStepLinks: [] };
5980
+ collectSteps(children.slice(index + 1, end === -1 ? children.length : end), section);
5981
+ sections.push(section);
5982
+ }
5983
+ return sections;
5984
+ }
5985
+ function collectSteps(content, section) {
5986
+ const lists = content.filter((node) => node.type === "list");
5987
+ const paragraphs = content.filter((node) => node.type === "paragraph");
5988
+ const steps = lists.length > 0 ? lists.flatMap((list) => list.children) : paragraphs.slice(content[0]?.type === "paragraph" ? 1 : 0);
5989
+ for (const step of steps) {
5990
+ section.stepTexts.push(getTextContent(step));
5991
+ if (section.stepTexts.length === 1) collectDocLinks(step, section.firstStepLinks);
5278
5992
  }
5279
- return { texts, firstStepLinks };
5280
5993
  }
5281
5994
  function collectDocLinks(node, out) {
5282
- if (node.type === "link" && node.url.endsWith(".md")) out.push(node.url);
5995
+ if (node.type === "link" && isDocLink(node.url)) out.push(node.url);
5283
5996
  if ("children" in node) {
5284
5997
  for (const child of node.children) collectDocLinks(child, out);
5285
5998
  }
@@ -5288,7 +6001,7 @@ var glossaryTermLinkingRule = {
5288
6001
  meta: {
5289
6002
  type: "problem",
5290
6003
  docs: {
5291
- description: "A Guide whose steps use glossary terms must link that Reference from its first step.",
6004
+ description: "A How To section whose steps use glossary terms must link that Reference from the first step, and every block of a shared glossary must be read by one of them.",
5292
6005
  recommended: true
5293
6006
  }
5294
6007
  },
@@ -5300,25 +6013,55 @@ var glossaryTermLinkingRule = {
5300
6013
  const docsRoot = findDocsRoot(filename);
5301
6014
  if (!docsRoot) return;
5302
6015
  const docs = getProjectDocs(docsRoot);
5303
- const guideDir = path46.dirname(filename);
6016
+ const guideDir = path52.dirname(filename);
6017
+ const referencesDir = path52.join(guideDir, "references");
5304
6018
  const guideReferences = docs.filter(
5305
- (doc) => doc.kind === "reference" && path46.dirname(doc.filePath) === guideDir
6019
+ (doc) => doc.kind === "reference" && path52.dirname(doc.filePath) === referencesDir
5306
6020
  );
5307
- if (guideReferences.length === 0) return;
6021
+ const glossaryReferences = guideReferences.filter((doc) => isGlossary(doc.filePath));
6022
+ if (glossaryReferences.length === 0) return;
5308
6023
  const glossaryTerms = [];
5309
- for (const ref of guideReferences) {
6024
+ for (const ref of glossaryReferences) {
5310
6025
  glossaryTerms.push(...extractGlossaryTerms(ref.filePath));
5311
6026
  }
5312
6027
  if (glossaryTerms.length === 0) return;
5313
- const { texts: stepTexts, firstStepLinks } = collectSteps(node);
5314
- const usedTerms = glossaryTerms.filter((term) => stepTexts.some((text) => text.includes(term)));
5315
- if (usedTerms.length === 0) return;
5316
- const hasRefLink = firstStepLinks.some((link) => guideReferences.some((ref) => link.includes(ref.fileName)));
5317
- if (!hasRefLink) {
5318
- context.report({
5319
- node,
5320
- message: `guide uses glossary terms (${usedTerms.join(", ")}) but first step does not link the reference`
5321
- });
6028
+ const sections = collectSections(node);
6029
+ for (const ref of glossaryReferences) {
6030
+ for (const heading of extractBlockHeadings(ref.filePath)) {
6031
+ const anchor = headingAnchor(heading);
6032
+ const anchored = sections.some(
6033
+ (section) => section.firstStepLinks.some((link) => {
6034
+ const fragment = link.split("#")[1] ?? "";
6035
+ if (fragment.toLowerCase() !== anchor) return false;
6036
+ return path52.resolve(guideDir, linkTarget(link)) === path52.resolve(ref.filePath);
6037
+ })
6038
+ );
6039
+ if (!anchored) {
6040
+ context.report({
6041
+ node,
6042
+ message: `shared glossary block "${heading}" is not read by any How To section's first step`
6043
+ });
6044
+ }
6045
+ }
6046
+ }
6047
+ for (const section of sections) {
6048
+ const normalizedStepTexts = section.stepTexts.map(normalize);
6049
+ const usedTerms = glossaryTerms.filter(
6050
+ (entry) => entry.names.some((name) => {
6051
+ const needle = normalize(name);
6052
+ return normalizedStepTexts.some((text) => text.includes(needle));
6053
+ })
6054
+ ).map((entry) => entry.term);
6055
+ if (usedTerms.length === 0) continue;
6056
+ const hasGlossaryLink = section.firstStepLinks.some(
6057
+ (link) => glossaryReferences.some((ref) => link.includes(ref.fileName))
6058
+ );
6059
+ if (!hasGlossaryLink) {
6060
+ context.report({
6061
+ node: section.heading,
6062
+ message: `guide section "${section.title}" uses glossary terms (${usedTerms.join(", ")}) but its first step does not link the glossary reference`
6063
+ });
6064
+ }
5322
6065
  }
5323
6066
  }
5324
6067
  };
@@ -5326,8 +6069,8 @@ var glossaryTermLinkingRule = {
5326
6069
  };
5327
6070
 
5328
6071
  // eslint/rules/documentation/guide-mentions-documents.ts
5329
- import { existsSync as existsSync3 } from "fs";
5330
- import path47 from "path";
6072
+ import { existsSync as existsSync4 } from "fs";
6073
+ import path53 from "path";
5331
6074
  function visitSteps3(node, check) {
5332
6075
  if (node.type === "list" && node.ordered) {
5333
6076
  for (const child of node.children) check(child);
@@ -5337,14 +6080,11 @@ function visitSteps3(node, check) {
5337
6080
  }
5338
6081
  }
5339
6082
  function collectMarkdownLinks(node, out) {
5340
- if (node.type === "link" && node.url.endsWith(".md")) out.push(node);
6083
+ if (node.type === "link" && isDocLink(node.url)) out.push(node);
5341
6084
  if ("children" in node) {
5342
6085
  for (const child of node.children) collectMarkdownLinks(child, out);
5343
6086
  }
5344
6087
  }
5345
- function linkTarget(url) {
5346
- return url.split("#")[0] ?? url;
5347
- }
5348
6088
  var guideMentionsDocumentsRule = {
5349
6089
  meta: {
5350
6090
  type: "problem",
@@ -5360,12 +6100,12 @@ var guideMentionsDocumentsRule = {
5360
6100
  if (!filename.endsWith("-guide.md")) return;
5361
6101
  const docsRoot = findDocsRoot(filename);
5362
6102
  if (!docsRoot) return;
5363
- const guideDir = path47.dirname(filename);
5364
- if (path47.basename(filename, ".md") !== path47.basename(guideDir)) return;
6103
+ const guideDir = path53.dirname(filename);
6104
+ if (path53.basename(filename, ".md") !== path53.basename(guideDir)) return;
5365
6105
  const docs = getProjectDocs(docsRoot);
5366
6106
  const owned = docs.filter((doc) => {
5367
- const parent = path47.dirname(doc.filePath);
5368
- return parent === path47.join(guideDir, "rules") || parent === path47.join(guideDir, "references");
6107
+ const parent = path53.dirname(doc.filePath);
6108
+ return parent === path53.join(guideDir, "rules") || parent === path53.join(guideDir, "references");
5369
6109
  });
5370
6110
  const allLinks = [];
5371
6111
  collectMarkdownLinks(node, allLinks);
@@ -5373,7 +6113,7 @@ var guideMentionsDocumentsRule = {
5373
6113
  visitSteps3(node, (item) => {
5374
6114
  collectMarkdownLinks(item, stepLinks);
5375
6115
  });
5376
- const mentionsOf = (links, fileName) => links.some((link) => link.url.split("/").pop() === fileName);
6116
+ const mentionsOf = (links, fileName) => links.some((link) => linkTarget(link.url).split("/").pop() === fileName);
5377
6117
  for (const doc of owned) {
5378
6118
  if (doc.kind === "rule") {
5379
6119
  if (!mentionsOf(stepLinks, doc.fileName)) {
@@ -5392,8 +6132,8 @@ var guideMentionsDocumentsRule = {
5392
6132
  for (const link of allLinks) {
5393
6133
  const target = linkTarget(link.url);
5394
6134
  if (!target.endsWith(".md")) continue;
5395
- const resolved = path47.normalize(path47.join(guideDir, target));
5396
- if (!existsSync3(resolved)) {
6135
+ const resolved = path53.normalize(path53.join(guideDir, target));
6136
+ if (!existsSync4(resolved)) {
5397
6137
  context.report({
5398
6138
  node: link,
5399
6139
  message: `Guide links a document that does not exist: ${link.url}`
@@ -5470,6 +6210,7 @@ var documentationRules = {
5470
6210
  "no-cross-document-link": noCrossDocumentLinkRule,
5471
6211
  "reference-no-rfc-vocabulary": referenceNoRfcVocabularyRule,
5472
6212
  "reference-block-headings": referenceBlockHeadingsRule,
6213
+ "reference-max-heading-depth": referenceMaxHeadingDepthRule,
5473
6214
  "support-document-placement": supportDocumentPlacementRule,
5474
6215
  "no-template-prompt": noTemplatePromptRule,
5475
6216
  "guide-folder-entry-point": guideFolderEntryPointRule,
@@ -5942,11 +6683,11 @@ var themeVariableNamespaceRule = {
5942
6683
 
5943
6684
  // eslint/rules/tailwind/css-entry-point.ts
5944
6685
  import { statSync as statSync6 } from "fs";
5945
- import path50 from "path";
6686
+ import path56 from "path";
5946
6687
 
5947
6688
  // eslint/rules/tailwind/source-files.ts
5948
- import { readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
5949
- import path48 from "path";
6689
+ import { readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
6690
+ import path54 from "path";
5950
6691
  var CSS_EXTENSIONS = [".css"];
5951
6692
  var MODULE_EXTENSIONS3 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
5952
6693
  var SOURCE_EXTENSIONS = [...MODULE_EXTENSIONS3, ...CSS_EXTENSIONS];
@@ -5959,7 +6700,7 @@ function findFiles(dir, extensions) {
5959
6700
  }
5960
6701
  return entries.flatMap((entry) => {
5961
6702
  if (entry.startsWith(".") || entry === "node_modules") return [];
5962
- const entryPath = path48.join(dir, entry);
6703
+ const entryPath = path54.join(dir, entry);
5963
6704
  let stats;
5964
6705
  try {
5965
6706
  stats = statSync5(entryPath);
@@ -5967,7 +6708,7 @@ function findFiles(dir, extensions) {
5967
6708
  return [];
5968
6709
  }
5969
6710
  if (stats.isDirectory()) return findFiles(entryPath, extensions);
5970
- return extensions.includes(path48.extname(entry)) ? [entryPath] : [];
6711
+ return extensions.includes(path54.extname(entry)) ? [entryPath] : [];
5971
6712
  });
5972
6713
  }
5973
6714
  function cachedTextReader() {
@@ -5976,7 +6717,7 @@ function cachedTextReader() {
5976
6717
  let text = texts.get(file);
5977
6718
  if (text === void 0) {
5978
6719
  try {
5979
- text = readFileSync8(file, "utf8");
6720
+ text = readFileSync10(file, "utf8");
5980
6721
  } catch {
5981
6722
  text = "";
5982
6723
  }
@@ -5990,7 +6731,7 @@ function escapeRegExp(text) {
5990
6731
  }
5991
6732
 
5992
6733
  // eslint/rules/tailwind/stylesheet-graph.ts
5993
- import path49 from "path";
6734
+ import path55 from "path";
5994
6735
  function registersTailwind(text) {
5995
6736
  return /@import\s+(?:url\(\s*)?["']tailwindcss["']\s*\)?/i.test(text);
5996
6737
  }
@@ -6004,25 +6745,25 @@ function moduleImports(text, fileName) {
6004
6745
  return new RegExp(`(?:import|require)\\s*\\(?\\s*["'][^"']*${escaped}["']`, "i").test(text);
6005
6746
  }
6006
6747
  function resolveSpecifier2(fromFile, spec, sourceRoot) {
6007
- if (spec.startsWith("/")) return path49.resolve(spec);
6008
- if (spec.startsWith("./") || spec.startsWith("../")) return path49.resolve(path49.dirname(fromFile), spec);
6009
- if (spec.startsWith("@/")) return path49.resolve(sourceRoot, spec.slice(2));
6748
+ if (spec.startsWith("/")) return path55.resolve(spec);
6749
+ if (spec.startsWith("./") || spec.startsWith("../")) return path55.resolve(path55.dirname(fromFile), spec);
6750
+ if (spec.startsWith("@/")) return path55.resolve(sourceRoot, spec.slice(2));
6010
6751
  return void 0;
6011
6752
  }
6012
6753
  function buildStylesheetGraph(options) {
6013
6754
  const { cssFiles, sourceRoot, textOf } = options;
6014
- const cssSet = new Set(cssFiles.map((file) => path49.normalize(file)));
6755
+ const cssSet = new Set(cssFiles.map((file) => path55.normalize(file)));
6015
6756
  const globals = cssFiles.filter((file) => registersTailwind(textOf(file)));
6016
6757
  const reachable = /* @__PURE__ */ new Set();
6017
6758
  const queue = [...globals];
6018
- for (const global of globals) reachable.add(path49.normalize(global));
6759
+ for (const global of globals) reachable.add(path55.normalize(global));
6019
6760
  while (queue.length > 0) {
6020
6761
  const from = queue.shift();
6021
6762
  if (!from) continue;
6022
6763
  for (const spec of importedSpecifiers(textOf(from))) {
6023
6764
  const target = resolveSpecifier2(from, spec, sourceRoot);
6024
6765
  if (!target) continue;
6025
- const normalized = path49.normalize(target);
6766
+ const normalized = path55.normalize(target);
6026
6767
  if (cssSet.has(normalized) && !reachable.has(normalized)) {
6027
6768
  reachable.add(normalized);
6028
6769
  queue.push(normalized);
@@ -6034,7 +6775,7 @@ function buildStylesheetGraph(options) {
6034
6775
  for (const spec of importedSpecifiers(textOf(global))) {
6035
6776
  const target = resolveSpecifier2(global, spec, sourceRoot);
6036
6777
  if (!target) continue;
6037
- const normalized = path49.normalize(target);
6778
+ const normalized = path55.normalize(target);
6038
6779
  if (cssSet.has(normalized)) directChildren.add(normalized);
6039
6780
  }
6040
6781
  }
@@ -6067,7 +6808,7 @@ var cssEntryPointRule = {
6067
6808
  return {
6068
6809
  "StyleSheet:exit"(node) {
6069
6810
  if (globals.length === 0) return;
6070
- const current = path50.normalize(path50.resolve(context.filename));
6811
+ const current = path56.normalize(path56.resolve(context.filename));
6071
6812
  if (globals.includes(current)) {
6072
6813
  if (globals.length > 1) {
6073
6814
  context.report({
@@ -6076,7 +6817,7 @@ var cssEntryPointRule = {
6076
6817
  });
6077
6818
  return;
6078
6819
  }
6079
- const basename = path50.basename(current);
6820
+ const basename = path56.basename(current);
6080
6821
  const importCount = moduleFiles.filter((modulePath) => moduleImports(textOf(modulePath), basename)).length;
6081
6822
  if (importCount !== 1) {
6082
6823
  context.report({
@@ -6404,8 +7145,8 @@ var nextjsStackRule = {
6404
7145
  };
6405
7146
 
6406
7147
  // eslint/rules/package-json/vitest-coverage.ts
6407
- import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
6408
- import path51 from "path";
7148
+ import { existsSync as existsSync5, readFileSync as readFileSync11 } from "fs";
7149
+ import path57 from "path";
6409
7150
  function memberName4(member) {
6410
7151
  return member.name.type === "String" ? member.name.value : member.name.name;
6411
7152
  }
@@ -6474,7 +7215,7 @@ var vitestCoverageRule = {
6474
7215
  message: 'package.json must declare a "test:unit:coverage" script that runs Vitest with coverage.'
6475
7216
  });
6476
7217
  }
6477
- const configName = VITEST_CONFIG_NAMES.find((name) => existsSync4(path51.join(context.cwd, name)));
7218
+ const configName = VITEST_CONFIG_NAMES.find((name) => existsSync5(path57.join(context.cwd, name)));
6478
7219
  if (!configName) {
6479
7220
  context.report({
6480
7221
  node,
@@ -6482,7 +7223,7 @@ var vitestCoverageRule = {
6482
7223
  });
6483
7224
  return;
6484
7225
  }
6485
- const content = readFileSync9(path51.join(context.cwd, configName), "utf8");
7226
+ const content = readFileSync11(path57.join(context.cwd, configName), "utf8");
6486
7227
  for (const metric of THRESHOLD_METRICS) {
6487
7228
  if (!new RegExp(`\\b${metric}\\s*:\\s*[1-9]\\d*`).test(content)) {
6488
7229
  context.report({ node, message: `${configName} must set a coverage threshold above zero for ${metric}.` });
@@ -6528,8 +7269,8 @@ var nextjsPackageJsonRules = {
6528
7269
  };
6529
7270
 
6530
7271
  // eslint/rules/husky/husky-hook.ts
6531
- import { existsSync as existsSync5, readFileSync as readFileSync10 } from "fs";
6532
- import path52 from "path";
7272
+ import { existsSync as existsSync6, readFileSync as readFileSync12 } from "fs";
7273
+ import path58 from "path";
6533
7274
  var VITEST_CONFIG_NAMES2 = [
6534
7275
  "vitest.config.ts",
6535
7276
  "vitest.config.mts",
@@ -6555,15 +7296,15 @@ var huskyHookRule = {
6555
7296
  const root = node.body;
6556
7297
  if (root.type !== "Object") return;
6557
7298
  if (!context.filename.endsWith("package.json")) return;
6558
- const hookPath = path52.join(context.cwd, ".husky", "pre-commit");
6559
- if (!existsSync5(hookPath)) {
7299
+ const hookPath = path58.join(context.cwd, ".husky", "pre-commit");
7300
+ if (!existsSync6(hookPath)) {
6560
7301
  context.report({
6561
7302
  node,
6562
7303
  message: "No .husky/pre-commit hook found. Configure husky to run checks before commits."
6563
7304
  });
6564
7305
  return;
6565
7306
  }
6566
- const content = readFileSync10(hookPath, "utf8");
7307
+ const content = readFileSync12(hookPath, "utf8");
6567
7308
  const scripts = root.members.find((member) => memberName5(member) === "scripts");
6568
7309
  const scriptNames = new Set(scripts?.value.type === "Object" ? scripts.value.members.map(memberName5) : []);
6569
7310
  const requireNamedScript = (name) => {
@@ -6579,7 +7320,7 @@ var huskyHookRule = {
6579
7320
  }
6580
7321
  requireNamedScript("typecheck");
6581
7322
  requireNamedScript("test:unit:coverage");
6582
- const vitestConfigName = VITEST_CONFIG_NAMES2.find((name) => existsSync5(path52.join(context.cwd, name)));
7323
+ const vitestConfigName = VITEST_CONFIG_NAMES2.find((name) => existsSync6(path58.join(context.cwd, name)));
6583
7324
  if (vitestConfigName !== void 0) {
6584
7325
  const coverageIndex = content.indexOf("npm run test:unit:coverage");
6585
7326
  const localAddIndex = content.indexOf(`git add ${vitestConfigName}`);
@@ -6593,8 +7334,8 @@ var huskyHookRule = {
6593
7334
  if (!content.includes("libyear --limit-major-individual=1")) {
6594
7335
  context.report({ node, message: ".husky/pre-commit must run npx libyear --limit-major-individual=1." });
6595
7336
  }
6596
- const suppressionsPath = path52.join(context.cwd, "eslint-suppressions.json");
6597
- if (existsSync5(suppressionsPath)) {
7337
+ const suppressionsPath = path58.join(context.cwd, "eslint-suppressions.json");
7338
+ if (existsSync6(suppressionsPath)) {
6598
7339
  requireNamedScript("lint:prune");
6599
7340
  const pruneIndex = content.indexOf("npm run lint:prune");
6600
7341
  const localAddIndex = content.indexOf("git add eslint-suppressions.json");
@@ -6624,73 +7365,33 @@ var huskyRules = {
6624
7365
  "husky-hook": huskyHookRule
6625
7366
  };
6626
7367
 
6627
- // eslint/rules/vulyk/vulyk-dependency.ts
6628
- function memberName6(member) {
6629
- return member.name.type === "String" ? member.name.value : member.name.name;
6630
- }
6631
- function dependency(root, sectionName) {
6632
- if (root.type !== "Object") return void 0;
6633
- const section = root.members.find((member) => memberName6(member) === sectionName);
6634
- if (section?.value.type !== "Object") return void 0;
6635
- return section.value.members.find((member) => memberName6(member) === "vulyk");
6636
- }
6637
- var vulykDependencyRule = {
6638
- meta: {
6639
- schema: [],
6640
- type: "problem",
6641
- docs: {
6642
- description: "Require vulyk in devDependencies so its typed config and CLI use the pinned package."
6643
- }
6644
- },
6645
- create(context) {
6646
- return {
6647
- Document(node) {
6648
- const runtimeDependency = dependency(node.body, "dependencies");
6649
- const developmentDependency = dependency(node.body, "devDependencies");
6650
- if (runtimeDependency) {
6651
- context.report({
6652
- node: runtimeDependency,
6653
- message: "vulyk must be listed in devDependencies, not dependencies."
6654
- });
6655
- }
6656
- if (!developmentDependency && !runtimeDependency) {
6657
- context.report({
6658
- node,
6659
- message: "vulyk must be listed in package.json as a devDependency."
6660
- });
6661
- }
6662
- }
6663
- };
6664
- }
6665
- };
6666
-
6667
- // eslint/rules/vulyk/vulyk-docs.ts
6668
- import { existsSync as existsSync6, readFileSync as readFileSync11 } from "fs";
6669
- import path53 from "path";
7368
+ // eslint/rules/vulyk/tracked-docs.ts
7369
+ import { existsSync as existsSync7, readFileSync as readFileSync13 } from "fs";
7370
+ import path59 from "path";
6670
7371
  var PASIKA_REPO = "Bredansky/pasika";
6671
- var BASE_REQUIRED_DOCS = [
7372
+ var BASE_REQUIRED_TRACKED_DOCS = [
6672
7373
  { name: "documentation-guide", path: "docs/documentation-guide" },
6673
7374
  { name: "pasika-adoption-guide", path: "docs/pasika-adoption-guide" },
6674
7375
  { name: "repository-policy", path: "docs/repository-policy.md" }
6675
7376
  ];
6676
- var NEXTJS_REQUIRED_DOCS = [
7377
+ var NEXTJS_REQUIRED_TRACKED_DOCS = [
6677
7378
  { name: "next-codebase-guide", path: "docs/next-codebase-guide" },
6678
7379
  { name: "next-tailwind-guide", path: "docs/next-tailwind-guide" }
6679
7380
  ];
6680
- function memberName7(member) {
7381
+ function memberName6(member) {
6681
7382
  return member.name.type === "String" ? member.name.value : member.name.name;
6682
7383
  }
6683
7384
  function hasDependency(root, name) {
6684
- const section = root.members.find((member) => memberName7(member) === "dependencies");
7385
+ const section = root.members.find((member) => memberName6(member) === "dependencies");
6685
7386
  if (section?.value.type !== "Object") return false;
6686
- return section.value.members.some((member) => memberName7(member) === name);
7387
+ return section.value.members.some((member) => memberName6(member) === name);
6687
7388
  }
6688
- var vulykDocsRule = {
7389
+ var trackedDocsRule = {
6689
7390
  meta: {
6690
7391
  schema: [],
6691
7392
  type: "problem",
6692
7393
  docs: {
6693
- description: "Require vulyk.config.ts to track the framework's required docs from pasika and the generated AGENTS.md."
7394
+ description: "Require vulyk.config.ts to track the framework's required tracked docs from pasika and the generated AGENTS.md."
6694
7395
  }
6695
7396
  },
6696
7397
  create(context) {
@@ -6699,34 +7400,34 @@ var vulykDocsRule = {
6699
7400
  if (!context.filename.endsWith("package.json")) return;
6700
7401
  const root = node.body;
6701
7402
  if (root.type !== "Object") return;
6702
- const projectRoot = path53.dirname(path53.resolve(context.filename));
6703
- const configPath = path53.join(projectRoot, "vulyk.config.ts");
6704
- if (!existsSync6(configPath)) {
7403
+ const projectRoot = path59.dirname(path59.resolve(context.filename));
7404
+ const configPath = path59.join(projectRoot, "vulyk.config.ts");
7405
+ if (!existsSync7(configPath)) {
6705
7406
  context.report({
6706
7407
  node,
6707
- message: "No vulyk.config.ts found. Run npx vulyk init to create one that tracks the framework's docs."
7408
+ message: "No vulyk.config.ts found. Run npx vulyk init to create one that tracks the framework's required tracked docs."
6708
7409
  });
6709
7410
  return;
6710
7411
  }
6711
- const config = readFileSync11(configPath, "utf8");
7412
+ const config = readFileSync13(configPath, "utf8");
6712
7413
  if (!config.includes(PASIKA_REPO)) {
6713
7414
  context.report({
6714
7415
  node,
6715
- message: "vulyk.config.ts must track the framework's docs from the pasika repository."
7416
+ message: "vulyk.config.ts must track the framework's required tracked docs from the pasika repository."
6716
7417
  });
6717
7418
  return;
6718
7419
  }
6719
- const requiredDocs = hasDependency(root, "next") ? [...BASE_REQUIRED_DOCS, ...NEXTJS_REQUIRED_DOCS] : BASE_REQUIRED_DOCS;
6720
- for (const doc of requiredDocs) {
6721
- if (!config.includes(doc.path)) {
7420
+ const requiredTrackedDocs = hasDependency(root, "next") ? [...BASE_REQUIRED_TRACKED_DOCS, ...NEXTJS_REQUIRED_TRACKED_DOCS] : BASE_REQUIRED_TRACKED_DOCS;
7421
+ for (const trackedDoc of requiredTrackedDocs) {
7422
+ if (!config.includes(trackedDoc.path)) {
6722
7423
  context.report({
6723
7424
  node,
6724
- message: `vulyk.config.ts must track the framework's ${doc.name} docs from pasika (${PASIKA_REPO}/${doc.path}).`
7425
+ message: `vulyk.config.ts must track the framework's ${trackedDoc.name} tracked docs from pasika (${PASIKA_REPO}/${trackedDoc.path}).`
6725
7426
  });
6726
7427
  }
6727
7428
  }
6728
- const agentsPath = path53.join(projectRoot, "AGENTS.md");
6729
- if (!existsSync6(agentsPath)) {
7429
+ const agentsPath = path59.join(projectRoot, "AGENTS.md");
7430
+ if (!existsSync7(agentsPath)) {
6730
7431
  context.report({
6731
7432
  node,
6732
7433
  message: "No AGENTS.md found. Run npx vulyk agents to generate the agent file that routes to the tracked docs."
@@ -6737,10 +7438,50 @@ var vulykDocsRule = {
6737
7438
  }
6738
7439
  };
6739
7440
 
7441
+ // eslint/rules/vulyk/vulyk-dependency.ts
7442
+ function memberName7(member) {
7443
+ return member.name.type === "String" ? member.name.value : member.name.name;
7444
+ }
7445
+ function dependency(root, sectionName) {
7446
+ if (root.type !== "Object") return void 0;
7447
+ const section = root.members.find((member) => memberName7(member) === sectionName);
7448
+ if (section?.value.type !== "Object") return void 0;
7449
+ return section.value.members.find((member) => memberName7(member) === "vulyk");
7450
+ }
7451
+ var vulykDependencyRule = {
7452
+ meta: {
7453
+ schema: [],
7454
+ type: "problem",
7455
+ docs: {
7456
+ description: "Require vulyk in devDependencies so its typed config and CLI use the pinned package."
7457
+ }
7458
+ },
7459
+ create(context) {
7460
+ return {
7461
+ Document(node) {
7462
+ const runtimeDependency = dependency(node.body, "dependencies");
7463
+ const developmentDependency = dependency(node.body, "devDependencies");
7464
+ if (runtimeDependency) {
7465
+ context.report({
7466
+ node: runtimeDependency,
7467
+ message: "vulyk must be listed in devDependencies, not dependencies."
7468
+ });
7469
+ }
7470
+ if (!developmentDependency && !runtimeDependency) {
7471
+ context.report({
7472
+ node,
7473
+ message: "vulyk must be listed in package.json as a devDependency."
7474
+ });
7475
+ }
7476
+ }
7477
+ };
7478
+ }
7479
+ };
7480
+
6740
7481
  // eslint/rules/vulyk/index.ts
6741
7482
  var vulykRules = {
6742
7483
  "vulyk-dependency": vulykDependencyRule,
6743
- "vulyk-docs": vulykDocsRule
7484
+ "tracked-docs": trackedDocsRule
6744
7485
  };
6745
7486
 
6746
7487
  // eslint/index.ts
@@ -6782,6 +7523,10 @@ var pasikaNextjsAppRules = {
6782
7523
  "cva-boolean-variants": cvaBooleanVariantsRule,
6783
7524
  "cross-feature-import": crossFeatureImportRule,
6784
7525
  "pure-function-extract": pureFunctionExtractRule,
7526
+ "root-support-placement": rootSupportPlacementRule,
7527
+ "route-handler-shape": routeHandlerShapeRule,
7528
+ "http-error-usage": httpErrorUsageRule,
7529
+ "with-response-helper": withResponseHelperRule,
6785
7530
  "hook-complexity": hookComplexityRule,
6786
7531
  "locale-dotted-path": localeDottedPathRule,
6787
7532
  "locales-location": localesLocationRule,
@@ -6886,6 +7631,11 @@ var zirkaConfig = {
6886
7631
  plugins: { pasika: pasikaPlugin },
6887
7632
  rules: { "pasika/zirka-baseline": "error" }
6888
7633
  };
7634
+ var nextjsHelperConfig = {
7635
+ files: ["eslint.config.{ts,mts,cts,js,mjs,cjs}"],
7636
+ plugins: { pasika: pasikaPlugin },
7637
+ rules: { "pasika/cn-helper": "error", "pasika/with-response-helper": "error" }
7638
+ };
6889
7639
  var documentationConfig = {
6890
7640
  files: ["docs/**/*.md"],
6891
7641
  // vulyk-generated agent files are not authored docs: with per-directory
@@ -6915,6 +7665,7 @@ var pasikaNextjsAppWithDiagnostic = (preset) => {
6915
7665
  var pasikaNextjsApp = pasikaNextjsAppWithDiagnostic([
6916
7666
  ...pasikaApp,
6917
7667
  pasikaNextjsAppPackageJsonConfig,
7668
+ nextjsHelperConfig,
6918
7669
  pasikaNextjsAppConfig,
6919
7670
  tailwindStructureRules,
6920
7671
  tailwindImportGraph