pasika 0.7.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/eslint/pasika/index.d.ts +3 -0
- package/dist/eslint/pasika/index.js +291 -122
- package/package.json +1 -1
|
@@ -199,11 +199,14 @@ declare const pasikaPlugin: {
|
|
|
199
199
|
"enforce-barrel-exports": eslint.Rule.RuleModule;
|
|
200
200
|
"config-extraction": eslint.Rule.RuleModule;
|
|
201
201
|
"value-extraction": eslint.Rule.RuleModule;
|
|
202
|
+
"constant-casing": eslint.Rule.RuleModule;
|
|
202
203
|
"type-extraction": eslint.Rule.RuleModule;
|
|
203
204
|
"zod-schema-validation": eslint.Rule.RuleModule;
|
|
205
|
+
"schema-casing": eslint.Rule.RuleModule;
|
|
204
206
|
"source-under-src": eslint.Rule.RuleModule;
|
|
205
207
|
"zirka-baseline": eslint.Rule.RuleModule;
|
|
206
208
|
"component-placement": eslint.Rule.RuleModule;
|
|
209
|
+
"component-casing": eslint.Rule.RuleModule;
|
|
207
210
|
"application-structure": eslint.Rule.RuleModule;
|
|
208
211
|
"data-testid-case": eslint.Rule.RuleModule;
|
|
209
212
|
"jsx-hygiene": eslint.Rule.RuleModule;
|
|
@@ -137,6 +137,30 @@ function parseComponentInfo(text, filename, options) {
|
|
|
137
137
|
}
|
|
138
138
|
return components;
|
|
139
139
|
}
|
|
140
|
+
function findJsxReturningDeclarations(text, filename) {
|
|
141
|
+
const sourceFile = ts.createSourceFile(path.resolve(filename), text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
142
|
+
const results = [];
|
|
143
|
+
const record = (identifier, body) => {
|
|
144
|
+
if (isHookCallName(identifier.text) || !containsJsx(body)) return;
|
|
145
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(identifier.getStart(sourceFile));
|
|
146
|
+
results.push({ name: identifier.text, line: line + 1, column: character });
|
|
147
|
+
};
|
|
148
|
+
for (const statement of sourceFile.statements) {
|
|
149
|
+
if (ts.isFunctionDeclaration(statement) && statement.name) {
|
|
150
|
+
record(statement.name, statement);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (ts.isVariableStatement(statement)) {
|
|
154
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
155
|
+
if (!ts.isIdentifier(declaration.name)) continue;
|
|
156
|
+
const initializer = declaration.initializer;
|
|
157
|
+
if (!initializer || !ts.isArrowFunction(initializer) && !ts.isFunctionExpression(initializer)) continue;
|
|
158
|
+
record(declaration.name, initializer);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return results;
|
|
163
|
+
}
|
|
140
164
|
function jsxTagName(element) {
|
|
141
165
|
const tagName2 = ts.isJsxElement(element) ? element.openingElement.tagName : element.tagName;
|
|
142
166
|
return ts.isIdentifier(tagName2) ? tagName2.text : void 0;
|
|
@@ -2056,7 +2080,7 @@ var supportFolderShapeRule = {
|
|
|
2056
2080
|
schema: [],
|
|
2057
2081
|
type: "problem",
|
|
2058
2082
|
docs: {
|
|
2059
|
-
description: "Require support
|
|
2083
|
+
description: "Require a support folder to either define its exports directly in index.ts or re-export every sibling from it, never both."
|
|
2060
2084
|
}
|
|
2061
2085
|
},
|
|
2062
2086
|
create(context) {
|
|
@@ -2078,13 +2102,22 @@ var supportFolderShapeRule = {
|
|
|
2078
2102
|
Program(node) {
|
|
2079
2103
|
const source = context.sourceCode.text;
|
|
2080
2104
|
const hasDirectExport = /export\s+(?:const|let|var|function|class|type|interface|enum)\b/.test(source);
|
|
2081
|
-
if (hasDirectExport) return;
|
|
2082
2105
|
const exportedFiles = /* @__PURE__ */ new Set();
|
|
2083
2106
|
const exportPattern = /export\s+(?:\{[^}]*\}|\*[^;]*)\s+from\s+["'](?<specifier>\.[^"']+)["']/g;
|
|
2084
2107
|
for (const match of source.matchAll(exportPattern)) {
|
|
2085
2108
|
const specifier = match.groups?.specifier;
|
|
2086
2109
|
if (specifier) exportedFiles.add(path16.basename(specifier));
|
|
2087
2110
|
}
|
|
2111
|
+
const hasAnyReExport = exportedFiles.size > 0;
|
|
2112
|
+
const guide = `docs/next-codebase-guide/rules/${folder === "constants" ? "constants" : "types-and-schemas"}-rule.md`;
|
|
2113
|
+
if (hasDirectExport && hasAnyReExport) {
|
|
2114
|
+
context.report({
|
|
2115
|
+
node,
|
|
2116
|
+
message: `${folder}/index.ts mixes direct exports with re-exported support files; pick one strategy for the whole folder. See ${guide}`
|
|
2117
|
+
});
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
if (hasDirectExport) return;
|
|
2088
2121
|
const missing = siblingModules.filter((entry) => {
|
|
2089
2122
|
const stem = entry.replace(/\.(?:[cm]?tsx?|jsx?)$/, "");
|
|
2090
2123
|
return !exportedFiles.has(stem);
|
|
@@ -2092,15 +2125,53 @@ var supportFolderShapeRule = {
|
|
|
2092
2125
|
if (missing.length === 0) return;
|
|
2093
2126
|
context.report({
|
|
2094
2127
|
node,
|
|
2095
|
-
message: `${folder}/index.ts must
|
|
2128
|
+
message: `${folder}/index.ts must re-export every support file: ${missing.join(", ")}. See ${guide}`
|
|
2096
2129
|
});
|
|
2097
2130
|
}
|
|
2098
2131
|
};
|
|
2099
2132
|
}
|
|
2100
2133
|
};
|
|
2101
2134
|
|
|
2102
|
-
// eslint/rules/
|
|
2135
|
+
// eslint/rules/constant-casing.ts
|
|
2103
2136
|
import path17 from "path";
|
|
2137
|
+
var NEXTJS_ROUTE_HANDLER_NAMES = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
2138
|
+
function isScreamingSnakeCase(name) {
|
|
2139
|
+
return /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/.test(name);
|
|
2140
|
+
}
|
|
2141
|
+
var constantCasingRule = {
|
|
2142
|
+
meta: {
|
|
2143
|
+
schema: [],
|
|
2144
|
+
type: "problem",
|
|
2145
|
+
docs: {
|
|
2146
|
+
description: "Require a module-level constant to be camelCase, unless a framework requires a specific name."
|
|
2147
|
+
}
|
|
2148
|
+
},
|
|
2149
|
+
create(context) {
|
|
2150
|
+
const filename = path17.resolve(context.filename);
|
|
2151
|
+
const sourceRoot = sourceRootOf(context);
|
|
2152
|
+
if (!filename.startsWith(sourceRoot + path17.sep)) return {};
|
|
2153
|
+
return {
|
|
2154
|
+
VariableDeclarator(node) {
|
|
2155
|
+
if (node.id.type !== "Identifier") return;
|
|
2156
|
+
const declaration = node.parent;
|
|
2157
|
+
if (declaration.type !== "VariableDeclaration" || declaration.kind !== "const") return;
|
|
2158
|
+
const container = declaration.parent;
|
|
2159
|
+
const isModuleLevel = container.type === "Program" || container.type === "ExportNamedDeclaration" && container.parent.type === "Program";
|
|
2160
|
+
if (!isModuleLevel) return;
|
|
2161
|
+
const { name } = node.id;
|
|
2162
|
+
if (!isScreamingSnakeCase(name)) return;
|
|
2163
|
+
if (NEXTJS_ROUTE_HANDLER_NAMES.has(name)) return;
|
|
2164
|
+
context.report({
|
|
2165
|
+
node,
|
|
2166
|
+
message: `${name} must be camelCase, unless a framework requires this exact name. See docs/next-codebase-guide/rules/constants-rule.md`
|
|
2167
|
+
});
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
|
|
2173
|
+
// eslint/rules/import-through-index.ts
|
|
2174
|
+
import path18 from "path";
|
|
2104
2175
|
var importThroughIndexRule = {
|
|
2105
2176
|
meta: {
|
|
2106
2177
|
schema: [],
|
|
@@ -2110,7 +2181,7 @@ var importThroughIndexRule = {
|
|
|
2110
2181
|
}
|
|
2111
2182
|
},
|
|
2112
2183
|
create(context) {
|
|
2113
|
-
const filename =
|
|
2184
|
+
const filename = path18.resolve(context.filename);
|
|
2114
2185
|
const sourceRoot = sourceRootOf2(context, filename);
|
|
2115
2186
|
return {
|
|
2116
2187
|
Program(node) {
|
|
@@ -2122,7 +2193,7 @@ var importThroughIndexRule = {
|
|
|
2122
2193
|
(segment) => ["constants", "types", "schemas"].includes(segment)
|
|
2123
2194
|
);
|
|
2124
2195
|
const supportFolder = supportFolderIndex >= 0 ? targetSegments[supportFolderIndex] : void 0;
|
|
2125
|
-
if (!supportFolder ||
|
|
2196
|
+
if (!supportFolder || path18.basename(target).startsWith("index.")) continue;
|
|
2126
2197
|
const folderIndex = targetSegments.slice(0, supportFolderIndex + 1);
|
|
2127
2198
|
const expected = `@/${folderIndex.join("/")}`;
|
|
2128
2199
|
context.report({
|
|
@@ -2144,14 +2215,14 @@ function importSpecifiers(source) {
|
|
|
2144
2215
|
return specifiers;
|
|
2145
2216
|
}
|
|
2146
2217
|
function sourceRootOf2(context, filename) {
|
|
2147
|
-
const marker = `${
|
|
2218
|
+
const marker = `${path18.sep}src${path18.sep}`;
|
|
2148
2219
|
const srcIndex = filename.lastIndexOf(marker);
|
|
2149
2220
|
if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
|
|
2150
|
-
return
|
|
2221
|
+
return path18.resolve(context.cwd ?? process.cwd(), "src");
|
|
2151
2222
|
}
|
|
2152
2223
|
|
|
2153
2224
|
// eslint/rules/util-file-name.ts
|
|
2154
|
-
import
|
|
2225
|
+
import path19 from "path";
|
|
2155
2226
|
function toKebabCase2(value) {
|
|
2156
2227
|
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();
|
|
2157
2228
|
}
|
|
@@ -2164,7 +2235,7 @@ var utilFileNameRule = {
|
|
|
2164
2235
|
}
|
|
2165
2236
|
},
|
|
2166
2237
|
create(context) {
|
|
2167
|
-
const filename =
|
|
2238
|
+
const filename = path19.resolve(context.filename);
|
|
2168
2239
|
const segments = filename.replace(/\\/g, "/").split("/");
|
|
2169
2240
|
if (!segments.includes("utils")) return {};
|
|
2170
2241
|
let module;
|
|
@@ -2178,13 +2249,13 @@ var utilFileNameRule = {
|
|
|
2178
2249
|
const functionName = functions[0]?.name;
|
|
2179
2250
|
if (!functionName) return {};
|
|
2180
2251
|
const expected = toKebabCase2(functionName);
|
|
2181
|
-
const actual =
|
|
2252
|
+
const actual = path19.basename(filename, path19.extname(filename));
|
|
2182
2253
|
if (!expected || actual === expected) return {};
|
|
2183
2254
|
return {
|
|
2184
2255
|
Program(node) {
|
|
2185
2256
|
context.report({
|
|
2186
2257
|
node,
|
|
2187
|
-
message: `A utility file exporting ${functionName} must be named ${expected}.${
|
|
2258
|
+
message: `A utility file exporting ${functionName} must be named ${expected}.${path19.extname(filename).slice(1)}.`
|
|
2188
2259
|
});
|
|
2189
2260
|
}
|
|
2190
2261
|
};
|
|
@@ -2192,7 +2263,7 @@ var utilFileNameRule = {
|
|
|
2192
2263
|
};
|
|
2193
2264
|
|
|
2194
2265
|
// eslint/rules/no-util-barrel.ts
|
|
2195
|
-
import
|
|
2266
|
+
import path20 from "path";
|
|
2196
2267
|
var noUtilBarrelRule = {
|
|
2197
2268
|
meta: {
|
|
2198
2269
|
schema: [],
|
|
@@ -2202,7 +2273,7 @@ var noUtilBarrelRule = {
|
|
|
2202
2273
|
}
|
|
2203
2274
|
},
|
|
2204
2275
|
create(context) {
|
|
2205
|
-
const filename =
|
|
2276
|
+
const filename = path20.resolve(context.filename);
|
|
2206
2277
|
const sourceRoot = sourceRootOf3(context, filename);
|
|
2207
2278
|
return {
|
|
2208
2279
|
Program(node) {
|
|
@@ -2211,7 +2282,7 @@ var noUtilBarrelRule = {
|
|
|
2211
2282
|
if (!target) continue;
|
|
2212
2283
|
const segments = target.replace(/\\/g, "/").split("/");
|
|
2213
2284
|
const utilsIndex = segments.lastIndexOf("utils");
|
|
2214
|
-
if (utilsIndex < 0 || !
|
|
2285
|
+
if (utilsIndex < 0 || !path20.basename(target).startsWith("index.")) continue;
|
|
2215
2286
|
context.report({
|
|
2216
2287
|
node,
|
|
2217
2288
|
message: `Import utilities directly instead of through "${specifier}". See docs/next-codebase-guide/rules/utilities-rule.md`
|
|
@@ -2231,10 +2302,10 @@ function importSpecifiers2(source) {
|
|
|
2231
2302
|
return specifiers;
|
|
2232
2303
|
}
|
|
2233
2304
|
function sourceRootOf3(context, filename) {
|
|
2234
|
-
const marker = `${
|
|
2305
|
+
const marker = `${path20.sep}src${path20.sep}`;
|
|
2235
2306
|
const srcIndex = filename.lastIndexOf(marker);
|
|
2236
2307
|
if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
|
|
2237
|
-
return
|
|
2308
|
+
return path20.resolve(context.cwd ?? process.cwd(), "src");
|
|
2238
2309
|
}
|
|
2239
2310
|
|
|
2240
2311
|
// eslint/rules/jsx-hygiene.ts
|
|
@@ -2327,7 +2398,7 @@ var jsxHygieneRule = {
|
|
|
2327
2398
|
};
|
|
2328
2399
|
|
|
2329
2400
|
// eslint/rules/interactive-component.ts
|
|
2330
|
-
import
|
|
2401
|
+
import path21 from "path";
|
|
2331
2402
|
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
2332
2403
|
"a",
|
|
2333
2404
|
"button",
|
|
@@ -2482,8 +2553,8 @@ var interactiveComponentRule = {
|
|
|
2482
2553
|
},
|
|
2483
2554
|
create(context) {
|
|
2484
2555
|
if (!context.filename.endsWith(".tsx") && !context.filename.endsWith(".jsx")) return {};
|
|
2485
|
-
const filename =
|
|
2486
|
-
const base =
|
|
2556
|
+
const filename = path21.resolve(context.filename);
|
|
2557
|
+
const base = path21.basename(filename, path21.extname(filename));
|
|
2487
2558
|
if (NEXT_ROUTING_FILES3.has(base)) return {};
|
|
2488
2559
|
return {
|
|
2489
2560
|
JSXElement(node) {
|
|
@@ -2734,12 +2805,12 @@ var cvaBooleanVariantsRule = {
|
|
|
2734
2805
|
};
|
|
2735
2806
|
|
|
2736
2807
|
// eslint/rules/cross-feature-import.ts
|
|
2737
|
-
import
|
|
2808
|
+
import path22 from "path";
|
|
2738
2809
|
var FEATURES_SEGMENT = "features";
|
|
2739
2810
|
function featureNameOf(resolvedPath, sourceRoot) {
|
|
2740
|
-
const relative =
|
|
2811
|
+
const relative = path22.relative(sourceRoot, resolvedPath);
|
|
2741
2812
|
if (relative.startsWith("..")) return void 0;
|
|
2742
|
-
const segments = relative.split(
|
|
2813
|
+
const segments = relative.split(path22.sep);
|
|
2743
2814
|
if (segments[0] !== FEATURES_SEGMENT || segments.length < 2) return void 0;
|
|
2744
2815
|
return segments[1];
|
|
2745
2816
|
}
|
|
@@ -2755,9 +2826,9 @@ var crossFeatureImportRule = {
|
|
|
2755
2826
|
const filename = context.filename;
|
|
2756
2827
|
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
|
|
2757
2828
|
const sourceRoot = sourceRootOf(context);
|
|
2758
|
-
const fileRelative =
|
|
2829
|
+
const fileRelative = path22.relative(sourceRoot, filename);
|
|
2759
2830
|
if (fileRelative.startsWith("..")) return {};
|
|
2760
|
-
const fileSegments = fileRelative.split(
|
|
2831
|
+
const fileSegments = fileRelative.split(path22.sep);
|
|
2761
2832
|
const isInCompositions = fileSegments[0] === "compositions";
|
|
2762
2833
|
const isInApp = fileSegments[0] === "app";
|
|
2763
2834
|
const isConfig = fileSegments[0] === "config";
|
|
@@ -2771,9 +2842,9 @@ var crossFeatureImportRule = {
|
|
|
2771
2842
|
if (typeof source.value !== "string") return;
|
|
2772
2843
|
let resolved;
|
|
2773
2844
|
if (source.value.startsWith("@/")) {
|
|
2774
|
-
resolved =
|
|
2845
|
+
resolved = path22.resolve(sourceRoot, source.value.slice(2));
|
|
2775
2846
|
} else if (source.value.startsWith(".")) {
|
|
2776
|
-
resolved =
|
|
2847
|
+
resolved = path22.resolve(path22.dirname(filename), source.value);
|
|
2777
2848
|
}
|
|
2778
2849
|
if (!resolved) return;
|
|
2779
2850
|
const feature = featureNameOf(resolved, sourceRoot);
|
|
@@ -2792,7 +2863,7 @@ var crossFeatureImportRule = {
|
|
|
2792
2863
|
};
|
|
2793
2864
|
|
|
2794
2865
|
// eslint/rules/pure-function-extract.ts
|
|
2795
|
-
import
|
|
2866
|
+
import path23 from "path";
|
|
2796
2867
|
function isComponentLikeName(name) {
|
|
2797
2868
|
return /^[A-Z]/.test(name);
|
|
2798
2869
|
}
|
|
@@ -2821,9 +2892,9 @@ var pureFunctionExtractRule = {
|
|
|
2821
2892
|
const filename = context.filename;
|
|
2822
2893
|
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
|
|
2823
2894
|
const sourceRoot = sourceRootOf(context);
|
|
2824
|
-
const relative =
|
|
2895
|
+
const relative = path23.relative(sourceRoot, filename);
|
|
2825
2896
|
if (relative.startsWith("..")) return {};
|
|
2826
|
-
const segments = relative.split(
|
|
2897
|
+
const segments = relative.split(path23.sep);
|
|
2827
2898
|
if (segments[0] === "utils") return {};
|
|
2828
2899
|
if (segments[0] === "app") return {};
|
|
2829
2900
|
const supportFolders = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
|
|
@@ -2863,7 +2934,7 @@ var pureFunctionExtractRule = {
|
|
|
2863
2934
|
};
|
|
2864
2935
|
|
|
2865
2936
|
// eslint/rules/hook-complexity.ts
|
|
2866
|
-
import
|
|
2937
|
+
import path24 from "path";
|
|
2867
2938
|
import ts4 from "typescript";
|
|
2868
2939
|
var REACT_HOOKS = /* @__PURE__ */ new Set([
|
|
2869
2940
|
"useState",
|
|
@@ -2916,9 +2987,9 @@ var hookComplexityRule = {
|
|
|
2916
2987
|
create(context) {
|
|
2917
2988
|
const filename = context.filename;
|
|
2918
2989
|
const sourceRoot = sourceRootOf(context);
|
|
2919
|
-
const relative =
|
|
2990
|
+
const relative = path24.relative(sourceRoot, filename);
|
|
2920
2991
|
if (relative.startsWith("..")) return {};
|
|
2921
|
-
const segments = relative.split(
|
|
2992
|
+
const segments = relative.split(path24.sep);
|
|
2922
2993
|
const sourceText = context.sourceCode.text;
|
|
2923
2994
|
function checkHook(node, name, body, exported) {
|
|
2924
2995
|
if (!exported) return;
|
|
@@ -2960,9 +3031,9 @@ var hookComplexityRule = {
|
|
|
2960
3031
|
};
|
|
2961
3032
|
|
|
2962
3033
|
// eslint/rules/locale-dotted-path.ts
|
|
2963
|
-
import
|
|
3034
|
+
import path25 from "path";
|
|
2964
3035
|
function isInLocalesDir(filename) {
|
|
2965
|
-
const segments =
|
|
3036
|
+
const segments = path25.resolve(filename).split(path25.sep);
|
|
2966
3037
|
const srcIdx = segments.lastIndexOf("src");
|
|
2967
3038
|
return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
|
|
2968
3039
|
}
|
|
@@ -3011,9 +3082,9 @@ var localeDottedPathRule = {
|
|
|
3011
3082
|
};
|
|
3012
3083
|
|
|
3013
3084
|
// eslint/rules/locales-location.ts
|
|
3014
|
-
import
|
|
3085
|
+
import path26 from "path";
|
|
3015
3086
|
function isLocalesFile(filename) {
|
|
3016
|
-
const segments =
|
|
3087
|
+
const segments = path26.resolve(filename).split(path26.sep);
|
|
3017
3088
|
const srcIdx = segments.lastIndexOf("src");
|
|
3018
3089
|
return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
|
|
3019
3090
|
}
|
|
@@ -3032,7 +3103,7 @@ var localesLocationRule = {
|
|
|
3032
3103
|
create(context) {
|
|
3033
3104
|
if (isLocalesFile(context.filename)) return {};
|
|
3034
3105
|
const filename = context.filename;
|
|
3035
|
-
const segments =
|
|
3106
|
+
const segments = path26.resolve(filename).split(path26.sep);
|
|
3036
3107
|
const srcIdx = segments.lastIndexOf("src");
|
|
3037
3108
|
if (srcIdx === -1) return {};
|
|
3038
3109
|
const folder = segments[srcIdx + 1];
|
|
@@ -3054,7 +3125,7 @@ var localesLocationRule = {
|
|
|
3054
3125
|
};
|
|
3055
3126
|
|
|
3056
3127
|
// eslint/rules/hook-extraction.ts
|
|
3057
|
-
import
|
|
3128
|
+
import path27 from "path";
|
|
3058
3129
|
var hookExtractionRule = {
|
|
3059
3130
|
meta: {
|
|
3060
3131
|
schema: [],
|
|
@@ -3065,7 +3136,7 @@ var hookExtractionRule = {
|
|
|
3065
3136
|
},
|
|
3066
3137
|
create(context) {
|
|
3067
3138
|
const sourceRoot = sourceRootOf(context);
|
|
3068
|
-
const file =
|
|
3139
|
+
const file = path27.resolve(context.filename);
|
|
3069
3140
|
const segments = segmentsOf(file, sourceRoot);
|
|
3070
3141
|
if (segments.length === 0) return {};
|
|
3071
3142
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3091,7 +3162,7 @@ var hookExtractionRule = {
|
|
|
3091
3162
|
};
|
|
3092
3163
|
|
|
3093
3164
|
// eslint/rules/value-extraction.ts
|
|
3094
|
-
import
|
|
3165
|
+
import path28 from "path";
|
|
3095
3166
|
var valueExtractionRule = {
|
|
3096
3167
|
meta: {
|
|
3097
3168
|
schema: [],
|
|
@@ -3102,7 +3173,7 @@ var valueExtractionRule = {
|
|
|
3102
3173
|
},
|
|
3103
3174
|
create(context) {
|
|
3104
3175
|
const sourceRoot = sourceRootOf(context);
|
|
3105
|
-
const file =
|
|
3176
|
+
const file = path28.resolve(context.filename);
|
|
3106
3177
|
const segments = segmentsOf(file, sourceRoot);
|
|
3107
3178
|
if (segments.length === 0 || segments[0] !== "app") return {};
|
|
3108
3179
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3123,7 +3194,7 @@ var valueExtractionRule = {
|
|
|
3123
3194
|
};
|
|
3124
3195
|
|
|
3125
3196
|
// eslint/rules/config-extraction.ts
|
|
3126
|
-
import
|
|
3197
|
+
import path29 from "path";
|
|
3127
3198
|
var configExtractionRule = {
|
|
3128
3199
|
meta: {
|
|
3129
3200
|
schema: [],
|
|
@@ -3134,7 +3205,7 @@ var configExtractionRule = {
|
|
|
3134
3205
|
},
|
|
3135
3206
|
create(context) {
|
|
3136
3207
|
const sourceRoot = sourceRootOf(context);
|
|
3137
|
-
const file =
|
|
3208
|
+
const file = path29.resolve(context.filename);
|
|
3138
3209
|
const segments = segmentsOf(file, sourceRoot);
|
|
3139
3210
|
if (segments.length < 3 || segments[0] !== "config") return {};
|
|
3140
3211
|
if (SUPPORT_FOLDERS2.has(segments[2] ?? "")) return {};
|
|
@@ -3172,7 +3243,7 @@ var configExtractionRule = {
|
|
|
3172
3243
|
};
|
|
3173
3244
|
|
|
3174
3245
|
// eslint/rules/component-nesting.ts
|
|
3175
|
-
import
|
|
3246
|
+
import path30 from "path";
|
|
3176
3247
|
var componentNestingRule = {
|
|
3177
3248
|
meta: {
|
|
3178
3249
|
schema: [],
|
|
@@ -3183,7 +3254,7 @@ var componentNestingRule = {
|
|
|
3183
3254
|
},
|
|
3184
3255
|
create(context) {
|
|
3185
3256
|
const sourceRoot = sourceRootOf(context);
|
|
3186
|
-
const file =
|
|
3257
|
+
const file = path30.resolve(context.filename);
|
|
3187
3258
|
const segments = segmentsOf(file, sourceRoot);
|
|
3188
3259
|
if (segments.length !== 4 || segments[0] !== "features") return {};
|
|
3189
3260
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3216,7 +3287,7 @@ var componentNestingRule = {
|
|
|
3216
3287
|
};
|
|
3217
3288
|
|
|
3218
3289
|
// eslint/rules/stay-flat.ts
|
|
3219
|
-
import
|
|
3290
|
+
import path31 from "path";
|
|
3220
3291
|
var stayFlatRule = {
|
|
3221
3292
|
meta: {
|
|
3222
3293
|
schema: [],
|
|
@@ -3227,7 +3298,7 @@ var stayFlatRule = {
|
|
|
3227
3298
|
},
|
|
3228
3299
|
create(context) {
|
|
3229
3300
|
const sourceRoot = sourceRootOf(context);
|
|
3230
|
-
const file =
|
|
3301
|
+
const file = path31.resolve(context.filename);
|
|
3231
3302
|
const segments = segmentsOf(file, sourceRoot);
|
|
3232
3303
|
if (segments.length !== 3 || segments[0] !== "features") return {};
|
|
3233
3304
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3267,7 +3338,7 @@ var stayFlatRule = {
|
|
|
3267
3338
|
};
|
|
3268
3339
|
|
|
3269
3340
|
// eslint/rules/type-extraction.ts
|
|
3270
|
-
import
|
|
3341
|
+
import path32 from "path";
|
|
3271
3342
|
var typeExtractionRule = {
|
|
3272
3343
|
meta: {
|
|
3273
3344
|
schema: [],
|
|
@@ -3278,7 +3349,7 @@ var typeExtractionRule = {
|
|
|
3278
3349
|
},
|
|
3279
3350
|
create(context) {
|
|
3280
3351
|
const sourceRoot = sourceRootOf(context);
|
|
3281
|
-
const file =
|
|
3352
|
+
const file = path32.resolve(context.filename);
|
|
3282
3353
|
const segments = segmentsOf(file, sourceRoot);
|
|
3283
3354
|
if (segments.length === 0) return {};
|
|
3284
3355
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3324,7 +3395,7 @@ var typeExtractionRule = {
|
|
|
3324
3395
|
};
|
|
3325
3396
|
|
|
3326
3397
|
// eslint/rules/locale-placement.ts
|
|
3327
|
-
import
|
|
3398
|
+
import path33 from "path";
|
|
3328
3399
|
import { readFileSync as readFileSync4 } from "fs";
|
|
3329
3400
|
import ts5 from "typescript";
|
|
3330
3401
|
var LOCALE_ACCESS = /\blocales\.(?<key>[A-Za-z_$][\w$]*)/g;
|
|
@@ -3372,7 +3443,7 @@ var localePlacementRule = {
|
|
|
3372
3443
|
},
|
|
3373
3444
|
create(context) {
|
|
3374
3445
|
const sourceRoot = sourceRootOf(context);
|
|
3375
|
-
const file =
|
|
3446
|
+
const file = path33.resolve(context.filename);
|
|
3376
3447
|
const segments = segmentsOf(file, sourceRoot);
|
|
3377
3448
|
if (segments.length === 0) return {};
|
|
3378
3449
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3450,7 +3521,7 @@ var localePlacementRule = {
|
|
|
3450
3521
|
};
|
|
3451
3522
|
|
|
3452
3523
|
// eslint/rules/sole-state-owner.ts
|
|
3453
|
-
import
|
|
3524
|
+
import path34 from "path";
|
|
3454
3525
|
import ts6 from "typescript";
|
|
3455
3526
|
function findStateHooks(node) {
|
|
3456
3527
|
const hooks = [];
|
|
@@ -3530,7 +3601,7 @@ var soleStateOwnerRule = {
|
|
|
3530
3601
|
}
|
|
3531
3602
|
},
|
|
3532
3603
|
create(context) {
|
|
3533
|
-
const filename =
|
|
3604
|
+
const filename = path34.resolve(context.filename);
|
|
3534
3605
|
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
|
|
3535
3606
|
const text = context.sourceCode.text;
|
|
3536
3607
|
const components = parseComponentInfo(text, filename);
|
|
@@ -3609,7 +3680,7 @@ function usesOutsideJsx(declaration, hook, children) {
|
|
|
3609
3680
|
}
|
|
3610
3681
|
|
|
3611
3682
|
// eslint/rules/locale-key-shape.ts
|
|
3612
|
-
import
|
|
3683
|
+
import path35 from "path";
|
|
3613
3684
|
var MAX_KEY_LENGTH = 30;
|
|
3614
3685
|
var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
|
|
3615
3686
|
"Button",
|
|
@@ -3659,7 +3730,7 @@ var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
|
|
|
3659
3730
|
var CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
|
|
3660
3731
|
var ENGLISH = /^[A-Za-z0-9_]*$/;
|
|
3661
3732
|
function isLocalesFile2(filename) {
|
|
3662
|
-
const segments =
|
|
3733
|
+
const segments = path35.resolve(filename).split(path35.sep);
|
|
3663
3734
|
const srcIdx = segments.lastIndexOf("src");
|
|
3664
3735
|
return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
|
|
3665
3736
|
}
|
|
@@ -3730,7 +3801,7 @@ var localeKeyShapeRule = {
|
|
|
3730
3801
|
};
|
|
3731
3802
|
|
|
3732
3803
|
// eslint/rules/shared-style-dedup.ts
|
|
3733
|
-
import
|
|
3804
|
+
import path36 from "path";
|
|
3734
3805
|
import { readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
3735
3806
|
var CLASS_NAME = /className="(?<classes>[^"]+)"/g;
|
|
3736
3807
|
var comboCache;
|
|
@@ -3768,7 +3839,7 @@ var sharedStyleDedupRule = {
|
|
|
3768
3839
|
},
|
|
3769
3840
|
create(context) {
|
|
3770
3841
|
const sourceRoot = sourceRootOf(context);
|
|
3771
|
-
const file =
|
|
3842
|
+
const file = path36.resolve(context.filename);
|
|
3772
3843
|
const segments = segmentsOf(file, sourceRoot);
|
|
3773
3844
|
if (segments.length === 0) return {};
|
|
3774
3845
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3971,8 +4042,103 @@ var zodSchemaValidationRule = {
|
|
|
3971
4042
|
}
|
|
3972
4043
|
};
|
|
3973
4044
|
|
|
4045
|
+
// eslint/rules/schema-casing.ts
|
|
4046
|
+
import path37 from "path";
|
|
4047
|
+
function isCamelCase(name) {
|
|
4048
|
+
return /^[a-z][a-zA-Z0-9]*$/.test(name);
|
|
4049
|
+
}
|
|
4050
|
+
function rootIdentifierName(node) {
|
|
4051
|
+
let current = node;
|
|
4052
|
+
for (; ; ) {
|
|
4053
|
+
if (current.type === "Identifier") return current.name;
|
|
4054
|
+
if (current.type === "CallExpression") {
|
|
4055
|
+
current = current.callee;
|
|
4056
|
+
continue;
|
|
4057
|
+
}
|
|
4058
|
+
if (current.type === "MemberExpression") {
|
|
4059
|
+
current = current.object;
|
|
4060
|
+
continue;
|
|
4061
|
+
}
|
|
4062
|
+
return void 0;
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
var schemaCasingRule = {
|
|
4066
|
+
meta: {
|
|
4067
|
+
schema: [],
|
|
4068
|
+
type: "problem",
|
|
4069
|
+
docs: {
|
|
4070
|
+
description: "Require a Zod schema constant to be named in camelCase."
|
|
4071
|
+
}
|
|
4072
|
+
},
|
|
4073
|
+
create(context) {
|
|
4074
|
+
const filename = path37.resolve(context.filename);
|
|
4075
|
+
const sourceRoot = sourceRootOf(context);
|
|
4076
|
+
if (!filename.startsWith(sourceRoot + path37.sep)) return {};
|
|
4077
|
+
let zodLocalName;
|
|
4078
|
+
return {
|
|
4079
|
+
ImportDeclaration(node) {
|
|
4080
|
+
if (node.source.value !== "zod") return;
|
|
4081
|
+
for (const specifier of node.specifiers) {
|
|
4082
|
+
if (specifier.type === "ImportSpecifier" && specifier.imported.type === "Identifier" && specifier.imported.name === "z") {
|
|
4083
|
+
zodLocalName = specifier.local.name;
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
},
|
|
4087
|
+
VariableDeclarator(node) {
|
|
4088
|
+
if (!zodLocalName) return;
|
|
4089
|
+
if (node.id.type !== "Identifier") return;
|
|
4090
|
+
const declaration = node.parent;
|
|
4091
|
+
if (declaration.type !== "VariableDeclaration" || declaration.kind !== "const") return;
|
|
4092
|
+
const container = declaration.parent;
|
|
4093
|
+
const isModuleLevel = container.type === "Program" || container.type === "ExportNamedDeclaration" && container.parent.type === "Program";
|
|
4094
|
+
if (!isModuleLevel) return;
|
|
4095
|
+
if (!node.init || rootIdentifierName(node.init) !== zodLocalName) return;
|
|
4096
|
+
const { name } = node.id;
|
|
4097
|
+
if (isCamelCase(name)) return;
|
|
4098
|
+
context.report({
|
|
4099
|
+
node,
|
|
4100
|
+
message: `${name} must be camelCase. See docs/next-codebase-guide/rules/types-and-schemas-rule.md`
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
};
|
|
4104
|
+
}
|
|
4105
|
+
};
|
|
4106
|
+
|
|
4107
|
+
// eslint/rules/component-casing.ts
|
|
4108
|
+
import path38 from "path";
|
|
4109
|
+
function isPascalCase6(name) {
|
|
4110
|
+
return /^[A-Z][A-Za-z0-9]*$/.test(name);
|
|
4111
|
+
}
|
|
4112
|
+
var componentCasingRule = {
|
|
4113
|
+
meta: {
|
|
4114
|
+
schema: [],
|
|
4115
|
+
type: "problem",
|
|
4116
|
+
docs: {
|
|
4117
|
+
description: "Require a JSX-returning function or const to be named in PascalCase."
|
|
4118
|
+
}
|
|
4119
|
+
},
|
|
4120
|
+
create(context) {
|
|
4121
|
+
const filename = path38.resolve(context.filename);
|
|
4122
|
+
const sourceRoot = sourceRootOf(context);
|
|
4123
|
+
if (!filename.startsWith(sourceRoot + path38.sep)) return {};
|
|
4124
|
+
if (path38.extname(filename) !== ".tsx") return {};
|
|
4125
|
+
return {
|
|
4126
|
+
Program(node) {
|
|
4127
|
+
for (const declaration of findJsxReturningDeclarations(context.sourceCode.text, filename)) {
|
|
4128
|
+
if (isPascalCase6(declaration.name)) continue;
|
|
4129
|
+
context.report({
|
|
4130
|
+
node,
|
|
4131
|
+
loc: { line: declaration.line, column: declaration.column },
|
|
4132
|
+
message: `${declaration.name} must be PascalCase; only a PascalCase name resolves as a JSX component. See docs/next-codebase-guide/rules/smart-vs-dumb-component-rule.md`
|
|
4133
|
+
});
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
};
|
|
4137
|
+
}
|
|
4138
|
+
};
|
|
4139
|
+
|
|
3974
4140
|
// eslint/rules/source-under-src.ts
|
|
3975
|
-
import
|
|
4141
|
+
import path39 from "path";
|
|
3976
4142
|
var NON_SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set([
|
|
3977
4143
|
".agents",
|
|
3978
4144
|
".cache",
|
|
@@ -4013,14 +4179,14 @@ var sourceUnderSrcRule = {
|
|
|
4013
4179
|
}
|
|
4014
4180
|
},
|
|
4015
4181
|
create(context) {
|
|
4016
|
-
const filename =
|
|
4182
|
+
const filename = path39.resolve(context.filename);
|
|
4017
4183
|
if (!MODULE_EXTENSION.test(filename)) return {};
|
|
4018
|
-
const relative =
|
|
4184
|
+
const relative = path39.relative(context.cwd, filename).replace(/\\/g, "/");
|
|
4019
4185
|
if (relative === "src" || relative.startsWith("src/")) return {};
|
|
4020
4186
|
const topLevel = relative.split("/")[0] ?? "";
|
|
4021
4187
|
if (NON_SOURCE_ROOT_DIRS.has(topLevel)) return {};
|
|
4022
4188
|
if (!relative.includes("/")) {
|
|
4023
|
-
const basename =
|
|
4189
|
+
const basename = path39.basename(filename);
|
|
4024
4190
|
if (CONFIG_FILE.test(basename) || DECLARATION_FILE.test(basename) || basename.startsWith(".")) return {};
|
|
4025
4191
|
}
|
|
4026
4192
|
return {
|
|
@@ -4037,7 +4203,7 @@ var sourceUnderSrcRule = {
|
|
|
4037
4203
|
|
|
4038
4204
|
// eslint/rules/zirka-baseline.ts
|
|
4039
4205
|
import fs5 from "fs";
|
|
4040
|
-
import
|
|
4206
|
+
import path40 from "path";
|
|
4041
4207
|
var ESLINT_CONFIG = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
|
|
4042
4208
|
var PRETTIER_CONFIGS = [
|
|
4043
4209
|
"prettier.config.mjs",
|
|
@@ -4056,10 +4222,10 @@ var zirkaBaselineRule = {
|
|
|
4056
4222
|
}
|
|
4057
4223
|
},
|
|
4058
4224
|
create(context) {
|
|
4059
|
-
const filename =
|
|
4060
|
-
const basename =
|
|
4225
|
+
const filename = path40.resolve(context.filename);
|
|
4226
|
+
const basename = path40.basename(filename);
|
|
4061
4227
|
if (!ESLINT_CONFIG.test(basename)) return {};
|
|
4062
|
-
const projectRoot =
|
|
4228
|
+
const projectRoot = path40.dirname(filename);
|
|
4063
4229
|
const report3 = (message) => {
|
|
4064
4230
|
context.report({
|
|
4065
4231
|
node: context.sourceCode.ast,
|
|
@@ -4074,7 +4240,7 @@ var zirkaBaselineRule = {
|
|
|
4074
4240
|
'ESLint config must take its configuration from zirka (import { styleguide } from "zirka") instead of restating rules locally.'
|
|
4075
4241
|
);
|
|
4076
4242
|
}
|
|
4077
|
-
const tsconfigPath =
|
|
4243
|
+
const tsconfigPath = path40.join(projectRoot, "tsconfig.json");
|
|
4078
4244
|
if (!fs5.existsSync(tsconfigPath)) {
|
|
4079
4245
|
report3('No tsconfig.json found. Create one extending the zirka TypeScript base config ("zirka/typescript").');
|
|
4080
4246
|
} else {
|
|
@@ -4092,13 +4258,13 @@ var zirkaBaselineRule = {
|
|
|
4092
4258
|
report3('tsconfig.json must extend the zirka TypeScript base config ("zirka/typescript").');
|
|
4093
4259
|
}
|
|
4094
4260
|
}
|
|
4095
|
-
const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs5.existsSync(
|
|
4261
|
+
const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs5.existsSync(path40.join(projectRoot, name)));
|
|
4096
4262
|
if (!prettierConfigFile) {
|
|
4097
4263
|
report3(
|
|
4098
4264
|
"No prettier config found. Create one that takes its configuration from zirka (styleguide({ prettier: true }).prettierConfig)."
|
|
4099
4265
|
);
|
|
4100
4266
|
} else {
|
|
4101
|
-
const content = fs5.readFileSync(
|
|
4267
|
+
const content = fs5.readFileSync(path40.join(projectRoot, prettierConfigFile), "utf8");
|
|
4102
4268
|
if (!content.includes("zirka")) {
|
|
4103
4269
|
report3(
|
|
4104
4270
|
"The prettier config must take its configuration from zirka (styleguide({ prettier: true }).prettierConfig) instead of restating it locally."
|
|
@@ -4168,7 +4334,7 @@ var docKindSuffixRule = {
|
|
|
4168
4334
|
};
|
|
4169
4335
|
|
|
4170
4336
|
// eslint/rules/documentation/title-matches-file-name.ts
|
|
4171
|
-
import
|
|
4337
|
+
import path41 from "path";
|
|
4172
4338
|
function toExpectedFileName(title) {
|
|
4173
4339
|
return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
|
|
4174
4340
|
}
|
|
@@ -4188,7 +4354,7 @@ var titleMatchesFileNameRule = {
|
|
|
4188
4354
|
if (!filename.endsWith(".md")) return;
|
|
4189
4355
|
const title = getTextContent(node).trim();
|
|
4190
4356
|
const expectedFileName = toExpectedFileName(title);
|
|
4191
|
-
const actualFileName =
|
|
4357
|
+
const actualFileName = path41.basename(filename);
|
|
4192
4358
|
if (!title) {
|
|
4193
4359
|
context.report({
|
|
4194
4360
|
node,
|
|
@@ -4631,20 +4797,20 @@ var referenceBlockHeadingsRule = {
|
|
|
4631
4797
|
|
|
4632
4798
|
// eslint/rules/documentation/support-document-placement.ts
|
|
4633
4799
|
import { existsSync as existsSync2 } from "fs";
|
|
4634
|
-
import
|
|
4800
|
+
import path42 from "path";
|
|
4635
4801
|
function checkPlacement(filename, kind) {
|
|
4636
|
-
const parentFolder =
|
|
4802
|
+
const parentFolder = path42.basename(path42.dirname(filename));
|
|
4637
4803
|
const expectedParent = `${kind}s`;
|
|
4638
4804
|
if (parentFolder !== expectedParent) {
|
|
4639
4805
|
return `${kind} lives in "${parentFolder}/" instead of "${expectedParent}/"`;
|
|
4640
4806
|
}
|
|
4641
|
-
const guideFolderPath =
|
|
4642
|
-
const guideFolder =
|
|
4807
|
+
const guideFolderPath = path42.dirname(path42.dirname(filename));
|
|
4808
|
+
const guideFolder = path42.basename(guideFolderPath);
|
|
4643
4809
|
if (!guideFolder.endsWith("-guide")) {
|
|
4644
4810
|
return `${kind} owner folder "${guideFolder}/" does not use the "*-guide/" suffix`;
|
|
4645
4811
|
}
|
|
4646
4812
|
const entryPoint = `${guideFolder}.md`;
|
|
4647
|
-
if (!existsSync2(
|
|
4813
|
+
if (!existsSync2(path42.join(guideFolderPath, entryPoint))) {
|
|
4648
4814
|
return `${kind} owner folder "${guideFolder}/" has no "${entryPoint}" entry point`;
|
|
4649
4815
|
}
|
|
4650
4816
|
return void 0;
|
|
@@ -4703,11 +4869,11 @@ var noTemplatePromptRule = {
|
|
|
4703
4869
|
};
|
|
4704
4870
|
|
|
4705
4871
|
// eslint/rules/documentation/guide-folder-entry-point.ts
|
|
4706
|
-
import
|
|
4872
|
+
import path44 from "path";
|
|
4707
4873
|
|
|
4708
4874
|
// eslint/rules/documentation/project-index.ts
|
|
4709
4875
|
import { readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
4710
|
-
import
|
|
4876
|
+
import path43 from "path";
|
|
4711
4877
|
var KIND_BY_SUFFIX = [
|
|
4712
4878
|
["-rule.md", "rule"],
|
|
4713
4879
|
["-guide.md", "guide"],
|
|
@@ -4716,7 +4882,7 @@ var KIND_BY_SUFFIX = [
|
|
|
4716
4882
|
];
|
|
4717
4883
|
function listMarkdownFiles(dir) {
|
|
4718
4884
|
return readdirSync3(dir).flatMap((entry) => {
|
|
4719
|
-
const entryPath =
|
|
4885
|
+
const entryPath = path43.join(dir, entry);
|
|
4720
4886
|
if (statSync4(entryPath).isDirectory()) {
|
|
4721
4887
|
return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
|
|
4722
4888
|
}
|
|
@@ -4734,11 +4900,11 @@ function getProjectDocs(docsRoot) {
|
|
|
4734
4900
|
if (cached) return cached;
|
|
4735
4901
|
const files = listMarkdownFiles(docsRoot);
|
|
4736
4902
|
const docs = files.sort((a, b) => a.localeCompare(b)).map((filePath) => {
|
|
4737
|
-
const fileName =
|
|
4903
|
+
const fileName = path43.basename(filePath);
|
|
4738
4904
|
const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
|
|
4739
4905
|
return {
|
|
4740
4906
|
filePath,
|
|
4741
|
-
doc:
|
|
4907
|
+
doc: path43.relative(docsRoot, filePath).split(path43.sep).join("/"),
|
|
4742
4908
|
fileName,
|
|
4743
4909
|
kind,
|
|
4744
4910
|
title: extractTitle(filePath)
|
|
@@ -4748,12 +4914,12 @@ function getProjectDocs(docsRoot) {
|
|
|
4748
4914
|
return docs;
|
|
4749
4915
|
}
|
|
4750
4916
|
function findDocsRoot(filePath) {
|
|
4751
|
-
let dir =
|
|
4917
|
+
let dir = path43.dirname(filePath);
|
|
4752
4918
|
for (; ; ) {
|
|
4753
|
-
if (
|
|
4919
|
+
if (path43.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
|
|
4754
4920
|
return dir;
|
|
4755
4921
|
}
|
|
4756
|
-
const parent =
|
|
4922
|
+
const parent = path43.dirname(dir);
|
|
4757
4923
|
if (parent === dir) return void 0;
|
|
4758
4924
|
dir = parent;
|
|
4759
4925
|
}
|
|
@@ -4777,13 +4943,13 @@ var guideFolderEntryPointRule = {
|
|
|
4777
4943
|
if (!docsRoot) return;
|
|
4778
4944
|
const docs = getProjectDocs(docsRoot);
|
|
4779
4945
|
const guideFolders = new Set(
|
|
4780
|
-
docs.filter((doc) => ["rules", "references"].includes(
|
|
4946
|
+
docs.filter((doc) => ["rules", "references"].includes(path44.basename(path44.dirname(doc.filePath)))).map((doc) => path44.dirname(path44.dirname(doc.filePath))).filter((folder) => path44.resolve(folder) !== path44.resolve(docsRoot))
|
|
4781
4947
|
);
|
|
4782
|
-
const currentDir =
|
|
4948
|
+
const currentDir = path44.dirname(filename);
|
|
4783
4949
|
if (guideFolders.has(currentDir)) {
|
|
4784
|
-
const expectedEntryPoint = `${
|
|
4950
|
+
const expectedEntryPoint = `${path44.basename(currentDir)}.md`;
|
|
4785
4951
|
const hasEntryPoint = docs.some(
|
|
4786
|
-
(doc) => doc.kind === "guide" &&
|
|
4952
|
+
(doc) => doc.kind === "guide" && path44.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
|
|
4787
4953
|
);
|
|
4788
4954
|
if (!hasEntryPoint) {
|
|
4789
4955
|
context.report({
|
|
@@ -5008,7 +5174,7 @@ var noNestedHowToRule = {
|
|
|
5008
5174
|
|
|
5009
5175
|
// eslint/rules/documentation/glossary-term-linking.ts
|
|
5010
5176
|
import { readFileSync as readFileSync7 } from "fs";
|
|
5011
|
-
import
|
|
5177
|
+
import path45 from "path";
|
|
5012
5178
|
function extractGlossaryTerms(filePath) {
|
|
5013
5179
|
const content = readFileSync7(filePath, "utf8");
|
|
5014
5180
|
const terms = [];
|
|
@@ -5062,9 +5228,9 @@ var glossaryTermLinkingRule = {
|
|
|
5062
5228
|
const docsRoot = findDocsRoot(filename);
|
|
5063
5229
|
if (!docsRoot) return;
|
|
5064
5230
|
const docs = getProjectDocs(docsRoot);
|
|
5065
|
-
const guideDir =
|
|
5231
|
+
const guideDir = path45.dirname(filename);
|
|
5066
5232
|
const guideReferences = docs.filter(
|
|
5067
|
-
(doc) => doc.kind === "reference" &&
|
|
5233
|
+
(doc) => doc.kind === "reference" && path45.dirname(doc.filePath) === guideDir
|
|
5068
5234
|
);
|
|
5069
5235
|
if (guideReferences.length === 0) return;
|
|
5070
5236
|
const glossaryTerms = [];
|
|
@@ -5089,7 +5255,7 @@ var glossaryTermLinkingRule = {
|
|
|
5089
5255
|
|
|
5090
5256
|
// eslint/rules/documentation/guide-mentions-documents.ts
|
|
5091
5257
|
import { existsSync as existsSync3 } from "fs";
|
|
5092
|
-
import
|
|
5258
|
+
import path46 from "path";
|
|
5093
5259
|
function visitSteps3(node, check) {
|
|
5094
5260
|
if (node.type === "list" && node.ordered) {
|
|
5095
5261
|
for (const child of node.children) check(child);
|
|
@@ -5122,12 +5288,12 @@ var guideMentionsDocumentsRule = {
|
|
|
5122
5288
|
if (!filename.endsWith("-guide.md")) return;
|
|
5123
5289
|
const docsRoot = findDocsRoot(filename);
|
|
5124
5290
|
if (!docsRoot) return;
|
|
5125
|
-
const guideDir =
|
|
5126
|
-
if (
|
|
5291
|
+
const guideDir = path46.dirname(filename);
|
|
5292
|
+
if (path46.basename(filename, ".md") !== path46.basename(guideDir)) return;
|
|
5127
5293
|
const docs = getProjectDocs(docsRoot);
|
|
5128
5294
|
const owned = docs.filter((doc) => {
|
|
5129
|
-
const parent =
|
|
5130
|
-
return parent ===
|
|
5295
|
+
const parent = path46.dirname(doc.filePath);
|
|
5296
|
+
return parent === path46.join(guideDir, "rules") || parent === path46.join(guideDir, "references");
|
|
5131
5297
|
});
|
|
5132
5298
|
const allLinks = [];
|
|
5133
5299
|
collectMarkdownLinks(node, allLinks);
|
|
@@ -5154,7 +5320,7 @@ var guideMentionsDocumentsRule = {
|
|
|
5154
5320
|
for (const link of allLinks) {
|
|
5155
5321
|
const target = linkTarget(link.url);
|
|
5156
5322
|
if (!target.endsWith(".md")) continue;
|
|
5157
|
-
const resolved =
|
|
5323
|
+
const resolved = path46.normalize(path46.join(guideDir, target));
|
|
5158
5324
|
if (!existsSync3(resolved)) {
|
|
5159
5325
|
context.report({
|
|
5160
5326
|
node: link,
|
|
@@ -5704,11 +5870,11 @@ var themeVariableNamespaceRule = {
|
|
|
5704
5870
|
|
|
5705
5871
|
// eslint/rules/tailwind/css-entry-point.ts
|
|
5706
5872
|
import { statSync as statSync6 } from "fs";
|
|
5707
|
-
import
|
|
5873
|
+
import path49 from "path";
|
|
5708
5874
|
|
|
5709
5875
|
// eslint/rules/tailwind/source-files.ts
|
|
5710
5876
|
import { readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
5711
|
-
import
|
|
5877
|
+
import path47 from "path";
|
|
5712
5878
|
var CSS_EXTENSIONS = [".css"];
|
|
5713
5879
|
var MODULE_EXTENSIONS3 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
5714
5880
|
var SOURCE_EXTENSIONS = [...MODULE_EXTENSIONS3, ...CSS_EXTENSIONS];
|
|
@@ -5721,7 +5887,7 @@ function findFiles(dir, extensions) {
|
|
|
5721
5887
|
}
|
|
5722
5888
|
return entries.flatMap((entry) => {
|
|
5723
5889
|
if (entry.startsWith(".") || entry === "node_modules") return [];
|
|
5724
|
-
const entryPath =
|
|
5890
|
+
const entryPath = path47.join(dir, entry);
|
|
5725
5891
|
let stats;
|
|
5726
5892
|
try {
|
|
5727
5893
|
stats = statSync5(entryPath);
|
|
@@ -5729,7 +5895,7 @@ function findFiles(dir, extensions) {
|
|
|
5729
5895
|
return [];
|
|
5730
5896
|
}
|
|
5731
5897
|
if (stats.isDirectory()) return findFiles(entryPath, extensions);
|
|
5732
|
-
return extensions.includes(
|
|
5898
|
+
return extensions.includes(path47.extname(entry)) ? [entryPath] : [];
|
|
5733
5899
|
});
|
|
5734
5900
|
}
|
|
5735
5901
|
function cachedTextReader() {
|
|
@@ -5752,7 +5918,7 @@ function escapeRegExp(text) {
|
|
|
5752
5918
|
}
|
|
5753
5919
|
|
|
5754
5920
|
// eslint/rules/tailwind/stylesheet-graph.ts
|
|
5755
|
-
import
|
|
5921
|
+
import path48 from "path";
|
|
5756
5922
|
function registersTailwind(text) {
|
|
5757
5923
|
return /@import\s+(?:url\(\s*)?["']tailwindcss["']\s*\)?/i.test(text);
|
|
5758
5924
|
}
|
|
@@ -5766,25 +5932,25 @@ function moduleImports(text, fileName) {
|
|
|
5766
5932
|
return new RegExp(`(?:import|require)\\s*\\(?\\s*["'][^"']*${escaped}["']`, "i").test(text);
|
|
5767
5933
|
}
|
|
5768
5934
|
function resolveSpecifier2(fromFile, spec, sourceRoot) {
|
|
5769
|
-
if (spec.startsWith("/")) return
|
|
5770
|
-
if (spec.startsWith("./") || spec.startsWith("../")) return
|
|
5771
|
-
if (spec.startsWith("@/")) return
|
|
5935
|
+
if (spec.startsWith("/")) return path48.resolve(spec);
|
|
5936
|
+
if (spec.startsWith("./") || spec.startsWith("../")) return path48.resolve(path48.dirname(fromFile), spec);
|
|
5937
|
+
if (spec.startsWith("@/")) return path48.resolve(sourceRoot, spec.slice(2));
|
|
5772
5938
|
return void 0;
|
|
5773
5939
|
}
|
|
5774
5940
|
function buildStylesheetGraph(options) {
|
|
5775
5941
|
const { cssFiles, sourceRoot, textOf } = options;
|
|
5776
|
-
const cssSet = new Set(cssFiles.map((file) =>
|
|
5942
|
+
const cssSet = new Set(cssFiles.map((file) => path48.normalize(file)));
|
|
5777
5943
|
const globals = cssFiles.filter((file) => registersTailwind(textOf(file)));
|
|
5778
5944
|
const reachable = /* @__PURE__ */ new Set();
|
|
5779
5945
|
const queue = [...globals];
|
|
5780
|
-
for (const global of globals) reachable.add(
|
|
5946
|
+
for (const global of globals) reachable.add(path48.normalize(global));
|
|
5781
5947
|
while (queue.length > 0) {
|
|
5782
5948
|
const from = queue.shift();
|
|
5783
5949
|
if (!from) continue;
|
|
5784
5950
|
for (const spec of importedSpecifiers(textOf(from))) {
|
|
5785
5951
|
const target = resolveSpecifier2(from, spec, sourceRoot);
|
|
5786
5952
|
if (!target) continue;
|
|
5787
|
-
const normalized =
|
|
5953
|
+
const normalized = path48.normalize(target);
|
|
5788
5954
|
if (cssSet.has(normalized) && !reachable.has(normalized)) {
|
|
5789
5955
|
reachable.add(normalized);
|
|
5790
5956
|
queue.push(normalized);
|
|
@@ -5796,7 +5962,7 @@ function buildStylesheetGraph(options) {
|
|
|
5796
5962
|
for (const spec of importedSpecifiers(textOf(global))) {
|
|
5797
5963
|
const target = resolveSpecifier2(global, spec, sourceRoot);
|
|
5798
5964
|
if (!target) continue;
|
|
5799
|
-
const normalized =
|
|
5965
|
+
const normalized = path48.normalize(target);
|
|
5800
5966
|
if (cssSet.has(normalized)) directChildren.add(normalized);
|
|
5801
5967
|
}
|
|
5802
5968
|
}
|
|
@@ -5829,7 +5995,7 @@ var cssEntryPointRule = {
|
|
|
5829
5995
|
return {
|
|
5830
5996
|
"StyleSheet:exit"(node) {
|
|
5831
5997
|
if (globals.length === 0) return;
|
|
5832
|
-
const current =
|
|
5998
|
+
const current = path49.normalize(path49.resolve(context.filename));
|
|
5833
5999
|
if (globals.includes(current)) {
|
|
5834
6000
|
if (globals.length > 1) {
|
|
5835
6001
|
context.report({
|
|
@@ -5838,7 +6004,7 @@ var cssEntryPointRule = {
|
|
|
5838
6004
|
});
|
|
5839
6005
|
return;
|
|
5840
6006
|
}
|
|
5841
|
-
const basename =
|
|
6007
|
+
const basename = path49.basename(current);
|
|
5842
6008
|
const importCount = moduleFiles.filter((modulePath) => moduleImports(textOf(modulePath), basename)).length;
|
|
5843
6009
|
if (importCount !== 1) {
|
|
5844
6010
|
context.report({
|
|
@@ -6167,7 +6333,7 @@ var nextjsStackRule = {
|
|
|
6167
6333
|
|
|
6168
6334
|
// eslint/rules/package-json/vitest-coverage.ts
|
|
6169
6335
|
import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
|
|
6170
|
-
import
|
|
6336
|
+
import path50 from "path";
|
|
6171
6337
|
function memberName4(member) {
|
|
6172
6338
|
return member.name.type === "String" ? member.name.value : member.name.name;
|
|
6173
6339
|
}
|
|
@@ -6236,7 +6402,7 @@ var vitestCoverageRule = {
|
|
|
6236
6402
|
message: 'package.json must declare a "test:unit:coverage" script that runs Vitest with coverage.'
|
|
6237
6403
|
});
|
|
6238
6404
|
}
|
|
6239
|
-
const configName = VITEST_CONFIG_NAMES.find((name) => existsSync4(
|
|
6405
|
+
const configName = VITEST_CONFIG_NAMES.find((name) => existsSync4(path50.join(context.cwd, name)));
|
|
6240
6406
|
if (!configName) {
|
|
6241
6407
|
context.report({
|
|
6242
6408
|
node,
|
|
@@ -6244,7 +6410,7 @@ var vitestCoverageRule = {
|
|
|
6244
6410
|
});
|
|
6245
6411
|
return;
|
|
6246
6412
|
}
|
|
6247
|
-
const content = readFileSync9(
|
|
6413
|
+
const content = readFileSync9(path50.join(context.cwd, configName), "utf8");
|
|
6248
6414
|
for (const metric of THRESHOLD_METRICS) {
|
|
6249
6415
|
if (!new RegExp(`\\b${metric}\\s*:\\s*[1-9]\\d*`).test(content)) {
|
|
6250
6416
|
context.report({ node, message: `${configName} must set a coverage threshold above zero for ${metric}.` });
|
|
@@ -6291,7 +6457,7 @@ var nextjsPackageJsonRules = {
|
|
|
6291
6457
|
|
|
6292
6458
|
// eslint/rules/husky/husky-hook.ts
|
|
6293
6459
|
import { existsSync as existsSync5, readFileSync as readFileSync10 } from "fs";
|
|
6294
|
-
import
|
|
6460
|
+
import path51 from "path";
|
|
6295
6461
|
var VITEST_CONFIG_NAMES2 = [
|
|
6296
6462
|
"vitest.config.ts",
|
|
6297
6463
|
"vitest.config.mts",
|
|
@@ -6317,7 +6483,7 @@ var huskyHookRule = {
|
|
|
6317
6483
|
const root = node.body;
|
|
6318
6484
|
if (root.type !== "Object") return;
|
|
6319
6485
|
if (!context.filename.endsWith("package.json")) return;
|
|
6320
|
-
const hookPath =
|
|
6486
|
+
const hookPath = path51.join(context.cwd, ".husky", "pre-commit");
|
|
6321
6487
|
if (!existsSync5(hookPath)) {
|
|
6322
6488
|
context.report({
|
|
6323
6489
|
node,
|
|
@@ -6341,7 +6507,7 @@ var huskyHookRule = {
|
|
|
6341
6507
|
}
|
|
6342
6508
|
requireNamedScript("typecheck");
|
|
6343
6509
|
requireNamedScript("test:unit:coverage");
|
|
6344
|
-
const vitestConfigName = VITEST_CONFIG_NAMES2.find((name) => existsSync5(
|
|
6510
|
+
const vitestConfigName = VITEST_CONFIG_NAMES2.find((name) => existsSync5(path51.join(context.cwd, name)));
|
|
6345
6511
|
if (vitestConfigName !== void 0) {
|
|
6346
6512
|
const coverageIndex = content.indexOf("npm run test:unit:coverage");
|
|
6347
6513
|
const localAddIndex = content.indexOf(`git add ${vitestConfigName}`);
|
|
@@ -6355,7 +6521,7 @@ var huskyHookRule = {
|
|
|
6355
6521
|
if (!content.includes("libyear --limit-major-individual=1")) {
|
|
6356
6522
|
context.report({ node, message: ".husky/pre-commit must run npx libyear --limit-major-individual=1." });
|
|
6357
6523
|
}
|
|
6358
|
-
const suppressionsPath =
|
|
6524
|
+
const suppressionsPath = path51.join(context.cwd, "eslint-suppressions.json");
|
|
6359
6525
|
if (existsSync5(suppressionsPath)) {
|
|
6360
6526
|
requireNamedScript("lint:prune");
|
|
6361
6527
|
const pruneIndex = content.indexOf("npm run lint:prune");
|
|
@@ -6428,7 +6594,7 @@ var vulykDependencyRule = {
|
|
|
6428
6594
|
|
|
6429
6595
|
// eslint/rules/vulyk/vulyk-docs.ts
|
|
6430
6596
|
import { existsSync as existsSync6, readFileSync as readFileSync11 } from "fs";
|
|
6431
|
-
import
|
|
6597
|
+
import path52 from "path";
|
|
6432
6598
|
var PASIKA_REPO = "Bredansky/pasika";
|
|
6433
6599
|
var BASE_REQUIRED_DOCS = [
|
|
6434
6600
|
{ name: "documentation-guide", path: "docs/documentation-guide" },
|
|
@@ -6461,8 +6627,8 @@ var vulykDocsRule = {
|
|
|
6461
6627
|
if (!context.filename.endsWith("package.json")) return;
|
|
6462
6628
|
const root = node.body;
|
|
6463
6629
|
if (root.type !== "Object") return;
|
|
6464
|
-
const projectRoot =
|
|
6465
|
-
const configPath =
|
|
6630
|
+
const projectRoot = path52.dirname(path52.resolve(context.filename));
|
|
6631
|
+
const configPath = path52.join(projectRoot, "vulyk.config.ts");
|
|
6466
6632
|
if (!existsSync6(configPath)) {
|
|
6467
6633
|
context.report({
|
|
6468
6634
|
node,
|
|
@@ -6487,7 +6653,7 @@ var vulykDocsRule = {
|
|
|
6487
6653
|
});
|
|
6488
6654
|
}
|
|
6489
6655
|
}
|
|
6490
|
-
const agentsPath =
|
|
6656
|
+
const agentsPath = path52.join(projectRoot, "AGENTS.md");
|
|
6491
6657
|
if (!existsSync6(agentsPath)) {
|
|
6492
6658
|
context.report({
|
|
6493
6659
|
node,
|
|
@@ -6519,12 +6685,15 @@ var pasikaNextjsAppRules = {
|
|
|
6519
6685
|
"enforce-barrel-exports": enforceBarrelExportsRule,
|
|
6520
6686
|
"config-extraction": configExtractionRule,
|
|
6521
6687
|
"value-extraction": valueExtractionRule,
|
|
6688
|
+
"constant-casing": constantCasingRule,
|
|
6522
6689
|
"type-extraction": typeExtractionRule,
|
|
6523
6690
|
"zod-schema-validation": zodSchemaValidationRule,
|
|
6691
|
+
"schema-casing": schemaCasingRule,
|
|
6524
6692
|
"source-under-src": sourceUnderSrcRule,
|
|
6525
6693
|
"zirka-baseline": zirkaBaselineRule,
|
|
6526
6694
|
// Next.js/React application rules.
|
|
6527
6695
|
"component-placement": componentPlacementRule,
|
|
6696
|
+
"component-casing": componentCasingRule,
|
|
6528
6697
|
"application-structure": applicationStructureRule,
|
|
6529
6698
|
"data-testid-case": dataTestIdCaseRule,
|
|
6530
6699
|
"jsx-hygiene": jsxHygieneRule,
|