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