eslint-plugin-zod 3.8.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -11
- package/dist/index.cjs +15 -0
- package/dist/index.mjs +15 -0
- package/dist/rules/no-number-schema-with-finite.cjs +44 -0
- package/dist/rules/no-number-schema-with-finite.mjs +44 -0
- package/dist/rules/no-number-schema-with-is-finite.cjs +38 -0
- package/dist/rules/no-number-schema-with-is-finite.mjs +37 -0
- package/dist/rules/no-number-schema-with-is-int.cjs +38 -0
- package/dist/rules/no-number-schema-with-is-int.mjs +37 -0
- package/dist/rules/no-number-schema-with-safe.cjs +57 -0
- package/dist/rules/no-number-schema-with-safe.mjs +57 -0
- package/dist/rules/no-number-schema-with-step.cjs +43 -0
- package/dist/rules/no-number-schema-with-step.mjs +42 -0
- package/dist/utils/build-zod-chain-remove-method-fix.cjs +15 -0
- package/dist/utils/build-zod-chain-remove-method-fix.mjs +15 -0
- package/dist/utils/detect-zod-schema-root-node.cjs +11 -0
- package/dist/utils/detect-zod-schema-root-node.mjs +11 -1
- package/dist/utils/track-zod-schema-imports.cjs +2 -1
- package/dist/utils/track-zod-schema-imports.mjs +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,17 +56,22 @@ This plugin is primarily built for `zod`, so some rules are exclusive to `zod` a
|
|
|
56
56
|
|
|
57
57
|
### `zod` exclusive rules
|
|
58
58
|
|
|
59
|
-
| Name | Description
|
|
60
|
-
| :--------------------------------------------------------------------------------- |
|
|
61
|
-
| [array-style](docs/rules/array-style.md) | Enforce consistent Zod array style
|
|
62
|
-
| [no-number-schema-with-
|
|
63
|
-
| [no-
|
|
64
|
-
| [no-
|
|
65
|
-
| [no-
|
|
66
|
-
| [no-
|
|
67
|
-
| [
|
|
68
|
-
| [
|
|
69
|
-
| [
|
|
59
|
+
| Name | Description | 💼 | 🔧 | 💡 | ❌ |
|
|
60
|
+
| :--------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | :-- | :-- | :-- | :-- |
|
|
61
|
+
| [array-style](docs/rules/array-style.md) | Enforce consistent Zod array style | ✅ | 🔧 | | |
|
|
62
|
+
| [no-number-schema-with-finite](docs/rules/no-number-schema-with-finite.md) | Disallow deprecated `z.number().finite()`. In Zod 4+ number schemas do not allow infinite values by default, so it is a no-op. | ✅ | 🔧 | | |
|
|
63
|
+
| [no-number-schema-with-int](docs/rules/no-number-schema-with-int.md) | Disallow usage of `z.number().int()` as it is considered legacy | ✅ | 🔧 | | |
|
|
64
|
+
| [no-number-schema-with-is-finite](docs/rules/no-number-schema-with-is-finite.md) | Disallow using deprecated `isFinite` on a Zod number schema; in v4+ it is always `true`. | ✅ | | | |
|
|
65
|
+
| [no-number-schema-with-is-int](docs/rules/no-number-schema-with-is-int.md) | Disallow using deprecated `isInt` on a Zod number schema; check the `format` property instead. | ✅ | | | |
|
|
66
|
+
| [no-number-schema-with-safe](docs/rules/no-number-schema-with-safe.md) | Disallow deprecated `z.number().safe()`. Use `z.int()`; `.safe()` is now identical to `.int()`. | ✅ | 🔧 | | |
|
|
67
|
+
| [no-number-schema-with-step](docs/rules/no-number-schema-with-step.md) | Disallow deprecated `z.number().step()`. Use `.multipleOf()` instead. | ✅ | 🔧 | | |
|
|
68
|
+
| [no-optional-and-default-together](docs/rules/no-optional-and-default-together.md) | Disallow using both `.optional()` and `.default()` on the same Zod schema | ✅ | 🔧 | | |
|
|
69
|
+
| [no-string-schema-with-uuid](docs/rules/no-string-schema-with-uuid.md) | Disallow usage of `z.string().uuid()` in favor of the dedicated `z.uuid()` schema | ✅ | 🔧 | | |
|
|
70
|
+
| [no-throw-in-refine](docs/rules/no-throw-in-refine.md) | Disallow throwing errors directly inside Zod refine callbacks | ✅ | | | |
|
|
71
|
+
| [no-transform-in-record-key](docs/rules/no-transform-in-record-key.md) | Disallow transforms in z.record() key schemas, which can cause silent key mutations and data loss through key collisions | | | | |
|
|
72
|
+
| [prefer-enum-over-literal-union](docs/rules/prefer-enum-over-literal-union.md) | Prefer `z.enum()` over `z.union()` when all members are string literals. | ✅ | 🔧 | | |
|
|
73
|
+
| [prefer-meta-last](docs/rules/prefer-meta-last.md) | Enforce `.meta()` as last method | ✅ | 🔧 | | |
|
|
74
|
+
| [prefer-string-schema-with-trim](docs/rules/prefer-string-schema-with-trim.md) | Enforce `z.string().trim()` to prevent accidental leading/trailing whitespace | ✅ | 🔧 | | |
|
|
70
75
|
|
|
71
76
|
<!-- end auto-generated rules list -->
|
|
72
77
|
|
package/dist/index.cjs
CHANGED
|
@@ -6,7 +6,12 @@ const require_consistent_object_schema_type = require("./rules/consistent-object
|
|
|
6
6
|
const require_consistent_schema_output_type_style = require("./rules/consistent-schema-output-type-style.cjs");
|
|
7
7
|
const require_no_any_schema = require("./rules/no-any-schema.cjs");
|
|
8
8
|
const require_no_empty_custom_schema = require("./rules/no-empty-custom-schema.cjs");
|
|
9
|
+
const require_no_number_schema_with_finite = require("./rules/no-number-schema-with-finite.cjs");
|
|
9
10
|
const require_no_number_schema_with_int = require("./rules/no-number-schema-with-int.cjs");
|
|
11
|
+
const require_no_number_schema_with_is_finite = require("./rules/no-number-schema-with-is-finite.cjs");
|
|
12
|
+
const require_no_number_schema_with_is_int = require("./rules/no-number-schema-with-is-int.cjs");
|
|
13
|
+
const require_no_number_schema_with_safe = require("./rules/no-number-schema-with-safe.cjs");
|
|
14
|
+
const require_no_number_schema_with_step = require("./rules/no-number-schema-with-step.cjs");
|
|
10
15
|
const require_no_optional_and_default_together = require("./rules/no-optional-and-default-together.cjs");
|
|
11
16
|
const require_no_string_schema_with_uuid = require("./rules/no-string-schema-with-uuid.cjs");
|
|
12
17
|
const require_no_throw_in_refine = require("./rules/no-throw-in-refine.cjs");
|
|
@@ -35,7 +40,12 @@ const eslintPluginZod = {
|
|
|
35
40
|
"consistent-schema-output-type-style": require_consistent_schema_output_type_style.consistentSchemaOutputTypeStyle,
|
|
36
41
|
"no-any-schema": require_no_any_schema.noAnySchema,
|
|
37
42
|
"no-empty-custom-schema": require_no_empty_custom_schema.noEmptyCustomSchema,
|
|
43
|
+
"no-number-schema-with-finite": require_no_number_schema_with_finite.noNumberSchemaWithFinite,
|
|
38
44
|
"no-number-schema-with-int": require_no_number_schema_with_int.noNumberSchemaWithInt,
|
|
45
|
+
"no-number-schema-with-is-finite": require_no_number_schema_with_is_finite.noNumberSchemaWithIsFinite,
|
|
46
|
+
"no-number-schema-with-is-int": require_no_number_schema_with_is_int.noNumberSchemaWithIsInt,
|
|
47
|
+
"no-number-schema-with-safe": require_no_number_schema_with_safe.noNumberSchemaWithSafe,
|
|
48
|
+
"no-number-schema-with-step": require_no_number_schema_with_step.noNumberSchemaWithStep,
|
|
39
49
|
"no-string-schema-with-uuid": require_no_string_schema_with_uuid.noStringSchemaWithUuid,
|
|
40
50
|
"no-optional-and-default-together": require_no_optional_and_default_together.noOptionalAndDefaultTogether,
|
|
41
51
|
"no-throw-in-refine": require_no_throw_in_refine.noThrowInRefine,
|
|
@@ -64,7 +74,12 @@ const recommendedConfig = {
|
|
|
64
74
|
"zod/consistent-import": "error",
|
|
65
75
|
"zod/no-any-schema": "error",
|
|
66
76
|
"zod/no-empty-custom-schema": "error",
|
|
77
|
+
"zod/no-number-schema-with-finite": "error",
|
|
67
78
|
"zod/no-number-schema-with-int": "error",
|
|
79
|
+
"zod/no-number-schema-with-is-finite": "error",
|
|
80
|
+
"zod/no-number-schema-with-is-int": "error",
|
|
81
|
+
"zod/no-number-schema-with-safe": "error",
|
|
82
|
+
"zod/no-number-schema-with-step": "error",
|
|
68
83
|
"zod/no-string-schema-with-uuid": "error",
|
|
69
84
|
"zod/no-optional-and-default-together": "error",
|
|
70
85
|
"zod/no-throw-in-refine": "error",
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,12 @@ import { consistentObjectSchemaType } from "./rules/consistent-object-schema-typ
|
|
|
6
6
|
import { consistentSchemaOutputTypeStyle } from "./rules/consistent-schema-output-type-style.mjs";
|
|
7
7
|
import { noAnySchema } from "./rules/no-any-schema.mjs";
|
|
8
8
|
import { noEmptyCustomSchema } from "./rules/no-empty-custom-schema.mjs";
|
|
9
|
+
import { noNumberSchemaWithFinite } from "./rules/no-number-schema-with-finite.mjs";
|
|
9
10
|
import { noNumberSchemaWithInt } from "./rules/no-number-schema-with-int.mjs";
|
|
11
|
+
import { noNumberSchemaWithIsFinite } from "./rules/no-number-schema-with-is-finite.mjs";
|
|
12
|
+
import { noNumberSchemaWithIsInt } from "./rules/no-number-schema-with-is-int.mjs";
|
|
13
|
+
import { noNumberSchemaWithSafe } from "./rules/no-number-schema-with-safe.mjs";
|
|
14
|
+
import { noNumberSchemaWithStep } from "./rules/no-number-schema-with-step.mjs";
|
|
10
15
|
import { noOptionalAndDefaultTogether } from "./rules/no-optional-and-default-together.mjs";
|
|
11
16
|
import { noStringSchemaWithUuid } from "./rules/no-string-schema-with-uuid.mjs";
|
|
12
17
|
import { noThrowInRefine } from "./rules/no-throw-in-refine.mjs";
|
|
@@ -35,7 +40,12 @@ const eslintPluginZod = {
|
|
|
35
40
|
"consistent-schema-output-type-style": consistentSchemaOutputTypeStyle,
|
|
36
41
|
"no-any-schema": noAnySchema,
|
|
37
42
|
"no-empty-custom-schema": noEmptyCustomSchema,
|
|
43
|
+
"no-number-schema-with-finite": noNumberSchemaWithFinite,
|
|
38
44
|
"no-number-schema-with-int": noNumberSchemaWithInt,
|
|
45
|
+
"no-number-schema-with-is-finite": noNumberSchemaWithIsFinite,
|
|
46
|
+
"no-number-schema-with-is-int": noNumberSchemaWithIsInt,
|
|
47
|
+
"no-number-schema-with-safe": noNumberSchemaWithSafe,
|
|
48
|
+
"no-number-schema-with-step": noNumberSchemaWithStep,
|
|
39
49
|
"no-string-schema-with-uuid": noStringSchemaWithUuid,
|
|
40
50
|
"no-optional-and-default-together": noOptionalAndDefaultTogether,
|
|
41
51
|
"no-throw-in-refine": noThrowInRefine,
|
|
@@ -64,7 +74,12 @@ const recommendedConfig = {
|
|
|
64
74
|
"zod/consistent-import": "error",
|
|
65
75
|
"zod/no-any-schema": "error",
|
|
66
76
|
"zod/no-empty-custom-schema": "error",
|
|
77
|
+
"zod/no-number-schema-with-finite": "error",
|
|
67
78
|
"zod/no-number-schema-with-int": "error",
|
|
79
|
+
"zod/no-number-schema-with-is-finite": "error",
|
|
80
|
+
"zod/no-number-schema-with-is-int": "error",
|
|
81
|
+
"zod/no-number-schema-with-safe": "error",
|
|
82
|
+
"zod/no-number-schema-with-step": "error",
|
|
68
83
|
"zod/no-string-schema-with-uuid": "error",
|
|
69
84
|
"zod/no-optional-and-default-together": "error",
|
|
70
85
|
"zod/no-throw-in-refine": "error",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
|
|
2
|
+
const require_track_zod_schema_imports = require("../utils/track-zod-schema-imports.cjs");
|
|
3
|
+
const require_build_zod_chain_remove_method_fix = require("../utils/build-zod-chain-remove-method-fix.cjs");
|
|
4
|
+
//#region src/rules/no-number-schema-with-finite.ts
|
|
5
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = require_track_zod_schema_imports.createZodSchemaImportTrack("zod");
|
|
6
|
+
const noNumberSchemaWithFinite = require_create_plugin_rule.createZodPluginRule({
|
|
7
|
+
name: "no-number-schema-with-finite",
|
|
8
|
+
meta: {
|
|
9
|
+
fixable: "code",
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
zodImportAllowedSource,
|
|
13
|
+
description: "Disallow deprecated `z.number().finite()`. In Zod 4+ number schemas do not allow infinite values by default, so it is a no-op."
|
|
14
|
+
},
|
|
15
|
+
messages: { removeFinite: "`.finite()` is deprecated. In Zod 4+ `z.number()` does not allow infinite values by default. Remove this call." },
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
|
|
21
|
+
return {
|
|
22
|
+
ImportDeclaration: importDeclarationListener,
|
|
23
|
+
CallExpression(node) {
|
|
24
|
+
if (detectZodSchemaRootNode(node)?.schemaType !== "number") return;
|
|
25
|
+
const methods = collectZodChainMethods(node);
|
|
26
|
+
const finiteIndex = methods.findIndex((m) => m.name === "finite" && m.node === node);
|
|
27
|
+
if (finiteIndex === -1) return;
|
|
28
|
+
context.report({
|
|
29
|
+
node,
|
|
30
|
+
messageId: "removeFinite",
|
|
31
|
+
fix(fixer) {
|
|
32
|
+
return require_build_zod_chain_remove_method_fix.buildZodChainRemoveMethodFix({
|
|
33
|
+
fixer,
|
|
34
|
+
methods,
|
|
35
|
+
removeIndex: finiteIndex
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
//#endregion
|
|
44
|
+
exports.noNumberSchemaWithFinite = noNumberSchemaWithFinite;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
|
|
2
|
+
import { createZodSchemaImportTrack } from "../utils/track-zod-schema-imports.mjs";
|
|
3
|
+
import { buildZodChainRemoveMethodFix } from "../utils/build-zod-chain-remove-method-fix.mjs";
|
|
4
|
+
//#region src/rules/no-number-schema-with-finite.ts
|
|
5
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = createZodSchemaImportTrack("zod");
|
|
6
|
+
const noNumberSchemaWithFinite = createZodPluginRule({
|
|
7
|
+
name: "no-number-schema-with-finite",
|
|
8
|
+
meta: {
|
|
9
|
+
fixable: "code",
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
zodImportAllowedSource,
|
|
13
|
+
description: "Disallow deprecated `z.number().finite()`. In Zod 4+ number schemas do not allow infinite values by default, so it is a no-op."
|
|
14
|
+
},
|
|
15
|
+
messages: { removeFinite: "`.finite()` is deprecated. In Zod 4+ `z.number()` does not allow infinite values by default. Remove this call." },
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
|
|
21
|
+
return {
|
|
22
|
+
ImportDeclaration: importDeclarationListener,
|
|
23
|
+
CallExpression(node) {
|
|
24
|
+
if (detectZodSchemaRootNode(node)?.schemaType !== "number") return;
|
|
25
|
+
const methods = collectZodChainMethods(node);
|
|
26
|
+
const finiteIndex = methods.findIndex((m) => m.name === "finite" && m.node === node);
|
|
27
|
+
if (finiteIndex === -1) return;
|
|
28
|
+
context.report({
|
|
29
|
+
node,
|
|
30
|
+
messageId: "removeFinite",
|
|
31
|
+
fix(fixer) {
|
|
32
|
+
return buildZodChainRemoveMethodFix({
|
|
33
|
+
fixer,
|
|
34
|
+
methods,
|
|
35
|
+
removeIndex: finiteIndex
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
//#endregion
|
|
44
|
+
export { noNumberSchemaWithFinite };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
|
|
3
|
+
const require_track_zod_schema_imports = require("../utils/track-zod-schema-imports.cjs");
|
|
4
|
+
let _typescript_eslint_utils = require("@typescript-eslint/utils");
|
|
5
|
+
//#region src/rules/no-number-schema-with-is-finite.ts
|
|
6
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = require_track_zod_schema_imports.createZodSchemaImportTrack("zod");
|
|
7
|
+
const noNumberSchemaWithIsFinite = require_create_plugin_rule.createZodPluginRule({
|
|
8
|
+
name: "no-number-schema-with-is-finite",
|
|
9
|
+
meta: {
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
zodImportAllowedSource,
|
|
13
|
+
description: "Disallow using deprecated `isFinite` on a Zod number schema; in v4+ it is always `true`."
|
|
14
|
+
},
|
|
15
|
+
messages: { deprecated: "`isFinite` is deprecated. Number schemas no longer accept infinite values, so this is always `true` for `z.number()`." },
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
const { importDeclarationListener, isZodNumberSchemaCallExpression } = trackZodSchemaImports();
|
|
21
|
+
return {
|
|
22
|
+
ImportDeclaration: importDeclarationListener,
|
|
23
|
+
MemberExpression(node) {
|
|
24
|
+
if (node.computed) return;
|
|
25
|
+
if (node.property.type !== _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return;
|
|
26
|
+
if (node.property.name !== "isFinite") return;
|
|
27
|
+
if (node.object.type !== _typescript_eslint_utils.AST_NODE_TYPES.CallExpression) return;
|
|
28
|
+
if (!isZodNumberSchemaCallExpression(node.object)) return;
|
|
29
|
+
context.report({
|
|
30
|
+
node,
|
|
31
|
+
messageId: "deprecated"
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
exports.noNumberSchemaWithIsFinite = noNumberSchemaWithIsFinite;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
|
|
2
|
+
import { createZodSchemaImportTrack } from "../utils/track-zod-schema-imports.mjs";
|
|
3
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
4
|
+
//#region src/rules/no-number-schema-with-is-finite.ts
|
|
5
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = createZodSchemaImportTrack("zod");
|
|
6
|
+
const noNumberSchemaWithIsFinite = createZodPluginRule({
|
|
7
|
+
name: "no-number-schema-with-is-finite",
|
|
8
|
+
meta: {
|
|
9
|
+
type: "problem",
|
|
10
|
+
docs: {
|
|
11
|
+
zodImportAllowedSource,
|
|
12
|
+
description: "Disallow using deprecated `isFinite` on a Zod number schema; in v4+ it is always `true`."
|
|
13
|
+
},
|
|
14
|
+
messages: { deprecated: "`isFinite` is deprecated. Number schemas no longer accept infinite values, so this is always `true` for `z.number()`." },
|
|
15
|
+
schema: []
|
|
16
|
+
},
|
|
17
|
+
defaultOptions: [],
|
|
18
|
+
create(context) {
|
|
19
|
+
const { importDeclarationListener, isZodNumberSchemaCallExpression } = trackZodSchemaImports();
|
|
20
|
+
return {
|
|
21
|
+
ImportDeclaration: importDeclarationListener,
|
|
22
|
+
MemberExpression(node) {
|
|
23
|
+
if (node.computed) return;
|
|
24
|
+
if (node.property.type !== AST_NODE_TYPES.Identifier) return;
|
|
25
|
+
if (node.property.name !== "isFinite") return;
|
|
26
|
+
if (node.object.type !== AST_NODE_TYPES.CallExpression) return;
|
|
27
|
+
if (!isZodNumberSchemaCallExpression(node.object)) return;
|
|
28
|
+
context.report({
|
|
29
|
+
node,
|
|
30
|
+
messageId: "deprecated"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { noNumberSchemaWithIsFinite };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
|
|
3
|
+
const require_track_zod_schema_imports = require("../utils/track-zod-schema-imports.cjs");
|
|
4
|
+
let _typescript_eslint_utils = require("@typescript-eslint/utils");
|
|
5
|
+
//#region src/rules/no-number-schema-with-is-int.ts
|
|
6
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = require_track_zod_schema_imports.createZodSchemaImportTrack("zod");
|
|
7
|
+
const noNumberSchemaWithIsInt = require_create_plugin_rule.createZodPluginRule({
|
|
8
|
+
name: "no-number-schema-with-is-int",
|
|
9
|
+
meta: {
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
zodImportAllowedSource,
|
|
13
|
+
description: "Disallow using deprecated `isInt` on a Zod number schema; check the `format` property instead."
|
|
14
|
+
},
|
|
15
|
+
messages: { useFormat: "`isInt` is deprecated. Check the `format` property on the number schema instead (or compare to `\"int\"` or `\"float\"`)." },
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
const { importDeclarationListener, isZodNumberSchemaCallExpression } = trackZodSchemaImports();
|
|
21
|
+
return {
|
|
22
|
+
ImportDeclaration: importDeclarationListener,
|
|
23
|
+
MemberExpression(node) {
|
|
24
|
+
if (node.computed) return;
|
|
25
|
+
if (node.property.type !== _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return;
|
|
26
|
+
if (node.property.name !== "isInt") return;
|
|
27
|
+
if (node.object.type !== _typescript_eslint_utils.AST_NODE_TYPES.CallExpression) return;
|
|
28
|
+
if (!isZodNumberSchemaCallExpression(node.object)) return;
|
|
29
|
+
context.report({
|
|
30
|
+
node,
|
|
31
|
+
messageId: "useFormat"
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
exports.noNumberSchemaWithIsInt = noNumberSchemaWithIsInt;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
|
|
2
|
+
import { createZodSchemaImportTrack } from "../utils/track-zod-schema-imports.mjs";
|
|
3
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
4
|
+
//#region src/rules/no-number-schema-with-is-int.ts
|
|
5
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = createZodSchemaImportTrack("zod");
|
|
6
|
+
const noNumberSchemaWithIsInt = createZodPluginRule({
|
|
7
|
+
name: "no-number-schema-with-is-int",
|
|
8
|
+
meta: {
|
|
9
|
+
type: "problem",
|
|
10
|
+
docs: {
|
|
11
|
+
zodImportAllowedSource,
|
|
12
|
+
description: "Disallow using deprecated `isInt` on a Zod number schema; check the `format` property instead."
|
|
13
|
+
},
|
|
14
|
+
messages: { useFormat: "`isInt` is deprecated. Check the `format` property on the number schema instead (or compare to `\"int\"` or `\"float\"`)." },
|
|
15
|
+
schema: []
|
|
16
|
+
},
|
|
17
|
+
defaultOptions: [],
|
|
18
|
+
create(context) {
|
|
19
|
+
const { importDeclarationListener, isZodNumberSchemaCallExpression } = trackZodSchemaImports();
|
|
20
|
+
return {
|
|
21
|
+
ImportDeclaration: importDeclarationListener,
|
|
22
|
+
MemberExpression(node) {
|
|
23
|
+
if (node.computed) return;
|
|
24
|
+
if (node.property.type !== AST_NODE_TYPES.Identifier) return;
|
|
25
|
+
if (node.property.name !== "isInt") return;
|
|
26
|
+
if (node.object.type !== AST_NODE_TYPES.CallExpression) return;
|
|
27
|
+
if (!isZodNumberSchemaCallExpression(node.object)) return;
|
|
28
|
+
context.report({
|
|
29
|
+
node,
|
|
30
|
+
messageId: "useFormat"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { noNumberSchemaWithIsInt };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
|
|
2
|
+
const require_track_zod_schema_imports = require("../utils/track-zod-schema-imports.cjs");
|
|
3
|
+
const require_build_zod_chain_replacement_fix = require("../utils/build-zod-chain-replacement-fix.cjs");
|
|
4
|
+
//#region src/rules/no-number-schema-with-safe.ts
|
|
5
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = require_track_zod_schema_imports.createZodSchemaImportTrack("zod");
|
|
6
|
+
const noNumberSchemaWithSafe = require_create_plugin_rule.createZodPluginRule({
|
|
7
|
+
name: "no-number-schema-with-safe",
|
|
8
|
+
meta: {
|
|
9
|
+
fixable: "code",
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
zodImportAllowedSource,
|
|
13
|
+
description: "Disallow deprecated `z.number().safe()`. Use `z.int()`; `.safe()` is now identical to `.int()`."
|
|
14
|
+
},
|
|
15
|
+
messages: { useInt: "`.safe()` is deprecated; it is identical to `.int()`. Use `z.int()` (or the equivalent) instead of chaining `.safe()` on `z.number()`." },
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
const { sourceCode } = context;
|
|
21
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
|
|
22
|
+
return {
|
|
23
|
+
ImportDeclaration: importDeclarationListener,
|
|
24
|
+
CallExpression(node) {
|
|
25
|
+
const zodSchemaMeta = detectZodSchemaRootNode(node);
|
|
26
|
+
if (zodSchemaMeta?.schemaType !== "number") return;
|
|
27
|
+
const methods = collectZodChainMethods(node);
|
|
28
|
+
const safeIndex = methods.findIndex((m) => m.name === "safe" && m.node === node);
|
|
29
|
+
if (safeIndex === -1) return;
|
|
30
|
+
const numberIndex = methods.findIndex((m) => m.name === "number");
|
|
31
|
+
if (zodSchemaMeta.schemaDecl === "named") {
|
|
32
|
+
context.report({
|
|
33
|
+
node,
|
|
34
|
+
messageId: "useInt"
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
context.report({
|
|
39
|
+
node,
|
|
40
|
+
messageId: "useInt",
|
|
41
|
+
fix(fixer) {
|
|
42
|
+
return require_build_zod_chain_replacement_fix.buildZodChainReplacementFix({
|
|
43
|
+
sourceCode,
|
|
44
|
+
fixer,
|
|
45
|
+
methods,
|
|
46
|
+
fromIndex: numberIndex,
|
|
47
|
+
toIndex: safeIndex,
|
|
48
|
+
toMethodName: "int"
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
//#endregion
|
|
57
|
+
exports.noNumberSchemaWithSafe = noNumberSchemaWithSafe;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
|
|
2
|
+
import { createZodSchemaImportTrack } from "../utils/track-zod-schema-imports.mjs";
|
|
3
|
+
import { buildZodChainReplacementFix } from "../utils/build-zod-chain-replacement-fix.mjs";
|
|
4
|
+
//#region src/rules/no-number-schema-with-safe.ts
|
|
5
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = createZodSchemaImportTrack("zod");
|
|
6
|
+
const noNumberSchemaWithSafe = createZodPluginRule({
|
|
7
|
+
name: "no-number-schema-with-safe",
|
|
8
|
+
meta: {
|
|
9
|
+
fixable: "code",
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
zodImportAllowedSource,
|
|
13
|
+
description: "Disallow deprecated `z.number().safe()`. Use `z.int()`; `.safe()` is now identical to `.int()`."
|
|
14
|
+
},
|
|
15
|
+
messages: { useInt: "`.safe()` is deprecated; it is identical to `.int()`. Use `z.int()` (or the equivalent) instead of chaining `.safe()` on `z.number()`." },
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
const { sourceCode } = context;
|
|
21
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
|
|
22
|
+
return {
|
|
23
|
+
ImportDeclaration: importDeclarationListener,
|
|
24
|
+
CallExpression(node) {
|
|
25
|
+
const zodSchemaMeta = detectZodSchemaRootNode(node);
|
|
26
|
+
if (zodSchemaMeta?.schemaType !== "number") return;
|
|
27
|
+
const methods = collectZodChainMethods(node);
|
|
28
|
+
const safeIndex = methods.findIndex((m) => m.name === "safe" && m.node === node);
|
|
29
|
+
if (safeIndex === -1) return;
|
|
30
|
+
const numberIndex = methods.findIndex((m) => m.name === "number");
|
|
31
|
+
if (zodSchemaMeta.schemaDecl === "named") {
|
|
32
|
+
context.report({
|
|
33
|
+
node,
|
|
34
|
+
messageId: "useInt"
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
context.report({
|
|
39
|
+
node,
|
|
40
|
+
messageId: "useInt",
|
|
41
|
+
fix(fixer) {
|
|
42
|
+
return buildZodChainReplacementFix({
|
|
43
|
+
sourceCode,
|
|
44
|
+
fixer,
|
|
45
|
+
methods,
|
|
46
|
+
fromIndex: numberIndex,
|
|
47
|
+
toIndex: safeIndex,
|
|
48
|
+
toMethodName: "int"
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
//#endregion
|
|
57
|
+
export { noNumberSchemaWithSafe };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
|
|
3
|
+
const require_track_zod_schema_imports = require("../utils/track-zod-schema-imports.cjs");
|
|
4
|
+
let _typescript_eslint_utils = require("@typescript-eslint/utils");
|
|
5
|
+
//#region src/rules/no-number-schema-with-step.ts
|
|
6
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = require_track_zod_schema_imports.createZodSchemaImportTrack("zod");
|
|
7
|
+
const noNumberSchemaWithStep = require_create_plugin_rule.createZodPluginRule({
|
|
8
|
+
name: "no-number-schema-with-step",
|
|
9
|
+
meta: {
|
|
10
|
+
fixable: "code",
|
|
11
|
+
type: "problem",
|
|
12
|
+
docs: {
|
|
13
|
+
zodImportAllowedSource,
|
|
14
|
+
description: "Disallow deprecated `z.number().step()`. Use `.multipleOf()` instead."
|
|
15
|
+
},
|
|
16
|
+
messages: { useMultipleOf: "`.step()` is deprecated. Use `.multipleOf()` with the same argument instead." },
|
|
17
|
+
schema: []
|
|
18
|
+
},
|
|
19
|
+
defaultOptions: [],
|
|
20
|
+
create(context) {
|
|
21
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
|
|
22
|
+
return {
|
|
23
|
+
ImportDeclaration: importDeclarationListener,
|
|
24
|
+
CallExpression(node) {
|
|
25
|
+
if (detectZodSchemaRootNode(node)?.schemaType !== "number") return;
|
|
26
|
+
if (collectZodChainMethods(node).findIndex((m) => m.name === "step" && m.node === node) === -1) return;
|
|
27
|
+
const { callee } = node;
|
|
28
|
+
if (callee.type !== _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression) return;
|
|
29
|
+
if (callee.property.type !== _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return;
|
|
30
|
+
const { property } = callee;
|
|
31
|
+
context.report({
|
|
32
|
+
node,
|
|
33
|
+
messageId: "useMultipleOf",
|
|
34
|
+
fix(fixer) {
|
|
35
|
+
return fixer.replaceText(property, "multipleOf");
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
//#endregion
|
|
43
|
+
exports.noNumberSchemaWithStep = noNumberSchemaWithStep;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
|
|
2
|
+
import { createZodSchemaImportTrack } from "../utils/track-zod-schema-imports.mjs";
|
|
3
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
4
|
+
//#region src/rules/no-number-schema-with-step.ts
|
|
5
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = createZodSchemaImportTrack("zod");
|
|
6
|
+
const noNumberSchemaWithStep = createZodPluginRule({
|
|
7
|
+
name: "no-number-schema-with-step",
|
|
8
|
+
meta: {
|
|
9
|
+
fixable: "code",
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
zodImportAllowedSource,
|
|
13
|
+
description: "Disallow deprecated `z.number().step()`. Use `.multipleOf()` instead."
|
|
14
|
+
},
|
|
15
|
+
messages: { useMultipleOf: "`.step()` is deprecated. Use `.multipleOf()` with the same argument instead." },
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
|
|
21
|
+
return {
|
|
22
|
+
ImportDeclaration: importDeclarationListener,
|
|
23
|
+
CallExpression(node) {
|
|
24
|
+
if (detectZodSchemaRootNode(node)?.schemaType !== "number") return;
|
|
25
|
+
if (collectZodChainMethods(node).findIndex((m) => m.name === "step" && m.node === node) === -1) return;
|
|
26
|
+
const { callee } = node;
|
|
27
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression) return;
|
|
28
|
+
if (callee.property.type !== AST_NODE_TYPES.Identifier) return;
|
|
29
|
+
const { property } = callee;
|
|
30
|
+
context.report({
|
|
31
|
+
node,
|
|
32
|
+
messageId: "useMultipleOf",
|
|
33
|
+
fix(fixer) {
|
|
34
|
+
return fixer.replaceText(property, "multipleOf");
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
42
|
+
export { noNumberSchemaWithStep };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/utils/build-zod-chain-remove-method-fix.ts
|
|
2
|
+
/**
|
|
3
|
+
* Remove one call from a zod method chain, e.g. `z.number().min(0).finite()` →
|
|
4
|
+
* `z.number().min(0)`.
|
|
5
|
+
*/
|
|
6
|
+
function buildZodChainRemoveMethodFix(opts) {
|
|
7
|
+
const { fixer, methods, removeIndex } = opts;
|
|
8
|
+
if (removeIndex < 1) return null;
|
|
9
|
+
const prev = methods[removeIndex - 1]?.node;
|
|
10
|
+
const toRemove = methods[removeIndex]?.node;
|
|
11
|
+
if (!prev?.range || !toRemove?.range) return null;
|
|
12
|
+
return fixer.removeRange([prev.range[1], toRemove.range[1]]);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
exports.buildZodChainRemoveMethodFix = buildZodChainRemoveMethodFix;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/utils/build-zod-chain-remove-method-fix.ts
|
|
2
|
+
/**
|
|
3
|
+
* Remove one call from a zod method chain, e.g. `z.number().min(0).finite()` →
|
|
4
|
+
* `z.number().min(0)`.
|
|
5
|
+
*/
|
|
6
|
+
function buildZodChainRemoveMethodFix(opts) {
|
|
7
|
+
const { fixer, methods, removeIndex } = opts;
|
|
8
|
+
if (removeIndex < 1) return null;
|
|
9
|
+
const prev = methods[removeIndex - 1]?.node;
|
|
10
|
+
const toRemove = methods[removeIndex]?.node;
|
|
11
|
+
if (!prev?.range || !toRemove?.range) return null;
|
|
12
|
+
return fixer.removeRange([prev.range[1], toRemove.range[1]]);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { buildZodChainRemoveMethodFix };
|
|
@@ -71,6 +71,16 @@ function parseZodCallExpression(call, zodNamespaces, zodNamedImports) {
|
|
|
71
71
|
};
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* True when `node` is a zod number schema call chain (e.g. `z.number().min(1)`) or `number().min(1)`.
|
|
76
|
+
* Used for member access like `z.number().isInt` where the call is not the outermost expression
|
|
77
|
+
* in the file (so {@link detectZodSchemaRootNode} does not apply).
|
|
78
|
+
*/
|
|
79
|
+
function isZodNumberSchemaCallExpression(node, zodNamespaces, zodNamedImports) {
|
|
80
|
+
if (node.type !== _typescript_eslint_utils.AST_NODE_TYPES.CallExpression) return false;
|
|
81
|
+
const parsed = parseZodCallExpression(node, zodNamespaces, zodNamedImports);
|
|
82
|
+
return parsed !== null && parsed.schemaType === "number";
|
|
83
|
+
}
|
|
74
84
|
/** Examine an expression (argument) for zod schema CallExpressions.
|
|
75
85
|
* Supports:
|
|
76
86
|
* - direct CallExpression (string(), z.string(), etc)
|
|
@@ -100,3 +110,4 @@ function detectZodSchemaRootNode(node, zodNamespaces, zodNamedImports) {
|
|
|
100
110
|
}
|
|
101
111
|
//#endregion
|
|
102
112
|
exports.detectZodSchemaRootNode = detectZodSchemaRootNode;
|
|
113
|
+
exports.isZodNumberSchemaCallExpression = isZodNumberSchemaCallExpression;
|
|
@@ -70,6 +70,16 @@ function parseZodCallExpression(call, zodNamespaces, zodNamedImports) {
|
|
|
70
70
|
};
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* True when `node` is a zod number schema call chain (e.g. `z.number().min(1)`) or `number().min(1)`.
|
|
75
|
+
* Used for member access like `z.number().isInt` where the call is not the outermost expression
|
|
76
|
+
* in the file (so {@link detectZodSchemaRootNode} does not apply).
|
|
77
|
+
*/
|
|
78
|
+
function isZodNumberSchemaCallExpression(node, zodNamespaces, zodNamedImports) {
|
|
79
|
+
if (node.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
80
|
+
const parsed = parseZodCallExpression(node, zodNamespaces, zodNamedImports);
|
|
81
|
+
return parsed !== null && parsed.schemaType === "number";
|
|
82
|
+
}
|
|
73
83
|
/** Examine an expression (argument) for zod schema CallExpressions.
|
|
74
84
|
* Supports:
|
|
75
85
|
* - direct CallExpression (string(), z.string(), etc)
|
|
@@ -98,4 +108,4 @@ function detectZodSchemaRootNode(node, zodNamespaces, zodNamedImports) {
|
|
|
98
108
|
};
|
|
99
109
|
}
|
|
100
110
|
//#endregion
|
|
101
|
-
export { detectZodSchemaRootNode };
|
|
111
|
+
export { detectZodSchemaRootNode, isZodNumberSchemaCallExpression };
|
|
@@ -58,7 +58,8 @@ function trackZodSchemaImports(importAllowedSource) {
|
|
|
58
58
|
getNamedImportOriginal: (localName) => zodNamedImports.get(localName),
|
|
59
59
|
getNamedImportLocal: (originalName) => zodNamedImportsByOriginal.get(originalName),
|
|
60
60
|
detectZodSchemaRootNode: (node) => require_detect_zod_schema_root_node.detectZodSchemaRootNode(node, zodNamespaces, zodNamedImports),
|
|
61
|
-
collectZodChainMethods
|
|
61
|
+
collectZodChainMethods,
|
|
62
|
+
isZodNumberSchemaCallExpression: (node) => require_detect_zod_schema_root_node.isZodNumberSchemaCallExpression(node, zodNamespaces, zodNamedImports)
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { detectZodSchemaRootNode } from "./detect-zod-schema-root-node.mjs";
|
|
1
|
+
import { detectZodSchemaRootNode, isZodNumberSchemaCallExpression } from "./detect-zod-schema-root-node.mjs";
|
|
2
2
|
import { isZodImportSource } from "./is-zod-import-source.mjs";
|
|
3
3
|
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
4
4
|
//#region src/utils/track-zod-schema-imports.ts
|
|
@@ -57,7 +57,8 @@ function trackZodSchemaImports(importAllowedSource) {
|
|
|
57
57
|
getNamedImportOriginal: (localName) => zodNamedImports.get(localName),
|
|
58
58
|
getNamedImportLocal: (originalName) => zodNamedImportsByOriginal.get(originalName),
|
|
59
59
|
detectZodSchemaRootNode: (node) => detectZodSchemaRootNode(node, zodNamespaces, zodNamedImports),
|
|
60
|
-
collectZodChainMethods
|
|
60
|
+
collectZodChainMethods,
|
|
61
|
+
isZodNumberSchemaCallExpression: (node) => isZodNumberSchemaCallExpression(node, zodNamespaces, zodNamedImports)
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
/**
|