@sarj/eslint-plugin 15.17.1 → 15.17.3

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/index.cjs CHANGED
@@ -5890,7 +5890,7 @@ var NO_RAW_FETCH_OUTSIDE_CLIENTS_DOCUMENTATION = {
5890
5890
  rationale: "Scattered fetch calls bypass shared transport policy and are harder to stub and observe consistently.",
5891
5891
  remediation: "Move the request into a client module and call that abstraction from application code.",
5892
5892
  category: "architecture",
5893
- limitations: ["Tests, client-layer paths, constructed handoffs, and pre-signed URL transfers are excluded."],
5893
+ limitations: ["Tests, client-layer paths, constructed handoffs, and pre-signed URL transfers are excluded. Configure the same literal Next.js basePath here and on prefer-server-actions so one rule owns each internal mutation."],
5894
5894
  examples: [
5895
5895
  { id: "client-call", title: "Use a client abstraction", outcome: "no-match", files: [{ path: "src/routes/handler.ts", source: "const response = await billingClient.getInvoice(id);" }], focusPath: "src/routes/handler.ts", expectedCount: 0, public: true },
5896
5896
  { id: "raw-fetch", title: "Do not call global fetch here", outcome: "match", files: [{ path: "src/routes/handler.ts", source: "const response = await fetch('/api/invoices');" }], focusPath: "src/routes/handler.ts", expectedCount: 1, public: true }
@@ -5935,7 +5935,10 @@ var ANALYTICS_SEGMENTS2 = /* @__PURE__ */ new Set([
5935
5935
  ]);
5936
5936
  var SERVER_ACTION_SKIP_FILE_RE = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
5937
5937
  var NON_REACT_FRAMEWORK_RE = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
5938
- var NEXT_MODULE_PATH_RE = /(?:^|[/\\])(?:app|pages)[/\\]/u;
5938
+ var BASE_PATH_RE = /^\/(?!$)(?!.*[?#])(?:[^/]+\/)*[^/]+$/u;
5939
+ function isValidBasePath(basePath) {
5940
+ return BASE_PATH_RE.test(basePath) && !basePath.split("/").some((segment) => segment === "." || segment === "..");
5941
+ }
5939
5942
  function isGlobalFetchCall(node, resolvesToGlobal) {
5940
5943
  const callee = node.callee;
5941
5944
  if (callee.type === "Identifier") {
@@ -6031,6 +6034,10 @@ var no_raw_fetch_outside_clients_default = createRule({
6031
6034
  type: "array",
6032
6035
  items: { type: "string" },
6033
6036
  description: "Regular-expression sources matched against the filename. Replaces the defaults."
6037
+ },
6038
+ basePath: {
6039
+ type: "string",
6040
+ pattern: "^/(?!$)(?!.*[?#])(?!(?:.*/)?\\.\\.?(?:/|$))(?:[^/]+/)*[^/]+$"
6034
6041
  }
6035
6042
  },
6036
6043
  additionalProperties: false
@@ -6052,12 +6059,18 @@ var no_raw_fetch_outside_clients_default = createRule({
6052
6059
  (statement) => statement.type === import_utils33.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && NON_REACT_FRAMEWORK_RE.test(statement.source.value)
6053
6060
  );
6054
6061
  const hasUseClientDirective = context.sourceCode.ast.body.some(
6055
- (statement) => statement.type === import_utils33.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils33.AST_NODE_TYPES.Literal && statement.expression.value === "use client"
6062
+ (statement) => statement.type === import_utils33.AST_NODE_TYPES.ExpressionStatement && statement.directive === "use client"
6056
6063
  );
6057
- const hasNextImport = context.sourceCode.ast.body.some(
6058
- (statement) => statement.type === import_utils33.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && (statement.source.value === "next" || statement.source.value.startsWith("next/"))
6064
+ const hasUseServerDirective = context.sourceCode.ast.body.some(
6065
+ (statement) => statement.type === import_utils33.AST_NODE_TYPES.ExpressionStatement && statement.directive === "use server"
6059
6066
  );
6060
- const hasNextEvidence = hasNextImport || hasUseClientDirective && NEXT_MODULE_PATH_RE.test(filename);
6067
+ const importsServerOnly = context.sourceCode.ast.body.some(
6068
+ (statement) => statement.type === import_utils33.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && (statement.source.value === "server-only" || statement.source.value === "next/server")
6069
+ );
6070
+ const internalApiPrefixes = ["/api"];
6071
+ if (options?.basePath !== void 0 && isValidBasePath(options.basePath)) {
6072
+ internalApiPrefixes.push(`${options.basePath}/api`);
6073
+ }
6061
6074
  function resolvesToGlobal(identifier) {
6062
6075
  const variable = import_utils33.ASTUtils.findVariable(
6063
6076
  context.sourceCode.getScope(identifier),
@@ -6090,15 +6103,15 @@ var no_raw_fetch_outside_clients_default = createRule({
6090
6103
  function isInternalApiUrl(node) {
6091
6104
  const resolved = resolveNode2(node ?? void 0);
6092
6105
  if (resolved?.type === import_utils33.AST_NODE_TYPES.Literal) {
6093
- return typeof resolved.value === "string" && /^\/(?!\/)(?:[^/]+\/)*api(?:\/|$)/.test(resolved.value);
6106
+ return typeof resolved.value === "string" && internalApiPrefixes.some(
6107
+ (prefix) => resolved.value === prefix || resolved.value.startsWith(`${prefix}/`)
6108
+ );
6094
6109
  }
6095
6110
  if (resolved?.type === import_utils33.AST_NODE_TYPES.TemplateLiteral) {
6096
6111
  const prefix = resolved.quasis[0]?.value.cooked;
6097
- return typeof prefix === "string" && /^\/(?!\/)(?:[^/]+\/)*api(?:\/|$)/.test(prefix);
6098
- }
6099
- if (resolved?.type === import_utils33.AST_NODE_TYPES.CallExpression && resolved.callee.type === import_utils33.AST_NODE_TYPES.Identifier && resolved.callee.name === "withBase") {
6100
- const first = resolved.arguments[0];
6101
- return first !== void 0 && first.type !== import_utils33.AST_NODE_TYPES.SpreadElement ? isInternalApiUrl(first) : false;
6112
+ return typeof prefix === "string" && internalApiPrefixes.some(
6113
+ (apiPrefix) => prefix === apiPrefix || prefix.startsWith(`${apiPrefix}/`)
6114
+ );
6102
6115
  }
6103
6116
  return resolved?.type === import_utils33.AST_NODE_TYPES.BinaryExpression && resolved.operator === "+" && isInternalApiUrl(resolved.left);
6104
6117
  }
@@ -6118,7 +6131,7 @@ var no_raw_fetch_outside_clients_default = createRule({
6118
6131
  return resolved?.type === import_utils33.AST_NODE_TYPES.LogicalExpression && resolved.operator === "||" && (isMutationMethod2(resolved.left) || isMutationMethod2(resolved.right));
6119
6132
  }
6120
6133
  function serverActionOwns(node) {
6121
- if (node.callee.type !== import_utils33.AST_NODE_TYPES.Identifier || !hasNextEvidence || SERVER_ACTION_SKIP_FILE_RE.test(filename) || nonReactFramework) {
6134
+ if (node.callee.type !== import_utils33.AST_NODE_TYPES.Identifier || !hasUseClientDirective || hasUseServerDirective || importsServerOnly || SERVER_ACTION_SKIP_FILE_RE.test(filename) || nonReactFramework) {
6122
6135
  return false;
6123
6136
  }
6124
6137
  const url = node.arguments[0];
@@ -9775,25 +9788,25 @@ var no_unsafe_mock_casting_default = createRule({
9775
9788
  }
9776
9789
  const directBindings = /* @__PURE__ */ new Set();
9777
9790
  const namespaceBindings = /* @__PURE__ */ new Set();
9778
- function resolve(identifier) {
9791
+ function resolve2(identifier) {
9779
9792
  return import_utils55.ASTUtils.findVariable(
9780
9793
  context.sourceCode.getScope(identifier),
9781
9794
  identifier.name
9782
9795
  );
9783
9796
  }
9784
9797
  function record(identifier, destination) {
9785
- const binding = resolve(identifier);
9798
+ const binding = resolve2(identifier);
9786
9799
  if (binding !== null) destination.add(binding);
9787
9800
  }
9788
9801
  function isMockTypeReference(node) {
9789
9802
  if (node.type !== import_utils55.AST_NODE_TYPES.TSTypeReference) return false;
9790
9803
  const typeName = node.typeName;
9791
9804
  if (typeName.type === import_utils55.AST_NODE_TYPES.Identifier) {
9792
- const binding = resolve(typeName);
9805
+ const binding = resolve2(typeName);
9793
9806
  return binding !== null && directBindings.has(binding);
9794
9807
  }
9795
9808
  if (typeName.type === import_utils55.AST_NODE_TYPES.TSQualifiedName && typeName.left.type === import_utils55.AST_NODE_TYPES.Identifier && MOCK_TYPE_NAMES.has(typeName.right.name)) {
9796
- const binding = resolve(typeName.left);
9809
+ const binding = resolve2(typeName.left);
9797
9810
  return binding !== null && namespaceBindings.has(binding);
9798
9811
  }
9799
9812
  return false;
@@ -11197,6 +11210,10 @@ function unwrapTransparentExport(node) {
11197
11210
 
11198
11211
  // src/rules/prefer-shadcn-primitives.ts
11199
11212
  var import_utils66 = require("@typescript-eslint/utils");
11213
+ var import_typescript_estree = require("@typescript-eslint/typescript-estree");
11214
+ var import_node_fs = require("fs");
11215
+ var import_node_path = require("path");
11216
+ var import_jsonc_parser = require("jsonc-parser");
11200
11217
  var PREFER_SHADCN_PRIMITIVES_DOCUMENTATION = {
11201
11218
  summary: "Require visible raw JSX controls to use the corresponding shared shadcn primitive.",
11202
11219
  rationale: "Shared primitives centralize interaction, accessibility, and visual behavior across the product.",
@@ -11204,22 +11221,35 @@ var PREFER_SHADCN_PRIMITIVES_DOCUMENTATION = {
11204
11221
  category: "style",
11205
11222
  limitations: [
11206
11223
  "Hidden and file inputs, unassociated labels, and non-control semantic elements are excluded.",
11207
- "Tests and the shared components/ui primitive implementation tree are excluded."
11224
+ "Tests and the shared components/ui primitive implementation tree are excluded.",
11225
+ "Package-local project detection is opt-in and fails closed unless components.json, one unambiguous tsconfig/jsconfig alias, the exact primitive module, and its expected export all exist."
11208
11226
  ],
11209
11227
  examples: [
11210
11228
  { id: "shared-button", title: "Use a shared button", outcome: "no-match", files: [{ path: "src/form.tsx", source: "import { Button } from '@/components/ui/button'; const action = <Button>Save</Button>;" }], focusPath: "src/form.tsx", expectedCount: 0, public: true },
11211
11229
  { id: "raw-button", title: "Do not use a raw button", outcome: "match", files: [{ path: "src/form.tsx", source: "import { Card } from '@/components/ui/card'; const action = <button>Save</button>;" }], focusPath: "src/form.tsx", expectedCount: 1, public: true }
11212
11230
  ]
11213
11231
  };
11214
- var SHADCN_PRIMITIVES = {
11215
- button: "Button",
11216
- dialog: "Dialog or AlertDialog family",
11217
- input: "Input",
11218
- label: "Label",
11219
- progress: "Progress",
11220
- select: "Select family",
11221
- table: "Table family",
11222
- textarea: "Textarea"
11232
+ var RAW_PRIMITIVES = {
11233
+ button: { capability: "Button", replacement: "Button" },
11234
+ dialog: { capability: "Dialog", replacement: "Dialog or AlertDialog family" },
11235
+ input: { capability: "Input", replacement: "Input" },
11236
+ label: { capability: "Label", replacement: "Label" },
11237
+ progress: { capability: "Progress", replacement: "Progress" },
11238
+ select: { capability: "Select", replacement: "Select family" },
11239
+ table: { capability: "Table", replacement: "Table family" },
11240
+ textarea: { capability: "Textarea", replacement: "Textarea" }
11241
+ };
11242
+ var CAPABILITIES = {
11243
+ Button: "button",
11244
+ Checkbox: "checkbox",
11245
+ Dialog: "dialog",
11246
+ Input: "input",
11247
+ Label: "label",
11248
+ Progress: "progress",
11249
+ RadioGroup: "radio-group",
11250
+ Select: "select",
11251
+ Table: "table",
11252
+ Textarea: "textarea"
11223
11253
  };
11224
11254
  var LABELABLE_ELEMENTS = /* @__PURE__ */ new Set([
11225
11255
  "button",
@@ -11231,7 +11261,7 @@ var LABELABLE_ELEMENTS = /* @__PURE__ */ new Set([
11231
11261
  "textarea"
11232
11262
  ]);
11233
11263
  var SHARED_PRIMITIVE_IMPLEMENTATION_RE = /(?:^|\/)components\/ui(?:\/|$)/i;
11234
- var SHARED_PRIMITIVE_IMPORT_RE = /(?:^|\/)components\/ui\/[^/]+$/i;
11264
+ var SHARED_PRIMITIVE_IMPORT_RE = /(?:^|\/)components\/ui\/([^/]+)$/i;
11235
11265
  var AMBIGUOUS_INPUT_TYPES = /* @__PURE__ */ new Set([
11236
11266
  "button",
11237
11267
  "color",
@@ -11240,10 +11270,175 @@ var AMBIGUOUS_INPUT_TYPES = /* @__PURE__ */ new Set([
11240
11270
  "reset",
11241
11271
  "submit"
11242
11272
  ]);
11273
+ var MAX_PROJECT_FILE_BYTES = 1048576;
11274
+ var PROJECT_PRIMITIVES_CACHE = /* @__PURE__ */ new Map();
11275
+ function detectProjectPrimitives(filename, requested) {
11276
+ const packageRoot = findPackageRoot((0, import_node_path.dirname)(filename));
11277
+ if (packageRoot === null) return { available: /* @__PURE__ */ new Set(), uiRoot: null };
11278
+ const manifest = readJsonc((0, import_node_path.join)(packageRoot, "components.json"));
11279
+ const alias = stringProperty(manifest, "aliases", "ui");
11280
+ const unresolvedUiRoot = alias === null ? null : resolveAlias(packageRoot, alias);
11281
+ if (unresolvedUiRoot === null) return { available: /* @__PURE__ */ new Set(), uiRoot: null };
11282
+ const uiRoot = safeContainedDirectory(packageRoot, unresolvedUiRoot);
11283
+ if (uiRoot === null) {
11284
+ return { available: /* @__PURE__ */ new Set(), uiRoot: null };
11285
+ }
11286
+ const moduleCandidates = /* @__PURE__ */ new Map();
11287
+ for (const [capability, moduleName] of Object.entries(CAPABILITIES)) {
11288
+ if (!requested.has(capability)) continue;
11289
+ moduleCandidates.set(capability, ["tsx", "ts", "jsx", "js"].flatMap((extension) => [
11290
+ (0, import_node_path.join)(uiRoot, `${moduleName}.${extension}`),
11291
+ (0, import_node_path.join)(uiRoot, moduleName, `index.${extension}`)
11292
+ ]));
11293
+ }
11294
+ const fingerprint = [
11295
+ (0, import_node_path.join)(packageRoot, "components.json"),
11296
+ (0, import_node_path.join)(packageRoot, "tsconfig.json"),
11297
+ (0, import_node_path.join)(packageRoot, "jsconfig.json"),
11298
+ ...[...moduleCandidates.values()].flat()
11299
+ ].map(fileFingerprint).join("|");
11300
+ const cacheKey = `${packageRoot}:${[...requested].sort().join(",")}`;
11301
+ const cached = PROJECT_PRIMITIVES_CACHE.get(cacheKey);
11302
+ if (cached?.fingerprint === fingerprint) return cached.value;
11303
+ const available = /* @__PURE__ */ new Set();
11304
+ for (const [capability, candidates] of moduleCandidates) {
11305
+ if (candidates.some(
11306
+ (candidate2) => exportsPrimitive(candidate2, capability)
11307
+ )) {
11308
+ available.add(capability);
11309
+ }
11310
+ }
11311
+ const value = { available, uiRoot };
11312
+ PROJECT_PRIMITIVES_CACHE.set(cacheKey, { fingerprint, value });
11313
+ return value;
11314
+ }
11315
+ function fileFingerprint(path) {
11316
+ try {
11317
+ const stat = (0, import_node_fs.lstatSync)(path);
11318
+ return stat.isFile() ? `${path}:${stat.size}:${stat.mtimeMs}` : `${path}:excluded`;
11319
+ } catch {
11320
+ return `${path}:missing`;
11321
+ }
11322
+ }
11323
+ function isWithin2(root, candidate2) {
11324
+ const path = (0, import_node_path.relative)(root, candidate2);
11325
+ return path === "" || !path.startsWith(`..${import_node_path.sep}`) && path !== ".." && !(0, import_node_path.isAbsolute)(path);
11326
+ }
11327
+ function safeContainedDirectory(root, candidate2) {
11328
+ try {
11329
+ if (!(0, import_node_fs.lstatSync)(candidate2).isDirectory()) return null;
11330
+ const realRoot = (0, import_node_fs.realpathSync)(root);
11331
+ const realCandidate = (0, import_node_fs.realpathSync)(candidate2);
11332
+ return isWithin2(realRoot, realCandidate) ? realCandidate : null;
11333
+ } catch {
11334
+ return null;
11335
+ }
11336
+ }
11337
+ function findPackageRoot(startDir) {
11338
+ let dir = startDir;
11339
+ const filesystemRoot = (0, import_node_path.parse)(dir).root;
11340
+ for (; ; ) {
11341
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "package.json"))) return dir;
11342
+ const parent = (0, import_node_path.dirname)(dir);
11343
+ if (dir === filesystemRoot || parent === dir) return null;
11344
+ dir = parent;
11345
+ }
11346
+ }
11347
+ function readJsonc(path) {
11348
+ try {
11349
+ const source = readSmallRegularFile(path);
11350
+ if (source === null) return null;
11351
+ const errors = [];
11352
+ const value = (0, import_jsonc_parser.parse)(source, errors, {
11353
+ allowTrailingComma: true,
11354
+ disallowComments: false
11355
+ });
11356
+ return errors.length === 0 ? value : null;
11357
+ } catch {
11358
+ return null;
11359
+ }
11360
+ }
11361
+ function readSmallRegularFile(path) {
11362
+ try {
11363
+ const stat = (0, import_node_fs.lstatSync)(path);
11364
+ if (!stat.isFile() || stat.size > MAX_PROJECT_FILE_BYTES) return null;
11365
+ return (0, import_node_fs.readFileSync)(path, "utf8");
11366
+ } catch {
11367
+ return null;
11368
+ }
11369
+ }
11370
+ function stringProperty(value, ...keys) {
11371
+ let current = value;
11372
+ for (const key of keys) {
11373
+ if (typeof current !== "object" || current === null || !(key in current)) return null;
11374
+ current = current[key];
11375
+ }
11376
+ return typeof current === "string" && current.trim() !== "" ? current : null;
11377
+ }
11378
+ function resolveAlias(packageRoot, alias) {
11379
+ if ((0, import_node_path.isAbsolute)(alias)) return null;
11380
+ if (alias.startsWith(".")) return (0, import_node_path.resolve)(packageRoot, alias);
11381
+ const configPath = ["tsconfig.json", "jsconfig.json"].map((name) => (0, import_node_path.join)(packageRoot, name)).find(import_node_fs.existsSync);
11382
+ if (configPath === void 0) return null;
11383
+ const config = readJsonc(configPath);
11384
+ if (typeof config !== "object" || config === null) return null;
11385
+ const compilerOptions = config["compilerOptions"];
11386
+ if (typeof compilerOptions !== "object" || compilerOptions === null) return null;
11387
+ const options = compilerOptions;
11388
+ const baseUrl = typeof options["baseUrl"] === "string" ? options["baseUrl"] : ".";
11389
+ const paths = options["paths"];
11390
+ if (typeof paths !== "object" || paths === null) return null;
11391
+ const matches = [];
11392
+ for (const [pattern, rawTargets] of Object.entries(paths)) {
11393
+ if (!Array.isArray(rawTargets) || rawTargets.length !== 1) {
11394
+ continue;
11395
+ }
11396
+ const [target] = rawTargets;
11397
+ if (typeof target !== "string") continue;
11398
+ const star = pattern.indexOf("*");
11399
+ if (star === -1) {
11400
+ if (pattern === alias) matches.push(target);
11401
+ continue;
11402
+ }
11403
+ const prefix = pattern.slice(0, star);
11404
+ const suffix = pattern.slice(star + 1);
11405
+ if (!alias.startsWith(prefix) || !alias.endsWith(suffix)) continue;
11406
+ const substitution = alias.slice(prefix.length, alias.length - suffix.length);
11407
+ matches.push(target.replace("*", substitution));
11408
+ }
11409
+ const [match] = matches;
11410
+ if (matches.length !== 1 || match === void 0) return null;
11411
+ return (0, import_node_path.resolve)(packageRoot, baseUrl, match);
11412
+ }
11413
+ function exportsPrimitive(path, exportName) {
11414
+ try {
11415
+ const source = readSmallRegularFile(path);
11416
+ if (source === null) return false;
11417
+ const program = (0, import_typescript_estree.parse)(source, { jsx: true, sourceType: "module" });
11418
+ return program.body.some((statement) => {
11419
+ if (statement.type !== import_utils66.AST_NODE_TYPES.ExportNamedDeclaration) return false;
11420
+ if (statement.exportKind === "type") return false;
11421
+ if (statement.specifiers.some(
11422
+ (specifier) => specifier.type === import_utils66.AST_NODE_TYPES.ExportSpecifier && specifier.exportKind !== "type" && specifier.exported.type === import_utils66.AST_NODE_TYPES.Identifier && specifier.exported.name === exportName
11423
+ )) {
11424
+ return true;
11425
+ }
11426
+ const declaration = statement.declaration;
11427
+ if (declaration?.type === import_utils66.AST_NODE_TYPES.VariableDeclaration) {
11428
+ return declaration.declarations.some(
11429
+ (item) => item.id.type === import_utils66.AST_NODE_TYPES.Identifier && item.id.name === exportName
11430
+ );
11431
+ }
11432
+ return (declaration?.type === import_utils66.AST_NODE_TYPES.FunctionDeclaration || declaration?.type === import_utils66.AST_NODE_TYPES.ClassDeclaration) && declaration.id?.name === exportName;
11433
+ });
11434
+ } catch {
11435
+ return false;
11436
+ }
11437
+ }
11243
11438
  function rawElementName(node) {
11244
11439
  if (node.name.type !== import_utils66.AST_NODE_TYPES.JSXIdentifier) return null;
11245
11440
  const name = node.name.name;
11246
- return Object.hasOwn(SHADCN_PRIMITIVES, name) ? name : null;
11441
+ return Object.hasOwn(RAW_PRIMITIVES, name) ? name : null;
11247
11442
  }
11248
11443
  function effectiveAttribute(node, attributeName) {
11249
11444
  for (const attribute of node.attributes.toReversed()) {
@@ -11312,15 +11507,19 @@ function isStaticallyAssociatedLabel(node) {
11312
11507
  return node.parent.type === import_utils66.AST_NODE_TYPES.JSXElement && containsLabelableElement(node.parent);
11313
11508
  }
11314
11509
  function replacementFor(node, element) {
11315
- if (element !== "input") return SHADCN_PRIMITIVES[element];
11510
+ if (element !== "input") return RAW_PRIMITIVES[element];
11316
11511
  const typeAttribute = effectiveAttribute(node, "type");
11317
11512
  if (typeAttribute.kind === "unknown") return null;
11318
11513
  const inputType = typeAttribute.kind === "known" ? typeAttribute.value.toLowerCase() : "text";
11319
11514
  if (inputType === "hidden" || inputType === "file") return null;
11320
- if (inputType === "checkbox") return "Checkbox";
11321
- if (inputType === "radio") return "RadioGroup family";
11515
+ if (inputType === "checkbox") {
11516
+ return { capability: "Checkbox", replacement: "Checkbox" };
11517
+ }
11518
+ if (inputType === "radio") {
11519
+ return { capability: "RadioGroup", replacement: "RadioGroup family" };
11520
+ }
11322
11521
  if (AMBIGUOUS_INPUT_TYPES.has(inputType)) return null;
11323
- return "Input";
11522
+ return RAW_PRIMITIVES.input;
11324
11523
  }
11325
11524
  var prefer_shadcn_primitives_default = createRule({
11326
11525
  name: "prefer-shadcn-primitives",
@@ -11334,7 +11533,8 @@ var prefer_shadcn_primitives_default = createRule({
11334
11533
  {
11335
11534
  type: "object",
11336
11535
  properties: {
11337
- assumeAvailable: { type: "boolean" }
11536
+ assumeAvailable: { type: "boolean" },
11537
+ detectProjectPrimitives: { type: "boolean" }
11338
11538
  },
11339
11539
  additionalProperties: false
11340
11540
  }
@@ -11346,15 +11546,21 @@ var prefer_shadcn_primitives_default = createRule({
11346
11546
  defaultOptions: [{}],
11347
11547
  create(context, [options]) {
11348
11548
  const filename = context.filename.replaceAll("\\", "/");
11349
- if (isTestFile(filename) || SHARED_PRIMITIVE_IMPLEMENTATION_RE.test(filename)) {
11549
+ if (isTestFile(filename) || isStoryFile(filename) || isGeneratedFile(filename, context.sourceCode.text) || SHARED_PRIMITIVE_IMPLEMENTATION_RE.test(filename)) {
11350
11550
  return {};
11351
11551
  }
11352
- let hasSharedPrimitiveImport = options?.assumeAvailable ?? false;
11552
+ const detectsProject = options?.detectProjectPrimitives === true;
11553
+ let hasSharedPrimitiveImport = false;
11554
+ const importedCapabilities = /* @__PURE__ */ new Set();
11353
11555
  const candidates = [];
11354
11556
  return {
11355
11557
  ImportDeclaration(node) {
11356
- if (typeof node.source.value === "string" && SHARED_PRIMITIVE_IMPORT_RE.test(node.source.value)) {
11357
- hasSharedPrimitiveImport = true;
11558
+ if (typeof node.source.value !== "string") return;
11559
+ const match = SHARED_PRIMITIVE_IMPORT_RE.exec(node.source.value);
11560
+ if (match?.[1] === void 0) return;
11561
+ hasSharedPrimitiveImport = true;
11562
+ for (const [capability, moduleName] of Object.entries(CAPABILITIES)) {
11563
+ if (match[1].toLowerCase() === moduleName) importedCapabilities.add(capability);
11358
11564
  }
11359
11565
  },
11360
11566
  JSXOpeningElement(node) {
@@ -11363,11 +11569,15 @@ var prefer_shadcn_primitives_default = createRule({
11363
11569
  if (element === "label" && !isStaticallyAssociatedLabel(node)) return;
11364
11570
  const replacement = replacementFor(node, element);
11365
11571
  if (replacement === null) return;
11366
- candidates.push({ element, node, replacement });
11572
+ candidates.push({ element, node, ...replacement });
11367
11573
  },
11368
11574
  "Program:exit"() {
11369
- if (!hasSharedPrimitiveImport) return;
11370
- for (const { element, node, replacement } of candidates) {
11575
+ const requested = new Set(candidates.map(({ capability }) => capability));
11576
+ const projectPrimitives = detectsProject && requested.size > 0 ? detectProjectPrimitives(context.filename, requested) : { available: /* @__PURE__ */ new Set(), uiRoot: null };
11577
+ if (projectPrimitives.uiRoot !== null && isWithin2(projectPrimitives.uiRoot, realpathOrOriginal(context.filename))) return;
11578
+ for (const { capability, element, node, replacement } of candidates) {
11579
+ const available = options?.assumeAvailable === true || (detectsProject ? projectPrimitives.available.has(capability) || importedCapabilities.has(capability) : hasSharedPrimitiveImport);
11580
+ if (!available) continue;
11371
11581
  context.report({
11372
11582
  node,
11373
11583
  messageId: "preferShadcnPrimitive",
@@ -11378,6 +11588,13 @@ var prefer_shadcn_primitives_default = createRule({
11378
11588
  };
11379
11589
  }
11380
11590
  });
11591
+ function realpathOrOriginal(path) {
11592
+ try {
11593
+ return (0, import_node_fs.realpathSync)(path);
11594
+ } catch {
11595
+ return path;
11596
+ }
11597
+ }
11381
11598
 
11382
11599
  // src/rules/prefer-module-level-constant.ts
11383
11600
  var import_utils67 = require("@typescript-eslint/utils");
@@ -12048,99 +12265,367 @@ var prefer_module_level_schema_default = createRule({
12048
12265
 
12049
12266
  // src/rules/prefer-module-level-refined-schema.ts
12050
12267
  var import_utils69 = require("@typescript-eslint/utils");
12268
+ var BENCHMARK_PATH_RE = /(^|[/\\])(?:benchmarks?|bench)[/\\]/;
12051
12269
  var FACTORIES = /* @__PURE__ */ new Set([
12052
12270
  "array",
12271
+ "base64",
12272
+ "base64url",
12053
12273
  "bigint",
12054
12274
  "boolean",
12275
+ "cidrv4",
12276
+ "cidrv6",
12277
+ "codec",
12278
+ "custom",
12055
12279
  "date",
12056
- "number",
12057
- "string"
12058
- ]);
12059
- var REFINEMENTS = /* @__PURE__ */ new Set([
12060
- "brand",
12061
- "check",
12280
+ "datetime",
12281
+ "duration",
12062
12282
  "email",
12063
- "finite",
12064
- "int",
12065
- "length",
12066
- "max",
12067
- "min",
12068
- "multipleOf",
12069
- "nonempty",
12070
- "positive",
12071
- "regex",
12072
- "refine",
12073
- "safe",
12074
- "superRefine",
12075
- "transform",
12076
- "trim",
12283
+ "emoji",
12284
+ "enum",
12285
+ "file",
12286
+ "function",
12287
+ "hash",
12288
+ "hex",
12289
+ "hostname",
12290
+ "instanceof",
12291
+ "ipv4",
12292
+ "ipv6",
12293
+ "json",
12294
+ "jwt",
12295
+ "literal",
12296
+ "map",
12297
+ "nan",
12298
+ "nativeEnum",
12299
+ "never",
12300
+ "null",
12301
+ "nullable",
12302
+ "nullish",
12303
+ "number",
12304
+ "optional",
12305
+ "partialRecord",
12306
+ "preprocess",
12307
+ "promise",
12308
+ "set",
12309
+ "string",
12310
+ "stringbool",
12311
+ "symbol",
12312
+ "templateLiteral",
12313
+ "time",
12314
+ "undefined",
12077
12315
  "url",
12078
- "uuid"
12316
+ "uuid",
12317
+ "void"
12318
+ ]);
12319
+ var COMPOSITE_FACTORIES = /* @__PURE__ */ new Set([
12320
+ "discriminatedUnion",
12321
+ "intersection",
12322
+ "looseObject",
12323
+ "object",
12324
+ "record",
12325
+ "strictObject",
12326
+ "tuple",
12327
+ "union"
12328
+ ]);
12329
+ var FACTORY_NAMESPACES = /* @__PURE__ */ new Set(["coerce", "iso"]);
12330
+ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
12331
+ "decode",
12332
+ "decodeAsync",
12333
+ "encode",
12334
+ "encodeAsync",
12335
+ "flattenError",
12336
+ "formatError",
12337
+ "implement",
12338
+ "isNullable",
12339
+ "isOptional",
12340
+ "parse",
12341
+ "parseAsync",
12342
+ "prettifyError",
12343
+ "registry",
12344
+ "safeDecode",
12345
+ "safeDecodeAsync",
12346
+ "safeEncode",
12347
+ "safeEncodeAsync",
12348
+ "safeParse",
12349
+ "safeParseAsync",
12350
+ "spa",
12351
+ "toJSONSchema",
12352
+ "treeifyError"
12353
+ ]);
12354
+ var MEMO_CALLEES2 = /* @__PURE__ */ new Set([
12355
+ "lazy",
12356
+ "memo",
12357
+ "once",
12358
+ "useMemo"
12359
+ ]);
12360
+ var I18N_CALLEE_NAMES2 = /* @__PURE__ */ new Set([
12361
+ "$t",
12362
+ "defineMessage",
12363
+ "gettext",
12364
+ "msg",
12365
+ "ngettext",
12366
+ "t",
12367
+ "translate"
12368
+ ]);
12369
+ var I18N_RECEIVER_NAMES2 = /* @__PURE__ */ new Set([
12370
+ "$i18n",
12371
+ "i18n",
12372
+ "intl"
12373
+ ]);
12374
+ var FUNCTION_TYPES9 = /* @__PURE__ */ new Set([
12375
+ import_utils69.AST_NODE_TYPES.ArrowFunctionExpression,
12376
+ import_utils69.AST_NODE_TYPES.FunctionDeclaration,
12377
+ import_utils69.AST_NODE_TYPES.FunctionExpression
12079
12378
  ]);
12080
12379
  var PREFER_MODULE_LEVEL_REFINED_SCHEMA_DOCUMENTATION = {
12081
- summary: "Declare closed, refined Zod scalar and array schemas at module scope.",
12380
+ summary: "Declare closed Zod scalar, format, and wrapper schemas at module scope.",
12082
12381
  rationale: "A closed validation pipeline created inside a function is rebuilt on every invocation and obscures a reusable constraint.",
12083
- remediation: "Move the validation schema to module scope and call parse on the shared schema.",
12382
+ remediation: "Move the validation schema to module scope, name it with a PascalCase Schema suffix, and call parse on the shared schema.",
12084
12383
  category: "performance",
12085
- limitations: ["Only direct Zod scalar/array chains with at least two refinement methods and no non-literal arguments are reported."],
12384
+ limitations: [
12385
+ "Composite object/record/tuple/union schemas are owned by prefer-module-level-schema.",
12386
+ "Schemas that depend on function-local or mutable state, localized text, receiver state, lazy construction, or recognized memoization are excluded.",
12387
+ "Literal string z.enum domains are owned by prefer-shared-zod-enum."
12388
+ ],
12086
12389
  examples: [
12087
- { id: "module-refinement", title: "Share the validation schema", outcome: "no-match", files: [{ path: "src/options.ts", source: "import { z } from 'zod'; const BatchSize = z.number().int().min(1).max(1000); export function parse(value: unknown) { return BatchSize.parse(value); }" }], focusPath: "src/options.ts", expectedCount: 0, public: true },
12088
- { id: "local-refinement", title: "Do not rebuild a closed validation chain", outcome: "match", files: [{ path: "src/options.ts", source: "import { z } from 'zod'; export function parse(value: unknown) { return z.number().int().min(1).max(1000).parse(value); }" }], focusPath: "src/options.ts", expectedCount: 1, public: true }
12390
+ {
12391
+ id: "module-refinement",
12392
+ title: "Share the validation schema",
12393
+ outcome: "no-match",
12394
+ files: [{
12395
+ path: "src/options.ts",
12396
+ source: "import { z } from 'zod'; const BatchSizeSchema = z.number().int().min(1).max(1000); export function parse(value: unknown) { return BatchSizeSchema.parse(value); }"
12397
+ }],
12398
+ focusPath: "src/options.ts",
12399
+ expectedCount: 0,
12400
+ public: true
12401
+ },
12402
+ {
12403
+ id: "local-refinement",
12404
+ title: "Do not rebuild a closed validation chain",
12405
+ outcome: "match",
12406
+ files: [{
12407
+ path: "src/options.ts",
12408
+ source: "import { z } from 'zod'; export function parse(value: unknown) { return z.string().trim().min(1).max(128).parse(value); }"
12409
+ }],
12410
+ focusPath: "src/options.ts",
12411
+ expectedCount: 1,
12412
+ public: true
12413
+ }
12089
12414
  ]
12090
12415
  };
12091
- function enclosingFunction4(node) {
12416
+ function outermostEnclosingFunction2(node) {
12417
+ let outermost;
12092
12418
  let current = node.parent ?? void 0;
12093
12419
  while (current !== void 0) {
12094
- if ([import_utils69.AST_NODE_TYPES.ArrowFunctionExpression, import_utils69.AST_NODE_TYPES.FunctionDeclaration, import_utils69.AST_NODE_TYPES.FunctionExpression].includes(current.type)) return current;
12420
+ if (FUNCTION_TYPES9.has(current.type)) outermost = current;
12095
12421
  current = current.parent ?? void 0;
12096
12422
  }
12097
- return void 0;
12423
+ return outermost;
12098
12424
  }
12099
12425
  function collectReferences2(scope, output) {
12100
12426
  output.push(...scope.references);
12101
12427
  for (const child of scope.childScopes) collectReferences2(child, output);
12102
12428
  }
12429
+ function subtreeSome2(root, predicate) {
12430
+ let found = false;
12431
+ const visit = (value) => {
12432
+ if (found || value === null || typeof value !== "object") return;
12433
+ if (Array.isArray(value)) {
12434
+ for (const item of value) visit(item);
12435
+ return;
12436
+ }
12437
+ const candidate2 = value;
12438
+ if (typeof candidate2.type !== "string") return;
12439
+ if (predicate(candidate2)) {
12440
+ found = true;
12441
+ return;
12442
+ }
12443
+ for (const key of Object.keys(candidate2)) {
12444
+ if (key === "parent" || key === "loc" || key === "range") continue;
12445
+ visit(candidate2[key]);
12446
+ }
12447
+ };
12448
+ visit(root);
12449
+ return found;
12450
+ }
12451
+ function readsReceiver2(node) {
12452
+ return subtreeSome2(
12453
+ node,
12454
+ (inner) => inner.type === import_utils69.AST_NODE_TYPES.ThisExpression || inner.type === import_utils69.AST_NODE_TYPES.Super || inner.type === import_utils69.AST_NODE_TYPES.Identifier && inner.name === "arguments"
12455
+ );
12456
+ }
12457
+ function buildsLocalizedText2(node) {
12458
+ return subtreeSome2(node, (inner) => {
12459
+ if (inner.type === import_utils69.AST_NODE_TYPES.TaggedTemplateExpression) return true;
12460
+ if (inner.type !== import_utils69.AST_NODE_TYPES.CallExpression) return false;
12461
+ const { callee } = inner;
12462
+ if (callee.type === import_utils69.AST_NODE_TYPES.Identifier)
12463
+ return I18N_CALLEE_NAMES2.has(callee.name);
12464
+ return callee.type === import_utils69.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils69.AST_NODE_TYPES.Identifier && I18N_RECEIVER_NAMES2.has(callee.object.name);
12465
+ });
12466
+ }
12467
+ function calleeChainRoot(node) {
12468
+ let current = node;
12469
+ for (; ; ) {
12470
+ if (current.type === import_utils69.AST_NODE_TYPES.Identifier) return current;
12471
+ if (current.type === import_utils69.AST_NODE_TYPES.MemberExpression) {
12472
+ current = current.object;
12473
+ continue;
12474
+ }
12475
+ if (current.type === import_utils69.AST_NODE_TYPES.CallExpression) {
12476
+ current = current.callee;
12477
+ continue;
12478
+ }
12479
+ return null;
12480
+ }
12481
+ }
12482
+ function chainMemberNames(node) {
12483
+ const names = [];
12484
+ let current = node;
12485
+ for (; ; ) {
12486
+ if (current.type === import_utils69.AST_NODE_TYPES.MemberExpression) {
12487
+ if (current.computed || current.property.type !== import_utils69.AST_NODE_TYPES.Identifier)
12488
+ return [];
12489
+ names.push(current.property.name);
12490
+ current = current.object;
12491
+ continue;
12492
+ }
12493
+ if (current.type === import_utils69.AST_NODE_TYPES.CallExpression) {
12494
+ current = current.callee;
12495
+ continue;
12496
+ }
12497
+ break;
12498
+ }
12499
+ names.reverse();
12500
+ return names;
12501
+ }
12502
+ function schemaExpression2(node) {
12503
+ let current = node;
12504
+ for (; ; ) {
12505
+ const parent = current.parent ?? void 0;
12506
+ if (parent?.type === import_utils69.AST_NODE_TYPES.MemberExpression && parent.object === current && !parent.computed && parent.property.type === import_utils69.AST_NODE_TYPES.Identifier && parent.parent?.type === import_utils69.AST_NODE_TYPES.CallExpression && parent.parent.callee === parent) {
12507
+ if (NON_SCHEMA_TERMINALS.has(parent.property.name)) return current;
12508
+ current = parent.parent;
12509
+ continue;
12510
+ }
12511
+ if (parent?.type === import_utils69.AST_NODE_TYPES.TSAsExpression || parent?.type === import_utils69.AST_NODE_TYPES.TSNonNullExpression || parent?.type === import_utils69.AST_NODE_TYPES.TSSatisfiesExpression || parent?.type === import_utils69.AST_NODE_TYPES.TSTypeAssertion) {
12512
+ current = parent;
12513
+ continue;
12514
+ }
12515
+ return current;
12516
+ }
12517
+ }
12103
12518
  var prefer_module_level_refined_schema_default = createRule({
12104
12519
  name: "prefer-module-level-refined-schema",
12105
12520
  documentation: PREFER_MODULE_LEVEL_REFINED_SCHEMA_DOCUMENTATION,
12106
- meta: { type: "suggestion", docs: { description: "Declare closed, refined Zod scalar and array schemas at module scope." }, schema: [], messages: { hoistRefinedSchema: "Move this closed refined Zod schema to module scope and reuse it for parsing." } },
12521
+ meta: {
12522
+ type: "suggestion",
12523
+ docs: {
12524
+ description: "Declare closed Zod scalar, format, and wrapper schemas at module scope."
12525
+ },
12526
+ schema: [],
12527
+ messages: {
12528
+ hoistRefinedSchema: "Move this closed Zod schema to module scope, give it a PascalCase Schema name, and reuse it for parsing."
12529
+ }
12530
+ },
12107
12531
  defaultOptions: [],
12108
12532
  create(context) {
12109
- if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
12110
- const namespaces = /* @__PURE__ */ new Set();
12533
+ if (isTestFile(context.filename) || BENCHMARK_PATH_RE.test(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text))
12534
+ return {};
12535
+ const zodBindings = /* @__PURE__ */ new Set();
12536
+ function resolvedBinding(identifier) {
12537
+ return import_utils69.ASTUtils.findVariable(
12538
+ context.sourceCode.getScope(identifier),
12539
+ identifier.name
12540
+ );
12541
+ }
12542
+ function recordZodBinding(identifier) {
12543
+ const binding = resolvedBinding(identifier);
12544
+ if (binding !== null) zodBindings.add(binding);
12545
+ }
12546
+ function factoryName(node, allowed) {
12547
+ if (node.callee.type !== import_utils69.AST_NODE_TYPES.MemberExpression) return null;
12548
+ const root = calleeChainRoot(node.callee);
12549
+ if (root === null) return null;
12550
+ const binding = resolvedBinding(root);
12551
+ if (binding === null || !zodBindings.has(binding)) return null;
12552
+ const names = chainMemberNames(node.callee);
12553
+ if (names.length === 1 && allowed.has(names[0] ?? ""))
12554
+ return names[0] ?? null;
12555
+ if (names.length === 2 && FACTORY_NAMESPACES.has(names[0] ?? "") && allowed.has(names[1] ?? ""))
12556
+ return names[1] ?? null;
12557
+ return null;
12558
+ }
12559
+ function isSharedEnumDomain(node, factory) {
12560
+ if (factory !== "enum") return false;
12561
+ const [argument] = node.arguments;
12562
+ return argument?.type === import_utils69.AST_NODE_TYPES.ArrayExpression && argument.elements.length >= 2 && argument.elements.every(
12563
+ (element) => element?.type === import_utils69.AST_NODE_TYPES.Literal && typeof element.value === "string"
12564
+ );
12565
+ }
12566
+ function isNestedInOwnedFactory(node) {
12567
+ let current = node;
12568
+ while (current.parent != null) {
12569
+ current = current.parent;
12570
+ if (FUNCTION_TYPES9.has(current.type)) return false;
12571
+ if (current.type === import_utils69.AST_NODE_TYPES.CallExpression && (factoryName(current, FACTORIES) !== null || factoryName(current, COMPOSITE_FACTORIES) !== null))
12572
+ return true;
12573
+ }
12574
+ return false;
12575
+ }
12576
+ function isMemoized(node) {
12577
+ let current = node.parent ?? void 0;
12578
+ while (current !== void 0) {
12579
+ if (current.type === import_utils69.AST_NODE_TYPES.CallExpression && (current.callee.type === import_utils69.AST_NODE_TYPES.Identifier && MEMO_CALLEES2.has(current.callee.name) || current.callee.type === import_utils69.AST_NODE_TYPES.MemberExpression && !current.callee.computed && current.callee.property.type === import_utils69.AST_NODE_TYPES.Identifier && MEMO_CALLEES2.has(current.callee.property.name)))
12580
+ return true;
12581
+ current = current.parent ?? void 0;
12582
+ }
12583
+ return false;
12584
+ }
12585
+ function closesOverNothing(node, enclosing) {
12586
+ const references = [];
12587
+ collectReferences2(context.sourceCode.getScope(node), references);
12588
+ const [start, end] = node.range;
12589
+ const [functionStart, functionEnd] = enclosing.range;
12590
+ for (const reference of references) {
12591
+ const [referenceStart] = reference.identifier.range;
12592
+ if (referenceStart < start || referenceStart >= end) continue;
12593
+ const resolved = reference.resolved;
12594
+ if (resolved === null) continue;
12595
+ for (const definition of resolved.defs) {
12596
+ if (definition.type === "ImportBinding") {
12597
+ const parent = reference.identifier.parent;
12598
+ if (parent?.type === import_utils69.AST_NODE_TYPES.CallExpression && parent.callee === reference.identifier && !zodBindings.has(resolved))
12599
+ return false;
12600
+ continue;
12601
+ }
12602
+ if (definition.node.type === import_utils69.AST_NODE_TYPES.VariableDeclarator && definition.node.parent.type === import_utils69.AST_NODE_TYPES.VariableDeclaration && definition.node.parent.kind !== "const")
12603
+ return false;
12604
+ const [definitionStart, definitionEnd] = definition.node.range;
12605
+ if (definitionStart >= start && definitionEnd <= end) continue;
12606
+ if (definitionStart >= functionStart && definitionEnd <= functionEnd)
12607
+ return false;
12608
+ }
12609
+ }
12610
+ return true;
12611
+ }
12111
12612
  return {
12112
12613
  ImportDeclaration(node) {
12113
12614
  if (!isZodModule(node.source.value)) return;
12114
- for (const specifier of node.specifiers) if (specifier.type === import_utils69.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils69.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils69.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === import_utils69.AST_NODE_TYPES.Identifier && specifier.imported.name === "z") namespaces.add(specifier.local.name);
12615
+ for (const specifier of node.specifiers) {
12616
+ if (specifier.type === import_utils69.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils69.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils69.AST_NODE_TYPES.ImportSpecifier && (specifier.imported.type === import_utils69.AST_NODE_TYPES.Identifier ? specifier.imported.name === "z" : specifier.imported.value === "z"))
12617
+ recordZodBinding(specifier.local);
12618
+ }
12115
12619
  },
12116
12620
  CallExpression(node) {
12117
- const enclosing = enclosingFunction4(node);
12118
- if (enclosing === void 0 || node.parent?.type !== import_utils69.AST_NODE_TYPES.MemberExpression || node.parent.object !== node) return;
12119
- if (node.callee.type !== import_utils69.AST_NODE_TYPES.MemberExpression || node.callee.computed || node.callee.object.type !== import_utils69.AST_NODE_TYPES.Identifier || !namespaces.has(node.callee.object.name) || node.callee.property.type !== import_utils69.AST_NODE_TYPES.Identifier || !FACTORIES.has(node.callee.property.name)) return;
12120
- let current = node;
12121
- let refinements = 0;
12122
- while (current.parent?.type === import_utils69.AST_NODE_TYPES.MemberExpression && current.parent.object === current && !current.parent.computed && current.parent.property.type === import_utils69.AST_NODE_TYPES.Identifier && current.parent.parent?.type === import_utils69.AST_NODE_TYPES.CallExpression && current.parent.parent.callee === current.parent) {
12123
- const call = current.parent.parent;
12124
- if (["parse", "parseAsync", "safeParse", "safeParseAsync"].includes(current.parent.property.name)) break;
12125
- if (REFINEMENTS.has(current.parent.property.name)) refinements += 1;
12126
- current = call;
12127
- }
12128
- if (refinements < 2) return;
12129
- const references = [];
12130
- collectReferences2(context.sourceCode.getScope(current), references);
12131
- const [start, end] = current.range;
12132
- const [functionStart, functionEnd] = enclosing.range;
12133
- for (const reference of references) {
12134
- const [referenceStart] = reference.identifier.range;
12135
- if (referenceStart < start || referenceStart >= end || reference.resolved === null) continue;
12136
- for (const definition of reference.resolved.defs) {
12137
- if (definition.type === "ImportBinding") continue;
12138
- if (definition.node.type === import_utils69.AST_NODE_TYPES.VariableDeclarator && definition.node.parent.type === import_utils69.AST_NODE_TYPES.VariableDeclaration && definition.node.parent.kind !== "const") return;
12139
- const [definitionStart, definitionEnd] = definition.node.range;
12140
- if (definitionStart >= start && definitionEnd <= end) continue;
12141
- if (definitionStart >= functionStart && definitionEnd <= functionEnd) return;
12142
- }
12143
- }
12621
+ const factory = factoryName(node, FACTORIES);
12622
+ if (factory === null || isSharedEnumDomain(node, factory) || isNestedInOwnedFactory(node) || isMemoized(node))
12623
+ return;
12624
+ const enclosing = outermostEnclosingFunction2(node);
12625
+ if (enclosing === void 0) return;
12626
+ const expression = schemaExpression2(node);
12627
+ if (readsReceiver2(expression) || buildsLocalizedText2(expression) || !closesOverNothing(expression, enclosing))
12628
+ return;
12144
12629
  context.report({ node, messageId: "hoistRefinedSchema" });
12145
12630
  }
12146
12631
  };
@@ -12154,20 +12639,21 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
12154
12639
  rationale: "One multi-value literal expresses the same closed value domain without repeated schema wrappers.",
12155
12640
  remediation: "Replace the union with z.literal([value1, value2, ...]).",
12156
12641
  category: "maintainability",
12642
+ autofix: "safe",
12157
12643
  limitations: [
12158
- "Bare `zod` imports are analyzed only when the rule option explicitly declares `zodMajorVersion: 4`; `zod/v4` imports are self-declaring."
12644
+ "Bare zod imports are analyzed only when the rule option explicitly declares zodMajorVersion: 4; explicit zod/v4 entrypoints are self-declaring.",
12645
+ "All-string domains are left to zod/prefer-enum-over-literal-union.",
12646
+ "A finding containing comments is not autofixed because moving its trivia is ambiguous."
12159
12647
  ],
12160
12648
  examples: [
12161
12649
  {
12162
12650
  id: "multi-value",
12163
12651
  title: "Use one multi-value literal",
12164
12652
  outcome: "no-match",
12165
- files: [
12166
- {
12167
- path: "src/schema.ts",
12168
- source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
12169
- }
12170
- ],
12653
+ files: [{
12654
+ path: "src/schema.ts",
12655
+ source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
12656
+ }],
12171
12657
  focusPath: "src/schema.ts",
12172
12658
  expectedCount: 0,
12173
12659
  public: true
@@ -12176,80 +12662,116 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
12176
12662
  id: "literal-union",
12177
12663
  title: "Avoid repeated literal wrappers",
12178
12664
  outcome: "match",
12179
- files: [
12180
- {
12181
- path: "src/schema.ts",
12182
- source: "import { z } from 'zod'; export const Version = z.union([z.literal(1), z.literal(2), z.literal(3)]);"
12183
- }
12184
- ],
12665
+ files: [{
12666
+ path: "src/schema.ts",
12667
+ source: "import { z } from 'zod'; export const Version = z.union([z.literal(1), z.literal(2), z.literal(3)]);"
12668
+ }],
12669
+ fixedFiles: [{
12670
+ path: "src/schema.ts",
12671
+ source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
12672
+ }],
12185
12673
  focusPath: "src/schema.ts",
12186
12674
  expectedCount: 1,
12187
12675
  public: true
12188
12676
  }
12189
12677
  ]
12190
12678
  };
12191
- function memberCall(node, object, method) {
12192
- return node.callee.type === import_utils70.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.object.type === import_utils70.AST_NODE_TYPES.Identifier && node.callee.object.name === object && node.callee.property.type === import_utils70.AST_NODE_TYPES.Identifier && node.callee.property.name === method;
12679
+ function isStaticPrimitive(node, context) {
12680
+ if (node.type === import_utils70.AST_NODE_TYPES.Literal) {
12681
+ return node.value === null || ["bigint", "boolean", "number", "string"].includes(typeof node.value);
12682
+ }
12683
+ if (node.type === import_utils70.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0)
12684
+ return true;
12685
+ if (node.type === import_utils70.AST_NODE_TYPES.Identifier && node.name === "undefined") {
12686
+ const binding = import_utils70.ASTUtils.findVariable(
12687
+ context.sourceCode.getScope(node),
12688
+ node.name
12689
+ );
12690
+ return binding === null || binding.defs.length === 0;
12691
+ }
12692
+ return node.type === import_utils70.AST_NODE_TYPES.UnaryExpression && node.operator === "-" && node.argument.type === import_utils70.AST_NODE_TYPES.Literal && ["bigint", "number"].includes(typeof node.argument.value);
12693
+ }
12694
+ function isStaticString(node) {
12695
+ return node.type === import_utils70.AST_NODE_TYPES.Literal && typeof node.value === "string" || node.type === import_utils70.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0;
12193
12696
  }
12194
12697
  var prefer_multi_value_zod_literal_default = createRule({
12195
12698
  name: "prefer-multi-value-zod-literal",
12196
12699
  documentation: PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION,
12197
12700
  meta: {
12198
12701
  type: "suggestion",
12702
+ fixable: "code",
12199
12703
  docs: {
12200
12704
  description: "Use the Zod 4 multi-value literal API instead of a union of literal schemas."
12201
12705
  },
12202
- schema: [
12203
- {
12204
- type: "object",
12205
- additionalProperties: false,
12206
- properties: {
12207
- zodMajorVersion: { type: "integer", minimum: 4, maximum: 4 }
12208
- }
12706
+ schema: [{
12707
+ type: "object",
12708
+ additionalProperties: false,
12709
+ properties: {
12710
+ zodMajorVersion: { type: "integer", minimum: 4, maximum: 4 }
12209
12711
  }
12210
- ],
12712
+ }],
12211
12713
  messages: {
12212
- useMultiValueLiteral: "Replace this literal-schema union with `{{zod}}.literal([\u2026])`."
12714
+ useMultiValueLiteral: "Replace this literal-schema union with {{zod}}.literal([\u2026])."
12213
12715
  }
12214
12716
  },
12215
12717
  defaultOptions: [{}],
12216
12718
  create(context, [options]) {
12217
12719
  if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text))
12218
12720
  return {};
12219
- const namespaces = /* @__PURE__ */ new Set();
12220
- const zod4Namespaces = /* @__PURE__ */ new Set();
12721
+ const zodBindings = /* @__PURE__ */ new Set();
12722
+ const zod4Bindings = /* @__PURE__ */ new Set();
12723
+ function resolvedBinding(identifier) {
12724
+ return import_utils70.ASTUtils.findVariable(
12725
+ context.sourceCode.getScope(identifier),
12726
+ identifier.name
12727
+ );
12728
+ }
12729
+ function directMemberCall(node, binding, method) {
12730
+ if (node.callee.type !== import_utils70.AST_NODE_TYPES.MemberExpression || node.callee.computed || node.callee.object.type !== import_utils70.AST_NODE_TYPES.Identifier || node.callee.property.type !== import_utils70.AST_NODE_TYPES.Identifier || node.callee.property.name !== method)
12731
+ return false;
12732
+ return resolvedBinding(node.callee.object) === binding;
12733
+ }
12221
12734
  return {
12222
12735
  ImportDeclaration(node) {
12223
12736
  if (!isZodModule(node.source.value)) return;
12737
+ const isExplicitV4 = /^zod\/v4(?:$|[-/])/.test(node.source.value);
12224
12738
  for (const specifier of node.specifiers) {
12225
- if (specifier.type === import_utils70.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils70.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils70.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === import_utils70.AST_NODE_TYPES.Identifier && specifier.imported.name === "z") {
12226
- namespaces.add(specifier.local.name);
12227
- if (node.source.value === "zod/v4" || node.source.value.startsWith("zod/v4/"))
12228
- zod4Namespaces.add(specifier.local.name);
12739
+ if (specifier.type === import_utils70.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils70.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils70.AST_NODE_TYPES.ImportSpecifier && (specifier.imported.type === import_utils70.AST_NODE_TYPES.Identifier ? specifier.imported.name === "z" : specifier.imported.value === "z")) {
12740
+ const binding = resolvedBinding(specifier.local);
12741
+ if (binding === null) continue;
12742
+ zodBindings.add(binding);
12743
+ if (isExplicitV4) zod4Bindings.add(binding);
12229
12744
  }
12230
12745
  }
12231
12746
  },
12232
12747
  CallExpression(node) {
12233
- const namespace = [...namespaces].find(
12234
- (name) => memberCall(node, name, "union")
12235
- );
12236
- if (namespace === void 0 || options?.zodMajorVersion !== 4 && !zod4Namespaces.has(namespace) || node.arguments.length !== 1)
12748
+ if (node.callee.type !== import_utils70.AST_NODE_TYPES.MemberExpression || node.callee.object.type !== import_utils70.AST_NODE_TYPES.Identifier)
12749
+ return;
12750
+ const binding = resolvedBinding(node.callee.object);
12751
+ if (binding === null || !zodBindings.has(binding) || !directMemberCall(node, binding, "union") || options?.zodMajorVersion !== 4 && !zod4Bindings.has(binding) || node.arguments.length !== 1)
12237
12752
  return;
12238
12753
  const [argument] = node.arguments;
12239
- if (argument?.type !== import_utils70.AST_NODE_TYPES.ArrayExpression || argument.elements.length < 3 || argument.elements.some((element) => {
12240
- if (element === null || element.type !== import_utils70.AST_NODE_TYPES.CallExpression || !memberCall(element, namespace, "literal") || element.arguments.length !== 1)
12241
- return true;
12242
- const [value] = element.arguments;
12243
- return value === void 0 || value.type === import_utils70.AST_NODE_TYPES.SpreadElement || ![
12244
- import_utils70.AST_NODE_TYPES.Literal,
12245
- import_utils70.AST_NODE_TYPES.TemplateLiteral
12246
- ].includes(value.type);
12247
- }))
12754
+ if (argument?.type !== import_utils70.AST_NODE_TYPES.ArrayExpression || argument.elements.length < 2)
12248
12755
  return;
12756
+ const values = [];
12757
+ for (const element of argument.elements) {
12758
+ if (element === null || element.type !== import_utils70.AST_NODE_TYPES.CallExpression || !directMemberCall(element, binding, "literal") || element.arguments.length !== 1)
12759
+ return;
12760
+ const [value] = element.arguments;
12761
+ if (value === void 0 || !isStaticPrimitive(value, context)) return;
12762
+ values.push(value);
12763
+ }
12764
+ if (values.every(isStaticString)) return;
12765
+ const namespace = node.callee.object.name;
12766
+ const hasComments = context.sourceCode.getCommentsInside(node).length > 0;
12249
12767
  context.report({
12250
12768
  node,
12251
12769
  messageId: "useMultiValueLiteral",
12252
- data: { zod: namespace }
12770
+ data: { zod: namespace },
12771
+ fix: hasComments ? null : (fixer) => fixer.replaceText(
12772
+ node,
12773
+ `${namespace}.literal([${values.map((value) => context.sourceCode.getText(value)).join(", ")}])`
12774
+ )
12253
12775
  });
12254
12776
  }
12255
12777
  };
@@ -12401,11 +12923,11 @@ var prefer_native_random_uuid_default = createRule({
12401
12923
  create(context) {
12402
12924
  const directBindings = /* @__PURE__ */ new Set();
12403
12925
  const namespaceBindings = /* @__PURE__ */ new Set();
12404
- function resolve(identifier) {
12926
+ function resolve2(identifier) {
12405
12927
  return import_utils73.ASTUtils.findVariable(context.sourceCode.getScope(identifier), identifier.name);
12406
12928
  }
12407
12929
  function record(identifier, destination) {
12408
- const variable = resolve(identifier);
12930
+ const variable = resolve2(identifier);
12409
12931
  if (variable !== null) destination.add(variable);
12410
12932
  }
12411
12933
  function report2(node) {
@@ -12433,7 +12955,7 @@ var prefer_native_random_uuid_default = createRule({
12433
12955
  },
12434
12956
  VariableDeclarator(node) {
12435
12957
  if (node.parent.kind !== "const" || !requireUuid(node.init)) return;
12436
- if (node.init?.type !== import_utils73.AST_NODE_TYPES.CallExpression || node.init.callee.type !== import_utils73.AST_NODE_TYPES.Identifier || (resolve(node.init.callee)?.defs.length ?? 0) > 0) {
12958
+ if (node.init?.type !== import_utils73.AST_NODE_TYPES.CallExpression || node.init.callee.type !== import_utils73.AST_NODE_TYPES.Identifier || (resolve2(node.init.callee)?.defs.length ?? 0) > 0) {
12437
12959
  return;
12438
12960
  }
12439
12961
  if (node.id.type === import_utils73.AST_NODE_TYPES.Identifier) {
@@ -12450,14 +12972,14 @@ var prefer_native_random_uuid_default = createRule({
12450
12972
  "CallExpression:exit"(node) {
12451
12973
  if (node.arguments.length !== 0) return;
12452
12974
  if (node.callee.type === import_utils73.AST_NODE_TYPES.Identifier) {
12453
- const variable2 = resolve(node.callee);
12975
+ const variable2 = resolve2(node.callee);
12454
12976
  if (variable2 !== null && directBindings.has(variable2)) report2(node);
12455
12977
  return;
12456
12978
  }
12457
12979
  if (node.callee.type !== import_utils73.AST_NODE_TYPES.MemberExpression || node.callee.computed || node.callee.object.type !== import_utils73.AST_NODE_TYPES.Identifier || node.callee.property.type !== import_utils73.AST_NODE_TYPES.Identifier || node.callee.property.name !== "v4") {
12458
12980
  return;
12459
12981
  }
12460
- const variable = resolve(node.callee.object);
12982
+ const variable = resolve2(node.callee.object);
12461
12983
  if (variable !== null && namespaceBindings.has(variable)) report2(node);
12462
12984
  }
12463
12985
  };
@@ -12487,22 +13009,22 @@ function memberName4(node) {
12487
13009
  }
12488
13010
  return null;
12489
13011
  }
12490
- function isCryptoLoader(node, resolve) {
13012
+ function isCryptoLoader(node, resolve2) {
12491
13013
  if (node.type !== import_utils74.AST_NODE_TYPES.CallExpression || node.arguments.length !== 1) return false;
12492
13014
  const [argument] = node.arguments;
12493
13015
  if (argument === void 0 || argument.type === import_utils74.AST_NODE_TYPES.SpreadElement || !isCryptoSpecifier(argument)) {
12494
13016
  return false;
12495
13017
  }
12496
13018
  if (node.callee.type === import_utils74.AST_NODE_TYPES.Identifier) {
12497
- return node.callee.name === "require" && isUnshadowedBuiltinIdentifier(node.callee, resolve);
13019
+ return node.callee.name === "require" && isUnshadowedBuiltinIdentifier(node.callee, resolve2);
12498
13020
  }
12499
- return node.callee.type === import_utils74.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils74.AST_NODE_TYPES.Identifier && node.callee.object.name === "process" && isUnshadowedBuiltinIdentifier(node.callee.object, resolve) && memberName4(node.callee) === "getBuiltinModule";
13021
+ return node.callee.type === import_utils74.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils74.AST_NODE_TYPES.Identifier && node.callee.object.name === "process" && isUnshadowedBuiltinIdentifier(node.callee.object, resolve2) && memberName4(node.callee) === "getBuiltinModule";
12500
13022
  }
12501
13023
  function isCryptoSpecifier(node) {
12502
13024
  return node.type === import_utils74.AST_NODE_TYPES.Literal && (node.value === "crypto" || node.value === "node:crypto");
12503
13025
  }
12504
- function isUnshadowedBuiltinIdentifier(identifier, resolve) {
12505
- const variable = resolve(identifier);
13026
+ function isUnshadowedBuiltinIdentifier(identifier, resolve2) {
13027
+ const variable = resolve2(identifier);
12506
13028
  return variable === null || variable.defs.length === 0;
12507
13029
  }
12508
13030
  function propertyName4(node) {
@@ -12520,14 +13042,14 @@ var prefer_node_crypto_hash_default = createRule({
12520
13042
  create(context) {
12521
13043
  const directBindings = /* @__PURE__ */ new Set();
12522
13044
  const namespaceBindings = /* @__PURE__ */ new Set();
12523
- function resolve(identifier) {
13045
+ function resolve2(identifier) {
12524
13046
  return import_utils74.ASTUtils.findVariable(
12525
13047
  context.sourceCode.getScope(identifier),
12526
13048
  identifier.name
12527
13049
  );
12528
13050
  }
12529
13051
  function record(identifier, destination) {
12530
- const variable = resolve(identifier);
13052
+ const variable = resolve2(identifier);
12531
13053
  if (variable !== null) destination.add(variable);
12532
13054
  }
12533
13055
  return {
@@ -12542,7 +13064,7 @@ var prefer_node_crypto_hash_default = createRule({
12542
13064
  }
12543
13065
  },
12544
13066
  VariableDeclarator(node) {
12545
- if (node.parent.kind !== "const" || node.init === null || !isCryptoLoader(node.init, resolve)) {
13067
+ if (node.parent.kind !== "const" || node.init === null || !isCryptoLoader(node.init, resolve2)) {
12546
13068
  return;
12547
13069
  }
12548
13070
  if (node.id.type === import_utils74.AST_NODE_TYPES.Identifier) {
@@ -12566,7 +13088,7 @@ var prefer_node_crypto_hash_default = createRule({
12566
13088
  create,
12567
13089
  directBindings,
12568
13090
  namespaceBindings,
12569
- resolve
13091
+ resolve2
12570
13092
  ))
12571
13093
  return;
12572
13094
  context.report({ node, messageId: "preferNodeCryptoHash" });
@@ -12580,17 +13102,17 @@ function importedName5(node) {
12580
13102
  function isMemberCall(node, name) {
12581
13103
  return node.callee.type === import_utils74.AST_NODE_TYPES.MemberExpression && memberName4(node.callee) === name;
12582
13104
  }
12583
- function isCreateHashCall(node, directBindings, namespaceBindings, resolve) {
13105
+ function isCreateHashCall(node, directBindings, namespaceBindings, resolve2) {
12584
13106
  if (node.callee.type === import_utils74.AST_NODE_TYPES.Identifier) {
12585
- const variable2 = resolve(node.callee);
13107
+ const variable2 = resolve2(node.callee);
12586
13108
  return variable2 !== null && directBindings.has(variable2);
12587
13109
  }
12588
13110
  if (node.callee.type !== import_utils74.AST_NODE_TYPES.MemberExpression || memberName4(node.callee) !== "createHash") {
12589
13111
  return false;
12590
13112
  }
12591
- if (isCryptoLoader(node.callee.object, resolve)) return true;
13113
+ if (isCryptoLoader(node.callee.object, resolve2)) return true;
12592
13114
  if (node.callee.object.type !== import_utils74.AST_NODE_TYPES.Identifier) return false;
12593
- const variable = resolve(node.callee.object);
13115
+ const variable = resolve2(node.callee.object);
12594
13116
  return variable !== null && namespaceBindings.has(variable);
12595
13117
  }
12596
13118
 
@@ -13912,7 +14434,30 @@ var import_fs = require("fs");
13912
14434
  var import_path = require("path");
13913
14435
 
13914
14436
  // src/rules/_tailwind.ts
13915
- var tailwindBase = (token) => token.replace(/^(?:[a-z0-9-]+:)+/i, "").replace(/^!/, "");
14437
+ var tailwindVariantPrefix = (token) => {
14438
+ let bracketDepth = 0;
14439
+ let parenthesisDepth = 0;
14440
+ let escaped = false;
14441
+ let end = 0;
14442
+ for (let index = 0; index < token.length; index += 1) {
14443
+ const character = token[index];
14444
+ if (escaped) {
14445
+ escaped = false;
14446
+ continue;
14447
+ }
14448
+ if (character === "\\") {
14449
+ escaped = true;
14450
+ continue;
14451
+ }
14452
+ if (character === "[") bracketDepth += 1;
14453
+ else if (character === "]") bracketDepth = Math.max(0, bracketDepth - 1);
14454
+ else if (character === "(") parenthesisDepth += 1;
14455
+ else if (character === ")") parenthesisDepth = Math.max(0, parenthesisDepth - 1);
14456
+ else if (character === ":" && bracketDepth === 0 && parenthesisDepth === 0) end = index + 1;
14457
+ }
14458
+ return token.slice(0, end);
14459
+ };
14460
+ var tailwindBase = (token) => token.slice(tailwindVariantPrefix(token).length).replace(/^!/, "");
13916
14461
  var classTokens = (value) => value.split(/\s+/).filter(Boolean);
13917
14462
 
13918
14463
  // src/rules/prefer-semantic-colors.ts
@@ -13921,7 +14466,10 @@ var PREFER_SEMANTIC_COLORS_DOCUMENTATION = {
13921
14466
  rationale: "Semantic tokens keep themes and product meaning consistent while raw colors couple components to a palette value.",
13922
14467
  remediation: "Replace raw palette and literal colors with the closest semantic design-system token or CSS variable.",
13923
14468
  category: "style",
13924
- limitations: ["Email, PDF, icon artwork, masks, gradients, stories, and explicitly configured non-token projects have targeted exclusions."],
14469
+ limitations: [
14470
+ "Email, PDF, video-rendering, print-only, icon artwork, masks, gradients, stories, and explicitly configured non-token projects have targeted exclusions.",
14471
+ "Opaque-foreground checks are opt-in and require both a same-variant semantic background class and its package-local declared foreground token."
14472
+ ],
13925
14473
  examples: [
13926
14474
  { id: "semantic-text-color", title: "Use a semantic color token", outcome: "no-match", files: [{ path: "src/notice.tsx", source: 'const notice = <div className="text-destructive" />;' }], focusPath: "src/notice.tsx", expectedCount: 0, public: true },
13927
14475
  { id: "raw-text-color", title: "Do not use a raw palette color", outcome: "match", files: [{ path: "src/notice.tsx", source: 'const notice = <div className="text-red-500" />;' }], focusPath: "src/notice.tsx", expectedCount: 1, public: true }
@@ -14049,7 +14597,13 @@ var SVG_SHAPE_PRIMITIVES = /* @__PURE__ */ new Set([
14049
14597
  "tspan",
14050
14598
  "use"
14051
14599
  ]);
14052
- var EMAIL_OR_PDF_MODULE_RE = /^@react-(?:email|pdf)\//;
14600
+ var EXTERNAL_RENDERER_MODULE_RE = /^(?:@react-(?:email|pdf)\/|remotion$|@remotion\/)/;
14601
+ var OPAQUE_FOREGROUND_RE = /^text-(?:white|black)(?:\/100)?$/;
14602
+ var SEMANTIC_BACKGROUND_RE = /^bg-([a-z][a-z0-9-]*)$/;
14603
+ var CSS_COMMENT_RE = /\/\*[\s\S]*?\*\//gu;
14604
+ var DECLARED_FOREGROUND_RE = /--(?:color-)?([a-z][a-z0-9-]*)-foreground\s*:/giu;
14605
+ var MAX_TOKEN_STYLESHEET_BYTES = 1048576;
14606
+ var SEMANTIC_DECLARATIONS_CACHE = /* @__PURE__ */ new Map();
14053
14607
  function isSvgLikeElementName(name) {
14054
14608
  return name === "svg" || SVG_DEFS_CONTAINERS.has(name) || /svg$/i.test(name);
14055
14609
  }
@@ -14186,20 +14740,68 @@ var expandWorkspaceGlob = (root, glob) => {
14186
14740
  if (star === -1) return [(0, import_path.join)(root, glob)];
14187
14741
  const prefix = glob.slice(0, star).replace(/\/$/u, "");
14188
14742
  const parent = prefix === "" ? root : (0, import_path.join)(root, prefix);
14189
- if (!(0, import_fs.existsSync)(parent)) return [];
14190
- return (0, import_fs.readdirSync)(parent, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => (0, import_path.join)(parent, entry.name));
14743
+ if (!(0, import_fs.existsSync)(parent) || !(0, import_fs.lstatSync)(parent).isDirectory()) return [];
14744
+ return (0, import_fs.readdirSync)(parent, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).sort((left, right) => left.name.localeCompare(right.name)).map((entry) => (0, import_path.join)(parent, entry.name));
14191
14745
  };
14192
14746
  var propName = (key) => {
14193
14747
  if (key.type === import_utils82.AST_NODE_TYPES.Identifier) return key.name;
14194
14748
  if (key.type === import_utils82.AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
14195
14749
  return null;
14196
14750
  };
14197
- var staticallyImportsEmailOrPdfRenderer = (program) => program.body.some((statement) => {
14751
+ var staticallyImportsExternalRenderer = (program) => program.body.some((statement) => {
14198
14752
  if (statement.type !== import_utils82.AST_NODE_TYPES.ImportDeclaration && statement.type !== import_utils82.AST_NODE_TYPES.ExportNamedDeclaration && statement.type !== import_utils82.AST_NODE_TYPES.ExportAllDeclaration) {
14199
14753
  return false;
14200
14754
  }
14201
- return statement.source !== null && typeof statement.source.value === "string" && EMAIL_OR_PDF_MODULE_RE.test(statement.source.value);
14755
+ return statement.source !== null && typeof statement.source.value === "string" && EXTERNAL_RENDERER_MODULE_RE.test(statement.source.value);
14202
14756
  });
14757
+ var semanticForegroundRoles = (filename) => {
14758
+ const packageRoot = nearestPackageRoot((0, import_path.dirname)(filename));
14759
+ if (packageRoot === null) return /* @__PURE__ */ new Set();
14760
+ const candidates = CSS_DETECTION_FILES.map((relative2) => (0, import_path.join)(packageRoot, relative2));
14761
+ const fingerprint = candidates.map(fileFingerprint2).join("|");
14762
+ const cached = SEMANTIC_DECLARATIONS_CACHE.get(packageRoot);
14763
+ if (cached?.fingerprint === fingerprint) return cached.value;
14764
+ const foregroundRoles = /* @__PURE__ */ new Set();
14765
+ for (const candidate2 of candidates) {
14766
+ const css = readTokenStylesheet(candidate2);
14767
+ if (css === null) continue;
14768
+ const declarations = css.replace(CSS_COMMENT_RE, "");
14769
+ for (const match of declarations.matchAll(DECLARED_FOREGROUND_RE)) {
14770
+ if (match[1] !== void 0) foregroundRoles.add(match[1].toLowerCase());
14771
+ }
14772
+ }
14773
+ SEMANTIC_DECLARATIONS_CACHE.set(packageRoot, { fingerprint, value: foregroundRoles });
14774
+ return foregroundRoles;
14775
+ };
14776
+ var nearestPackageRoot = (startDir) => {
14777
+ let dir = startDir;
14778
+ const root = (0, import_path.parse)(dir).root;
14779
+ for (; ; ) {
14780
+ if ((0, import_fs.existsSync)((0, import_path.join)(dir, "package.json"))) {
14781
+ return dir;
14782
+ }
14783
+ const parent = (0, import_path.dirname)(dir);
14784
+ if (dir === root || parent === dir) return null;
14785
+ dir = parent;
14786
+ }
14787
+ };
14788
+ var fileFingerprint2 = (path) => {
14789
+ try {
14790
+ const stat = (0, import_fs.lstatSync)(path);
14791
+ return stat.isFile() ? `${path}:${stat.size}:${stat.mtimeMs}` : `${path}:excluded`;
14792
+ } catch {
14793
+ return `${path}:missing`;
14794
+ }
14795
+ };
14796
+ var readTokenStylesheet = (path) => {
14797
+ try {
14798
+ const stat = (0, import_fs.lstatSync)(path);
14799
+ if (!stat.isFile() || stat.size > MAX_TOKEN_STYLESHEET_BYTES) return null;
14800
+ return (0, import_fs.readFileSync)(path, "utf8");
14801
+ } catch {
14802
+ return null;
14803
+ }
14804
+ };
14203
14805
  var prefer_semantic_colors_default = createRule({
14204
14806
  name: "prefer-semantic-colors",
14205
14807
  documentation: PREFER_SEMANTIC_COLORS_DOCUMENTATION,
@@ -14213,30 +14815,34 @@ var prefer_semantic_colors_default = createRule({
14213
14815
  type: "object",
14214
14816
  additionalProperties: false,
14215
14817
  properties: {
14216
- requireSemanticTokens: { type: "boolean" }
14818
+ requireSemanticTokens: { type: "boolean" },
14819
+ opaqueForegroundPairs: { type: "boolean" }
14217
14820
  }
14218
14821
  }
14219
14822
  ],
14220
14823
  messages: {
14221
14824
  rawPalette: "Raw palette class '{{class}}' \u2014 use a semantic token (e.g. text-foreground, bg-primary, text-destructive, bg-muted).",
14222
14825
  arbitraryColor: "Hardcoded color '{{class}}' \u2014 use a semantic token, or var(--\u2026). For charts/brand add an eslint-disable with a reason.",
14223
- inlineColor: "Hardcoded color '{{value}}' \u2014 use a semantic token / CSS variable. For charts/standalone pages add an eslint-disable with a reason."
14826
+ inlineColor: "Hardcoded color '{{value}}' \u2014 use a semantic token / CSS variable. For charts/standalone pages add an eslint-disable with a reason.",
14827
+ opaqueForegroundPair: "'{{class}}' bypasses the declared '{{replacement}}' token paired with '{{background}}'."
14224
14828
  }
14225
14829
  },
14226
14830
  defaultOptions: [{}],
14227
14831
  create(context, [options]) {
14228
14832
  if (STORIES_FILE_RE.test(context.filename)) return {};
14229
- if (staticallyImportsEmailOrPdfRenderer(context.sourceCode.ast)) return {};
14230
- if (options?.requireSemanticTokens === true && !hasSemanticTokenSystem(context.filename)) {
14231
- return {};
14232
- }
14233
- let importsEmailOrPdfRenderer = false;
14234
- const pendingReports = [];
14833
+ if (staticallyImportsExternalRenderer(context.sourceCode.ast)) return {};
14834
+ if (options?.requireSemanticTokens === true && !hasSemanticTokenSystem(context.filename)) return {};
14835
+ const foregroundRoles = options?.opaqueForegroundPairs === true ? semanticForegroundRoles(context.filename) : /* @__PURE__ */ new Set();
14836
+ const checkOpaqueForegroundPairs = foregroundRoles.size > 0;
14837
+ let importsExternalRenderer = false;
14838
+ const pendingReports = /* @__PURE__ */ new Map();
14235
14839
  const report2 = (node, messageId, data) => {
14236
- pendingReports.push({ node, messageId, data });
14840
+ const key = `${node.range[0]}:${node.range[1]}:${messageId}:${JSON.stringify(data)}`;
14841
+ pendingReports.set(key, { node, messageId, data });
14237
14842
  };
14238
14843
  const reportClasses = (value, node) => {
14239
- for (const token of classTokens(value)) {
14844
+ const tokens = classTokens(value);
14845
+ for (const token of tokens) {
14240
14846
  const base = tailwindBase(token);
14241
14847
  if (RAW_PALETTE_RE.test(base)) {
14242
14848
  report2(node, "rawPalette", { class: token });
@@ -14244,6 +14850,26 @@ var prefer_semantic_colors_default = createRule({
14244
14850
  report2(node, "arbitraryColor", { class: token });
14245
14851
  }
14246
14852
  }
14853
+ if (!checkOpaqueForegroundPairs || isInsideSvg(node)) return;
14854
+ for (const token of tokens) {
14855
+ const prefix = tailwindVariantPrefix(token);
14856
+ if (prefix.split(":").includes("print")) continue;
14857
+ const base = tailwindBase(token);
14858
+ if (!OPAQUE_FOREGROUND_RE.test(base)) continue;
14859
+ const semanticBackground = tokens.find((candidate2) => {
14860
+ if (tailwindVariantPrefix(candidate2) !== prefix) return false;
14861
+ const match = SEMANTIC_BACKGROUND_RE.exec(tailwindBase(candidate2));
14862
+ return match?.[1] !== void 0 && foregroundRoles.has(match[1]);
14863
+ });
14864
+ if (semanticBackground === void 0) continue;
14865
+ const role = SEMANTIC_BACKGROUND_RE.exec(tailwindBase(semanticBackground))?.[1];
14866
+ if (role === void 0) continue;
14867
+ report2(node, "opaqueForegroundPair", {
14868
+ background: semanticBackground,
14869
+ class: token,
14870
+ replacement: `${prefix}text-${role}-foreground`
14871
+ });
14872
+ }
14247
14873
  };
14248
14874
  const checkClassNode = (node) => {
14249
14875
  if (node === null) return;
@@ -14291,8 +14917,8 @@ var prefer_semantic_colors_default = createRule({
14291
14917
  }
14292
14918
  },
14293
14919
  CallExpression(node) {
14294
- if (node.callee.type === import_utils82.AST_NODE_TYPES.Identifier && node.callee.name === "require" && node.arguments[0]?.type === import_utils82.AST_NODE_TYPES.Literal && typeof node.arguments[0].value === "string" && EMAIL_OR_PDF_MODULE_RE.test(node.arguments[0].value)) {
14295
- importsEmailOrPdfRenderer = true;
14920
+ if (node.callee.type === import_utils82.AST_NODE_TYPES.Identifier && node.callee.name === "require" && node.arguments[0]?.type === import_utils82.AST_NODE_TYPES.Literal && typeof node.arguments[0].value === "string" && EXTERNAL_RENDERER_MODULE_RE.test(node.arguments[0].value)) {
14921
+ importsExternalRenderer = true;
14296
14922
  }
14297
14923
  if (node.callee.type === import_utils82.AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {
14298
14924
  for (const arg of node.arguments) {
@@ -14327,13 +14953,13 @@ var prefer_semantic_colors_default = createRule({
14327
14953
  if (name !== null && STYLE_COLOR_PROPS.has(name)) checkColorValueNode(node.value);
14328
14954
  },
14329
14955
  ImportExpression(node) {
14330
- if (node.source.type === import_utils82.AST_NODE_TYPES.Literal && typeof node.source.value === "string" && EMAIL_OR_PDF_MODULE_RE.test(node.source.value)) {
14331
- importsEmailOrPdfRenderer = true;
14956
+ if (node.source.type === import_utils82.AST_NODE_TYPES.Literal && typeof node.source.value === "string" && EXTERNAL_RENDERER_MODULE_RE.test(node.source.value)) {
14957
+ importsExternalRenderer = true;
14332
14958
  }
14333
14959
  },
14334
14960
  "Program:exit"() {
14335
- if (importsEmailOrPdfRenderer) return;
14336
- for (const descriptor of pendingReports) context.report(descriptor);
14961
+ if (importsExternalRenderer) return;
14962
+ for (const descriptor of pendingReports.values()) context.report(descriptor);
14337
14963
  }
14338
14964
  };
14339
14965
  }
@@ -14342,11 +14968,11 @@ var prefer_semantic_colors_default = createRule({
14342
14968
  // src/rules/prefer-server-actions.ts
14343
14969
  var import_utils83 = require("@typescript-eslint/utils");
14344
14970
  var PREFER_SERVER_ACTIONS_DOCUMENTATION = {
14345
- summary: "Prefer Next.js Server Actions over /api/* mutations.",
14971
+ summary: "Prefer Next.js Server Actions over same-origin API mutations.",
14346
14972
  rationale: "Server Actions preserve typed application calls and avoid an internal JSON request-response boundary.",
14347
14973
  remediation: "Move the mutation into a Server Action and invoke that action from the React client.",
14348
14974
  category: "architecture",
14349
- limitations: ["Only statically recognizable /api/ mutations in modules with positive Next.js evidence are reported: an explicit next import, or an app/pages path with a top-level use-client directive."],
14975
+ limitations: ["Only statically recognizable /api/ mutations, including one explicitly configured literal deployment base path, in use-client modules are reported; server boundaries and route handlers are excluded."],
14350
14976
  examples: [
14351
14977
  { id: "server-action-call", title: "Call a Server Action", outcome: "no-match", files: [{ path: "app/tasks/page.tsx", source: "import { createTask } from './actions'; await createTask(input);" }], focusPath: "app/tasks/page.tsx", expectedCount: 0, public: true },
14352
14978
  { id: "api-mutation", title: "Do not mutate through an API route", outcome: "match", files: [{ path: "app/tasks/page.tsx", source: "'use client'; await fetch('/api/tasks', { method: 'POST', body });" }], focusPath: "app/tasks/page.tsx", expectedCount: 1, public: true }
@@ -14354,12 +14980,21 @@ var PREFER_SERVER_ACTIONS_DOCUMENTATION = {
14354
14980
  };
14355
14981
  var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
14356
14982
  var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
14357
- var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
14983
+ var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|(?:^|\/)app(?:\/.*)?\/route\.[jt]sx?$|(?:^|\/)middleware\.[jt]sx?$|\/pages\/api\/)/;
14358
14984
  var NON_REACT_FRAMEWORK_RE2 = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
14359
- var NEXT_MODULE_PATH_RE2 = /(?:^|[/\\])(?:app|pages)[/\\]/u;
14985
+ var BASE_PATH_RE2 = /^\/(?!$)(?!.*[?#])(?:[^/]+\/)*[^/]+$/u;
14360
14986
  function getScope(context, node) {
14361
14987
  return context.sourceCode.getScope(node);
14362
14988
  }
14989
+ function resolvesToGlobalFetch(context, identifier) {
14990
+ let scope = getScope(context, identifier);
14991
+ while (scope) {
14992
+ const variable = scope.set.get(identifier.name);
14993
+ if (variable !== void 0) return variable.defs.length === 0;
14994
+ scope = scope.upper;
14995
+ }
14996
+ return true;
14997
+ }
14363
14998
  function resolveNode(node, context) {
14364
14999
  if (!node) return null;
14365
15000
  if (node.type !== "Identifier") return node;
@@ -14379,22 +15014,29 @@ function resolveNode(node, context) {
14379
15014
  }
14380
15015
  return node;
14381
15016
  }
14382
- function isApiUrl(node, context) {
15017
+ function isApiUrl(node, context, apiPrefixes) {
14383
15018
  const resolved = resolveNode(node, context);
14384
15019
  if (!resolved) return false;
14385
15020
  if (resolved.type === "Literal" && typeof resolved.value === "string") {
14386
- return resolved.value.startsWith("/api/");
15021
+ return apiPrefixes.some(
15022
+ (prefix) => resolved.value === prefix.slice(0, -1) || resolved.value.startsWith(prefix)
15023
+ );
14387
15024
  }
14388
15025
  if (resolved.type === "TemplateLiteral") {
14389
15026
  const firstQuasi = resolved.quasis[0];
14390
15027
  const cooked = firstQuasi?.value.cooked;
14391
- return typeof cooked === "string" && cooked.startsWith("/api/");
15028
+ return typeof cooked === "string" && apiPrefixes.some(
15029
+ (prefix) => cooked === prefix.slice(0, -1) || cooked.startsWith(prefix)
15030
+ );
14392
15031
  }
14393
15032
  if (resolved.type === "BinaryExpression" && resolved.operator === "+") {
14394
- return isApiUrl(resolved.left, context);
15033
+ return isApiUrl(resolved.left, context, apiPrefixes);
14395
15034
  }
14396
15035
  return false;
14397
15036
  }
15037
+ function isValidBasePath2(basePath) {
15038
+ return BASE_PATH_RE2.test(basePath) && !basePath.split("/").some((segment) => segment === "." || segment === "..");
15039
+ }
14398
15040
  function isMutationMethod(node, context) {
14399
15041
  const resolved = resolveNode(node, context);
14400
15042
  if (!resolved) return false;
@@ -14454,16 +15096,27 @@ var prefer_server_actions_default = createRule({
14454
15096
  meta: {
14455
15097
  type: "suggestion",
14456
15098
  docs: {
14457
- description: "Prefer Next.js Server Actions over /api/* mutations."
15099
+ description: "Prefer Next.js Server Actions over same-origin API mutations."
14458
15100
  },
14459
- schema: [],
15101
+ schema: [
15102
+ {
15103
+ type: "object",
15104
+ additionalProperties: false,
15105
+ properties: {
15106
+ basePath: {
15107
+ type: "string",
15108
+ pattern: "^/(?!$)(?!.*[?#])(?!(?:.*/)?\\.\\.?(?:/|$))(?:[^/]+/)*[^/]+$"
15109
+ }
15110
+ }
15111
+ }
15112
+ ],
14460
15113
  messages: {
14461
- preferServerAction: "Mutation against /api/* \u2014 prefer a Next.js Server Action for type-safety and to avoid the JSON round-trip."
15114
+ preferServerAction: "Mutation against a same-origin API route \u2014 prefer a Next.js Server Action for type-safety and to avoid the JSON round-trip."
14462
15115
  }
14463
15116
  },
14464
- defaultOptions: [],
14465
- create(context) {
14466
- const filename = context.filename;
15117
+ defaultOptions: [{}],
15118
+ create(context, [options]) {
15119
+ const filename = context.filename.replaceAll("\\", "/");
14467
15120
  if (SKIP_FILE_REGEX.test(filename)) {
14468
15121
  return {};
14469
15122
  }
@@ -14471,22 +15124,28 @@ var prefer_server_actions_default = createRule({
14471
15124
  (node) => node.type === "ImportDeclaration" && typeof node.source.value === "string" && NON_REACT_FRAMEWORK_RE2.test(node.source.value)
14472
15125
  );
14473
15126
  const hasUseClientDirective = context.sourceCode.ast.body.some(
14474
- (node) => node.type === "ExpressionStatement" && node.expression.type === "Literal" && node.expression.value === "use client"
15127
+ (node) => node.type === "ExpressionStatement" && node.directive === "use client"
14475
15128
  );
14476
- const hasNextImport = context.sourceCode.ast.body.some(
14477
- (node) => node.type === "ImportDeclaration" && typeof node.source.value === "string" && (node.source.value === "next" || node.source.value.startsWith("next/"))
15129
+ const hasUseServerDirective = context.sourceCode.ast.body.some(
15130
+ (node) => node.type === "ExpressionStatement" && node.directive === "use server"
14478
15131
  );
14479
- const hasNextEvidence = hasNextImport || hasUseClientDirective && NEXT_MODULE_PATH_RE2.test(filename);
14480
- if (!hasNextEvidence) {
15132
+ const importsServerOnly = context.sourceCode.ast.body.some(
15133
+ (node) => node.type === "ImportDeclaration" && typeof node.source.value === "string" && (node.source.value === "server-only" || node.source.value === "next/server")
15134
+ );
15135
+ if (!hasUseClientDirective || hasUseServerDirective || importsServerOnly) {
14481
15136
  return {};
14482
15137
  }
15138
+ const apiPrefixes = ["/api/"];
15139
+ if (options?.basePath !== void 0 && isValidBasePath2(options.basePath)) {
15140
+ apiPrefixes.push(`${options.basePath}/api/`);
15141
+ }
14483
15142
  return {
14484
15143
  CallExpression(node) {
14485
15144
  if (isNonReactFramework) return;
14486
15145
  let isMutation = false;
14487
- if (node.callee.type === "Identifier" && node.callee.name === "fetch") {
15146
+ if (node.callee.type === "Identifier" && node.callee.name === "fetch" && resolvesToGlobalFetch(context, node.callee)) {
14488
15147
  const urlArg = node.arguments[0];
14489
- if (urlArg && urlArg.type !== "SpreadElement" && isApiUrl(urlArg, context)) {
15148
+ if (urlArg && urlArg.type !== "SpreadElement" && isApiUrl(urlArg, context, apiPrefixes)) {
14490
15149
  const initArg = node.arguments[1];
14491
15150
  if (initArg && initArg.type !== "SpreadElement") {
14492
15151
  const resolvedInit = resolveNode(initArg, context);
@@ -14503,7 +15162,7 @@ var prefer_server_actions_default = createRule({
14503
15162
  const hasHandlerArg = node.arguments.some(
14504
15163
  (arg) => arg.type !== "SpreadElement" && isFunctionArgument(arg, context)
14505
15164
  );
14506
- if (urlArg && urlArg.type !== "SpreadElement" && !hasHandlerArg && isApiUrl(urlArg, context)) {
15165
+ if (urlArg && urlArg.type !== "SpreadElement" && !hasHandlerArg && isApiUrl(urlArg, context, apiPrefixes)) {
14507
15166
  isMutation = true;
14508
15167
  }
14509
15168
  }
@@ -14514,7 +15173,7 @@ var prefer_server_actions_default = createRule({
14514
15173
  if (configArg && configArg.type === "ObjectExpression") {
14515
15174
  const urlNode = getPropertyNode(configArg, "url");
14516
15175
  const methodNode = getPropertyNode(configArg, "method");
14517
- if (urlNode && isApiUrl(urlNode, context) && methodNode && isMutationMethod(methodNode, context)) {
15176
+ if (urlNode && isApiUrl(urlNode, context, apiPrefixes) && methodNode && isMutationMethod(methodNode, context)) {
14518
15177
  isMutation = true;
14519
15178
  }
14520
15179
  }
@@ -18436,8 +19095,8 @@ var REQUIRE_PASCAL_CASE_ZOD_SCHEMA_NAME_DOCUMENTATION = {
18436
19095
  ]
18437
19096
  };
18438
19097
  var PASCAL_SCHEMA_NAME_RE = /^[A-Z][A-Za-z0-9]*Schema$/;
18439
- var BENCHMARK_PATH_RE = /(^|[\\/])(?:benchmarks?|bench)[\\/]/;
18440
- var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
19098
+ var BENCHMARK_PATH_RE2 = /(^|[\\/])(?:benchmarks?|bench)[\\/]/;
19099
+ var NON_SCHEMA_TERMINALS2 = /* @__PURE__ */ new Set([
18441
19100
  "parse",
18442
19101
  "parseAsync",
18443
19102
  "safeParse",
@@ -18550,7 +19209,7 @@ var SCHEMA_RETURNING_METHODS = /* @__PURE__ */ new Set([
18550
19209
  "transform"
18551
19210
  ]);
18552
19211
  var terminalMethodName = (callee) => !callee.computed && callee.property.type === import_utils100.AST_NODE_TYPES.Identifier ? callee.property.name : null;
18553
- var calleeChainRoot = (node) => {
19212
+ var calleeChainRoot2 = (node) => {
18554
19213
  let current = node;
18555
19214
  for (; ; ) {
18556
19215
  if (current.type === import_utils100.AST_NODE_TYPES.Identifier) {
@@ -18567,7 +19226,7 @@ var calleeChainRoot = (node) => {
18567
19226
  return null;
18568
19227
  }
18569
19228
  };
18570
- var chainMemberNames = (node) => {
19229
+ var chainMemberNames2 = (node) => {
18571
19230
  const names = [];
18572
19231
  let current = node;
18573
19232
  for (; ; ) {
@@ -18627,7 +19286,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
18627
19286
  if (binding !== null) zodBindings.add(binding);
18628
19287
  }
18629
19288
  function isZodChain(node) {
18630
- const root = calleeChainRoot(node);
19289
+ const root = calleeChainRoot2(node);
18631
19290
  if (root === null) return false;
18632
19291
  const binding = resolvedBinding(root);
18633
19292
  return binding !== null && zodBindings.has(binding);
@@ -18643,16 +19302,16 @@ var require_pascal_case_zod_schema_name_default = createRule({
18643
19302
  return false;
18644
19303
  }
18645
19304
  const terminal = terminalMethodName(init.callee);
18646
- if (terminal === null || NON_SCHEMA_TERMINALS.has(terminal)) return false;
18647
- const names = chainMemberNames(init.callee);
19305
+ if (terminal === null || NON_SCHEMA_TERMINALS2.has(terminal)) return false;
19306
+ const names = chainMemberNames2(init.callee);
18648
19307
  if (names.length === 0) return false;
18649
19308
  if (isZodChain(init.callee)) {
18650
19309
  return ZOD_SCHEMA_FACTORIES.has(names[0] ?? "") || ZOD_FACTORY_NAMESPACES.has(names[0] ?? "") && ZOD_SCHEMA_FACTORIES.has(names[1] ?? "");
18651
19310
  }
18652
- const root = calleeChainRoot(init.callee);
19311
+ const root = calleeChainRoot2(init.callee);
18653
19312
  return root !== null && isSchemaBinding(root) && SCHEMA_RETURNING_METHODS.has(terminal);
18654
19313
  }
18655
- if (isTestFile(context.filename) || BENCHMARK_PATH_RE.test(context.filename.replaceAll("\\", "/")) || isGeneratedFile(context.filename, context.sourceCode.text)) {
19314
+ if (isTestFile(context.filename) || BENCHMARK_PATH_RE2.test(context.filename.replaceAll("\\", "/")) || isGeneratedFile(context.filename, context.sourceCode.text)) {
18656
19315
  return {};
18657
19316
  }
18658
19317
  return {
@@ -18855,14 +19514,14 @@ var RULES = {
18855
19514
  "require-static-next-matcher": require_static_next_matcher_default,
18856
19515
  "require-zod-form-validation": require_zod_form_validation_default,
18857
19516
  "store-insert-requires-on-conflict": store_insert_requires_on_conflict_default,
18858
- stepdown: stepdown_default,
19517
+ "stepdown": stepdown_default,
18859
19518
  "source-coupled-test": source_coupled_test_default,
18860
19519
  "sole-export-matches-filename": sole_export_matches_filename_default,
18861
19520
  "require-pascal-case-zod-schema-name": require_pascal_case_zod_schema_name_default
18862
19521
  };
18863
19522
  var meta = {
18864
19523
  name: "@sarj/eslint-plugin",
18865
- version: "15.17.1"
19524
+ version: "15.17.3"
18866
19525
  };
18867
19526
  var APPLICATION_ONLY_RULES = [
18868
19527
  "no-restricted-library-load",