@sarj/eslint-plugin 15.17.1 → 15.17.2

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
@@ -12048,99 +12048,367 @@ var prefer_module_level_schema_default = createRule({
12048
12048
 
12049
12049
  // src/rules/prefer-module-level-refined-schema.ts
12050
12050
  var import_utils69 = require("@typescript-eslint/utils");
12051
+ var BENCHMARK_PATH_RE = /(^|[/\\])(?:benchmarks?|bench)[/\\]/;
12051
12052
  var FACTORIES = /* @__PURE__ */ new Set([
12052
12053
  "array",
12054
+ "base64",
12055
+ "base64url",
12053
12056
  "bigint",
12054
12057
  "boolean",
12058
+ "cidrv4",
12059
+ "cidrv6",
12060
+ "codec",
12061
+ "custom",
12055
12062
  "date",
12056
- "number",
12057
- "string"
12058
- ]);
12059
- var REFINEMENTS = /* @__PURE__ */ new Set([
12060
- "brand",
12061
- "check",
12063
+ "datetime",
12064
+ "duration",
12062
12065
  "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",
12066
+ "emoji",
12067
+ "enum",
12068
+ "file",
12069
+ "function",
12070
+ "hash",
12071
+ "hex",
12072
+ "hostname",
12073
+ "instanceof",
12074
+ "ipv4",
12075
+ "ipv6",
12076
+ "json",
12077
+ "jwt",
12078
+ "literal",
12079
+ "map",
12080
+ "nan",
12081
+ "nativeEnum",
12082
+ "never",
12083
+ "null",
12084
+ "nullable",
12085
+ "nullish",
12086
+ "number",
12087
+ "optional",
12088
+ "partialRecord",
12089
+ "preprocess",
12090
+ "promise",
12091
+ "set",
12092
+ "string",
12093
+ "stringbool",
12094
+ "symbol",
12095
+ "templateLiteral",
12096
+ "time",
12097
+ "undefined",
12077
12098
  "url",
12078
- "uuid"
12099
+ "uuid",
12100
+ "void"
12101
+ ]);
12102
+ var COMPOSITE_FACTORIES = /* @__PURE__ */ new Set([
12103
+ "discriminatedUnion",
12104
+ "intersection",
12105
+ "looseObject",
12106
+ "object",
12107
+ "record",
12108
+ "strictObject",
12109
+ "tuple",
12110
+ "union"
12111
+ ]);
12112
+ var FACTORY_NAMESPACES = /* @__PURE__ */ new Set(["coerce", "iso"]);
12113
+ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
12114
+ "decode",
12115
+ "decodeAsync",
12116
+ "encode",
12117
+ "encodeAsync",
12118
+ "flattenError",
12119
+ "formatError",
12120
+ "implement",
12121
+ "isNullable",
12122
+ "isOptional",
12123
+ "parse",
12124
+ "parseAsync",
12125
+ "prettifyError",
12126
+ "registry",
12127
+ "safeDecode",
12128
+ "safeDecodeAsync",
12129
+ "safeEncode",
12130
+ "safeEncodeAsync",
12131
+ "safeParse",
12132
+ "safeParseAsync",
12133
+ "spa",
12134
+ "toJSONSchema",
12135
+ "treeifyError"
12136
+ ]);
12137
+ var MEMO_CALLEES2 = /* @__PURE__ */ new Set([
12138
+ "lazy",
12139
+ "memo",
12140
+ "once",
12141
+ "useMemo"
12142
+ ]);
12143
+ var I18N_CALLEE_NAMES2 = /* @__PURE__ */ new Set([
12144
+ "$t",
12145
+ "defineMessage",
12146
+ "gettext",
12147
+ "msg",
12148
+ "ngettext",
12149
+ "t",
12150
+ "translate"
12151
+ ]);
12152
+ var I18N_RECEIVER_NAMES2 = /* @__PURE__ */ new Set([
12153
+ "$i18n",
12154
+ "i18n",
12155
+ "intl"
12156
+ ]);
12157
+ var FUNCTION_TYPES9 = /* @__PURE__ */ new Set([
12158
+ import_utils69.AST_NODE_TYPES.ArrowFunctionExpression,
12159
+ import_utils69.AST_NODE_TYPES.FunctionDeclaration,
12160
+ import_utils69.AST_NODE_TYPES.FunctionExpression
12079
12161
  ]);
12080
12162
  var PREFER_MODULE_LEVEL_REFINED_SCHEMA_DOCUMENTATION = {
12081
- summary: "Declare closed, refined Zod scalar and array schemas at module scope.",
12163
+ summary: "Declare closed Zod scalar, format, and wrapper schemas at module scope.",
12082
12164
  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.",
12165
+ remediation: "Move the validation schema to module scope, name it with a PascalCase Schema suffix, and call parse on the shared schema.",
12084
12166
  category: "performance",
12085
- limitations: ["Only direct Zod scalar/array chains with at least two refinement methods and no non-literal arguments are reported."],
12167
+ limitations: [
12168
+ "Composite object/record/tuple/union schemas are owned by prefer-module-level-schema.",
12169
+ "Schemas that depend on function-local or mutable state, localized text, receiver state, lazy construction, or recognized memoization are excluded.",
12170
+ "Literal string z.enum domains are owned by prefer-shared-zod-enum."
12171
+ ],
12086
12172
  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 }
12173
+ {
12174
+ id: "module-refinement",
12175
+ title: "Share the validation schema",
12176
+ outcome: "no-match",
12177
+ files: [{
12178
+ path: "src/options.ts",
12179
+ source: "import { z } from 'zod'; const BatchSizeSchema = z.number().int().min(1).max(1000); export function parse(value: unknown) { return BatchSizeSchema.parse(value); }"
12180
+ }],
12181
+ focusPath: "src/options.ts",
12182
+ expectedCount: 0,
12183
+ public: true
12184
+ },
12185
+ {
12186
+ id: "local-refinement",
12187
+ title: "Do not rebuild a closed validation chain",
12188
+ outcome: "match",
12189
+ files: [{
12190
+ path: "src/options.ts",
12191
+ source: "import { z } from 'zod'; export function parse(value: unknown) { return z.string().trim().min(1).max(128).parse(value); }"
12192
+ }],
12193
+ focusPath: "src/options.ts",
12194
+ expectedCount: 1,
12195
+ public: true
12196
+ }
12089
12197
  ]
12090
12198
  };
12091
- function enclosingFunction4(node) {
12199
+ function outermostEnclosingFunction2(node) {
12200
+ let outermost;
12092
12201
  let current = node.parent ?? void 0;
12093
12202
  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;
12203
+ if (FUNCTION_TYPES9.has(current.type)) outermost = current;
12095
12204
  current = current.parent ?? void 0;
12096
12205
  }
12097
- return void 0;
12206
+ return outermost;
12098
12207
  }
12099
12208
  function collectReferences2(scope, output) {
12100
12209
  output.push(...scope.references);
12101
12210
  for (const child of scope.childScopes) collectReferences2(child, output);
12102
12211
  }
12212
+ function subtreeSome2(root, predicate) {
12213
+ let found = false;
12214
+ const visit = (value) => {
12215
+ if (found || value === null || typeof value !== "object") return;
12216
+ if (Array.isArray(value)) {
12217
+ for (const item of value) visit(item);
12218
+ return;
12219
+ }
12220
+ const candidate2 = value;
12221
+ if (typeof candidate2.type !== "string") return;
12222
+ if (predicate(candidate2)) {
12223
+ found = true;
12224
+ return;
12225
+ }
12226
+ for (const key of Object.keys(candidate2)) {
12227
+ if (key === "parent" || key === "loc" || key === "range") continue;
12228
+ visit(candidate2[key]);
12229
+ }
12230
+ };
12231
+ visit(root);
12232
+ return found;
12233
+ }
12234
+ function readsReceiver2(node) {
12235
+ return subtreeSome2(
12236
+ node,
12237
+ (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"
12238
+ );
12239
+ }
12240
+ function buildsLocalizedText2(node) {
12241
+ return subtreeSome2(node, (inner) => {
12242
+ if (inner.type === import_utils69.AST_NODE_TYPES.TaggedTemplateExpression) return true;
12243
+ if (inner.type !== import_utils69.AST_NODE_TYPES.CallExpression) return false;
12244
+ const { callee } = inner;
12245
+ if (callee.type === import_utils69.AST_NODE_TYPES.Identifier)
12246
+ return I18N_CALLEE_NAMES2.has(callee.name);
12247
+ 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);
12248
+ });
12249
+ }
12250
+ function calleeChainRoot(node) {
12251
+ let current = node;
12252
+ for (; ; ) {
12253
+ if (current.type === import_utils69.AST_NODE_TYPES.Identifier) return current;
12254
+ if (current.type === import_utils69.AST_NODE_TYPES.MemberExpression) {
12255
+ current = current.object;
12256
+ continue;
12257
+ }
12258
+ if (current.type === import_utils69.AST_NODE_TYPES.CallExpression) {
12259
+ current = current.callee;
12260
+ continue;
12261
+ }
12262
+ return null;
12263
+ }
12264
+ }
12265
+ function chainMemberNames(node) {
12266
+ const names = [];
12267
+ let current = node;
12268
+ for (; ; ) {
12269
+ if (current.type === import_utils69.AST_NODE_TYPES.MemberExpression) {
12270
+ if (current.computed || current.property.type !== import_utils69.AST_NODE_TYPES.Identifier)
12271
+ return [];
12272
+ names.push(current.property.name);
12273
+ current = current.object;
12274
+ continue;
12275
+ }
12276
+ if (current.type === import_utils69.AST_NODE_TYPES.CallExpression) {
12277
+ current = current.callee;
12278
+ continue;
12279
+ }
12280
+ break;
12281
+ }
12282
+ names.reverse();
12283
+ return names;
12284
+ }
12285
+ function schemaExpression2(node) {
12286
+ let current = node;
12287
+ for (; ; ) {
12288
+ const parent = current.parent ?? void 0;
12289
+ 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) {
12290
+ if (NON_SCHEMA_TERMINALS.has(parent.property.name)) return current;
12291
+ current = parent.parent;
12292
+ continue;
12293
+ }
12294
+ 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) {
12295
+ current = parent;
12296
+ continue;
12297
+ }
12298
+ return current;
12299
+ }
12300
+ }
12103
12301
  var prefer_module_level_refined_schema_default = createRule({
12104
12302
  name: "prefer-module-level-refined-schema",
12105
12303
  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." } },
12304
+ meta: {
12305
+ type: "suggestion",
12306
+ docs: {
12307
+ description: "Declare closed Zod scalar, format, and wrapper schemas at module scope."
12308
+ },
12309
+ schema: [],
12310
+ messages: {
12311
+ hoistRefinedSchema: "Move this closed Zod schema to module scope, give it a PascalCase Schema name, and reuse it for parsing."
12312
+ }
12313
+ },
12107
12314
  defaultOptions: [],
12108
12315
  create(context) {
12109
- if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
12110
- const namespaces = /* @__PURE__ */ new Set();
12316
+ if (isTestFile(context.filename) || BENCHMARK_PATH_RE.test(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text))
12317
+ return {};
12318
+ const zodBindings = /* @__PURE__ */ new Set();
12319
+ function resolvedBinding(identifier) {
12320
+ return import_utils69.ASTUtils.findVariable(
12321
+ context.sourceCode.getScope(identifier),
12322
+ identifier.name
12323
+ );
12324
+ }
12325
+ function recordZodBinding(identifier) {
12326
+ const binding = resolvedBinding(identifier);
12327
+ if (binding !== null) zodBindings.add(binding);
12328
+ }
12329
+ function factoryName(node, allowed) {
12330
+ if (node.callee.type !== import_utils69.AST_NODE_TYPES.MemberExpression) return null;
12331
+ const root = calleeChainRoot(node.callee);
12332
+ if (root === null) return null;
12333
+ const binding = resolvedBinding(root);
12334
+ if (binding === null || !zodBindings.has(binding)) return null;
12335
+ const names = chainMemberNames(node.callee);
12336
+ if (names.length === 1 && allowed.has(names[0] ?? ""))
12337
+ return names[0] ?? null;
12338
+ if (names.length === 2 && FACTORY_NAMESPACES.has(names[0] ?? "") && allowed.has(names[1] ?? ""))
12339
+ return names[1] ?? null;
12340
+ return null;
12341
+ }
12342
+ function isSharedEnumDomain(node, factory) {
12343
+ if (factory !== "enum") return false;
12344
+ const [argument] = node.arguments;
12345
+ return argument?.type === import_utils69.AST_NODE_TYPES.ArrayExpression && argument.elements.length >= 2 && argument.elements.every(
12346
+ (element) => element?.type === import_utils69.AST_NODE_TYPES.Literal && typeof element.value === "string"
12347
+ );
12348
+ }
12349
+ function isNestedInOwnedFactory(node) {
12350
+ let current = node;
12351
+ while (current.parent != null) {
12352
+ current = current.parent;
12353
+ if (FUNCTION_TYPES9.has(current.type)) return false;
12354
+ if (current.type === import_utils69.AST_NODE_TYPES.CallExpression && (factoryName(current, FACTORIES) !== null || factoryName(current, COMPOSITE_FACTORIES) !== null))
12355
+ return true;
12356
+ }
12357
+ return false;
12358
+ }
12359
+ function isMemoized(node) {
12360
+ let current = node.parent ?? void 0;
12361
+ while (current !== void 0) {
12362
+ 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)))
12363
+ return true;
12364
+ current = current.parent ?? void 0;
12365
+ }
12366
+ return false;
12367
+ }
12368
+ function closesOverNothing(node, enclosing) {
12369
+ const references = [];
12370
+ collectReferences2(context.sourceCode.getScope(node), references);
12371
+ const [start, end] = node.range;
12372
+ const [functionStart, functionEnd] = enclosing.range;
12373
+ for (const reference of references) {
12374
+ const [referenceStart] = reference.identifier.range;
12375
+ if (referenceStart < start || referenceStart >= end) continue;
12376
+ const resolved = reference.resolved;
12377
+ if (resolved === null) continue;
12378
+ for (const definition of resolved.defs) {
12379
+ if (definition.type === "ImportBinding") {
12380
+ const parent = reference.identifier.parent;
12381
+ if (parent?.type === import_utils69.AST_NODE_TYPES.CallExpression && parent.callee === reference.identifier && !zodBindings.has(resolved))
12382
+ return false;
12383
+ continue;
12384
+ }
12385
+ 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")
12386
+ return false;
12387
+ const [definitionStart, definitionEnd] = definition.node.range;
12388
+ if (definitionStart >= start && definitionEnd <= end) continue;
12389
+ if (definitionStart >= functionStart && definitionEnd <= functionEnd)
12390
+ return false;
12391
+ }
12392
+ }
12393
+ return true;
12394
+ }
12111
12395
  return {
12112
12396
  ImportDeclaration(node) {
12113
12397
  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);
12398
+ for (const specifier of node.specifiers) {
12399
+ 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"))
12400
+ recordZodBinding(specifier.local);
12401
+ }
12115
12402
  },
12116
12403
  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
- }
12404
+ const factory = factoryName(node, FACTORIES);
12405
+ if (factory === null || isSharedEnumDomain(node, factory) || isNestedInOwnedFactory(node) || isMemoized(node))
12406
+ return;
12407
+ const enclosing = outermostEnclosingFunction2(node);
12408
+ if (enclosing === void 0) return;
12409
+ const expression = schemaExpression2(node);
12410
+ if (readsReceiver2(expression) || buildsLocalizedText2(expression) || !closesOverNothing(expression, enclosing))
12411
+ return;
12144
12412
  context.report({ node, messageId: "hoistRefinedSchema" });
12145
12413
  }
12146
12414
  };
@@ -12154,20 +12422,21 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
12154
12422
  rationale: "One multi-value literal expresses the same closed value domain without repeated schema wrappers.",
12155
12423
  remediation: "Replace the union with z.literal([value1, value2, ...]).",
12156
12424
  category: "maintainability",
12425
+ autofix: "safe",
12157
12426
  limitations: [
12158
- "Bare `zod` imports are analyzed only when the rule option explicitly declares `zodMajorVersion: 4`; `zod/v4` imports are self-declaring."
12427
+ "Bare zod imports are analyzed only when the rule option explicitly declares zodMajorVersion: 4; explicit zod/v4 entrypoints are self-declaring.",
12428
+ "All-string domains are left to zod/prefer-enum-over-literal-union.",
12429
+ "A finding containing comments is not autofixed because moving its trivia is ambiguous."
12159
12430
  ],
12160
12431
  examples: [
12161
12432
  {
12162
12433
  id: "multi-value",
12163
12434
  title: "Use one multi-value literal",
12164
12435
  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
- ],
12436
+ files: [{
12437
+ path: "src/schema.ts",
12438
+ source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
12439
+ }],
12171
12440
  focusPath: "src/schema.ts",
12172
12441
  expectedCount: 0,
12173
12442
  public: true
@@ -12176,80 +12445,116 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
12176
12445
  id: "literal-union",
12177
12446
  title: "Avoid repeated literal wrappers",
12178
12447
  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
- ],
12448
+ files: [{
12449
+ path: "src/schema.ts",
12450
+ source: "import { z } from 'zod'; export const Version = z.union([z.literal(1), z.literal(2), z.literal(3)]);"
12451
+ }],
12452
+ fixedFiles: [{
12453
+ path: "src/schema.ts",
12454
+ source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
12455
+ }],
12185
12456
  focusPath: "src/schema.ts",
12186
12457
  expectedCount: 1,
12187
12458
  public: true
12188
12459
  }
12189
12460
  ]
12190
12461
  };
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;
12462
+ function isStaticPrimitive(node, context) {
12463
+ if (node.type === import_utils70.AST_NODE_TYPES.Literal) {
12464
+ return node.value === null || ["bigint", "boolean", "number", "string"].includes(typeof node.value);
12465
+ }
12466
+ if (node.type === import_utils70.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0)
12467
+ return true;
12468
+ if (node.type === import_utils70.AST_NODE_TYPES.Identifier && node.name === "undefined") {
12469
+ const binding = import_utils70.ASTUtils.findVariable(
12470
+ context.sourceCode.getScope(node),
12471
+ node.name
12472
+ );
12473
+ return binding === null || binding.defs.length === 0;
12474
+ }
12475
+ 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);
12476
+ }
12477
+ function isStaticString(node) {
12478
+ 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
12479
  }
12194
12480
  var prefer_multi_value_zod_literal_default = createRule({
12195
12481
  name: "prefer-multi-value-zod-literal",
12196
12482
  documentation: PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION,
12197
12483
  meta: {
12198
12484
  type: "suggestion",
12485
+ fixable: "code",
12199
12486
  docs: {
12200
12487
  description: "Use the Zod 4 multi-value literal API instead of a union of literal schemas."
12201
12488
  },
12202
- schema: [
12203
- {
12204
- type: "object",
12205
- additionalProperties: false,
12206
- properties: {
12207
- zodMajorVersion: { type: "integer", minimum: 4, maximum: 4 }
12208
- }
12489
+ schema: [{
12490
+ type: "object",
12491
+ additionalProperties: false,
12492
+ properties: {
12493
+ zodMajorVersion: { type: "integer", minimum: 4, maximum: 4 }
12209
12494
  }
12210
- ],
12495
+ }],
12211
12496
  messages: {
12212
- useMultiValueLiteral: "Replace this literal-schema union with `{{zod}}.literal([\u2026])`."
12497
+ useMultiValueLiteral: "Replace this literal-schema union with {{zod}}.literal([\u2026])."
12213
12498
  }
12214
12499
  },
12215
12500
  defaultOptions: [{}],
12216
12501
  create(context, [options]) {
12217
12502
  if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text))
12218
12503
  return {};
12219
- const namespaces = /* @__PURE__ */ new Set();
12220
- const zod4Namespaces = /* @__PURE__ */ new Set();
12504
+ const zodBindings = /* @__PURE__ */ new Set();
12505
+ const zod4Bindings = /* @__PURE__ */ new Set();
12506
+ function resolvedBinding(identifier) {
12507
+ return import_utils70.ASTUtils.findVariable(
12508
+ context.sourceCode.getScope(identifier),
12509
+ identifier.name
12510
+ );
12511
+ }
12512
+ function directMemberCall(node, binding, method) {
12513
+ 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)
12514
+ return false;
12515
+ return resolvedBinding(node.callee.object) === binding;
12516
+ }
12221
12517
  return {
12222
12518
  ImportDeclaration(node) {
12223
12519
  if (!isZodModule(node.source.value)) return;
12520
+ const isExplicitV4 = /^zod\/v4(?:$|[-/])/.test(node.source.value);
12224
12521
  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);
12522
+ 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")) {
12523
+ const binding = resolvedBinding(specifier.local);
12524
+ if (binding === null) continue;
12525
+ zodBindings.add(binding);
12526
+ if (isExplicitV4) zod4Bindings.add(binding);
12229
12527
  }
12230
12528
  }
12231
12529
  },
12232
12530
  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)
12531
+ if (node.callee.type !== import_utils70.AST_NODE_TYPES.MemberExpression || node.callee.object.type !== import_utils70.AST_NODE_TYPES.Identifier)
12532
+ return;
12533
+ const binding = resolvedBinding(node.callee.object);
12534
+ if (binding === null || !zodBindings.has(binding) || !directMemberCall(node, binding, "union") || options?.zodMajorVersion !== 4 && !zod4Bindings.has(binding) || node.arguments.length !== 1)
12237
12535
  return;
12238
12536
  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
- }))
12537
+ if (argument?.type !== import_utils70.AST_NODE_TYPES.ArrayExpression || argument.elements.length < 2)
12248
12538
  return;
12539
+ const values = [];
12540
+ for (const element of argument.elements) {
12541
+ if (element === null || element.type !== import_utils70.AST_NODE_TYPES.CallExpression || !directMemberCall(element, binding, "literal") || element.arguments.length !== 1)
12542
+ return;
12543
+ const [value] = element.arguments;
12544
+ if (value === void 0 || !isStaticPrimitive(value, context)) return;
12545
+ values.push(value);
12546
+ }
12547
+ if (values.every(isStaticString)) return;
12548
+ const namespace = node.callee.object.name;
12549
+ const hasComments = context.sourceCode.getCommentsInside(node).length > 0;
12249
12550
  context.report({
12250
12551
  node,
12251
12552
  messageId: "useMultiValueLiteral",
12252
- data: { zod: namespace }
12553
+ data: { zod: namespace },
12554
+ fix: hasComments ? null : (fixer) => fixer.replaceText(
12555
+ node,
12556
+ `${namespace}.literal([${values.map((value) => context.sourceCode.getText(value)).join(", ")}])`
12557
+ )
12253
12558
  });
12254
12559
  }
12255
12560
  };
@@ -18436,8 +18741,8 @@ var REQUIRE_PASCAL_CASE_ZOD_SCHEMA_NAME_DOCUMENTATION = {
18436
18741
  ]
18437
18742
  };
18438
18743
  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([
18744
+ var BENCHMARK_PATH_RE2 = /(^|[\\/])(?:benchmarks?|bench)[\\/]/;
18745
+ var NON_SCHEMA_TERMINALS2 = /* @__PURE__ */ new Set([
18441
18746
  "parse",
18442
18747
  "parseAsync",
18443
18748
  "safeParse",
@@ -18550,7 +18855,7 @@ var SCHEMA_RETURNING_METHODS = /* @__PURE__ */ new Set([
18550
18855
  "transform"
18551
18856
  ]);
18552
18857
  var terminalMethodName = (callee) => !callee.computed && callee.property.type === import_utils100.AST_NODE_TYPES.Identifier ? callee.property.name : null;
18553
- var calleeChainRoot = (node) => {
18858
+ var calleeChainRoot2 = (node) => {
18554
18859
  let current = node;
18555
18860
  for (; ; ) {
18556
18861
  if (current.type === import_utils100.AST_NODE_TYPES.Identifier) {
@@ -18567,7 +18872,7 @@ var calleeChainRoot = (node) => {
18567
18872
  return null;
18568
18873
  }
18569
18874
  };
18570
- var chainMemberNames = (node) => {
18875
+ var chainMemberNames2 = (node) => {
18571
18876
  const names = [];
18572
18877
  let current = node;
18573
18878
  for (; ; ) {
@@ -18627,7 +18932,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
18627
18932
  if (binding !== null) zodBindings.add(binding);
18628
18933
  }
18629
18934
  function isZodChain(node) {
18630
- const root = calleeChainRoot(node);
18935
+ const root = calleeChainRoot2(node);
18631
18936
  if (root === null) return false;
18632
18937
  const binding = resolvedBinding(root);
18633
18938
  return binding !== null && zodBindings.has(binding);
@@ -18643,16 +18948,16 @@ var require_pascal_case_zod_schema_name_default = createRule({
18643
18948
  return false;
18644
18949
  }
18645
18950
  const terminal = terminalMethodName(init.callee);
18646
- if (terminal === null || NON_SCHEMA_TERMINALS.has(terminal)) return false;
18647
- const names = chainMemberNames(init.callee);
18951
+ if (terminal === null || NON_SCHEMA_TERMINALS2.has(terminal)) return false;
18952
+ const names = chainMemberNames2(init.callee);
18648
18953
  if (names.length === 0) return false;
18649
18954
  if (isZodChain(init.callee)) {
18650
18955
  return ZOD_SCHEMA_FACTORIES.has(names[0] ?? "") || ZOD_FACTORY_NAMESPACES.has(names[0] ?? "") && ZOD_SCHEMA_FACTORIES.has(names[1] ?? "");
18651
18956
  }
18652
- const root = calleeChainRoot(init.callee);
18957
+ const root = calleeChainRoot2(init.callee);
18653
18958
  return root !== null && isSchemaBinding(root) && SCHEMA_RETURNING_METHODS.has(terminal);
18654
18959
  }
18655
- if (isTestFile(context.filename) || BENCHMARK_PATH_RE.test(context.filename.replaceAll("\\", "/")) || isGeneratedFile(context.filename, context.sourceCode.text)) {
18960
+ if (isTestFile(context.filename) || BENCHMARK_PATH_RE2.test(context.filename.replaceAll("\\", "/")) || isGeneratedFile(context.filename, context.sourceCode.text)) {
18656
18961
  return {};
18657
18962
  }
18658
18963
  return {
@@ -18855,14 +19160,14 @@ var RULES = {
18855
19160
  "require-static-next-matcher": require_static_next_matcher_default,
18856
19161
  "require-zod-form-validation": require_zod_form_validation_default,
18857
19162
  "store-insert-requires-on-conflict": store_insert_requires_on_conflict_default,
18858
- stepdown: stepdown_default,
19163
+ "stepdown": stepdown_default,
18859
19164
  "source-coupled-test": source_coupled_test_default,
18860
19165
  "sole-export-matches-filename": sole_export_matches_filename_default,
18861
19166
  "require-pascal-case-zod-schema-name": require_pascal_case_zod_schema_name_default
18862
19167
  };
18863
19168
  var meta = {
18864
19169
  name: "@sarj/eslint-plugin",
18865
- version: "15.17.1"
19170
+ version: "15.17.2"
18866
19171
  };
18867
19172
  var APPLICATION_ONLY_RULES = [
18868
19173
  "no-restricted-library-load",