pasika 0.7.7 → 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.
@@ -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/import-through-index.ts
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 = path17.resolve(context.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 || path17.basename(target).startsWith("index.")) continue;
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 = `${path17.sep}src${path17.sep}`;
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 path17.resolve(context.cwd ?? process.cwd(), "src");
2221
+ return path18.resolve(context.cwd ?? process.cwd(), "src");
2160
2222
  }
2161
2223
 
2162
2224
  // eslint/rules/util-file-name.ts
2163
- import path18 from "path";
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 = path18.resolve(context.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 = path18.basename(filename, path18.extname(filename));
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}.${path18.extname(filename).slice(1)}.`
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 path19 from "path";
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 = path19.resolve(context.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 || !path19.basename(target).startsWith("index.")) continue;
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 = `${path19.sep}src${path19.sep}`;
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 path19.resolve(context.cwd ?? process.cwd(), "src");
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 path20 from "path";
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 = path20.resolve(context.filename);
2495
- const base = path20.basename(filename, path20.extname(filename));
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 path21 from "path";
2808
+ import path22 from "path";
2747
2809
  var FEATURES_SEGMENT = "features";
2748
2810
  function featureNameOf(resolvedPath, sourceRoot) {
2749
- const relative = path21.relative(sourceRoot, resolvedPath);
2811
+ const relative = path22.relative(sourceRoot, resolvedPath);
2750
2812
  if (relative.startsWith("..")) return void 0;
2751
- const segments = relative.split(path21.sep);
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 = path21.relative(sourceRoot, filename);
2829
+ const fileRelative = path22.relative(sourceRoot, filename);
2768
2830
  if (fileRelative.startsWith("..")) return {};
2769
- const fileSegments = fileRelative.split(path21.sep);
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 = path21.resolve(sourceRoot, source.value.slice(2));
2845
+ resolved = path22.resolve(sourceRoot, source.value.slice(2));
2784
2846
  } else if (source.value.startsWith(".")) {
2785
- resolved = path21.resolve(path21.dirname(filename), source.value);
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 path22 from "path";
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 = path22.relative(sourceRoot, filename);
2895
+ const relative = path23.relative(sourceRoot, filename);
2834
2896
  if (relative.startsWith("..")) return {};
2835
- const segments = relative.split(path22.sep);
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 path23 from "path";
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 = path23.relative(sourceRoot, filename);
2990
+ const relative = path24.relative(sourceRoot, filename);
2929
2991
  if (relative.startsWith("..")) return {};
2930
- const segments = relative.split(path23.sep);
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 path24 from "path";
3034
+ import path25 from "path";
2973
3035
  function isInLocalesDir(filename) {
2974
- const segments = path24.resolve(filename).split(path24.sep);
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,9 +3082,9 @@ var localeDottedPathRule = {
3020
3082
  };
3021
3083
 
3022
3084
  // eslint/rules/locales-location.ts
3023
- import path25 from "path";
3085
+ import path26 from "path";
3024
3086
  function isLocalesFile(filename) {
3025
- const segments = path25.resolve(filename).split(path25.sep);
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
  }
@@ -3041,7 +3103,7 @@ var localesLocationRule = {
3041
3103
  create(context) {
3042
3104
  if (isLocalesFile(context.filename)) return {};
3043
3105
  const filename = context.filename;
3044
- const segments = path25.resolve(filename).split(path25.sep);
3106
+ const segments = path26.resolve(filename).split(path26.sep);
3045
3107
  const srcIdx = segments.lastIndexOf("src");
3046
3108
  if (srcIdx === -1) return {};
3047
3109
  const folder = segments[srcIdx + 1];
@@ -3063,7 +3125,7 @@ var localesLocationRule = {
3063
3125
  };
3064
3126
 
3065
3127
  // eslint/rules/hook-extraction.ts
3066
- import path26 from "path";
3128
+ import path27 from "path";
3067
3129
  var hookExtractionRule = {
3068
3130
  meta: {
3069
3131
  schema: [],
@@ -3074,7 +3136,7 @@ var hookExtractionRule = {
3074
3136
  },
3075
3137
  create(context) {
3076
3138
  const sourceRoot = sourceRootOf(context);
3077
- const file = path26.resolve(context.filename);
3139
+ const file = path27.resolve(context.filename);
3078
3140
  const segments = segmentsOf(file, sourceRoot);
3079
3141
  if (segments.length === 0) return {};
3080
3142
  const index = getProjectIndex(sourceRoot);
@@ -3100,7 +3162,7 @@ var hookExtractionRule = {
3100
3162
  };
3101
3163
 
3102
3164
  // eslint/rules/value-extraction.ts
3103
- import path27 from "path";
3165
+ import path28 from "path";
3104
3166
  var valueExtractionRule = {
3105
3167
  meta: {
3106
3168
  schema: [],
@@ -3111,7 +3173,7 @@ var valueExtractionRule = {
3111
3173
  },
3112
3174
  create(context) {
3113
3175
  const sourceRoot = sourceRootOf(context);
3114
- const file = path27.resolve(context.filename);
3176
+ const file = path28.resolve(context.filename);
3115
3177
  const segments = segmentsOf(file, sourceRoot);
3116
3178
  if (segments.length === 0 || segments[0] !== "app") return {};
3117
3179
  const index = getProjectIndex(sourceRoot);
@@ -3132,7 +3194,7 @@ var valueExtractionRule = {
3132
3194
  };
3133
3195
 
3134
3196
  // eslint/rules/config-extraction.ts
3135
- import path28 from "path";
3197
+ import path29 from "path";
3136
3198
  var configExtractionRule = {
3137
3199
  meta: {
3138
3200
  schema: [],
@@ -3143,7 +3205,7 @@ var configExtractionRule = {
3143
3205
  },
3144
3206
  create(context) {
3145
3207
  const sourceRoot = sourceRootOf(context);
3146
- const file = path28.resolve(context.filename);
3208
+ const file = path29.resolve(context.filename);
3147
3209
  const segments = segmentsOf(file, sourceRoot);
3148
3210
  if (segments.length < 3 || segments[0] !== "config") return {};
3149
3211
  if (SUPPORT_FOLDERS2.has(segments[2] ?? "")) return {};
@@ -3181,7 +3243,7 @@ var configExtractionRule = {
3181
3243
  };
3182
3244
 
3183
3245
  // eslint/rules/component-nesting.ts
3184
- import path29 from "path";
3246
+ import path30 from "path";
3185
3247
  var componentNestingRule = {
3186
3248
  meta: {
3187
3249
  schema: [],
@@ -3192,7 +3254,7 @@ var componentNestingRule = {
3192
3254
  },
3193
3255
  create(context) {
3194
3256
  const sourceRoot = sourceRootOf(context);
3195
- const file = path29.resolve(context.filename);
3257
+ const file = path30.resolve(context.filename);
3196
3258
  const segments = segmentsOf(file, sourceRoot);
3197
3259
  if (segments.length !== 4 || segments[0] !== "features") return {};
3198
3260
  const index = getProjectIndex(sourceRoot);
@@ -3225,7 +3287,7 @@ var componentNestingRule = {
3225
3287
  };
3226
3288
 
3227
3289
  // eslint/rules/stay-flat.ts
3228
- import path30 from "path";
3290
+ import path31 from "path";
3229
3291
  var stayFlatRule = {
3230
3292
  meta: {
3231
3293
  schema: [],
@@ -3236,7 +3298,7 @@ var stayFlatRule = {
3236
3298
  },
3237
3299
  create(context) {
3238
3300
  const sourceRoot = sourceRootOf(context);
3239
- const file = path30.resolve(context.filename);
3301
+ const file = path31.resolve(context.filename);
3240
3302
  const segments = segmentsOf(file, sourceRoot);
3241
3303
  if (segments.length !== 3 || segments[0] !== "features") return {};
3242
3304
  const index = getProjectIndex(sourceRoot);
@@ -3276,7 +3338,7 @@ var stayFlatRule = {
3276
3338
  };
3277
3339
 
3278
3340
  // eslint/rules/type-extraction.ts
3279
- import path31 from "path";
3341
+ import path32 from "path";
3280
3342
  var typeExtractionRule = {
3281
3343
  meta: {
3282
3344
  schema: [],
@@ -3287,7 +3349,7 @@ var typeExtractionRule = {
3287
3349
  },
3288
3350
  create(context) {
3289
3351
  const sourceRoot = sourceRootOf(context);
3290
- const file = path31.resolve(context.filename);
3352
+ const file = path32.resolve(context.filename);
3291
3353
  const segments = segmentsOf(file, sourceRoot);
3292
3354
  if (segments.length === 0) return {};
3293
3355
  const index = getProjectIndex(sourceRoot);
@@ -3333,7 +3395,7 @@ var typeExtractionRule = {
3333
3395
  };
3334
3396
 
3335
3397
  // eslint/rules/locale-placement.ts
3336
- import path32 from "path";
3398
+ import path33 from "path";
3337
3399
  import { readFileSync as readFileSync4 } from "fs";
3338
3400
  import ts5 from "typescript";
3339
3401
  var LOCALE_ACCESS = /\blocales\.(?<key>[A-Za-z_$][\w$]*)/g;
@@ -3381,7 +3443,7 @@ var localePlacementRule = {
3381
3443
  },
3382
3444
  create(context) {
3383
3445
  const sourceRoot = sourceRootOf(context);
3384
- const file = path32.resolve(context.filename);
3446
+ const file = path33.resolve(context.filename);
3385
3447
  const segments = segmentsOf(file, sourceRoot);
3386
3448
  if (segments.length === 0) return {};
3387
3449
  const index = getProjectIndex(sourceRoot);
@@ -3459,7 +3521,7 @@ var localePlacementRule = {
3459
3521
  };
3460
3522
 
3461
3523
  // eslint/rules/sole-state-owner.ts
3462
- import path33 from "path";
3524
+ import path34 from "path";
3463
3525
  import ts6 from "typescript";
3464
3526
  function findStateHooks(node) {
3465
3527
  const hooks = [];
@@ -3539,7 +3601,7 @@ var soleStateOwnerRule = {
3539
3601
  }
3540
3602
  },
3541
3603
  create(context) {
3542
- const filename = path33.resolve(context.filename);
3604
+ const filename = path34.resolve(context.filename);
3543
3605
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) return {};
3544
3606
  const text = context.sourceCode.text;
3545
3607
  const components = parseComponentInfo(text, filename);
@@ -3618,7 +3680,7 @@ function usesOutsideJsx(declaration, hook, children) {
3618
3680
  }
3619
3681
 
3620
3682
  // eslint/rules/locale-key-shape.ts
3621
- import path34 from "path";
3683
+ import path35 from "path";
3622
3684
  var MAX_KEY_LENGTH = 30;
3623
3685
  var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
3624
3686
  "Button",
@@ -3668,7 +3730,7 @@ var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
3668
3730
  var CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
3669
3731
  var ENGLISH = /^[A-Za-z0-9_]*$/;
3670
3732
  function isLocalesFile2(filename) {
3671
- const segments = path34.resolve(filename).split(path34.sep);
3733
+ const segments = path35.resolve(filename).split(path35.sep);
3672
3734
  const srcIdx = segments.lastIndexOf("src");
3673
3735
  return srcIdx !== -1 && segments[srcIdx + 1] === "locales";
3674
3736
  }
@@ -3739,7 +3801,7 @@ var localeKeyShapeRule = {
3739
3801
  };
3740
3802
 
3741
3803
  // eslint/rules/shared-style-dedup.ts
3742
- import path35 from "path";
3804
+ import path36 from "path";
3743
3805
  import { readFileSync as readFileSync5, statSync as statSync3 } from "fs";
3744
3806
  var CLASS_NAME = /className="(?<classes>[^"]+)"/g;
3745
3807
  var comboCache;
@@ -3777,7 +3839,7 @@ var sharedStyleDedupRule = {
3777
3839
  },
3778
3840
  create(context) {
3779
3841
  const sourceRoot = sourceRootOf(context);
3780
- const file = path35.resolve(context.filename);
3842
+ const file = path36.resolve(context.filename);
3781
3843
  const segments = segmentsOf(file, sourceRoot);
3782
3844
  if (segments.length === 0) return {};
3783
3845
  const index = getProjectIndex(sourceRoot);
@@ -3980,8 +4042,103 @@ var zodSchemaValidationRule = {
3980
4042
  }
3981
4043
  };
3982
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
+
3983
4140
  // eslint/rules/source-under-src.ts
3984
- import path36 from "path";
4141
+ import path39 from "path";
3985
4142
  var NON_SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set([
3986
4143
  ".agents",
3987
4144
  ".cache",
@@ -4022,14 +4179,14 @@ var sourceUnderSrcRule = {
4022
4179
  }
4023
4180
  },
4024
4181
  create(context) {
4025
- const filename = path36.resolve(context.filename);
4182
+ const filename = path39.resolve(context.filename);
4026
4183
  if (!MODULE_EXTENSION.test(filename)) return {};
4027
- const relative = path36.relative(context.cwd, filename).replace(/\\/g, "/");
4184
+ const relative = path39.relative(context.cwd, filename).replace(/\\/g, "/");
4028
4185
  if (relative === "src" || relative.startsWith("src/")) return {};
4029
4186
  const topLevel = relative.split("/")[0] ?? "";
4030
4187
  if (NON_SOURCE_ROOT_DIRS.has(topLevel)) return {};
4031
4188
  if (!relative.includes("/")) {
4032
- const basename = path36.basename(filename);
4189
+ const basename = path39.basename(filename);
4033
4190
  if (CONFIG_FILE.test(basename) || DECLARATION_FILE.test(basename) || basename.startsWith(".")) return {};
4034
4191
  }
4035
4192
  return {
@@ -4046,7 +4203,7 @@ var sourceUnderSrcRule = {
4046
4203
 
4047
4204
  // eslint/rules/zirka-baseline.ts
4048
4205
  import fs5 from "fs";
4049
- import path37 from "path";
4206
+ import path40 from "path";
4050
4207
  var ESLINT_CONFIG = /^eslint\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
4051
4208
  var PRETTIER_CONFIGS = [
4052
4209
  "prettier.config.mjs",
@@ -4065,10 +4222,10 @@ var zirkaBaselineRule = {
4065
4222
  }
4066
4223
  },
4067
4224
  create(context) {
4068
- const filename = path37.resolve(context.filename);
4069
- const basename = path37.basename(filename);
4225
+ const filename = path40.resolve(context.filename);
4226
+ const basename = path40.basename(filename);
4070
4227
  if (!ESLINT_CONFIG.test(basename)) return {};
4071
- const projectRoot = path37.dirname(filename);
4228
+ const projectRoot = path40.dirname(filename);
4072
4229
  const report3 = (message) => {
4073
4230
  context.report({
4074
4231
  node: context.sourceCode.ast,
@@ -4083,7 +4240,7 @@ var zirkaBaselineRule = {
4083
4240
  'ESLint config must take its configuration from zirka (import { styleguide } from "zirka") instead of restating rules locally.'
4084
4241
  );
4085
4242
  }
4086
- const tsconfigPath = path37.join(projectRoot, "tsconfig.json");
4243
+ const tsconfigPath = path40.join(projectRoot, "tsconfig.json");
4087
4244
  if (!fs5.existsSync(tsconfigPath)) {
4088
4245
  report3('No tsconfig.json found. Create one extending the zirka TypeScript base config ("zirka/typescript").');
4089
4246
  } else {
@@ -4101,13 +4258,13 @@ var zirkaBaselineRule = {
4101
4258
  report3('tsconfig.json must extend the zirka TypeScript base config ("zirka/typescript").');
4102
4259
  }
4103
4260
  }
4104
- const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs5.existsSync(path37.join(projectRoot, name)));
4261
+ const prettierConfigFile = PRETTIER_CONFIGS.find((name) => fs5.existsSync(path40.join(projectRoot, name)));
4105
4262
  if (!prettierConfigFile) {
4106
4263
  report3(
4107
4264
  "No prettier config found. Create one that takes its configuration from zirka (styleguide({ prettier: true }).prettierConfig)."
4108
4265
  );
4109
4266
  } else {
4110
- const content = fs5.readFileSync(path37.join(projectRoot, prettierConfigFile), "utf8");
4267
+ const content = fs5.readFileSync(path40.join(projectRoot, prettierConfigFile), "utf8");
4111
4268
  if (!content.includes("zirka")) {
4112
4269
  report3(
4113
4270
  "The prettier config must take its configuration from zirka (styleguide({ prettier: true }).prettierConfig) instead of restating it locally."
@@ -4177,7 +4334,7 @@ var docKindSuffixRule = {
4177
4334
  };
4178
4335
 
4179
4336
  // eslint/rules/documentation/title-matches-file-name.ts
4180
- import path38 from "path";
4337
+ import path41 from "path";
4181
4338
  function toExpectedFileName(title) {
4182
4339
  return `${title.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}.md`;
4183
4340
  }
@@ -4197,7 +4354,7 @@ var titleMatchesFileNameRule = {
4197
4354
  if (!filename.endsWith(".md")) return;
4198
4355
  const title = getTextContent(node).trim();
4199
4356
  const expectedFileName = toExpectedFileName(title);
4200
- const actualFileName = path38.basename(filename);
4357
+ const actualFileName = path41.basename(filename);
4201
4358
  if (!title) {
4202
4359
  context.report({
4203
4360
  node,
@@ -4640,20 +4797,20 @@ var referenceBlockHeadingsRule = {
4640
4797
 
4641
4798
  // eslint/rules/documentation/support-document-placement.ts
4642
4799
  import { existsSync as existsSync2 } from "fs";
4643
- import path39 from "path";
4800
+ import path42 from "path";
4644
4801
  function checkPlacement(filename, kind) {
4645
- const parentFolder = path39.basename(path39.dirname(filename));
4802
+ const parentFolder = path42.basename(path42.dirname(filename));
4646
4803
  const expectedParent = `${kind}s`;
4647
4804
  if (parentFolder !== expectedParent) {
4648
4805
  return `${kind} lives in "${parentFolder}/" instead of "${expectedParent}/"`;
4649
4806
  }
4650
- const guideFolderPath = path39.dirname(path39.dirname(filename));
4651
- const guideFolder = path39.basename(guideFolderPath);
4807
+ const guideFolderPath = path42.dirname(path42.dirname(filename));
4808
+ const guideFolder = path42.basename(guideFolderPath);
4652
4809
  if (!guideFolder.endsWith("-guide")) {
4653
4810
  return `${kind} owner folder "${guideFolder}/" does not use the "*-guide/" suffix`;
4654
4811
  }
4655
4812
  const entryPoint = `${guideFolder}.md`;
4656
- if (!existsSync2(path39.join(guideFolderPath, entryPoint))) {
4813
+ if (!existsSync2(path42.join(guideFolderPath, entryPoint))) {
4657
4814
  return `${kind} owner folder "${guideFolder}/" has no "${entryPoint}" entry point`;
4658
4815
  }
4659
4816
  return void 0;
@@ -4712,11 +4869,11 @@ var noTemplatePromptRule = {
4712
4869
  };
4713
4870
 
4714
4871
  // eslint/rules/documentation/guide-folder-entry-point.ts
4715
- import path41 from "path";
4872
+ import path44 from "path";
4716
4873
 
4717
4874
  // eslint/rules/documentation/project-index.ts
4718
4875
  import { readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
4719
- import path40 from "path";
4876
+ import path43 from "path";
4720
4877
  var KIND_BY_SUFFIX = [
4721
4878
  ["-rule.md", "rule"],
4722
4879
  ["-guide.md", "guide"],
@@ -4725,7 +4882,7 @@ var KIND_BY_SUFFIX = [
4725
4882
  ];
4726
4883
  function listMarkdownFiles(dir) {
4727
4884
  return readdirSync3(dir).flatMap((entry) => {
4728
- const entryPath = path40.join(dir, entry);
4885
+ const entryPath = path43.join(dir, entry);
4729
4886
  if (statSync4(entryPath).isDirectory()) {
4730
4887
  return entry.startsWith("_") ? [] : listMarkdownFiles(entryPath);
4731
4888
  }
@@ -4743,11 +4900,11 @@ function getProjectDocs(docsRoot) {
4743
4900
  if (cached) return cached;
4744
4901
  const files = listMarkdownFiles(docsRoot);
4745
4902
  const docs = files.sort((a, b) => a.localeCompare(b)).map((filePath) => {
4746
- const fileName = path40.basename(filePath);
4903
+ const fileName = path43.basename(filePath);
4747
4904
  const kind = KIND_BY_SUFFIX.find(([suffix]) => fileName.endsWith(suffix))?.[1];
4748
4905
  return {
4749
4906
  filePath,
4750
- doc: path40.relative(docsRoot, filePath).split(path40.sep).join("/"),
4907
+ doc: path43.relative(docsRoot, filePath).split(path43.sep).join("/"),
4751
4908
  fileName,
4752
4909
  kind,
4753
4910
  title: extractTitle(filePath)
@@ -4757,12 +4914,12 @@ function getProjectDocs(docsRoot) {
4757
4914
  return docs;
4758
4915
  }
4759
4916
  function findDocsRoot(filePath) {
4760
- let dir = path40.dirname(filePath);
4917
+ let dir = path43.dirname(filePath);
4761
4918
  for (; ; ) {
4762
- if (path40.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
4919
+ if (path43.basename(dir) === "docs" && statSync4(dir).isDirectory()) {
4763
4920
  return dir;
4764
4921
  }
4765
- const parent = path40.dirname(dir);
4922
+ const parent = path43.dirname(dir);
4766
4923
  if (parent === dir) return void 0;
4767
4924
  dir = parent;
4768
4925
  }
@@ -4786,13 +4943,13 @@ var guideFolderEntryPointRule = {
4786
4943
  if (!docsRoot) return;
4787
4944
  const docs = getProjectDocs(docsRoot);
4788
4945
  const guideFolders = new Set(
4789
- docs.filter((doc) => ["rules", "references"].includes(path41.basename(path41.dirname(doc.filePath)))).map((doc) => path41.dirname(path41.dirname(doc.filePath))).filter((folder) => path41.resolve(folder) !== path41.resolve(docsRoot))
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))
4790
4947
  );
4791
- const currentDir = path41.dirname(filename);
4948
+ const currentDir = path44.dirname(filename);
4792
4949
  if (guideFolders.has(currentDir)) {
4793
- const expectedEntryPoint = `${path41.basename(currentDir)}.md`;
4950
+ const expectedEntryPoint = `${path44.basename(currentDir)}.md`;
4794
4951
  const hasEntryPoint = docs.some(
4795
- (doc) => doc.kind === "guide" && path41.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
4952
+ (doc) => doc.kind === "guide" && path44.dirname(doc.filePath) === currentDir && doc.fileName === expectedEntryPoint
4796
4953
  );
4797
4954
  if (!hasEntryPoint) {
4798
4955
  context.report({
@@ -5017,7 +5174,7 @@ var noNestedHowToRule = {
5017
5174
 
5018
5175
  // eslint/rules/documentation/glossary-term-linking.ts
5019
5176
  import { readFileSync as readFileSync7 } from "fs";
5020
- import path42 from "path";
5177
+ import path45 from "path";
5021
5178
  function extractGlossaryTerms(filePath) {
5022
5179
  const content = readFileSync7(filePath, "utf8");
5023
5180
  const terms = [];
@@ -5071,9 +5228,9 @@ var glossaryTermLinkingRule = {
5071
5228
  const docsRoot = findDocsRoot(filename);
5072
5229
  if (!docsRoot) return;
5073
5230
  const docs = getProjectDocs(docsRoot);
5074
- const guideDir = path42.dirname(filename);
5231
+ const guideDir = path45.dirname(filename);
5075
5232
  const guideReferences = docs.filter(
5076
- (doc) => doc.kind === "reference" && path42.dirname(doc.filePath) === guideDir
5233
+ (doc) => doc.kind === "reference" && path45.dirname(doc.filePath) === guideDir
5077
5234
  );
5078
5235
  if (guideReferences.length === 0) return;
5079
5236
  const glossaryTerms = [];
@@ -5098,7 +5255,7 @@ var glossaryTermLinkingRule = {
5098
5255
 
5099
5256
  // eslint/rules/documentation/guide-mentions-documents.ts
5100
5257
  import { existsSync as existsSync3 } from "fs";
5101
- import path43 from "path";
5258
+ import path46 from "path";
5102
5259
  function visitSteps3(node, check) {
5103
5260
  if (node.type === "list" && node.ordered) {
5104
5261
  for (const child of node.children) check(child);
@@ -5131,12 +5288,12 @@ var guideMentionsDocumentsRule = {
5131
5288
  if (!filename.endsWith("-guide.md")) return;
5132
5289
  const docsRoot = findDocsRoot(filename);
5133
5290
  if (!docsRoot) return;
5134
- const guideDir = path43.dirname(filename);
5135
- if (path43.basename(filename, ".md") !== path43.basename(guideDir)) return;
5291
+ const guideDir = path46.dirname(filename);
5292
+ if (path46.basename(filename, ".md") !== path46.basename(guideDir)) return;
5136
5293
  const docs = getProjectDocs(docsRoot);
5137
5294
  const owned = docs.filter((doc) => {
5138
- const parent = path43.dirname(doc.filePath);
5139
- return parent === path43.join(guideDir, "rules") || parent === path43.join(guideDir, "references");
5295
+ const parent = path46.dirname(doc.filePath);
5296
+ return parent === path46.join(guideDir, "rules") || parent === path46.join(guideDir, "references");
5140
5297
  });
5141
5298
  const allLinks = [];
5142
5299
  collectMarkdownLinks(node, allLinks);
@@ -5163,7 +5320,7 @@ var guideMentionsDocumentsRule = {
5163
5320
  for (const link of allLinks) {
5164
5321
  const target = linkTarget(link.url);
5165
5322
  if (!target.endsWith(".md")) continue;
5166
- const resolved = path43.normalize(path43.join(guideDir, target));
5323
+ const resolved = path46.normalize(path46.join(guideDir, target));
5167
5324
  if (!existsSync3(resolved)) {
5168
5325
  context.report({
5169
5326
  node: link,
@@ -5713,11 +5870,11 @@ var themeVariableNamespaceRule = {
5713
5870
 
5714
5871
  // eslint/rules/tailwind/css-entry-point.ts
5715
5872
  import { statSync as statSync6 } from "fs";
5716
- import path46 from "path";
5873
+ import path49 from "path";
5717
5874
 
5718
5875
  // eslint/rules/tailwind/source-files.ts
5719
5876
  import { readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
5720
- import path44 from "path";
5877
+ import path47 from "path";
5721
5878
  var CSS_EXTENSIONS = [".css"];
5722
5879
  var MODULE_EXTENSIONS3 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
5723
5880
  var SOURCE_EXTENSIONS = [...MODULE_EXTENSIONS3, ...CSS_EXTENSIONS];
@@ -5730,7 +5887,7 @@ function findFiles(dir, extensions) {
5730
5887
  }
5731
5888
  return entries.flatMap((entry) => {
5732
5889
  if (entry.startsWith(".") || entry === "node_modules") return [];
5733
- const entryPath = path44.join(dir, entry);
5890
+ const entryPath = path47.join(dir, entry);
5734
5891
  let stats;
5735
5892
  try {
5736
5893
  stats = statSync5(entryPath);
@@ -5738,7 +5895,7 @@ function findFiles(dir, extensions) {
5738
5895
  return [];
5739
5896
  }
5740
5897
  if (stats.isDirectory()) return findFiles(entryPath, extensions);
5741
- return extensions.includes(path44.extname(entry)) ? [entryPath] : [];
5898
+ return extensions.includes(path47.extname(entry)) ? [entryPath] : [];
5742
5899
  });
5743
5900
  }
5744
5901
  function cachedTextReader() {
@@ -5761,7 +5918,7 @@ function escapeRegExp(text) {
5761
5918
  }
5762
5919
 
5763
5920
  // eslint/rules/tailwind/stylesheet-graph.ts
5764
- import path45 from "path";
5921
+ import path48 from "path";
5765
5922
  function registersTailwind(text) {
5766
5923
  return /@import\s+(?:url\(\s*)?["']tailwindcss["']\s*\)?/i.test(text);
5767
5924
  }
@@ -5775,25 +5932,25 @@ function moduleImports(text, fileName) {
5775
5932
  return new RegExp(`(?:import|require)\\s*\\(?\\s*["'][^"']*${escaped}["']`, "i").test(text);
5776
5933
  }
5777
5934
  function resolveSpecifier2(fromFile, spec, sourceRoot) {
5778
- if (spec.startsWith("/")) return path45.resolve(spec);
5779
- if (spec.startsWith("./") || spec.startsWith("../")) return path45.resolve(path45.dirname(fromFile), spec);
5780
- if (spec.startsWith("@/")) return path45.resolve(sourceRoot, spec.slice(2));
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));
5781
5938
  return void 0;
5782
5939
  }
5783
5940
  function buildStylesheetGraph(options) {
5784
5941
  const { cssFiles, sourceRoot, textOf } = options;
5785
- const cssSet = new Set(cssFiles.map((file) => path45.normalize(file)));
5942
+ const cssSet = new Set(cssFiles.map((file) => path48.normalize(file)));
5786
5943
  const globals = cssFiles.filter((file) => registersTailwind(textOf(file)));
5787
5944
  const reachable = /* @__PURE__ */ new Set();
5788
5945
  const queue = [...globals];
5789
- for (const global of globals) reachable.add(path45.normalize(global));
5946
+ for (const global of globals) reachable.add(path48.normalize(global));
5790
5947
  while (queue.length > 0) {
5791
5948
  const from = queue.shift();
5792
5949
  if (!from) continue;
5793
5950
  for (const spec of importedSpecifiers(textOf(from))) {
5794
5951
  const target = resolveSpecifier2(from, spec, sourceRoot);
5795
5952
  if (!target) continue;
5796
- const normalized = path45.normalize(target);
5953
+ const normalized = path48.normalize(target);
5797
5954
  if (cssSet.has(normalized) && !reachable.has(normalized)) {
5798
5955
  reachable.add(normalized);
5799
5956
  queue.push(normalized);
@@ -5805,7 +5962,7 @@ function buildStylesheetGraph(options) {
5805
5962
  for (const spec of importedSpecifiers(textOf(global))) {
5806
5963
  const target = resolveSpecifier2(global, spec, sourceRoot);
5807
5964
  if (!target) continue;
5808
- const normalized = path45.normalize(target);
5965
+ const normalized = path48.normalize(target);
5809
5966
  if (cssSet.has(normalized)) directChildren.add(normalized);
5810
5967
  }
5811
5968
  }
@@ -5838,7 +5995,7 @@ var cssEntryPointRule = {
5838
5995
  return {
5839
5996
  "StyleSheet:exit"(node) {
5840
5997
  if (globals.length === 0) return;
5841
- const current = path46.normalize(path46.resolve(context.filename));
5998
+ const current = path49.normalize(path49.resolve(context.filename));
5842
5999
  if (globals.includes(current)) {
5843
6000
  if (globals.length > 1) {
5844
6001
  context.report({
@@ -5847,7 +6004,7 @@ var cssEntryPointRule = {
5847
6004
  });
5848
6005
  return;
5849
6006
  }
5850
- const basename = path46.basename(current);
6007
+ const basename = path49.basename(current);
5851
6008
  const importCount = moduleFiles.filter((modulePath) => moduleImports(textOf(modulePath), basename)).length;
5852
6009
  if (importCount !== 1) {
5853
6010
  context.report({
@@ -6176,7 +6333,7 @@ var nextjsStackRule = {
6176
6333
 
6177
6334
  // eslint/rules/package-json/vitest-coverage.ts
6178
6335
  import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
6179
- import path47 from "path";
6336
+ import path50 from "path";
6180
6337
  function memberName4(member) {
6181
6338
  return member.name.type === "String" ? member.name.value : member.name.name;
6182
6339
  }
@@ -6245,7 +6402,7 @@ var vitestCoverageRule = {
6245
6402
  message: 'package.json must declare a "test:unit:coverage" script that runs Vitest with coverage.'
6246
6403
  });
6247
6404
  }
6248
- const configName = VITEST_CONFIG_NAMES.find((name) => existsSync4(path47.join(context.cwd, name)));
6405
+ const configName = VITEST_CONFIG_NAMES.find((name) => existsSync4(path50.join(context.cwd, name)));
6249
6406
  if (!configName) {
6250
6407
  context.report({
6251
6408
  node,
@@ -6253,7 +6410,7 @@ var vitestCoverageRule = {
6253
6410
  });
6254
6411
  return;
6255
6412
  }
6256
- const content = readFileSync9(path47.join(context.cwd, configName), "utf8");
6413
+ const content = readFileSync9(path50.join(context.cwd, configName), "utf8");
6257
6414
  for (const metric of THRESHOLD_METRICS) {
6258
6415
  if (!new RegExp(`\\b${metric}\\s*:\\s*[1-9]\\d*`).test(content)) {
6259
6416
  context.report({ node, message: `${configName} must set a coverage threshold above zero for ${metric}.` });
@@ -6300,7 +6457,7 @@ var nextjsPackageJsonRules = {
6300
6457
 
6301
6458
  // eslint/rules/husky/husky-hook.ts
6302
6459
  import { existsSync as existsSync5, readFileSync as readFileSync10 } from "fs";
6303
- import path48 from "path";
6460
+ import path51 from "path";
6304
6461
  var VITEST_CONFIG_NAMES2 = [
6305
6462
  "vitest.config.ts",
6306
6463
  "vitest.config.mts",
@@ -6326,7 +6483,7 @@ var huskyHookRule = {
6326
6483
  const root = node.body;
6327
6484
  if (root.type !== "Object") return;
6328
6485
  if (!context.filename.endsWith("package.json")) return;
6329
- const hookPath = path48.join(context.cwd, ".husky", "pre-commit");
6486
+ const hookPath = path51.join(context.cwd, ".husky", "pre-commit");
6330
6487
  if (!existsSync5(hookPath)) {
6331
6488
  context.report({
6332
6489
  node,
@@ -6350,7 +6507,7 @@ var huskyHookRule = {
6350
6507
  }
6351
6508
  requireNamedScript("typecheck");
6352
6509
  requireNamedScript("test:unit:coverage");
6353
- const vitestConfigName = VITEST_CONFIG_NAMES2.find((name) => existsSync5(path48.join(context.cwd, name)));
6510
+ const vitestConfigName = VITEST_CONFIG_NAMES2.find((name) => existsSync5(path51.join(context.cwd, name)));
6354
6511
  if (vitestConfigName !== void 0) {
6355
6512
  const coverageIndex = content.indexOf("npm run test:unit:coverage");
6356
6513
  const localAddIndex = content.indexOf(`git add ${vitestConfigName}`);
@@ -6364,7 +6521,7 @@ var huskyHookRule = {
6364
6521
  if (!content.includes("libyear --limit-major-individual=1")) {
6365
6522
  context.report({ node, message: ".husky/pre-commit must run npx libyear --limit-major-individual=1." });
6366
6523
  }
6367
- const suppressionsPath = path48.join(context.cwd, "eslint-suppressions.json");
6524
+ const suppressionsPath = path51.join(context.cwd, "eslint-suppressions.json");
6368
6525
  if (existsSync5(suppressionsPath)) {
6369
6526
  requireNamedScript("lint:prune");
6370
6527
  const pruneIndex = content.indexOf("npm run lint:prune");
@@ -6437,7 +6594,7 @@ var vulykDependencyRule = {
6437
6594
 
6438
6595
  // eslint/rules/vulyk/vulyk-docs.ts
6439
6596
  import { existsSync as existsSync6, readFileSync as readFileSync11 } from "fs";
6440
- import path49 from "path";
6597
+ import path52 from "path";
6441
6598
  var PASIKA_REPO = "Bredansky/pasika";
6442
6599
  var BASE_REQUIRED_DOCS = [
6443
6600
  { name: "documentation-guide", path: "docs/documentation-guide" },
@@ -6470,8 +6627,8 @@ var vulykDocsRule = {
6470
6627
  if (!context.filename.endsWith("package.json")) return;
6471
6628
  const root = node.body;
6472
6629
  if (root.type !== "Object") return;
6473
- const projectRoot = path49.dirname(path49.resolve(context.filename));
6474
- const configPath = path49.join(projectRoot, "vulyk.config.ts");
6630
+ const projectRoot = path52.dirname(path52.resolve(context.filename));
6631
+ const configPath = path52.join(projectRoot, "vulyk.config.ts");
6475
6632
  if (!existsSync6(configPath)) {
6476
6633
  context.report({
6477
6634
  node,
@@ -6496,7 +6653,7 @@ var vulykDocsRule = {
6496
6653
  });
6497
6654
  }
6498
6655
  }
6499
- const agentsPath = path49.join(projectRoot, "AGENTS.md");
6656
+ const agentsPath = path52.join(projectRoot, "AGENTS.md");
6500
6657
  if (!existsSync6(agentsPath)) {
6501
6658
  context.report({
6502
6659
  node,
@@ -6528,12 +6685,15 @@ var pasikaNextjsAppRules = {
6528
6685
  "enforce-barrel-exports": enforceBarrelExportsRule,
6529
6686
  "config-extraction": configExtractionRule,
6530
6687
  "value-extraction": valueExtractionRule,
6688
+ "constant-casing": constantCasingRule,
6531
6689
  "type-extraction": typeExtractionRule,
6532
6690
  "zod-schema-validation": zodSchemaValidationRule,
6691
+ "schema-casing": schemaCasingRule,
6533
6692
  "source-under-src": sourceUnderSrcRule,
6534
6693
  "zirka-baseline": zirkaBaselineRule,
6535
6694
  // Next.js/React application rules.
6536
6695
  "component-placement": componentPlacementRule,
6696
+ "component-casing": componentCasingRule,
6537
6697
  "application-structure": applicationStructureRule,
6538
6698
  "data-testid-case": dataTestIdCaseRule,
6539
6699
  "jsx-hygiene": jsxHygieneRule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.7.7",
3
+ "version": "0.8.0",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",