eslint-plugin-zod-utils 1.0.2 → 1.0.4
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 +21 -0
- package/dist/index.cjs +195 -15
- package/dist/index.js +195 -15
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -102,6 +102,27 @@ app.post("/users", {
|
|
|
102
102
|
```
|
|
103
103
|
|
|
104
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.
|
|
105
126
|
|
|
106
127
|
## Development
|
|
107
128
|
|
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,7 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
48
48
|
"enum",
|
|
49
49
|
"file",
|
|
50
50
|
"function",
|
|
51
|
+
"fromJSONSchema",
|
|
51
52
|
"instanceof",
|
|
52
53
|
"intersection",
|
|
53
54
|
"ipv4",
|
|
@@ -56,6 +57,7 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
56
57
|
"ksuid",
|
|
57
58
|
"lazy",
|
|
58
59
|
"literal",
|
|
60
|
+
"looseObject",
|
|
59
61
|
"map",
|
|
60
62
|
"nan",
|
|
61
63
|
"nanoid",
|
|
@@ -72,6 +74,7 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
72
74
|
"set",
|
|
73
75
|
"string",
|
|
74
76
|
"stringbool",
|
|
77
|
+
"strictObject",
|
|
75
78
|
"symbol",
|
|
76
79
|
"templateLiteral",
|
|
77
80
|
"tuple",
|
|
@@ -88,6 +91,38 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
88
91
|
"xid"
|
|
89
92
|
]);
|
|
90
93
|
var ZOD_NAMESPACE_IMPORTS = /* @__PURE__ */ new Set(["z", "coerce", "iso"]);
|
|
94
|
+
var ZOD_ISO_FACTORY_IMPORTS = /* @__PURE__ */ new Set(["date", "datetime", "duration", "time"]);
|
|
95
|
+
var ZOD_EXECUTION_METHODS = /* @__PURE__ */ new Set([
|
|
96
|
+
"parse",
|
|
97
|
+
"parseAsync",
|
|
98
|
+
"safeParse",
|
|
99
|
+
"safeParseAsync"
|
|
100
|
+
]);
|
|
101
|
+
var ZOD_SCHEMA_COMBINATOR_METHODS = /* @__PURE__ */ new Set([
|
|
102
|
+
"and",
|
|
103
|
+
"array",
|
|
104
|
+
"brand",
|
|
105
|
+
"catchall",
|
|
106
|
+
"deepPartial",
|
|
107
|
+
"describe",
|
|
108
|
+
"extend",
|
|
109
|
+
"merge",
|
|
110
|
+
"nullable",
|
|
111
|
+
"nullish",
|
|
112
|
+
"omit",
|
|
113
|
+
"optional",
|
|
114
|
+
"or",
|
|
115
|
+
"partial",
|
|
116
|
+
"passthrough",
|
|
117
|
+
"pick",
|
|
118
|
+
"readonly",
|
|
119
|
+
"refine",
|
|
120
|
+
"required",
|
|
121
|
+
"strict",
|
|
122
|
+
"strip",
|
|
123
|
+
"superRefine",
|
|
124
|
+
"transform"
|
|
125
|
+
]);
|
|
91
126
|
function findVariable(scope, name) {
|
|
92
127
|
let currentScope = scope;
|
|
93
128
|
while (currentScope) {
|
|
@@ -121,25 +156,131 @@ function getRootIdentifier(node) {
|
|
|
121
156
|
return null;
|
|
122
157
|
}
|
|
123
158
|
}
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
|
|
159
|
+
function getRootMethodName(node) {
|
|
160
|
+
let current = node;
|
|
161
|
+
let rootMethodName = null;
|
|
162
|
+
while (true) {
|
|
163
|
+
if (current.type === "CallExpression") {
|
|
164
|
+
current = current.callee;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (current.type === "MemberExpression") {
|
|
168
|
+
if (current.object.type === "Identifier" && !current.computed && current.property.type === "Identifier") {
|
|
169
|
+
rootMethodName = current.property.name;
|
|
170
|
+
}
|
|
171
|
+
current = current.object;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
return rootMethodName;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function getMemberPathFromRoot(node) {
|
|
178
|
+
const memberPath = [];
|
|
179
|
+
let current = node;
|
|
180
|
+
while (true) {
|
|
181
|
+
if (current.type === "CallExpression") {
|
|
182
|
+
current = current.callee;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (current.type === "ChainExpression") {
|
|
186
|
+
current = current.expression;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (current.type === "TSAsExpression" || current.type === "TSNonNullExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion") {
|
|
190
|
+
current = current.expression;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (current.type === "MemberExpression") {
|
|
194
|
+
if (current.computed || current.property.type !== "Identifier") {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
memberPath.unshift(current.property.name);
|
|
198
|
+
current = current.object;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
return memberPath;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function getCallMethodName(node) {
|
|
205
|
+
const { callee } = node;
|
|
206
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
207
|
+
return callee.property.name;
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
function getZodImportKind(node) {
|
|
212
|
+
if (node.type !== "ImportDefaultSpecifier" && node.type !== "ImportNamespaceSpecifier" && node.type !== "ImportSpecifier") {
|
|
213
|
+
return null;
|
|
127
214
|
}
|
|
128
215
|
const declaration = node.parent;
|
|
129
216
|
if (declaration?.type !== "ImportDeclaration") {
|
|
130
|
-
return
|
|
217
|
+
return null;
|
|
131
218
|
}
|
|
132
219
|
if (declaration.source.value !== "zod") {
|
|
133
|
-
return
|
|
220
|
+
return null;
|
|
134
221
|
}
|
|
135
|
-
if (node.type === "ImportNamespaceSpecifier") {
|
|
136
|
-
return
|
|
222
|
+
if (node.type === "ImportDefaultSpecifier" || node.type === "ImportNamespaceSpecifier") {
|
|
223
|
+
return { importedName: "z", type: "namespace" };
|
|
137
224
|
}
|
|
138
225
|
if (node.importKind === "type" || declaration.importKind === "type") {
|
|
139
|
-
return
|
|
226
|
+
return null;
|
|
140
227
|
}
|
|
141
228
|
const importedName = node.imported.type === "Identifier" ? node.imported.name : node.imported.value;
|
|
142
|
-
|
|
229
|
+
if (ZOD_NAMESPACE_IMPORTS.has(importedName)) {
|
|
230
|
+
return { importedName, type: "namespace" };
|
|
231
|
+
}
|
|
232
|
+
if (ZOD_FACTORY_IMPORTS.has(importedName)) {
|
|
233
|
+
return { type: "factory" };
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
function isZodSchemaNamespaceCall(importName, memberPath) {
|
|
238
|
+
if (importName === "coerce") {
|
|
239
|
+
return memberPath[0] !== void 0 && ZOD_FACTORY_IMPORTS.has(memberPath[0]);
|
|
240
|
+
}
|
|
241
|
+
if (importName === "iso") {
|
|
242
|
+
return memberPath[0] !== void 0 && ZOD_ISO_FACTORY_IMPORTS.has(memberPath[0]);
|
|
243
|
+
}
|
|
244
|
+
if (memberPath[0] === "coerce") {
|
|
245
|
+
return memberPath[1] !== void 0 && ZOD_FACTORY_IMPORTS.has(memberPath[1]);
|
|
246
|
+
}
|
|
247
|
+
if (memberPath[0] === "iso") {
|
|
248
|
+
return memberPath[1] !== void 0 && ZOD_ISO_FACTORY_IMPORTS.has(memberPath[1]);
|
|
249
|
+
}
|
|
250
|
+
return memberPath[0] !== void 0 && ZOD_FACTORY_IMPORTS.has(memberPath[0]);
|
|
251
|
+
}
|
|
252
|
+
function hasFullTypeInformation(services) {
|
|
253
|
+
return services.program !== null;
|
|
254
|
+
}
|
|
255
|
+
function isZodDeclarationFile(fileName) {
|
|
256
|
+
return /(?:^|[/\\])node_modules[/\\]zod[/\\]/u.test(fileName);
|
|
257
|
+
}
|
|
258
|
+
function isZodSchemaType(type, services, seen = /* @__PURE__ */ new Set()) {
|
|
259
|
+
if (!hasFullTypeInformation(services)) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
if (seen.has(type)) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
seen.add(type);
|
|
266
|
+
const symbols = [type.getSymbol(), type.aliasSymbol];
|
|
267
|
+
if (symbols.some(
|
|
268
|
+
(symbol) => symbol?.getDeclarations()?.some(
|
|
269
|
+
(declaration) => isZodDeclarationFile(declaration.getSourceFile().fileName)
|
|
270
|
+
)
|
|
271
|
+
)) {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
if (type.isUnionOrIntersection()) {
|
|
275
|
+
return type.types.some((subType) => isZodSchemaType(subType, services, seen));
|
|
276
|
+
}
|
|
277
|
+
if (type.getBaseTypes()?.some((baseType) => isZodSchemaType(baseType, services, seen))) {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
const parseSymbol = type.getProperty("parse");
|
|
281
|
+
return parseSymbol?.getDeclarations()?.some(
|
|
282
|
+
(declaration) => isZodDeclarationFile(declaration.getSourceFile().fileName)
|
|
283
|
+
) ?? false;
|
|
143
284
|
}
|
|
144
285
|
var noInlineZodSchema = import_utils.ESLintUtils.RuleCreator(
|
|
145
286
|
(ruleName2) => `https://www.npmjs.com/package/eslint-plugin-zod-utils#${ruleName2}`
|
|
@@ -158,19 +299,58 @@ var noInlineZodSchema = import_utils.ESLintUtils.RuleCreator(
|
|
|
158
299
|
defaultOptions: [],
|
|
159
300
|
create(context) {
|
|
160
301
|
const sourceCode = context.sourceCode;
|
|
302
|
+
const parserServices = import_utils.ESLintUtils.getParserServices(context, true);
|
|
303
|
+
function isZodExecutionCall(node) {
|
|
304
|
+
const methodName = getCallMethodName(node);
|
|
305
|
+
return methodName !== null && ZOD_EXECUTION_METHODS.has(methodName);
|
|
306
|
+
}
|
|
161
307
|
function isZodSchemaCall(node) {
|
|
308
|
+
if (isZodExecutionCall(node)) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
162
311
|
const root = getRootIdentifier(node.callee);
|
|
163
312
|
if (!root) {
|
|
164
313
|
return false;
|
|
165
314
|
}
|
|
166
315
|
const scope = sourceCode.getScope(root);
|
|
167
316
|
const variable = findVariable(scope, root.name);
|
|
168
|
-
return variable?.defs.some((definition) =>
|
|
317
|
+
return variable?.defs.some((definition) => {
|
|
318
|
+
const importKind = getZodImportKind(definition.node);
|
|
319
|
+
if (importKind?.type === "factory") {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
if (importKind?.type !== "namespace") {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
const memberPath = getMemberPathFromRoot(node.callee);
|
|
326
|
+
if (memberPath === null || memberPath.length === 0) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
return isZodSchemaNamespaceCall(importKind.importedName, memberPath);
|
|
330
|
+
}) ?? false;
|
|
331
|
+
}
|
|
332
|
+
function isTypedZodSchemaCombinatorCall(node) {
|
|
333
|
+
const rootMethodName = getRootMethodName(node);
|
|
334
|
+
if (rootMethodName === null || !ZOD_SCHEMA_COMBINATOR_METHODS.has(rootMethodName)) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
const root = getRootIdentifier(node.callee);
|
|
338
|
+
if (!root) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
if (!hasFullTypeInformation(parserServices)) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
const type = parserServices.getTypeAtLocation(root);
|
|
345
|
+
return isZodSchemaType(type, parserServices);
|
|
346
|
+
}
|
|
347
|
+
function isSchemaCreationCall(node) {
|
|
348
|
+
return isZodSchemaCall(node) || isTypedZodSchemaCombinatorCall(node);
|
|
169
349
|
}
|
|
170
|
-
function
|
|
350
|
+
function hasSchemaCreationCallAncestor(node) {
|
|
171
351
|
let current = node.parent;
|
|
172
352
|
while (current) {
|
|
173
|
-
if (current.type === "CallExpression" &&
|
|
353
|
+
if (current.type === "CallExpression" && isSchemaCreationCall(current)) {
|
|
174
354
|
return true;
|
|
175
355
|
}
|
|
176
356
|
current = current.parent;
|
|
@@ -192,10 +372,10 @@ var noInlineZodSchema = import_utils.ESLintUtils.RuleCreator(
|
|
|
192
372
|
}
|
|
193
373
|
return {
|
|
194
374
|
CallExpression(node) {
|
|
195
|
-
if (!
|
|
375
|
+
if (!isSchemaCreationCall(node)) {
|
|
196
376
|
return;
|
|
197
377
|
}
|
|
198
|
-
if (
|
|
378
|
+
if (hasSchemaCreationCallAncestor(node)) {
|
|
199
379
|
return;
|
|
200
380
|
}
|
|
201
381
|
if (!isInsideRepeatedExecutionPath(node)) {
|
|
@@ -218,7 +398,7 @@ var rules = {
|
|
|
218
398
|
var plugin = {
|
|
219
399
|
meta: {
|
|
220
400
|
name: "eslint-plugin-zod-utils",
|
|
221
|
-
version: "
|
|
401
|
+
version: "1.0.4"
|
|
222
402
|
},
|
|
223
403
|
rules,
|
|
224
404
|
configs: {}
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
20
20
|
"enum",
|
|
21
21
|
"file",
|
|
22
22
|
"function",
|
|
23
|
+
"fromJSONSchema",
|
|
23
24
|
"instanceof",
|
|
24
25
|
"intersection",
|
|
25
26
|
"ipv4",
|
|
@@ -28,6 +29,7 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
28
29
|
"ksuid",
|
|
29
30
|
"lazy",
|
|
30
31
|
"literal",
|
|
32
|
+
"looseObject",
|
|
31
33
|
"map",
|
|
32
34
|
"nan",
|
|
33
35
|
"nanoid",
|
|
@@ -44,6 +46,7 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
44
46
|
"set",
|
|
45
47
|
"string",
|
|
46
48
|
"stringbool",
|
|
49
|
+
"strictObject",
|
|
47
50
|
"symbol",
|
|
48
51
|
"templateLiteral",
|
|
49
52
|
"tuple",
|
|
@@ -60,6 +63,38 @@ var ZOD_FACTORY_IMPORTS = /* @__PURE__ */ new Set([
|
|
|
60
63
|
"xid"
|
|
61
64
|
]);
|
|
62
65
|
var ZOD_NAMESPACE_IMPORTS = /* @__PURE__ */ new Set(["z", "coerce", "iso"]);
|
|
66
|
+
var ZOD_ISO_FACTORY_IMPORTS = /* @__PURE__ */ new Set(["date", "datetime", "duration", "time"]);
|
|
67
|
+
var ZOD_EXECUTION_METHODS = /* @__PURE__ */ new Set([
|
|
68
|
+
"parse",
|
|
69
|
+
"parseAsync",
|
|
70
|
+
"safeParse",
|
|
71
|
+
"safeParseAsync"
|
|
72
|
+
]);
|
|
73
|
+
var ZOD_SCHEMA_COMBINATOR_METHODS = /* @__PURE__ */ new Set([
|
|
74
|
+
"and",
|
|
75
|
+
"array",
|
|
76
|
+
"brand",
|
|
77
|
+
"catchall",
|
|
78
|
+
"deepPartial",
|
|
79
|
+
"describe",
|
|
80
|
+
"extend",
|
|
81
|
+
"merge",
|
|
82
|
+
"nullable",
|
|
83
|
+
"nullish",
|
|
84
|
+
"omit",
|
|
85
|
+
"optional",
|
|
86
|
+
"or",
|
|
87
|
+
"partial",
|
|
88
|
+
"passthrough",
|
|
89
|
+
"pick",
|
|
90
|
+
"readonly",
|
|
91
|
+
"refine",
|
|
92
|
+
"required",
|
|
93
|
+
"strict",
|
|
94
|
+
"strip",
|
|
95
|
+
"superRefine",
|
|
96
|
+
"transform"
|
|
97
|
+
]);
|
|
63
98
|
function findVariable(scope, name) {
|
|
64
99
|
let currentScope = scope;
|
|
65
100
|
while (currentScope) {
|
|
@@ -93,25 +128,131 @@ function getRootIdentifier(node) {
|
|
|
93
128
|
return null;
|
|
94
129
|
}
|
|
95
130
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
131
|
+
function getRootMethodName(node) {
|
|
132
|
+
let current = node;
|
|
133
|
+
let rootMethodName = null;
|
|
134
|
+
while (true) {
|
|
135
|
+
if (current.type === "CallExpression") {
|
|
136
|
+
current = current.callee;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (current.type === "MemberExpression") {
|
|
140
|
+
if (current.object.type === "Identifier" && !current.computed && current.property.type === "Identifier") {
|
|
141
|
+
rootMethodName = current.property.name;
|
|
142
|
+
}
|
|
143
|
+
current = current.object;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
return rootMethodName;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function getMemberPathFromRoot(node) {
|
|
150
|
+
const memberPath = [];
|
|
151
|
+
let current = node;
|
|
152
|
+
while (true) {
|
|
153
|
+
if (current.type === "CallExpression") {
|
|
154
|
+
current = current.callee;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (current.type === "ChainExpression") {
|
|
158
|
+
current = current.expression;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (current.type === "TSAsExpression" || current.type === "TSNonNullExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion") {
|
|
162
|
+
current = current.expression;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (current.type === "MemberExpression") {
|
|
166
|
+
if (current.computed || current.property.type !== "Identifier") {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
memberPath.unshift(current.property.name);
|
|
170
|
+
current = current.object;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
return memberPath;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function getCallMethodName(node) {
|
|
177
|
+
const { callee } = node;
|
|
178
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
179
|
+
return callee.property.name;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
function getZodImportKind(node) {
|
|
184
|
+
if (node.type !== "ImportDefaultSpecifier" && node.type !== "ImportNamespaceSpecifier" && node.type !== "ImportSpecifier") {
|
|
185
|
+
return null;
|
|
99
186
|
}
|
|
100
187
|
const declaration = node.parent;
|
|
101
188
|
if (declaration?.type !== "ImportDeclaration") {
|
|
102
|
-
return
|
|
189
|
+
return null;
|
|
103
190
|
}
|
|
104
191
|
if (declaration.source.value !== "zod") {
|
|
105
|
-
return
|
|
192
|
+
return null;
|
|
106
193
|
}
|
|
107
|
-
if (node.type === "ImportNamespaceSpecifier") {
|
|
108
|
-
return
|
|
194
|
+
if (node.type === "ImportDefaultSpecifier" || node.type === "ImportNamespaceSpecifier") {
|
|
195
|
+
return { importedName: "z", type: "namespace" };
|
|
109
196
|
}
|
|
110
197
|
if (node.importKind === "type" || declaration.importKind === "type") {
|
|
111
|
-
return
|
|
198
|
+
return null;
|
|
112
199
|
}
|
|
113
200
|
const importedName = node.imported.type === "Identifier" ? node.imported.name : node.imported.value;
|
|
114
|
-
|
|
201
|
+
if (ZOD_NAMESPACE_IMPORTS.has(importedName)) {
|
|
202
|
+
return { importedName, type: "namespace" };
|
|
203
|
+
}
|
|
204
|
+
if (ZOD_FACTORY_IMPORTS.has(importedName)) {
|
|
205
|
+
return { type: "factory" };
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
function isZodSchemaNamespaceCall(importName, memberPath) {
|
|
210
|
+
if (importName === "coerce") {
|
|
211
|
+
return memberPath[0] !== void 0 && ZOD_FACTORY_IMPORTS.has(memberPath[0]);
|
|
212
|
+
}
|
|
213
|
+
if (importName === "iso") {
|
|
214
|
+
return memberPath[0] !== void 0 && ZOD_ISO_FACTORY_IMPORTS.has(memberPath[0]);
|
|
215
|
+
}
|
|
216
|
+
if (memberPath[0] === "coerce") {
|
|
217
|
+
return memberPath[1] !== void 0 && ZOD_FACTORY_IMPORTS.has(memberPath[1]);
|
|
218
|
+
}
|
|
219
|
+
if (memberPath[0] === "iso") {
|
|
220
|
+
return memberPath[1] !== void 0 && ZOD_ISO_FACTORY_IMPORTS.has(memberPath[1]);
|
|
221
|
+
}
|
|
222
|
+
return memberPath[0] !== void 0 && ZOD_FACTORY_IMPORTS.has(memberPath[0]);
|
|
223
|
+
}
|
|
224
|
+
function hasFullTypeInformation(services) {
|
|
225
|
+
return services.program !== null;
|
|
226
|
+
}
|
|
227
|
+
function isZodDeclarationFile(fileName) {
|
|
228
|
+
return /(?:^|[/\\])node_modules[/\\]zod[/\\]/u.test(fileName);
|
|
229
|
+
}
|
|
230
|
+
function isZodSchemaType(type, services, seen = /* @__PURE__ */ new Set()) {
|
|
231
|
+
if (!hasFullTypeInformation(services)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
if (seen.has(type)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
seen.add(type);
|
|
238
|
+
const symbols = [type.getSymbol(), type.aliasSymbol];
|
|
239
|
+
if (symbols.some(
|
|
240
|
+
(symbol) => symbol?.getDeclarations()?.some(
|
|
241
|
+
(declaration) => isZodDeclarationFile(declaration.getSourceFile().fileName)
|
|
242
|
+
)
|
|
243
|
+
)) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
if (type.isUnionOrIntersection()) {
|
|
247
|
+
return type.types.some((subType) => isZodSchemaType(subType, services, seen));
|
|
248
|
+
}
|
|
249
|
+
if (type.getBaseTypes()?.some((baseType) => isZodSchemaType(baseType, services, seen))) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
const parseSymbol = type.getProperty("parse");
|
|
253
|
+
return parseSymbol?.getDeclarations()?.some(
|
|
254
|
+
(declaration) => isZodDeclarationFile(declaration.getSourceFile().fileName)
|
|
255
|
+
) ?? false;
|
|
115
256
|
}
|
|
116
257
|
var noInlineZodSchema = ESLintUtils.RuleCreator(
|
|
117
258
|
(ruleName2) => `https://www.npmjs.com/package/eslint-plugin-zod-utils#${ruleName2}`
|
|
@@ -130,19 +271,58 @@ var noInlineZodSchema = ESLintUtils.RuleCreator(
|
|
|
130
271
|
defaultOptions: [],
|
|
131
272
|
create(context) {
|
|
132
273
|
const sourceCode = context.sourceCode;
|
|
274
|
+
const parserServices = ESLintUtils.getParserServices(context, true);
|
|
275
|
+
function isZodExecutionCall(node) {
|
|
276
|
+
const methodName = getCallMethodName(node);
|
|
277
|
+
return methodName !== null && ZOD_EXECUTION_METHODS.has(methodName);
|
|
278
|
+
}
|
|
133
279
|
function isZodSchemaCall(node) {
|
|
280
|
+
if (isZodExecutionCall(node)) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
134
283
|
const root = getRootIdentifier(node.callee);
|
|
135
284
|
if (!root) {
|
|
136
285
|
return false;
|
|
137
286
|
}
|
|
138
287
|
const scope = sourceCode.getScope(root);
|
|
139
288
|
const variable = findVariable(scope, root.name);
|
|
140
|
-
return variable?.defs.some((definition) =>
|
|
289
|
+
return variable?.defs.some((definition) => {
|
|
290
|
+
const importKind = getZodImportKind(definition.node);
|
|
291
|
+
if (importKind?.type === "factory") {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
if (importKind?.type !== "namespace") {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
const memberPath = getMemberPathFromRoot(node.callee);
|
|
298
|
+
if (memberPath === null || memberPath.length === 0) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
return isZodSchemaNamespaceCall(importKind.importedName, memberPath);
|
|
302
|
+
}) ?? false;
|
|
303
|
+
}
|
|
304
|
+
function isTypedZodSchemaCombinatorCall(node) {
|
|
305
|
+
const rootMethodName = getRootMethodName(node);
|
|
306
|
+
if (rootMethodName === null || !ZOD_SCHEMA_COMBINATOR_METHODS.has(rootMethodName)) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
const root = getRootIdentifier(node.callee);
|
|
310
|
+
if (!root) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
if (!hasFullTypeInformation(parserServices)) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
const type = parserServices.getTypeAtLocation(root);
|
|
317
|
+
return isZodSchemaType(type, parserServices);
|
|
318
|
+
}
|
|
319
|
+
function isSchemaCreationCall(node) {
|
|
320
|
+
return isZodSchemaCall(node) || isTypedZodSchemaCombinatorCall(node);
|
|
141
321
|
}
|
|
142
|
-
function
|
|
322
|
+
function hasSchemaCreationCallAncestor(node) {
|
|
143
323
|
let current = node.parent;
|
|
144
324
|
while (current) {
|
|
145
|
-
if (current.type === "CallExpression" &&
|
|
325
|
+
if (current.type === "CallExpression" && isSchemaCreationCall(current)) {
|
|
146
326
|
return true;
|
|
147
327
|
}
|
|
148
328
|
current = current.parent;
|
|
@@ -164,10 +344,10 @@ var noInlineZodSchema = ESLintUtils.RuleCreator(
|
|
|
164
344
|
}
|
|
165
345
|
return {
|
|
166
346
|
CallExpression(node) {
|
|
167
|
-
if (!
|
|
347
|
+
if (!isSchemaCreationCall(node)) {
|
|
168
348
|
return;
|
|
169
349
|
}
|
|
170
|
-
if (
|
|
350
|
+
if (hasSchemaCreationCallAncestor(node)) {
|
|
171
351
|
return;
|
|
172
352
|
}
|
|
173
353
|
if (!isInsideRepeatedExecutionPath(node)) {
|
|
@@ -190,7 +370,7 @@ var rules = {
|
|
|
190
370
|
var plugin = {
|
|
191
371
|
meta: {
|
|
192
372
|
name: "eslint-plugin-zod-utils",
|
|
193
|
-
version: "
|
|
373
|
+
version: "1.0.4"
|
|
194
374
|
},
|
|
195
375
|
rules,
|
|
196
376
|
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.4",
|
|
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"
|