eslint-plugin-zod-utils 1.0.1 → 1.0.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/README.md +35 -4
- package/dist/index.cjs +130 -24
- package/dist/index.js +130 -24
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ Legacy eslintrc:
|
|
|
49
49
|
|
|
50
50
|
### `zod-utils/no-inline-zod-schema`
|
|
51
51
|
|
|
52
|
-
Requires Zod schemas to be created
|
|
52
|
+
Requires Zod schemas to be created during module initialization. This avoids recreating schemas inside functions, callbacks, render paths, hooks, or instance fields.
|
|
53
53
|
|
|
54
54
|
This rule was inspired by the motivation behind [`babel-plugin-zod-hoist`](https://github.com/gajus/babel-plugin-zod-hoist#motivation), which documents the cost of repeatedly initializing equivalent Zod schemas and the benefits of hoisting them.
|
|
55
55
|
|
|
@@ -70,10 +70,10 @@ function parseUser(input: unknown) {
|
|
|
70
70
|
```ts
|
|
71
71
|
import { z } from "zod";
|
|
72
72
|
|
|
73
|
-
app.post("/users", {
|
|
74
|
-
|
|
73
|
+
app.post("/users", (request) => {
|
|
74
|
+
return z.object({
|
|
75
75
|
id: z.string(),
|
|
76
|
-
})
|
|
76
|
+
}).parse(request.body);
|
|
77
77
|
});
|
|
78
78
|
```
|
|
79
79
|
|
|
@@ -91,7 +91,38 @@ function parseUser(input: unknown) {
|
|
|
91
91
|
}
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
```ts
|
|
95
|
+
import { z } from "zod";
|
|
96
|
+
|
|
97
|
+
app.post("/users", {
|
|
98
|
+
body: z.object({
|
|
99
|
+
id: z.string(),
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
94
104
|
The rule understands `import { z } from "zod"`, `import * as zod from "zod"`, aliased `z` imports, and direct named schema factories such as `import { object, string } from "zod"`.
|
|
105
|
+
It also understands default imports such as `import z from "zod"`.
|
|
106
|
+
|
|
107
|
+
When type information is available through `@typescript-eslint/parser`, the rule also reports repeated derived schema creation from imported Zod schemas:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { UserSchema } from "./schemas";
|
|
111
|
+
|
|
112
|
+
function getPublicSchema() {
|
|
113
|
+
return UserSchema.pick({ id: true });
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { BaseSchema, TenantIdSchema } from "./schemas";
|
|
119
|
+
|
|
120
|
+
function getTenantSchema() {
|
|
121
|
+
return BaseSchema.extend({ tenantId: TenantIdSchema });
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This type-aware detection does not use schema-name conventions. If type information is unavailable, imported schema roots are not inferred from names such as `UserSchema` or `UserZodSchema`. The rule also does not assume a global `z` identifier refers to Zod.
|
|
95
126
|
|
|
96
127
|
## Development
|
|
97
128
|
|
package/dist/index.cjs
CHANGED
|
@@ -88,6 +88,37 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
88
88
|
"xid"
|
|
89
89
|
]);
|
|
90
90
|
var ZOD_NAMESPACE_IMPORTS = /* @__PURE__ */ new Set(["z", "coerce", "iso"]);
|
|
91
|
+
var ZOD_EXECUTION_METHODS = /* @__PURE__ */ new Set([
|
|
92
|
+
"parse",
|
|
93
|
+
"parseAsync",
|
|
94
|
+
"safeParse",
|
|
95
|
+
"safeParseAsync"
|
|
96
|
+
]);
|
|
97
|
+
var ZOD_SCHEMA_COMBINATOR_METHODS = /* @__PURE__ */ new Set([
|
|
98
|
+
"and",
|
|
99
|
+
"array",
|
|
100
|
+
"brand",
|
|
101
|
+
"catchall",
|
|
102
|
+
"deepPartial",
|
|
103
|
+
"describe",
|
|
104
|
+
"extend",
|
|
105
|
+
"merge",
|
|
106
|
+
"nullable",
|
|
107
|
+
"nullish",
|
|
108
|
+
"omit",
|
|
109
|
+
"optional",
|
|
110
|
+
"or",
|
|
111
|
+
"partial",
|
|
112
|
+
"passthrough",
|
|
113
|
+
"pick",
|
|
114
|
+
"readonly",
|
|
115
|
+
"refine",
|
|
116
|
+
"required",
|
|
117
|
+
"strict",
|
|
118
|
+
"strip",
|
|
119
|
+
"superRefine",
|
|
120
|
+
"transform"
|
|
121
|
+
]);
|
|
91
122
|
function findVariable(scope, name) {
|
|
92
123
|
let currentScope = scope;
|
|
93
124
|
while (currentScope) {
|
|
@@ -121,8 +152,33 @@ function getRootIdentifier(node) {
|
|
|
121
152
|
return null;
|
|
122
153
|
}
|
|
123
154
|
}
|
|
155
|
+
function getRootMethodName(node) {
|
|
156
|
+
let current = node;
|
|
157
|
+
let rootMethodName = null;
|
|
158
|
+
while (true) {
|
|
159
|
+
if (current.type === "CallExpression") {
|
|
160
|
+
current = current.callee;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (current.type === "MemberExpression") {
|
|
164
|
+
if (current.object.type === "Identifier" && !current.computed && current.property.type === "Identifier") {
|
|
165
|
+
rootMethodName = current.property.name;
|
|
166
|
+
}
|
|
167
|
+
current = current.object;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
return rootMethodName;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function getCallMethodName(node) {
|
|
174
|
+
const { callee } = node;
|
|
175
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
176
|
+
return callee.property.name;
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
124
180
|
function isZodImportSpecifier(node) {
|
|
125
|
-
if (node.type !== "ImportNamespaceSpecifier" && node.type !== "ImportSpecifier") {
|
|
181
|
+
if (node.type !== "ImportDefaultSpecifier" && node.type !== "ImportNamespaceSpecifier" && node.type !== "ImportSpecifier") {
|
|
126
182
|
return false;
|
|
127
183
|
}
|
|
128
184
|
const declaration = node.parent;
|
|
@@ -132,7 +188,7 @@ function isZodImportSpecifier(node) {
|
|
|
132
188
|
if (declaration.source.value !== "zod") {
|
|
133
189
|
return false;
|
|
134
190
|
}
|
|
135
|
-
if (node.type === "ImportNamespaceSpecifier") {
|
|
191
|
+
if (node.type === "ImportDefaultSpecifier" || node.type === "ImportNamespaceSpecifier") {
|
|
136
192
|
return true;
|
|
137
193
|
}
|
|
138
194
|
if (node.importKind === "type" || declaration.importKind === "type") {
|
|
@@ -141,27 +197,38 @@ function isZodImportSpecifier(node) {
|
|
|
141
197
|
const importedName = node.imported.type === "Identifier" ? node.imported.name : node.imported.value;
|
|
142
198
|
return ZOD_NAMESPACE_IMPORTS.has(importedName) || ZOD_FACTORY_IMPORTS.has(importedName);
|
|
143
199
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return current;
|
|
200
|
+
function hasFullTypeInformation(services) {
|
|
201
|
+
return services.program !== null;
|
|
202
|
+
}
|
|
203
|
+
function isZodDeclarationFile(fileName) {
|
|
204
|
+
return /(?:^|[/\\])node_modules[/\\]zod[/\\]/u.test(fileName);
|
|
150
205
|
}
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
if (expression.parent?.type !== "VariableDeclarator") {
|
|
206
|
+
function isZodSchemaType(type, services, seen = /* @__PURE__ */ new Set()) {
|
|
207
|
+
if (!hasFullTypeInformation(services)) {
|
|
154
208
|
return false;
|
|
155
209
|
}
|
|
156
|
-
if (
|
|
210
|
+
if (seen.has(type)) {
|
|
157
211
|
return false;
|
|
158
212
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
213
|
+
seen.add(type);
|
|
214
|
+
const symbols = [type.getSymbol(), type.aliasSymbol];
|
|
215
|
+
if (symbols.some(
|
|
216
|
+
(symbol) => symbol?.getDeclarations()?.some(
|
|
217
|
+
(declaration) => isZodDeclarationFile(declaration.getSourceFile().fileName)
|
|
218
|
+
)
|
|
219
|
+
)) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
if (type.isUnionOrIntersection()) {
|
|
223
|
+
return type.types.some((subType) => isZodSchemaType(subType, services, seen));
|
|
162
224
|
}
|
|
163
|
-
|
|
164
|
-
|
|
225
|
+
if (type.getBaseTypes()?.some((baseType) => isZodSchemaType(baseType, services, seen))) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
const parseSymbol = type.getProperty("parse");
|
|
229
|
+
return parseSymbol?.getDeclarations()?.some(
|
|
230
|
+
(declaration) => isZodDeclarationFile(declaration.getSourceFile().fileName)
|
|
231
|
+
) ?? false;
|
|
165
232
|
}
|
|
166
233
|
var noInlineZodSchema = import_utils.ESLintUtils.RuleCreator(
|
|
167
234
|
(ruleName2) => `https://www.npmjs.com/package/eslint-plugin-zod-utils#${ruleName2}`
|
|
@@ -173,14 +240,22 @@ var noInlineZodSchema = import_utils.ESLintUtils.RuleCreator(
|
|
|
173
240
|
description: "Disallow creating Zod schemas outside module scope."
|
|
174
241
|
},
|
|
175
242
|
messages: {
|
|
176
|
-
inlineSchema: "Create Zod schemas
|
|
243
|
+
inlineSchema: "Create Zod schemas during module initialization instead of inside functions, callbacks, or other repeated execution paths."
|
|
177
244
|
},
|
|
178
245
|
schema: []
|
|
179
246
|
},
|
|
180
247
|
defaultOptions: [],
|
|
181
248
|
create(context) {
|
|
182
249
|
const sourceCode = context.sourceCode;
|
|
250
|
+
const parserServices = import_utils.ESLintUtils.getParserServices(context, true);
|
|
251
|
+
function isZodExecutionCall(node) {
|
|
252
|
+
const methodName = getCallMethodName(node);
|
|
253
|
+
return methodName !== null && ZOD_EXECUTION_METHODS.has(methodName);
|
|
254
|
+
}
|
|
183
255
|
function isZodSchemaCall(node) {
|
|
256
|
+
if (isZodExecutionCall(node)) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
184
259
|
const root = getRootIdentifier(node.callee);
|
|
185
260
|
if (!root) {
|
|
186
261
|
return false;
|
|
@@ -189,10 +264,41 @@ var noInlineZodSchema = import_utils.ESLintUtils.RuleCreator(
|
|
|
189
264
|
const variable = findVariable(scope, root.name);
|
|
190
265
|
return variable?.defs.some((definition) => isZodImportSpecifier(definition.node)) ?? false;
|
|
191
266
|
}
|
|
192
|
-
function
|
|
267
|
+
function isTypedZodSchemaCombinatorCall(node) {
|
|
268
|
+
const rootMethodName = getRootMethodName(node);
|
|
269
|
+
if (rootMethodName === null || !ZOD_SCHEMA_COMBINATOR_METHODS.has(rootMethodName)) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
const root = getRootIdentifier(node.callee);
|
|
273
|
+
if (!root) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
if (!hasFullTypeInformation(parserServices)) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
const type = parserServices.getTypeAtLocation(root);
|
|
280
|
+
return isZodSchemaType(type, parserServices);
|
|
281
|
+
}
|
|
282
|
+
function isSchemaCreationCall(node) {
|
|
283
|
+
return isZodSchemaCall(node) || isTypedZodSchemaCombinatorCall(node);
|
|
284
|
+
}
|
|
285
|
+
function hasSchemaCreationCallAncestor(node) {
|
|
193
286
|
let current = node.parent;
|
|
194
287
|
while (current) {
|
|
195
|
-
if (current.type === "CallExpression" &&
|
|
288
|
+
if (current.type === "CallExpression" && isSchemaCreationCall(current)) {
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
current = current.parent;
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
function isInsideRepeatedExecutionPath(node) {
|
|
296
|
+
let current = node.parent;
|
|
297
|
+
while (current) {
|
|
298
|
+
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
if (current.type === "PropertyDefinition" && !current.static) {
|
|
196
302
|
return true;
|
|
197
303
|
}
|
|
198
304
|
current = current.parent;
|
|
@@ -201,13 +307,13 @@ var noInlineZodSchema = import_utils.ESLintUtils.RuleCreator(
|
|
|
201
307
|
}
|
|
202
308
|
return {
|
|
203
309
|
CallExpression(node) {
|
|
204
|
-
if (!
|
|
310
|
+
if (!isSchemaCreationCall(node)) {
|
|
205
311
|
return;
|
|
206
312
|
}
|
|
207
|
-
if (
|
|
313
|
+
if (hasSchemaCreationCallAncestor(node)) {
|
|
208
314
|
return;
|
|
209
315
|
}
|
|
210
|
-
if (
|
|
316
|
+
if (!isInsideRepeatedExecutionPath(node)) {
|
|
211
317
|
return;
|
|
212
318
|
}
|
|
213
319
|
context.report({
|
|
@@ -227,7 +333,7 @@ var rules = {
|
|
|
227
333
|
var plugin = {
|
|
228
334
|
meta: {
|
|
229
335
|
name: "eslint-plugin-zod-utils",
|
|
230
|
-
version: "
|
|
336
|
+
version: "1.0.3"
|
|
231
337
|
},
|
|
232
338
|
rules,
|
|
233
339
|
configs: {}
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,37 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
60
60
|
"xid"
|
|
61
61
|
]);
|
|
62
62
|
var ZOD_NAMESPACE_IMPORTS = /* @__PURE__ */ new Set(["z", "coerce", "iso"]);
|
|
63
|
+
var ZOD_EXECUTION_METHODS = /* @__PURE__ */ new Set([
|
|
64
|
+
"parse",
|
|
65
|
+
"parseAsync",
|
|
66
|
+
"safeParse",
|
|
67
|
+
"safeParseAsync"
|
|
68
|
+
]);
|
|
69
|
+
var ZOD_SCHEMA_COMBINATOR_METHODS = /* @__PURE__ */ new Set([
|
|
70
|
+
"and",
|
|
71
|
+
"array",
|
|
72
|
+
"brand",
|
|
73
|
+
"catchall",
|
|
74
|
+
"deepPartial",
|
|
75
|
+
"describe",
|
|
76
|
+
"extend",
|
|
77
|
+
"merge",
|
|
78
|
+
"nullable",
|
|
79
|
+
"nullish",
|
|
80
|
+
"omit",
|
|
81
|
+
"optional",
|
|
82
|
+
"or",
|
|
83
|
+
"partial",
|
|
84
|
+
"passthrough",
|
|
85
|
+
"pick",
|
|
86
|
+
"readonly",
|
|
87
|
+
"refine",
|
|
88
|
+
"required",
|
|
89
|
+
"strict",
|
|
90
|
+
"strip",
|
|
91
|
+
"superRefine",
|
|
92
|
+
"transform"
|
|
93
|
+
]);
|
|
63
94
|
function findVariable(scope, name) {
|
|
64
95
|
let currentScope = scope;
|
|
65
96
|
while (currentScope) {
|
|
@@ -93,8 +124,33 @@ function getRootIdentifier(node) {
|
|
|
93
124
|
return null;
|
|
94
125
|
}
|
|
95
126
|
}
|
|
127
|
+
function getRootMethodName(node) {
|
|
128
|
+
let current = node;
|
|
129
|
+
let rootMethodName = null;
|
|
130
|
+
while (true) {
|
|
131
|
+
if (current.type === "CallExpression") {
|
|
132
|
+
current = current.callee;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (current.type === "MemberExpression") {
|
|
136
|
+
if (current.object.type === "Identifier" && !current.computed && current.property.type === "Identifier") {
|
|
137
|
+
rootMethodName = current.property.name;
|
|
138
|
+
}
|
|
139
|
+
current = current.object;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
return rootMethodName;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function getCallMethodName(node) {
|
|
146
|
+
const { callee } = node;
|
|
147
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
148
|
+
return callee.property.name;
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
96
152
|
function isZodImportSpecifier(node) {
|
|
97
|
-
if (node.type !== "ImportNamespaceSpecifier" && node.type !== "ImportSpecifier") {
|
|
153
|
+
if (node.type !== "ImportDefaultSpecifier" && node.type !== "ImportNamespaceSpecifier" && node.type !== "ImportSpecifier") {
|
|
98
154
|
return false;
|
|
99
155
|
}
|
|
100
156
|
const declaration = node.parent;
|
|
@@ -104,7 +160,7 @@ function isZodImportSpecifier(node) {
|
|
|
104
160
|
if (declaration.source.value !== "zod") {
|
|
105
161
|
return false;
|
|
106
162
|
}
|
|
107
|
-
if (node.type === "ImportNamespaceSpecifier") {
|
|
163
|
+
if (node.type === "ImportDefaultSpecifier" || node.type === "ImportNamespaceSpecifier") {
|
|
108
164
|
return true;
|
|
109
165
|
}
|
|
110
166
|
if (node.importKind === "type" || declaration.importKind === "type") {
|
|
@@ -113,27 +169,38 @@ function isZodImportSpecifier(node) {
|
|
|
113
169
|
const importedName = node.imported.type === "Identifier" ? node.imported.name : node.imported.value;
|
|
114
170
|
return ZOD_NAMESPACE_IMPORTS.has(importedName) || ZOD_FACTORY_IMPORTS.has(importedName);
|
|
115
171
|
}
|
|
116
|
-
function
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return current;
|
|
172
|
+
function hasFullTypeInformation(services) {
|
|
173
|
+
return services.program !== null;
|
|
174
|
+
}
|
|
175
|
+
function isZodDeclarationFile(fileName) {
|
|
176
|
+
return /(?:^|[/\\])node_modules[/\\]zod[/\\]/u.test(fileName);
|
|
122
177
|
}
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
if (expression.parent?.type !== "VariableDeclarator") {
|
|
178
|
+
function isZodSchemaType(type, services, seen = /* @__PURE__ */ new Set()) {
|
|
179
|
+
if (!hasFullTypeInformation(services)) {
|
|
126
180
|
return false;
|
|
127
181
|
}
|
|
128
|
-
if (
|
|
182
|
+
if (seen.has(type)) {
|
|
129
183
|
return false;
|
|
130
184
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
185
|
+
seen.add(type);
|
|
186
|
+
const symbols = [type.getSymbol(), type.aliasSymbol];
|
|
187
|
+
if (symbols.some(
|
|
188
|
+
(symbol) => symbol?.getDeclarations()?.some(
|
|
189
|
+
(declaration) => isZodDeclarationFile(declaration.getSourceFile().fileName)
|
|
190
|
+
)
|
|
191
|
+
)) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (type.isUnionOrIntersection()) {
|
|
195
|
+
return type.types.some((subType) => isZodSchemaType(subType, services, seen));
|
|
134
196
|
}
|
|
135
|
-
|
|
136
|
-
|
|
197
|
+
if (type.getBaseTypes()?.some((baseType) => isZodSchemaType(baseType, services, seen))) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
const parseSymbol = type.getProperty("parse");
|
|
201
|
+
return parseSymbol?.getDeclarations()?.some(
|
|
202
|
+
(declaration) => isZodDeclarationFile(declaration.getSourceFile().fileName)
|
|
203
|
+
) ?? false;
|
|
137
204
|
}
|
|
138
205
|
var noInlineZodSchema = ESLintUtils.RuleCreator(
|
|
139
206
|
(ruleName2) => `https://www.npmjs.com/package/eslint-plugin-zod-utils#${ruleName2}`
|
|
@@ -145,14 +212,22 @@ var noInlineZodSchema = ESLintUtils.RuleCreator(
|
|
|
145
212
|
description: "Disallow creating Zod schemas outside module scope."
|
|
146
213
|
},
|
|
147
214
|
messages: {
|
|
148
|
-
inlineSchema: "Create Zod schemas
|
|
215
|
+
inlineSchema: "Create Zod schemas during module initialization instead of inside functions, callbacks, or other repeated execution paths."
|
|
149
216
|
},
|
|
150
217
|
schema: []
|
|
151
218
|
},
|
|
152
219
|
defaultOptions: [],
|
|
153
220
|
create(context) {
|
|
154
221
|
const sourceCode = context.sourceCode;
|
|
222
|
+
const parserServices = ESLintUtils.getParserServices(context, true);
|
|
223
|
+
function isZodExecutionCall(node) {
|
|
224
|
+
const methodName = getCallMethodName(node);
|
|
225
|
+
return methodName !== null && ZOD_EXECUTION_METHODS.has(methodName);
|
|
226
|
+
}
|
|
155
227
|
function isZodSchemaCall(node) {
|
|
228
|
+
if (isZodExecutionCall(node)) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
156
231
|
const root = getRootIdentifier(node.callee);
|
|
157
232
|
if (!root) {
|
|
158
233
|
return false;
|
|
@@ -161,10 +236,41 @@ var noInlineZodSchema = ESLintUtils.RuleCreator(
|
|
|
161
236
|
const variable = findVariable(scope, root.name);
|
|
162
237
|
return variable?.defs.some((definition) => isZodImportSpecifier(definition.node)) ?? false;
|
|
163
238
|
}
|
|
164
|
-
function
|
|
239
|
+
function isTypedZodSchemaCombinatorCall(node) {
|
|
240
|
+
const rootMethodName = getRootMethodName(node);
|
|
241
|
+
if (rootMethodName === null || !ZOD_SCHEMA_COMBINATOR_METHODS.has(rootMethodName)) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
const root = getRootIdentifier(node.callee);
|
|
245
|
+
if (!root) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
if (!hasFullTypeInformation(parserServices)) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
const type = parserServices.getTypeAtLocation(root);
|
|
252
|
+
return isZodSchemaType(type, parserServices);
|
|
253
|
+
}
|
|
254
|
+
function isSchemaCreationCall(node) {
|
|
255
|
+
return isZodSchemaCall(node) || isTypedZodSchemaCombinatorCall(node);
|
|
256
|
+
}
|
|
257
|
+
function hasSchemaCreationCallAncestor(node) {
|
|
165
258
|
let current = node.parent;
|
|
166
259
|
while (current) {
|
|
167
|
-
if (current.type === "CallExpression" &&
|
|
260
|
+
if (current.type === "CallExpression" && isSchemaCreationCall(current)) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
current = current.parent;
|
|
264
|
+
}
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
function isInsideRepeatedExecutionPath(node) {
|
|
268
|
+
let current = node.parent;
|
|
269
|
+
while (current) {
|
|
270
|
+
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
if (current.type === "PropertyDefinition" && !current.static) {
|
|
168
274
|
return true;
|
|
169
275
|
}
|
|
170
276
|
current = current.parent;
|
|
@@ -173,13 +279,13 @@ var noInlineZodSchema = ESLintUtils.RuleCreator(
|
|
|
173
279
|
}
|
|
174
280
|
return {
|
|
175
281
|
CallExpression(node) {
|
|
176
|
-
if (!
|
|
282
|
+
if (!isSchemaCreationCall(node)) {
|
|
177
283
|
return;
|
|
178
284
|
}
|
|
179
|
-
if (
|
|
285
|
+
if (hasSchemaCreationCallAncestor(node)) {
|
|
180
286
|
return;
|
|
181
287
|
}
|
|
182
|
-
if (
|
|
288
|
+
if (!isInsideRepeatedExecutionPath(node)) {
|
|
183
289
|
return;
|
|
184
290
|
}
|
|
185
291
|
context.report({
|
|
@@ -199,7 +305,7 @@ var rules = {
|
|
|
199
305
|
var plugin = {
|
|
200
306
|
meta: {
|
|
201
307
|
name: "eslint-plugin-zod-utils",
|
|
202
|
-
version: "
|
|
308
|
+
version: "1.0.3"
|
|
203
309
|
},
|
|
204
310
|
rules,
|
|
205
311
|
configs: {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-zod-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "ESLint utilities for safer Zod schema usage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"tsup": "^8.5.1",
|
|
47
47
|
"typescript": "^6.0.3",
|
|
48
48
|
"typescript-eslint": "^8.60.1",
|
|
49
|
-
"vitest": "^4.1.8"
|
|
49
|
+
"vitest": "^4.1.8",
|
|
50
|
+
"zod": "^4.4.3"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
53
|
"@typescript-eslint/utils": "^8.60.1"
|