pasika 0.7.7 → 0.8.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/dist/eslint/pasika/index.d.ts +3 -0
- package/dist/eslint/pasika/index.js +290 -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;
|
|
@@ -2108,8 +2132,46 @@ var supportFolderShapeRule = {
|
|
|
2108
2132
|
}
|
|
2109
2133
|
};
|
|
2110
2134
|
|
|
2111
|
-
// eslint/rules/
|
|
2135
|
+
// eslint/rules/constant-casing.ts
|
|
2112
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";
|
|
2113
2175
|
var importThroughIndexRule = {
|
|
2114
2176
|
meta: {
|
|
2115
2177
|
schema: [],
|
|
@@ -2119,7 +2181,7 @@ var importThroughIndexRule = {
|
|
|
2119
2181
|
}
|
|
2120
2182
|
},
|
|
2121
2183
|
create(context) {
|
|
2122
|
-
const filename =
|
|
2184
|
+
const filename = path18.resolve(context.filename);
|
|
2123
2185
|
const sourceRoot = sourceRootOf2(context, filename);
|
|
2124
2186
|
return {
|
|
2125
2187
|
Program(node) {
|
|
@@ -2131,7 +2193,7 @@ var importThroughIndexRule = {
|
|
|
2131
2193
|
(segment) => ["constants", "types", "schemas"].includes(segment)
|
|
2132
2194
|
);
|
|
2133
2195
|
const supportFolder = supportFolderIndex >= 0 ? targetSegments[supportFolderIndex] : void 0;
|
|
2134
|
-
if (!supportFolder ||
|
|
2196
|
+
if (!supportFolder || path18.basename(target).startsWith("index.")) continue;
|
|
2135
2197
|
const folderIndex = targetSegments.slice(0, supportFolderIndex + 1);
|
|
2136
2198
|
const expected = `@/${folderIndex.join("/")}`;
|
|
2137
2199
|
context.report({
|
|
@@ -2153,14 +2215,14 @@ function importSpecifiers(source) {
|
|
|
2153
2215
|
return specifiers;
|
|
2154
2216
|
}
|
|
2155
2217
|
function sourceRootOf2(context, filename) {
|
|
2156
|
-
const marker = `${
|
|
2218
|
+
const marker = `${path18.sep}src${path18.sep}`;
|
|
2157
2219
|
const srcIndex = filename.lastIndexOf(marker);
|
|
2158
2220
|
if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
|
|
2159
|
-
return
|
|
2221
|
+
return path18.resolve(context.cwd ?? process.cwd(), "src");
|
|
2160
2222
|
}
|
|
2161
2223
|
|
|
2162
2224
|
// eslint/rules/util-file-name.ts
|
|
2163
|
-
import
|
|
2225
|
+
import path19 from "path";
|
|
2164
2226
|
function toKebabCase2(value) {
|
|
2165
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();
|
|
2166
2228
|
}
|
|
@@ -2173,7 +2235,7 @@ var utilFileNameRule = {
|
|
|
2173
2235
|
}
|
|
2174
2236
|
},
|
|
2175
2237
|
create(context) {
|
|
2176
|
-
const filename =
|
|
2238
|
+
const filename = path19.resolve(context.filename);
|
|
2177
2239
|
const segments = filename.replace(/\\/g, "/").split("/");
|
|
2178
2240
|
if (!segments.includes("utils")) return {};
|
|
2179
2241
|
let module;
|
|
@@ -2187,13 +2249,13 @@ var utilFileNameRule = {
|
|
|
2187
2249
|
const functionName = functions[0]?.name;
|
|
2188
2250
|
if (!functionName) return {};
|
|
2189
2251
|
const expected = toKebabCase2(functionName);
|
|
2190
|
-
const actual =
|
|
2252
|
+
const actual = path19.basename(filename, path19.extname(filename));
|
|
2191
2253
|
if (!expected || actual === expected) return {};
|
|
2192
2254
|
return {
|
|
2193
2255
|
Program(node) {
|
|
2194
2256
|
context.report({
|
|
2195
2257
|
node,
|
|
2196
|
-
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)}.`
|
|
2197
2259
|
});
|
|
2198
2260
|
}
|
|
2199
2261
|
};
|
|
@@ -2201,7 +2263,7 @@ var utilFileNameRule = {
|
|
|
2201
2263
|
};
|
|
2202
2264
|
|
|
2203
2265
|
// eslint/rules/no-util-barrel.ts
|
|
2204
|
-
import
|
|
2266
|
+
import path20 from "path";
|
|
2205
2267
|
var noUtilBarrelRule = {
|
|
2206
2268
|
meta: {
|
|
2207
2269
|
schema: [],
|
|
@@ -2211,7 +2273,7 @@ var noUtilBarrelRule = {
|
|
|
2211
2273
|
}
|
|
2212
2274
|
},
|
|
2213
2275
|
create(context) {
|
|
2214
|
-
const filename =
|
|
2276
|
+
const filename = path20.resolve(context.filename);
|
|
2215
2277
|
const sourceRoot = sourceRootOf3(context, filename);
|
|
2216
2278
|
return {
|
|
2217
2279
|
Program(node) {
|
|
@@ -2220,7 +2282,7 @@ var noUtilBarrelRule = {
|
|
|
2220
2282
|
if (!target) continue;
|
|
2221
2283
|
const segments = target.replace(/\\/g, "/").split("/");
|
|
2222
2284
|
const utilsIndex = segments.lastIndexOf("utils");
|
|
2223
|
-
if (utilsIndex < 0 || !
|
|
2285
|
+
if (utilsIndex < 0 || !path20.basename(target).startsWith("index.")) continue;
|
|
2224
2286
|
context.report({
|
|
2225
2287
|
node,
|
|
2226
2288
|
message: `Import utilities directly instead of through "${specifier}". See docs/next-codebase-guide/rules/utilities-rule.md`
|
|
@@ -2240,10 +2302,10 @@ function importSpecifiers2(source) {
|
|
|
2240
2302
|
return specifiers;
|
|
2241
2303
|
}
|
|
2242
2304
|
function sourceRootOf3(context, filename) {
|
|
2243
|
-
const marker = `${
|
|
2305
|
+
const marker = `${path20.sep}src${path20.sep}`;
|
|
2244
2306
|
const srcIndex = filename.lastIndexOf(marker);
|
|
2245
2307
|
if (srcIndex >= 0) return filename.slice(0, srcIndex + marker.length - 1);
|
|
2246
|
-
return
|
|
2308
|
+
return path20.resolve(context.cwd ?? process.cwd(), "src");
|
|
2247
2309
|
}
|
|
2248
2310
|
|
|
2249
2311
|
// eslint/rules/jsx-hygiene.ts
|
|
@@ -2336,7 +2398,7 @@ var jsxHygieneRule = {
|
|
|
2336
2398
|
};
|
|
2337
2399
|
|
|
2338
2400
|
// eslint/rules/interactive-component.ts
|
|
2339
|
-
import
|
|
2401
|
+
import path21 from "path";
|
|
2340
2402
|
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
2341
2403
|
"a",
|
|
2342
2404
|
"button",
|
|
@@ -2491,8 +2553,8 @@ var interactiveComponentRule = {
|
|
|
2491
2553
|
},
|
|
2492
2554
|
create(context) {
|
|
2493
2555
|
if (!context.filename.endsWith(".tsx") && !context.filename.endsWith(".jsx")) return {};
|
|
2494
|
-
const filename =
|
|
2495
|
-
const base =
|
|
2556
|
+
const filename = path21.resolve(context.filename);
|
|
2557
|
+
const base = path21.basename(filename, path21.extname(filename));
|
|
2496
2558
|
if (NEXT_ROUTING_FILES3.has(base)) return {};
|
|
2497
2559
|
return {
|
|
2498
2560
|
JSXElement(node) {
|
|
@@ -2743,12 +2805,12 @@ var cvaBooleanVariantsRule = {
|
|
|
2743
2805
|
};
|
|
2744
2806
|
|
|
2745
2807
|
// eslint/rules/cross-feature-import.ts
|
|
2746
|
-
import
|
|
2808
|
+
import path22 from "path";
|
|
2747
2809
|
var FEATURES_SEGMENT = "features";
|
|
2748
2810
|
function featureNameOf(resolvedPath, sourceRoot) {
|
|
2749
|
-
const relative =
|
|
2811
|
+
const relative = path22.relative(sourceRoot, resolvedPath);
|
|
2750
2812
|
if (relative.startsWith("..")) return void 0;
|
|
2751
|
-
const segments = relative.split(
|
|
2813
|
+
const segments = relative.split(path22.sep);
|
|
2752
2814
|
if (segments[0] !== FEATURES_SEGMENT || segments.length < 2) return void 0;
|
|
2753
2815
|
return segments[1];
|
|
2754
2816
|
}
|
|
@@ -2764,9 +2826,9 @@ var crossFeatureImportRule = {
|
|
|
2764
2826
|
const filename = context.filename;
|
|
2765
2827
|
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
|
|
2766
2828
|
const sourceRoot = sourceRootOf(context);
|
|
2767
|
-
const fileRelative =
|
|
2829
|
+
const fileRelative = path22.relative(sourceRoot, filename);
|
|
2768
2830
|
if (fileRelative.startsWith("..")) return {};
|
|
2769
|
-
const fileSegments = fileRelative.split(
|
|
2831
|
+
const fileSegments = fileRelative.split(path22.sep);
|
|
2770
2832
|
const isInCompositions = fileSegments[0] === "compositions";
|
|
2771
2833
|
const isInApp = fileSegments[0] === "app";
|
|
2772
2834
|
const isConfig = fileSegments[0] === "config";
|
|
@@ -2780,9 +2842,9 @@ var crossFeatureImportRule = {
|
|
|
2780
2842
|
if (typeof source.value !== "string") return;
|
|
2781
2843
|
let resolved;
|
|
2782
2844
|
if (source.value.startsWith("@/")) {
|
|
2783
|
-
resolved =
|
|
2845
|
+
resolved = path22.resolve(sourceRoot, source.value.slice(2));
|
|
2784
2846
|
} else if (source.value.startsWith(".")) {
|
|
2785
|
-
resolved =
|
|
2847
|
+
resolved = path22.resolve(path22.dirname(filename), source.value);
|
|
2786
2848
|
}
|
|
2787
2849
|
if (!resolved) return;
|
|
2788
2850
|
const feature = featureNameOf(resolved, sourceRoot);
|
|
@@ -2801,7 +2863,7 @@ var crossFeatureImportRule = {
|
|
|
2801
2863
|
};
|
|
2802
2864
|
|
|
2803
2865
|
// eslint/rules/pure-function-extract.ts
|
|
2804
|
-
import
|
|
2866
|
+
import path23 from "path";
|
|
2805
2867
|
function isComponentLikeName(name) {
|
|
2806
2868
|
return /^[A-Z]/.test(name);
|
|
2807
2869
|
}
|
|
@@ -2830,9 +2892,9 @@ var pureFunctionExtractRule = {
|
|
|
2830
2892
|
const filename = context.filename;
|
|
2831
2893
|
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
|
|
2832
2894
|
const sourceRoot = sourceRootOf(context);
|
|
2833
|
-
const relative =
|
|
2895
|
+
const relative = path23.relative(sourceRoot, filename);
|
|
2834
2896
|
if (relative.startsWith("..")) return {};
|
|
2835
|
-
const segments = relative.split(
|
|
2897
|
+
const segments = relative.split(path23.sep);
|
|
2836
2898
|
if (segments[0] === "utils") return {};
|
|
2837
2899
|
if (segments[0] === "app") return {};
|
|
2838
2900
|
const supportFolders = /* @__PURE__ */ new Set(["hooks", "types", "schemas", "constants", "utils"]);
|
|
@@ -2872,7 +2934,7 @@ var pureFunctionExtractRule = {
|
|
|
2872
2934
|
};
|
|
2873
2935
|
|
|
2874
2936
|
// eslint/rules/hook-complexity.ts
|
|
2875
|
-
import
|
|
2937
|
+
import path24 from "path";
|
|
2876
2938
|
import ts4 from "typescript";
|
|
2877
2939
|
var REACT_HOOKS = /* @__PURE__ */ new Set([
|
|
2878
2940
|
"useState",
|
|
@@ -2925,9 +2987,9 @@ var hookComplexityRule = {
|
|
|
2925
2987
|
create(context) {
|
|
2926
2988
|
const filename = context.filename;
|
|
2927
2989
|
const sourceRoot = sourceRootOf(context);
|
|
2928
|
-
const relative =
|
|
2990
|
+
const relative = path24.relative(sourceRoot, filename);
|
|
2929
2991
|
if (relative.startsWith("..")) return {};
|
|
2930
|
-
const segments = relative.split(
|
|
2992
|
+
const segments = relative.split(path24.sep);
|
|
2931
2993
|
const sourceText = context.sourceCode.text;
|
|
2932
2994
|
function checkHook(node, name, body, exported) {
|
|
2933
2995
|
if (!exported) return;
|
|
@@ -2969,9 +3031,9 @@ var hookComplexityRule = {
|
|
|
2969
3031
|
};
|
|
2970
3032
|
|
|
2971
3033
|
// eslint/rules/locale-dotted-path.ts
|
|
2972
|
-
import
|
|
3034
|
+
import path25 from "path";
|
|
2973
3035
|
function isInLocalesDir(filename) {
|
|
2974
|
-
const segments =
|
|
3036
|
+
const segments = path25.resolve(filename).split(path25.sep);
|
|
2975
3037
|
const srcIdx = segments.lastIndexOf("src");
|
|
2976
3038
|
return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
|
|
2977
3039
|
}
|
|
@@ -3020,16 +3082,22 @@ var localeDottedPathRule = {
|
|
|
3020
3082
|
};
|
|
3021
3083
|
|
|
3022
3084
|
// eslint/rules/locales-location.ts
|
|
3023
|
-
import
|
|
3085
|
+
import path26 from "path";
|
|
3024
3086
|
function isLocalesFile(filename) {
|
|
3025
|
-
const segments =
|
|
3087
|
+
const segments = path26.resolve(filename).split(path26.sep);
|
|
3026
3088
|
const srcIdx = segments.lastIndexOf("src");
|
|
3027
3089
|
return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
|
|
3028
3090
|
}
|
|
3091
|
+
function isTestFile(filename) {
|
|
3092
|
+
return /\.(?:test|spec)\.[cm]?tsx?$/.test(filename);
|
|
3093
|
+
}
|
|
3029
3094
|
var LOCALE_NAME_RE = /^[a-z][a-zA-Z0-9]*$/;
|
|
3030
3095
|
function looksLikeLocaleKey(name) {
|
|
3031
3096
|
return LOCALE_NAME_RE.test(name);
|
|
3032
3097
|
}
|
|
3098
|
+
function looksLikeUserFacingString(value) {
|
|
3099
|
+
return typeof value === "string" && /^\p{Lu}/u.test(value);
|
|
3100
|
+
}
|
|
3033
3101
|
var localesLocationRule = {
|
|
3034
3102
|
meta: {
|
|
3035
3103
|
schema: [],
|
|
@@ -3039,9 +3107,9 @@ var localesLocationRule = {
|
|
|
3039
3107
|
}
|
|
3040
3108
|
},
|
|
3041
3109
|
create(context) {
|
|
3042
|
-
if (isLocalesFile(context.filename)) return {};
|
|
3110
|
+
if (isLocalesFile(context.filename) || isTestFile(context.filename)) return {};
|
|
3043
3111
|
const filename = context.filename;
|
|
3044
|
-
const segments =
|
|
3112
|
+
const segments = path26.resolve(filename).split(path26.sep);
|
|
3045
3113
|
const srcIdx = segments.lastIndexOf("src");
|
|
3046
3114
|
if (srcIdx === -1) return {};
|
|
3047
3115
|
const folder = segments[srcIdx + 1];
|
|
@@ -3049,8 +3117,10 @@ var localesLocationRule = {
|
|
|
3049
3117
|
return {
|
|
3050
3118
|
VariableDeclarator(node) {
|
|
3051
3119
|
if (node.id.type === "Identifier" && node.init?.type === "ObjectExpression" && node.init.properties.length > 0 && looksLikeLocaleKey(node.id.name)) {
|
|
3052
|
-
const
|
|
3053
|
-
|
|
3120
|
+
const hasUserFacingStringValues = node.init.properties.some(
|
|
3121
|
+
(p) => p.type === "Property" && p.value.type === "Literal" && looksLikeUserFacingString(p.value.value)
|
|
3122
|
+
);
|
|
3123
|
+
if (hasUserFacingStringValues) {
|
|
3054
3124
|
context.report({
|
|
3055
3125
|
node,
|
|
3056
3126
|
message: "User-facing strings must live in src/locales/, not inline in component files."
|
|
@@ -3063,7 +3133,7 @@ var localesLocationRule = {
|
|
|
3063
3133
|
};
|
|
3064
3134
|
|
|
3065
3135
|
// eslint/rules/hook-extraction.ts
|
|
3066
|
-
import
|
|
3136
|
+
import path27 from "path";
|
|
3067
3137
|
var hookExtractionRule = {
|
|
3068
3138
|
meta: {
|
|
3069
3139
|
schema: [],
|
|
@@ -3074,7 +3144,7 @@ var hookExtractionRule = {
|
|
|
3074
3144
|
},
|
|
3075
3145
|
create(context) {
|
|
3076
3146
|
const sourceRoot = sourceRootOf(context);
|
|
3077
|
-
const file =
|
|
3147
|
+
const file = path27.resolve(context.filename);
|
|
3078
3148
|
const segments = segmentsOf(file, sourceRoot);
|
|
3079
3149
|
if (segments.length === 0) return {};
|
|
3080
3150
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3100,7 +3170,7 @@ var hookExtractionRule = {
|
|
|
3100
3170
|
};
|
|
3101
3171
|
|
|
3102
3172
|
// eslint/rules/value-extraction.ts
|
|
3103
|
-
import
|
|
3173
|
+
import path28 from "path";
|
|
3104
3174
|
var valueExtractionRule = {
|
|
3105
3175
|
meta: {
|
|
3106
3176
|
schema: [],
|
|
@@ -3111,7 +3181,7 @@ var valueExtractionRule = {
|
|
|
3111
3181
|
},
|
|
3112
3182
|
create(context) {
|
|
3113
3183
|
const sourceRoot = sourceRootOf(context);
|
|
3114
|
-
const file =
|
|
3184
|
+
const file = path28.resolve(context.filename);
|
|
3115
3185
|
const segments = segmentsOf(file, sourceRoot);
|
|
3116
3186
|
if (segments.length === 0 || segments[0] !== "app") return {};
|
|
3117
3187
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3132,7 +3202,7 @@ var valueExtractionRule = {
|
|
|
3132
3202
|
};
|
|
3133
3203
|
|
|
3134
3204
|
// eslint/rules/config-extraction.ts
|
|
3135
|
-
import
|
|
3205
|
+
import path29 from "path";
|
|
3136
3206
|
var configExtractionRule = {
|
|
3137
3207
|
meta: {
|
|
3138
3208
|
schema: [],
|
|
@@ -3143,7 +3213,7 @@ var configExtractionRule = {
|
|
|
3143
3213
|
},
|
|
3144
3214
|
create(context) {
|
|
3145
3215
|
const sourceRoot = sourceRootOf(context);
|
|
3146
|
-
const file =
|
|
3216
|
+
const file = path29.resolve(context.filename);
|
|
3147
3217
|
const segments = segmentsOf(file, sourceRoot);
|
|
3148
3218
|
if (segments.length < 3 || segments[0] !== "config") return {};
|
|
3149
3219
|
if (SUPPORT_FOLDERS2.has(segments[2] ?? "")) return {};
|
|
@@ -3181,7 +3251,7 @@ var configExtractionRule = {
|
|
|
3181
3251
|
};
|
|
3182
3252
|
|
|
3183
3253
|
// eslint/rules/component-nesting.ts
|
|
3184
|
-
import
|
|
3254
|
+
import path30 from "path";
|
|
3185
3255
|
var componentNestingRule = {
|
|
3186
3256
|
meta: {
|
|
3187
3257
|
schema: [],
|
|
@@ -3192,7 +3262,7 @@ var componentNestingRule = {
|
|
|
3192
3262
|
},
|
|
3193
3263
|
create(context) {
|
|
3194
3264
|
const sourceRoot = sourceRootOf(context);
|
|
3195
|
-
const file =
|
|
3265
|
+
const file = path30.resolve(context.filename);
|
|
3196
3266
|
const segments = segmentsOf(file, sourceRoot);
|
|
3197
3267
|
if (segments.length !== 4 || segments[0] !== "features") return {};
|
|
3198
3268
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3225,7 +3295,7 @@ var componentNestingRule = {
|
|
|
3225
3295
|
};
|
|
3226
3296
|
|
|
3227
3297
|
// eslint/rules/stay-flat.ts
|
|
3228
|
-
import
|
|
3298
|
+
import path31 from "path";
|
|
3229
3299
|
var stayFlatRule = {
|
|
3230
3300
|
meta: {
|
|
3231
3301
|
schema: [],
|
|
@@ -3236,7 +3306,7 @@ var stayFlatRule = {
|
|
|
3236
3306
|
},
|
|
3237
3307
|
create(context) {
|
|
3238
3308
|
const sourceRoot = sourceRootOf(context);
|
|
3239
|
-
const file =
|
|
3309
|
+
const file = path31.resolve(context.filename);
|
|
3240
3310
|
const segments = segmentsOf(file, sourceRoot);
|
|
3241
3311
|
if (segments.length !== 3 || segments[0] !== "features") return {};
|
|
3242
3312
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3276,7 +3346,7 @@ var stayFlatRule = {
|
|
|
3276
3346
|
};
|
|
3277
3347
|
|
|
3278
3348
|
// eslint/rules/type-extraction.ts
|
|
3279
|
-
import
|
|
3349
|
+
import path32 from "path";
|
|
3280
3350
|
var typeExtractionRule = {
|
|
3281
3351
|
meta: {
|
|
3282
3352
|
schema: [],
|
|
@@ -3287,7 +3357,7 @@ var typeExtractionRule = {
|
|
|
3287
3357
|
},
|
|
3288
3358
|
create(context) {
|
|
3289
3359
|
const sourceRoot = sourceRootOf(context);
|
|
3290
|
-
const file =
|
|
3360
|
+
const file = path32.resolve(context.filename);
|
|
3291
3361
|
const segments = segmentsOf(file, sourceRoot);
|
|
3292
3362
|
if (segments.length === 0) return {};
|
|
3293
3363
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3333,7 +3403,7 @@ var typeExtractionRule = {
|
|
|
3333
3403
|
};
|
|
3334
3404
|
|
|
3335
3405
|
// eslint/rules/locale-placement.ts
|
|
3336
|
-
import
|
|
3406
|
+
import path33 from "path";
|
|
3337
3407
|
import { readFileSync as readFileSync4 } from "fs";
|
|
3338
3408
|
import ts5 from "typescript";
|
|
3339
3409
|
var LOCALE_ACCESS = /\blocales\.(?<key>[A-Za-z_$][\w$]*)/g;
|
|
@@ -3381,7 +3451,7 @@ var localePlacementRule = {
|
|
|
3381
3451
|
},
|
|
3382
3452
|
create(context) {
|
|
3383
3453
|
const sourceRoot = sourceRootOf(context);
|
|
3384
|
-
const file =
|
|
3454
|
+
const file = path33.resolve(context.filename);
|
|
3385
3455
|
const segments = segmentsOf(file, sourceRoot);
|
|
3386
3456
|
if (segments.length === 0) return {};
|
|
3387
3457
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3459,7 +3529,7 @@ var localePlacementRule = {
|
|
|
3459
3529
|
};
|
|
3460
3530
|
|
|
3461
3531
|
// eslint/rules/sole-state-owner.ts
|
|
3462
|
-
import
|
|
3532
|
+
import path34 from "path";
|
|
3463
3533
|
import ts6 from "typescript";
|
|
3464
3534
|
function findStateHooks(node) {
|
|
3465
3535
|
const hooks = [];
|
|
@@ -3539,7 +3609,7 @@ var soleStateOwnerRule = {
|
|
|
3539
3609
|
}
|
|
3540
3610
|
},
|
|
3541
3611
|
create(context) {
|
|
3542
|
-
const filename =
|
|
3612
|
+
const filename = path34.resolve(context.filename);
|
|
3543
3613
|
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
|
|
3544
3614
|
const text = context.sourceCode.text;
|
|
3545
3615
|
const components = parseComponentInfo(text, filename);
|
|
@@ -3618,7 +3688,7 @@ function usesOutsideJsx(declaration, hook, children) {
|
|
|
3618
3688
|
}
|
|
3619
3689
|
|
|
3620
3690
|
// eslint/rules/locale-key-shape.ts
|
|
3621
|
-
import
|
|
3691
|
+
import path35 from "path";
|
|
3622
3692
|
var MAX_KEY_LENGTH = 30;
|
|
3623
3693
|
var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
|
|
3624
3694
|
"Button",
|
|
@@ -3668,7 +3738,7 @@ var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
|
|
|
3668
3738
|
var CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
|
|
3669
3739
|
var ENGLISH = /^[A-Za-z0-9_]*$/;
|
|
3670
3740
|
function isLocalesFile2(filename) {
|
|
3671
|
-
const segments =
|
|
3741
|
+
const segments = path35.resolve(filename).split(path35.sep);
|
|
3672
3742
|
const srcIdx = segments.lastIndexOf("src");
|
|
3673
3743
|
return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
|
|
3674
3744
|
}
|
|
@@ -3739,7 +3809,7 @@ var localeKeyShapeRule = {
|
|
|
3739
3809
|
};
|
|
3740
3810
|
|
|
3741
3811
|
// eslint/rules/shared-style-dedup.ts
|
|
3742
|
-
import
|
|
3812
|
+
import path36 from "path";
|
|
3743
3813
|
import { readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
3744
3814
|
var CLASS_NAME = /className="(?<classes>[^"]+)"/g;
|
|
3745
3815
|
var comboCache;
|
|
@@ -3777,7 +3847,7 @@ var sharedStyleDedupRule = {
|
|
|
3777
3847
|
},
|
|
3778
3848
|
create(context) {
|
|
3779
3849
|
const sourceRoot = sourceRootOf(context);
|
|
3780
|
-
const file =
|
|
3850
|
+
const file = path36.resolve(context.filename);
|
|
3781
3851
|
const segments = segmentsOf(file, sourceRoot);
|
|
3782
3852
|
if (segments.length === 0) return {};
|
|
3783
3853
|
const index = getProjectIndex(sourceRoot);
|
|
@@ -3980,8 +4050,103 @@ var zodSchemaValidationRule = {
|
|
|
3980
4050
|
}
|
|
3981
4051
|
};
|
|
3982
4052
|
|
|
4053
|
+
// eslint/rules/schema-casing.ts
|
|
4054
|
+
import path37 from "path";
|
|
4055
|
+
function isCamelCase(name) {
|
|
4056
|
+
return /^[a-z][a-zA-Z0-9]*$/.test(name);
|
|
4057
|
+
}
|
|
4058
|
+
function rootIdentifierName(node) {
|
|
4059
|
+
let current = node;
|
|
4060
|
+
for (; ; ) {
|
|
4061
|
+
if (current.type === "Identifier") return current.name;
|
|
4062
|
+
if (current.type === "CallExpression") {
|
|
4063
|
+
current = current.callee;
|
|
4064
|
+
continue;
|
|
4065
|
+
}
|
|
4066
|
+
if (current.type === "MemberExpression") {
|
|
4067
|
+
current = current.object;
|
|
4068
|
+
continue;
|
|
4069
|
+
}
|
|
4070
|
+
return void 0;
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
var schemaCasingRule = {
|
|
4074
|
+
meta: {
|
|
4075
|
+
schema: [],
|
|
4076
|
+
type: "problem",
|
|
4077
|
+
docs: {
|
|
4078
|
+
description: "Require a Zod schema constant to be named in camelCase."
|
|
4079
|
+
}
|
|
4080
|
+
},
|
|
4081
|
+
create(context) {
|
|
4082
|
+
const filename = path37.resolve(context.filename);
|
|
4083
|
+
const sourceRoot = sourceRootOf(context);
|
|
4084
|
+
if (!filename.startsWith(sourceRoot + path37.sep)) return {};
|
|
4085
|
+
let zodLocalName;
|
|
4086
|
+
return {
|
|
4087
|
+
ImportDeclaration(node) {
|
|
4088
|
+
if (node.source.value !== "zod") return;
|
|
4089
|
+
for (const specifier of node.specifiers) {
|
|
4090
|
+
if (specifier.type === "ImportSpecifier" && specifier.imported.type === "Identifier" && specifier.imported.name === "z") {
|
|
4091
|
+
zodLocalName = specifier.local.name;
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
},
|
|
4095
|
+
VariableDeclarator(node) {
|
|
4096
|
+
if (!zodLocalName) return;
|
|
4097
|
+
if (node.id.type !== "Identifier") return;
|
|
4098
|
+
const declaration = node.parent;
|
|
4099
|
+
if (declaration.type !== "VariableDeclaration" || declaration.kind !== "const") return;
|
|
4100
|
+
const container = declaration.parent;
|
|
4101
|
+
const isModuleLevel = container.type === "Program" || container.type === "ExportNamedDeclaration" && container.parent.type === "Program";
|
|
4102
|
+
if (!isModuleLevel) return;
|
|
4103
|
+
if (!node.init || rootIdentifierName(node.init) !== zodLocalName) return;
|
|
4104
|
+
const { name } = node.id;
|
|
4105
|
+
if (isCamelCase(name)) return;
|
|
4106
|
+
context.report({
|
|
4107
|
+
node,
|
|
4108
|
+
message: `${name} must be camelCase. See docs/next-codebase-guide/rules/types-and-schemas-rule.md`
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
};
|
|
4114
|
+
|
|
4115
|
+
// eslint/rules/component-casing.ts
|
|
4116
|
+
import path38 from "path";
|
|
4117
|
+
function isPascalCase6(name) {
|
|
4118
|
+
return /^[A-Z][A-Za-z0-9]*$/.test(name);
|
|
4119
|
+
}
|
|
4120
|
+
var componentCasingRule = {
|
|
4121
|
+
meta: {
|
|
4122
|
+
schema: [],
|
|
4123
|
+
type: "problem",
|
|
4124
|
+
docs: {
|
|
4125
|
+
description: "Require a JSX-returning function or const to be named in PascalCase."
|
|
4126
|
+
}
|
|
4127
|
+
},
|
|
4128
|
+
create(context) {
|
|
4129
|
+
const filename = path38.resolve(context.filename);
|
|
4130
|
+
const sourceRoot = sourceRootOf(context);
|
|
4131
|
+
if (!filename.startsWith(sourceRoot + path38.sep)) return {};
|
|
4132
|
+
if (path38.extname(filename) !== ".tsx") return {};
|
|
4133
|
+
return {
|
|
4134
|
+
Program(node) {
|
|
4135
|
+
for (const declaration of findJsxReturningDeclarations(context.sourceCode.text, filename)) {
|
|
4136
|
+
if (isPascalCase6(declaration.name)) continue;
|
|
4137
|
+
context.report({
|
|
4138
|
+
node,
|
|
4139
|
+
loc: { line: declaration.line, column: declaration.column },
|
|
4140
|
+
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`
|
|
4141
|
+
});
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
};
|
|
4145
|
+
}
|
|
4146
|
+
};
|
|
4147
|
+
|
|
3983
4148
|
// eslint/rules/source-under-src.ts
|
|
3984
|
-
import
|
|
4149
|
+
import path39 from "path";
|
|
3985
4150
|
var NON_SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set([
|
|
3986
4151
|
".agents",
|
|
3987
4152
|
".cache",
|
|
@@ -4022,14 +4187,14 @@ var sourceUnderSrcRule = {
|
|
|
4022
4187
|
}
|
|
4023
4188
|
},
|
|
4024
4189
|
create(context) {
|
|
4025
|
-
const filename =
|
|
4190
|
+
const filename = path39.resolve(context.filename);
|
|
4026
4191
|
if (!MODULE_EXTENSION.test(filename)) return {};
|
|
4027
|
-
const relative =
|
|
4192
|
+
const relative = path39.relative(context.cwd, filename).replace(/\\/g, "/");
|
|
4028
4193
|
if (relative === "src" || relative.startsWith("src/")) return {};
|
|
4029
4194
|
const topLevel = relative.split("/")[0] ?? "";
|
|
4030
4195
|
if (NON_SOURCE_ROOT_DIRS.has(topLevel)) return {};
|
|
4031
4196
|
if (!relative.includes("/")) {
|
|
4032
|
-
const basename =
|
|
4197
|
+
const basename = path39.basename(filename);
|
|
4033
4198
|
if (CONFIG_FILE.test(basename) || DECLARATION_FILE.test(basename) || basename.startsWith(".")) return {};
|
|
4034
4199
|
}
|
|
4035
4200
|
return {
|
|
@@ -4046,7 +4211,7 @@ var sourceUnderSrcRule = {
|
|
|
4046
4211
|
|
|
4047
4212
|
// eslint/rules/zirka-baseline.ts
|
|
4048
4213
|
import fs5 from "fs";
|
|
4049
|
-
import
|
|
4214
|
+
import path40 from "path";
|
|
4050
4215
|
var ESLINT_CONFIG = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
|
|
4051
4216
|
var PRETTIER_CONFIGS = [
|
|
4052
4217
|
"prettier.config.mjs",
|
|
@@ -4065,10 +4230,10 @@ var zirkaBaselineRule = {
|
|
|
4065
4230
|
}
|
|
4066
4231
|
},
|
|
4067
4232
|
create(context) {
|
|
4068
|
-
const filename =
|
|
4069
|
-
const basename =
|
|
4233
|
+
const filename = path40.resolve(context.filename);
|
|
4234
|
+
const basename = path40.basename(filename);
|
|
4070
4235
|
if (!ESLINT_CONFIG.test(basename)) return {};
|
|
4071
|
-
const projectRoot =
|
|
4236
|
+
const projectRoot = path40.dirname(filename);
|
|
4072
4237
|
const report3 = (message) => {
|
|
4073
4238
|
context.report({
|
|
4074
4239
|
node: context.sourceCode.ast,
|
|
@@ -4083,7 +4248,7 @@ var zirkaBaselineRule = {
|
|
|
4083
4248
|
'ESLint config must take its configuration from zirka (import { styleguide } from "zirka") instead of restating rules locally.'
|
|
4084
4249
|
);
|
|
4085
4250
|
}
|
|
4086
|
-
const tsconfigPath =
|
|
4251
|
+
const tsconfigPath = path40.join(projectRoot, "tsconfig.json");
|
|
4087
4252
|
if (!fs5.existsSync(tsconfigPath)) {
|
|
4088
4253
|
report3('No tsconfig.json found. Create one extending the zirka TypeScript base config ("zirka/typescript").');
|
|
4089
4254
|
} else {
|
|
@@ -4101,13 +4266,13 @@ var zirkaBaselineRule = {
|
|
|
4101
4266
|
report3('tsconfig.json must extend the zirka TypeScript base config ("zirka/typescript").');
|
|
4102
4267
|
}
|
|
4103
4268
|
}
|
|
4104
|
-
const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs5.existsSync(
|
|
4269
|
+
const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs5.existsSync(path40.join(projectRoot, name)));
|
|
4105
4270
|
if (!prettierConfigFile) {
|
|
4106
4271
|
report3(
|
|
4107
4272
|
"No prettier config found. Create one that takes its configuration from zirka (styleguide({ prettier: true }).prettierConfig)."
|
|
4108
4273
|
);
|
|
4109
4274
|
} else {
|
|
4110
|
-
const content = fs5.readFileSync(
|
|
4275
|
+
const content = fs5.readFileSync(path40.join(projectRoot, prettierConfigFile), "utf8");
|
|
4111
4276
|
if (!content.includes("zirka")) {
|
|
4112
4277
|
report3(
|
|
4113
4278
|
"The prettier config must take its configuration from zirka (styleguide({ prettier: true }).prettierConfig) instead of restating it locally."
|
|
@@ -4177,7 +4342,7 @@ var docKindSuffixRule = {
|
|
|
4177
4342
|
};
|
|
4178
4343
|
|
|
4179
4344
|
// eslint/rules/documentation/title-matches-file-name.ts
|
|
4180
|
-
import
|
|
4345
|
+
import path41 from "path";
|
|
4181
4346
|
function toExpectedFileName(title) {
|
|
4182
4347
|
return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
|
|
4183
4348
|
}
|
|
@@ -4197,7 +4362,7 @@ var titleMatchesFileNameRule = {
|
|
|
4197
4362
|
if (!filename.endsWith(".md")) return;
|
|
4198
4363
|
const title = getTextContent(node).trim();
|
|
4199
4364
|
const expectedFileName = toExpectedFileName(title);
|
|
4200
|
-
const actualFileName =
|
|
4365
|
+
const actualFileName = path41.basename(filename);
|
|
4201
4366
|
if (!title) {
|
|
4202
4367
|
context.report({
|
|
4203
4368
|
node,
|
|
@@ -4640,20 +4805,20 @@ var referenceBlockHeadingsRule = {
|
|
|
4640
4805
|
|
|
4641
4806
|
// eslint/rules/documentation/support-document-placement.ts
|
|
4642
4807
|
import { existsSync as existsSync2 } from "fs";
|
|
4643
|
-
import
|
|
4808
|
+
import path42 from "path";
|
|
4644
4809
|
function checkPlacement(filename, kind) {
|
|
4645
|
-
const parentFolder =
|
|
4810
|
+
const parentFolder = path42.basename(path42.dirname(filename));
|
|
4646
4811
|
const expectedParent = `${kind}s`;
|
|
4647
4812
|
if (parentFolder !== expectedParent) {
|
|
4648
4813
|
return `${kind} lives in "${parentFolder}/" instead of "${expectedParent}/"`;
|
|
4649
4814
|
}
|
|
4650
|
-
const guideFolderPath =
|
|
4651
|
-
const guideFolder =
|
|
4815
|
+
const guideFolderPath = path42.dirname(path42.dirname(filename));
|
|
4816
|
+
const guideFolder = path42.basename(guideFolderPath);
|
|
4652
4817
|
if (!guideFolder.endsWith("-guide")) {
|
|
4653
4818
|
return `${kind} owner folder "${guideFolder}/" does not use the "*-guide/" suffix`;
|
|
4654
4819
|
}
|
|
4655
4820
|
const entryPoint = `${guideFolder}.md`;
|
|
4656
|
-
if (!existsSync2(
|
|
4821
|
+
if (!existsSync2(path42.join(guideFolderPath, entryPoint))) {
|
|
4657
4822
|
return `${kind} owner folder "${guideFolder}/" has no "${entryPoint}" entry point`;
|
|
4658
4823
|
}
|
|
4659
4824
|
return void 0;
|
|
@@ -4712,11 +4877,11 @@ var noTemplatePromptRule = {
|
|
|
4712
4877
|
};
|
|
4713
4878
|
|
|
4714
4879
|
// eslint/rules/documentation/guide-folder-entry-point.ts
|
|
4715
|
-
import
|
|
4880
|
+
import path44 from "path";
|
|
4716
4881
|
|
|
4717
4882
|
// eslint/rules/documentation/project-index.ts
|
|
4718
4883
|
import { readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
4719
|
-
import
|
|
4884
|
+
import path43 from "path";
|
|
4720
4885
|
var KIND_BY_SUFFIX = [
|
|
4721
4886
|
["-rule.md", "rule"],
|
|
4722
4887
|
["-guide.md", "guide"],
|
|
@@ -4725,7 +4890,7 @@ var KIND_BY_SUFFIX = [
|
|
|
4725
4890
|
];
|
|
4726
4891
|
function listMarkdownFiles(dir) {
|
|
4727
4892
|
return readdirSync3(dir).flatMap((entry) => {
|
|
4728
|
-
const entryPath =
|
|
4893
|
+
const entryPath = path43.join(dir, entry);
|
|
4729
4894
|
if (statSync4(entryPath).isDirectory()) {
|
|
4730
4895
|
return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
|
|
4731
4896
|
}
|
|
@@ -4743,11 +4908,11 @@ function getProjectDocs(docsRoot) {
|
|
|
4743
4908
|
if (cached) return cached;
|
|
4744
4909
|
const files = listMarkdownFiles(docsRoot);
|
|
4745
4910
|
const docs = files.sort((a, b) => a.localeCompare(b)).map((filePath) => {
|
|
4746
|
-
const fileName =
|
|
4911
|
+
const fileName = path43.basename(filePath);
|
|
4747
4912
|
const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
|
|
4748
4913
|
return {
|
|
4749
4914
|
filePath,
|
|
4750
|
-
doc:
|
|
4915
|
+
doc: path43.relative(docsRoot, filePath).split(path43.sep).join("/"),
|
|
4751
4916
|
fileName,
|
|
4752
4917
|
kind,
|
|
4753
4918
|
title: extractTitle(filePath)
|
|
@@ -4757,12 +4922,12 @@ function getProjectDocs(docsRoot) {
|
|
|
4757
4922
|
return docs;
|
|
4758
4923
|
}
|
|
4759
4924
|
function findDocsRoot(filePath) {
|
|
4760
|
-
let dir =
|
|
4925
|
+
let dir = path43.dirname(filePath);
|
|
4761
4926
|
for (; ; ) {
|
|
4762
|
-
if (
|
|
4927
|
+
if (path43.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
|
|
4763
4928
|
return dir;
|
|
4764
4929
|
}
|
|
4765
|
-
const parent =
|
|
4930
|
+
const parent = path43.dirname(dir);
|
|
4766
4931
|
if (parent === dir) return void 0;
|
|
4767
4932
|
dir = parent;
|
|
4768
4933
|
}
|
|
@@ -4786,13 +4951,13 @@ var guideFolderEntryPointRule = {
|
|
|
4786
4951
|
if (!docsRoot) return;
|
|
4787
4952
|
const docs = getProjectDocs(docsRoot);
|
|
4788
4953
|
const guideFolders = new Set(
|
|
4789
|
-
docs.filter((doc) => ["rules", "references"].includes(
|
|
4954
|
+
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))
|
|
4790
4955
|
);
|
|
4791
|
-
const currentDir =
|
|
4956
|
+
const currentDir = path44.dirname(filename);
|
|
4792
4957
|
if (guideFolders.has(currentDir)) {
|
|
4793
|
-
const expectedEntryPoint = `${
|
|
4958
|
+
const expectedEntryPoint = `${path44.basename(currentDir)}.md`;
|
|
4794
4959
|
const hasEntryPoint = docs.some(
|
|
4795
|
-
(doc) => doc.kind === "guide" &&
|
|
4960
|
+
(doc) => doc.kind === "guide" && path44.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
|
|
4796
4961
|
);
|
|
4797
4962
|
if (!hasEntryPoint) {
|
|
4798
4963
|
context.report({
|
|
@@ -5017,7 +5182,7 @@ var noNestedHowToRule = {
|
|
|
5017
5182
|
|
|
5018
5183
|
// eslint/rules/documentation/glossary-term-linking.ts
|
|
5019
5184
|
import { readFileSync as readFileSync7 } from "fs";
|
|
5020
|
-
import
|
|
5185
|
+
import path45 from "path";
|
|
5021
5186
|
function extractGlossaryTerms(filePath) {
|
|
5022
5187
|
const content = readFileSync7(filePath, "utf8");
|
|
5023
5188
|
const terms = [];
|
|
@@ -5071,9 +5236,9 @@ var glossaryTermLinkingRule = {
|
|
|
5071
5236
|
const docsRoot = findDocsRoot(filename);
|
|
5072
5237
|
if (!docsRoot) return;
|
|
5073
5238
|
const docs = getProjectDocs(docsRoot);
|
|
5074
|
-
const guideDir =
|
|
5239
|
+
const guideDir = path45.dirname(filename);
|
|
5075
5240
|
const guideReferences = docs.filter(
|
|
5076
|
-
(doc) => doc.kind === "reference" &&
|
|
5241
|
+
(doc) => doc.kind === "reference" && path45.dirname(doc.filePath) === guideDir
|
|
5077
5242
|
);
|
|
5078
5243
|
if (guideReferences.length === 0) return;
|
|
5079
5244
|
const glossaryTerms = [];
|
|
@@ -5098,7 +5263,7 @@ var glossaryTermLinkingRule = {
|
|
|
5098
5263
|
|
|
5099
5264
|
// eslint/rules/documentation/guide-mentions-documents.ts
|
|
5100
5265
|
import { existsSync as existsSync3 } from "fs";
|
|
5101
|
-
import
|
|
5266
|
+
import path46 from "path";
|
|
5102
5267
|
function visitSteps3(node, check) {
|
|
5103
5268
|
if (node.type === "list" && node.ordered) {
|
|
5104
5269
|
for (const child of node.children) check(child);
|
|
@@ -5131,12 +5296,12 @@ var guideMentionsDocumentsRule = {
|
|
|
5131
5296
|
if (!filename.endsWith("-guide.md")) return;
|
|
5132
5297
|
const docsRoot = findDocsRoot(filename);
|
|
5133
5298
|
if (!docsRoot) return;
|
|
5134
|
-
const guideDir =
|
|
5135
|
-
if (
|
|
5299
|
+
const guideDir = path46.dirname(filename);
|
|
5300
|
+
if (path46.basename(filename, ".md") !== path46.basename(guideDir)) return;
|
|
5136
5301
|
const docs = getProjectDocs(docsRoot);
|
|
5137
5302
|
const owned = docs.filter((doc) => {
|
|
5138
|
-
const parent =
|
|
5139
|
-
return parent ===
|
|
5303
|
+
const parent = path46.dirname(doc.filePath);
|
|
5304
|
+
return parent === path46.join(guideDir, "rules") || parent === path46.join(guideDir, "references");
|
|
5140
5305
|
});
|
|
5141
5306
|
const allLinks = [];
|
|
5142
5307
|
collectMarkdownLinks(node, allLinks);
|
|
@@ -5163,7 +5328,7 @@ var guideMentionsDocumentsRule = {
|
|
|
5163
5328
|
for (const link of allLinks) {
|
|
5164
5329
|
const target = linkTarget(link.url);
|
|
5165
5330
|
if (!target.endsWith(".md")) continue;
|
|
5166
|
-
const resolved =
|
|
5331
|
+
const resolved = path46.normalize(path46.join(guideDir, target));
|
|
5167
5332
|
if (!existsSync3(resolved)) {
|
|
5168
5333
|
context.report({
|
|
5169
5334
|
node: link,
|
|
@@ -5713,11 +5878,11 @@ var themeVariableNamespaceRule = {
|
|
|
5713
5878
|
|
|
5714
5879
|
// eslint/rules/tailwind/css-entry-point.ts
|
|
5715
5880
|
import { statSync as statSync6 } from "fs";
|
|
5716
|
-
import
|
|
5881
|
+
import path49 from "path";
|
|
5717
5882
|
|
|
5718
5883
|
// eslint/rules/tailwind/source-files.ts
|
|
5719
5884
|
import { readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
5720
|
-
import
|
|
5885
|
+
import path47 from "path";
|
|
5721
5886
|
var CSS_EXTENSIONS = [".css"];
|
|
5722
5887
|
var MODULE_EXTENSIONS3 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
5723
5888
|
var SOURCE_EXTENSIONS = [...MODULE_EXTENSIONS3, ...CSS_EXTENSIONS];
|
|
@@ -5730,7 +5895,7 @@ function findFiles(dir, extensions) {
|
|
|
5730
5895
|
}
|
|
5731
5896
|
return entries.flatMap((entry) => {
|
|
5732
5897
|
if (entry.startsWith(".") || entry === "node_modules") return [];
|
|
5733
|
-
const entryPath =
|
|
5898
|
+
const entryPath = path47.join(dir, entry);
|
|
5734
5899
|
let stats;
|
|
5735
5900
|
try {
|
|
5736
5901
|
stats = statSync5(entryPath);
|
|
@@ -5738,7 +5903,7 @@ function findFiles(dir, extensions) {
|
|
|
5738
5903
|
return [];
|
|
5739
5904
|
}
|
|
5740
5905
|
if (stats.isDirectory()) return findFiles(entryPath, extensions);
|
|
5741
|
-
return extensions.includes(
|
|
5906
|
+
return extensions.includes(path47.extname(entry)) ? [entryPath] : [];
|
|
5742
5907
|
});
|
|
5743
5908
|
}
|
|
5744
5909
|
function cachedTextReader() {
|
|
@@ -5761,7 +5926,7 @@ function escapeRegExp(text) {
|
|
|
5761
5926
|
}
|
|
5762
5927
|
|
|
5763
5928
|
// eslint/rules/tailwind/stylesheet-graph.ts
|
|
5764
|
-
import
|
|
5929
|
+
import path48 from "path";
|
|
5765
5930
|
function registersTailwind(text) {
|
|
5766
5931
|
return /@import\s+(?:url\(\s*)?["']tailwindcss["']\s*\)?/i.test(text);
|
|
5767
5932
|
}
|
|
@@ -5775,25 +5940,25 @@ function moduleImports(text, fileName) {
|
|
|
5775
5940
|
return new RegExp(`(?:import|require)\\s*\\(?\\s*["'][^"']*${escaped}["']`, "i").test(text);
|
|
5776
5941
|
}
|
|
5777
5942
|
function resolveSpecifier2(fromFile, spec, sourceRoot) {
|
|
5778
|
-
if (spec.startsWith("/")) return
|
|
5779
|
-
if (spec.startsWith("./") || spec.startsWith("../")) return
|
|
5780
|
-
if (spec.startsWith("@/")) return
|
|
5943
|
+
if (spec.startsWith("/")) return path48.resolve(spec);
|
|
5944
|
+
if (spec.startsWith("./") || spec.startsWith("../")) return path48.resolve(path48.dirname(fromFile), spec);
|
|
5945
|
+
if (spec.startsWith("@/")) return path48.resolve(sourceRoot, spec.slice(2));
|
|
5781
5946
|
return void 0;
|
|
5782
5947
|
}
|
|
5783
5948
|
function buildStylesheetGraph(options) {
|
|
5784
5949
|
const { cssFiles, sourceRoot, textOf } = options;
|
|
5785
|
-
const cssSet = new Set(cssFiles.map((file) =>
|
|
5950
|
+
const cssSet = new Set(cssFiles.map((file) => path48.normalize(file)));
|
|
5786
5951
|
const globals = cssFiles.filter((file) => registersTailwind(textOf(file)));
|
|
5787
5952
|
const reachable = /* @__PURE__ */ new Set();
|
|
5788
5953
|
const queue = [...globals];
|
|
5789
|
-
for (const global of globals) reachable.add(
|
|
5954
|
+
for (const global of globals) reachable.add(path48.normalize(global));
|
|
5790
5955
|
while (queue.length > 0) {
|
|
5791
5956
|
const from = queue.shift();
|
|
5792
5957
|
if (!from) continue;
|
|
5793
5958
|
for (const spec of importedSpecifiers(textOf(from))) {
|
|
5794
5959
|
const target = resolveSpecifier2(from, spec, sourceRoot);
|
|
5795
5960
|
if (!target) continue;
|
|
5796
|
-
const normalized =
|
|
5961
|
+
const normalized = path48.normalize(target);
|
|
5797
5962
|
if (cssSet.has(normalized) && !reachable.has(normalized)) {
|
|
5798
5963
|
reachable.add(normalized);
|
|
5799
5964
|
queue.push(normalized);
|
|
@@ -5805,7 +5970,7 @@ function buildStylesheetGraph(options) {
|
|
|
5805
5970
|
for (const spec of importedSpecifiers(textOf(global))) {
|
|
5806
5971
|
const target = resolveSpecifier2(global, spec, sourceRoot);
|
|
5807
5972
|
if (!target) continue;
|
|
5808
|
-
const normalized =
|
|
5973
|
+
const normalized = path48.normalize(target);
|
|
5809
5974
|
if (cssSet.has(normalized)) directChildren.add(normalized);
|
|
5810
5975
|
}
|
|
5811
5976
|
}
|
|
@@ -5838,7 +6003,7 @@ var cssEntryPointRule = {
|
|
|
5838
6003
|
return {
|
|
5839
6004
|
"StyleSheet:exit"(node) {
|
|
5840
6005
|
if (globals.length === 0) return;
|
|
5841
|
-
const current =
|
|
6006
|
+
const current = path49.normalize(path49.resolve(context.filename));
|
|
5842
6007
|
if (globals.includes(current)) {
|
|
5843
6008
|
if (globals.length > 1) {
|
|
5844
6009
|
context.report({
|
|
@@ -5847,7 +6012,7 @@ var cssEntryPointRule = {
|
|
|
5847
6012
|
});
|
|
5848
6013
|
return;
|
|
5849
6014
|
}
|
|
5850
|
-
const basename =
|
|
6015
|
+
const basename = path49.basename(current);
|
|
5851
6016
|
const importCount = moduleFiles.filter((modulePath) => moduleImports(textOf(modulePath), basename)).length;
|
|
5852
6017
|
if (importCount !== 1) {
|
|
5853
6018
|
context.report({
|
|
@@ -6176,7 +6341,7 @@ var nextjsStackRule = {
|
|
|
6176
6341
|
|
|
6177
6342
|
// eslint/rules/package-json/vitest-coverage.ts
|
|
6178
6343
|
import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
|
|
6179
|
-
import
|
|
6344
|
+
import path50 from "path";
|
|
6180
6345
|
function memberName4(member) {
|
|
6181
6346
|
return member.name.type === "String" ? member.name.value : member.name.name;
|
|
6182
6347
|
}
|
|
@@ -6245,7 +6410,7 @@ var vitestCoverageRule = {
|
|
|
6245
6410
|
message: 'package.json must declare a "test:unit:coverage" script that runs Vitest with coverage.'
|
|
6246
6411
|
});
|
|
6247
6412
|
}
|
|
6248
|
-
const configName = VITEST_CONFIG_NAMES.find((name) => existsSync4(
|
|
6413
|
+
const configName = VITEST_CONFIG_NAMES.find((name) => existsSync4(path50.join(context.cwd, name)));
|
|
6249
6414
|
if (!configName) {
|
|
6250
6415
|
context.report({
|
|
6251
6416
|
node,
|
|
@@ -6253,7 +6418,7 @@ var vitestCoverageRule = {
|
|
|
6253
6418
|
});
|
|
6254
6419
|
return;
|
|
6255
6420
|
}
|
|
6256
|
-
const content = readFileSync9(
|
|
6421
|
+
const content = readFileSync9(path50.join(context.cwd, configName), "utf8");
|
|
6257
6422
|
for (const metric of THRESHOLD_METRICS) {
|
|
6258
6423
|
if (!new RegExp(`\\b${metric}\\s*:\\s*[1-9]\\d*`).test(content)) {
|
|
6259
6424
|
context.report({ node, message: `${configName} must set a coverage threshold above zero for ${metric}.` });
|
|
@@ -6300,7 +6465,7 @@ var nextjsPackageJsonRules = {
|
|
|
6300
6465
|
|
|
6301
6466
|
// eslint/rules/husky/husky-hook.ts
|
|
6302
6467
|
import { existsSync as existsSync5, readFileSync as readFileSync10 } from "fs";
|
|
6303
|
-
import
|
|
6468
|
+
import path51 from "path";
|
|
6304
6469
|
var VITEST_CONFIG_NAMES2 = [
|
|
6305
6470
|
"vitest.config.ts",
|
|
6306
6471
|
"vitest.config.mts",
|
|
@@ -6326,7 +6491,7 @@ var huskyHookRule = {
|
|
|
6326
6491
|
const root = node.body;
|
|
6327
6492
|
if (root.type !== "Object") return;
|
|
6328
6493
|
if (!context.filename.endsWith("package.json")) return;
|
|
6329
|
-
const hookPath =
|
|
6494
|
+
const hookPath = path51.join(context.cwd, ".husky", "pre-commit");
|
|
6330
6495
|
if (!existsSync5(hookPath)) {
|
|
6331
6496
|
context.report({
|
|
6332
6497
|
node,
|
|
@@ -6350,7 +6515,7 @@ var huskyHookRule = {
|
|
|
6350
6515
|
}
|
|
6351
6516
|
requireNamedScript("typecheck");
|
|
6352
6517
|
requireNamedScript("test:unit:coverage");
|
|
6353
|
-
const vitestConfigName = VITEST_CONFIG_NAMES2.find((name) => existsSync5(
|
|
6518
|
+
const vitestConfigName = VITEST_CONFIG_NAMES2.find((name) => existsSync5(path51.join(context.cwd, name)));
|
|
6354
6519
|
if (vitestConfigName !== void 0) {
|
|
6355
6520
|
const coverageIndex = content.indexOf("npm run test:unit:coverage");
|
|
6356
6521
|
const localAddIndex = content.indexOf(`git add ${vitestConfigName}`);
|
|
@@ -6364,7 +6529,7 @@ var huskyHookRule = {
|
|
|
6364
6529
|
if (!content.includes("libyear --limit-major-individual=1")) {
|
|
6365
6530
|
context.report({ node, message: ".husky/pre-commit must run npx libyear --limit-major-individual=1." });
|
|
6366
6531
|
}
|
|
6367
|
-
const suppressionsPath =
|
|
6532
|
+
const suppressionsPath = path51.join(context.cwd, "eslint-suppressions.json");
|
|
6368
6533
|
if (existsSync5(suppressionsPath)) {
|
|
6369
6534
|
requireNamedScript("lint:prune");
|
|
6370
6535
|
const pruneIndex = content.indexOf("npm run lint:prune");
|
|
@@ -6437,7 +6602,7 @@ var vulykDependencyRule = {
|
|
|
6437
6602
|
|
|
6438
6603
|
// eslint/rules/vulyk/vulyk-docs.ts
|
|
6439
6604
|
import { existsSync as existsSync6, readFileSync as readFileSync11 } from "fs";
|
|
6440
|
-
import
|
|
6605
|
+
import path52 from "path";
|
|
6441
6606
|
var PASIKA_REPO = "Bredansky/pasika";
|
|
6442
6607
|
var BASE_REQUIRED_DOCS = [
|
|
6443
6608
|
{ name: "documentation-guide", path: "docs/documentation-guide" },
|
|
@@ -6470,8 +6635,8 @@ var vulykDocsRule = {
|
|
|
6470
6635
|
if (!context.filename.endsWith("package.json")) return;
|
|
6471
6636
|
const root = node.body;
|
|
6472
6637
|
if (root.type !== "Object") return;
|
|
6473
|
-
const projectRoot =
|
|
6474
|
-
const configPath =
|
|
6638
|
+
const projectRoot = path52.dirname(path52.resolve(context.filename));
|
|
6639
|
+
const configPath = path52.join(projectRoot, "vulyk.config.ts");
|
|
6475
6640
|
if (!existsSync6(configPath)) {
|
|
6476
6641
|
context.report({
|
|
6477
6642
|
node,
|
|
@@ -6496,7 +6661,7 @@ var vulykDocsRule = {
|
|
|
6496
6661
|
});
|
|
6497
6662
|
}
|
|
6498
6663
|
}
|
|
6499
|
-
const agentsPath =
|
|
6664
|
+
const agentsPath = path52.join(projectRoot, "AGENTS.md");
|
|
6500
6665
|
if (!existsSync6(agentsPath)) {
|
|
6501
6666
|
context.report({
|
|
6502
6667
|
node,
|
|
@@ -6528,12 +6693,15 @@ var pasikaNextjsAppRules = {
|
|
|
6528
6693
|
"enforce-barrel-exports": enforceBarrelExportsRule,
|
|
6529
6694
|
"config-extraction": configExtractionRule,
|
|
6530
6695
|
"value-extraction": valueExtractionRule,
|
|
6696
|
+
"constant-casing": constantCasingRule,
|
|
6531
6697
|
"type-extraction": typeExtractionRule,
|
|
6532
6698
|
"zod-schema-validation": zodSchemaValidationRule,
|
|
6699
|
+
"schema-casing": schemaCasingRule,
|
|
6533
6700
|
"source-under-src": sourceUnderSrcRule,
|
|
6534
6701
|
"zirka-baseline": zirkaBaselineRule,
|
|
6535
6702
|
// Next.js/React application rules.
|
|
6536
6703
|
"component-placement": componentPlacementRule,
|
|
6704
|
+
"component-casing": componentCasingRule,
|
|
6537
6705
|
"application-structure": applicationStructureRule,
|
|
6538
6706
|
"data-testid-case": dataTestIdCaseRule,
|
|
6539
6707
|
"jsx-hygiene": jsxHygieneRule,
|