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