eslint-plugin-zod 3.8.0 β 3.10.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 +18 -12
- package/dist/index.cjs +21 -4
- package/dist/index.mjs +21 -4
- package/dist/rules/consistent-schema-name.cjs +76 -0
- package/dist/rules/consistent-schema-name.mjs +75 -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/rules/require-schema-suffix.cjs +1 -0
- package/dist/rules/require-schema-suffix.mjs +1 -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
|
@@ -43,6 +43,7 @@ This plugin is primarily built for `zod`, so some rules are exclusive to `zod` a
|
|
|
43
43
|
| [consistent-import](docs/rules/consistent-import.md) | Enforce a consistent import style for Zod | β
| π§ | | |
|
|
44
44
|
| [consistent-import-source](docs/rules/consistent-import-source.md) | Enforce consistent source from Zod imports | | | π‘ | |
|
|
45
45
|
| [consistent-object-schema-type](docs/rules/consistent-object-schema-type.md) | Enforce consistent usage of Zod schema methods | | | π‘ | |
|
|
46
|
+
| [consistent-schema-name](docs/rules/consistent-schema-name.md) | Enforce a consistent naming convention for Zod schema variables | β
βοΈ | | | |
|
|
46
47
|
| [consistent-schema-output-type-style](docs/rules/consistent-schema-output-type-style.md) | Enforce consistent use of z.infer or z.output for schema type inference | | π§ | | |
|
|
47
48
|
| [no-any-schema](docs/rules/no-any-schema.md) | Disallow usage of `z.any()` in Zod schemas | β
βοΈ | | π‘ | |
|
|
48
49
|
| [no-empty-custom-schema](docs/rules/no-empty-custom-schema.md) | Disallow usage of `z.custom()` without arguments | β
| | | |
|
|
@@ -51,22 +52,27 @@ This plugin is primarily built for `zod`, so some rules are exclusive to `zod` a
|
|
|
51
52
|
| [prefer-namespace-import](docs/rules/prefer-namespace-import.md) | Enforce importing zod as a namespace import (`import * as z from 'zod'`) | | π§ | | β |
|
|
52
53
|
| [require-brand-type-parameter](docs/rules/require-brand-type-parameter.md) | Require type parameter on `.brand()` functions | β
βοΈ | | π‘ | |
|
|
53
54
|
| [require-error-message](docs/rules/require-error-message.md) | Enforce that custom refinements include an error message | β
βοΈ | π§ | | |
|
|
54
|
-
| [require-schema-suffix](docs/rules/require-schema-suffix.md) | Require schema suffix when declaring a Zod schema |
|
|
55
|
+
| [require-schema-suffix](docs/rules/require-schema-suffix.md) | Require schema suffix when declaring a Zod schema | | | | β |
|
|
55
56
|
| [schema-error-property-style](docs/rules/schema-error-property-style.md) | Enforce consistent style for error messages in Zod schema validation (using ESQuery patterns) | | | | |
|
|
56
57
|
|
|
57
58
|
### `zod` exclusive rules
|
|
58
59
|
|
|
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
|
-
| [
|
|
60
|
+
| NameΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description | πΌ | π§ | π‘ | β |
|
|
61
|
+
| :--------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | :-- | :-- | :-- | :-- |
|
|
62
|
+
| [array-style](docs/rules/array-style.md) | Enforce consistent Zod array style | β
| π§ | | |
|
|
63
|
+
| [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. | β
| π§ | | |
|
|
64
|
+
| [no-number-schema-with-int](docs/rules/no-number-schema-with-int.md) | Disallow usage of `z.number().int()` as it is considered legacy | β
| π§ | | |
|
|
65
|
+
| [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`. | β
| | | |
|
|
66
|
+
| [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. | β
| | | |
|
|
67
|
+
| [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()`. | β
| π§ | | |
|
|
68
|
+
| [no-number-schema-with-step](docs/rules/no-number-schema-with-step.md) | Disallow deprecated `z.number().step()`. Use `.multipleOf()` instead. | β
| π§ | | |
|
|
69
|
+
| [no-optional-and-default-together](docs/rules/no-optional-and-default-together.md) | Disallow using both `.optional()` and `.default()` on the same Zod schema | β
| π§ | | |
|
|
70
|
+
| [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 | β
| π§ | | |
|
|
71
|
+
| [no-throw-in-refine](docs/rules/no-throw-in-refine.md) | Disallow throwing errors directly inside Zod refine callbacks | β
| | | |
|
|
72
|
+
| [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 | | | | |
|
|
73
|
+
| [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. | β
| π§ | | |
|
|
74
|
+
| [prefer-meta-last](docs/rules/prefer-meta-last.md) | Enforce `.meta()` as last method | β
| π§ | | |
|
|
75
|
+
| [prefer-string-schema-with-trim](docs/rules/prefer-string-schema-with-trim.md) | Enforce `z.string().trim()` to prevent accidental leading/trailing whitespace | β
| π§ | | |
|
|
70
76
|
|
|
71
77
|
<!-- end auto-generated rules list -->
|
|
72
78
|
|
package/dist/index.cjs
CHANGED
|
@@ -3,10 +3,16 @@ const require_array_style = require("./rules/array-style.cjs");
|
|
|
3
3
|
const require_consistent_import_source = require("./rules/consistent-import-source.cjs");
|
|
4
4
|
const require_consistent_import = require("./rules/consistent-import.cjs");
|
|
5
5
|
const require_consistent_object_schema_type = require("./rules/consistent-object-schema-type.cjs");
|
|
6
|
+
const require_consistent_schema_name = require("./rules/consistent-schema-name.cjs");
|
|
6
7
|
const require_consistent_schema_output_type_style = require("./rules/consistent-schema-output-type-style.cjs");
|
|
7
8
|
const require_no_any_schema = require("./rules/no-any-schema.cjs");
|
|
8
9
|
const require_no_empty_custom_schema = require("./rules/no-empty-custom-schema.cjs");
|
|
10
|
+
const require_no_number_schema_with_finite = require("./rules/no-number-schema-with-finite.cjs");
|
|
9
11
|
const require_no_number_schema_with_int = require("./rules/no-number-schema-with-int.cjs");
|
|
12
|
+
const require_no_number_schema_with_is_finite = require("./rules/no-number-schema-with-is-finite.cjs");
|
|
13
|
+
const require_no_number_schema_with_is_int = require("./rules/no-number-schema-with-is-int.cjs");
|
|
14
|
+
const require_no_number_schema_with_safe = require("./rules/no-number-schema-with-safe.cjs");
|
|
15
|
+
const require_no_number_schema_with_step = require("./rules/no-number-schema-with-step.cjs");
|
|
10
16
|
const require_no_optional_and_default_together = require("./rules/no-optional-and-default-together.cjs");
|
|
11
17
|
const require_no_string_schema_with_uuid = require("./rules/no-string-schema-with-uuid.cjs");
|
|
12
18
|
const require_no_throw_in_refine = require("./rules/no-throw-in-refine.cjs");
|
|
@@ -32,10 +38,16 @@ const eslintPluginZod = {
|
|
|
32
38
|
"consistent-import-source": require_consistent_import_source.consistentImportSource,
|
|
33
39
|
"consistent-import": require_consistent_import.consistentImport,
|
|
34
40
|
"consistent-object-schema-type": require_consistent_object_schema_type.consistentObjectSchemaType,
|
|
41
|
+
"consistent-schema-name": require_consistent_schema_name.consistentSchemaName,
|
|
35
42
|
"consistent-schema-output-type-style": require_consistent_schema_output_type_style.consistentSchemaOutputTypeStyle,
|
|
36
43
|
"no-any-schema": require_no_any_schema.noAnySchema,
|
|
37
44
|
"no-empty-custom-schema": require_no_empty_custom_schema.noEmptyCustomSchema,
|
|
45
|
+
"no-number-schema-with-finite": require_no_number_schema_with_finite.noNumberSchemaWithFinite,
|
|
38
46
|
"no-number-schema-with-int": require_no_number_schema_with_int.noNumberSchemaWithInt,
|
|
47
|
+
"no-number-schema-with-is-finite": require_no_number_schema_with_is_finite.noNumberSchemaWithIsFinite,
|
|
48
|
+
"no-number-schema-with-is-int": require_no_number_schema_with_is_int.noNumberSchemaWithIsInt,
|
|
49
|
+
"no-number-schema-with-safe": require_no_number_schema_with_safe.noNumberSchemaWithSafe,
|
|
50
|
+
"no-number-schema-with-step": require_no_number_schema_with_step.noNumberSchemaWithStep,
|
|
39
51
|
"no-string-schema-with-uuid": require_no_string_schema_with_uuid.noStringSchemaWithUuid,
|
|
40
52
|
"no-optional-and-default-together": require_no_optional_and_default_together.noOptionalAndDefaultTogether,
|
|
41
53
|
"no-throw-in-refine": require_no_throw_in_refine.noThrowInRefine,
|
|
@@ -62,9 +74,15 @@ const recommendedConfig = {
|
|
|
62
74
|
rules: {
|
|
63
75
|
"zod/array-style": "error",
|
|
64
76
|
"zod/consistent-import": "error",
|
|
77
|
+
"zod/consistent-schema-name": "error",
|
|
65
78
|
"zod/no-any-schema": "error",
|
|
66
79
|
"zod/no-empty-custom-schema": "error",
|
|
80
|
+
"zod/no-number-schema-with-finite": "error",
|
|
67
81
|
"zod/no-number-schema-with-int": "error",
|
|
82
|
+
"zod/no-number-schema-with-is-finite": "error",
|
|
83
|
+
"zod/no-number-schema-with-is-int": "error",
|
|
84
|
+
"zod/no-number-schema-with-safe": "error",
|
|
85
|
+
"zod/no-number-schema-with-step": "error",
|
|
68
86
|
"zod/no-string-schema-with-uuid": "error",
|
|
69
87
|
"zod/no-optional-and-default-together": "error",
|
|
70
88
|
"zod/no-throw-in-refine": "error",
|
|
@@ -73,18 +91,17 @@ const recommendedConfig = {
|
|
|
73
91
|
"zod/prefer-meta-last": "error",
|
|
74
92
|
"zod/prefer-string-schema-with-trim": "error",
|
|
75
93
|
"zod/require-brand-type-parameter": "error",
|
|
76
|
-
"zod/require-error-message": "error"
|
|
77
|
-
"zod/require-schema-suffix": "error"
|
|
94
|
+
"zod/require-error-message": "error"
|
|
78
95
|
}
|
|
79
96
|
};
|
|
80
97
|
const recommendedConfigMini = {
|
|
81
98
|
...baseConfig,
|
|
82
99
|
rules: {
|
|
100
|
+
"zod/consistent-schema-name": "error",
|
|
83
101
|
"zod/no-any-schema": "error",
|
|
84
102
|
"zod/prefer-meta": "error",
|
|
85
103
|
"zod/require-brand-type-parameter": "error",
|
|
86
|
-
"zod/require-error-message": "error"
|
|
87
|
-
"zod/require-schema-suffix": "error"
|
|
104
|
+
"zod/require-error-message": "error"
|
|
88
105
|
}
|
|
89
106
|
};
|
|
90
107
|
var src_default = {
|
package/dist/index.mjs
CHANGED
|
@@ -3,10 +3,16 @@ import { arrayStyle } from "./rules/array-style.mjs";
|
|
|
3
3
|
import { consistentImportSource } from "./rules/consistent-import-source.mjs";
|
|
4
4
|
import { consistentImport } from "./rules/consistent-import.mjs";
|
|
5
5
|
import { consistentObjectSchemaType } from "./rules/consistent-object-schema-type.mjs";
|
|
6
|
+
import { consistentSchemaName } from "./rules/consistent-schema-name.mjs";
|
|
6
7
|
import { consistentSchemaOutputTypeStyle } from "./rules/consistent-schema-output-type-style.mjs";
|
|
7
8
|
import { noAnySchema } from "./rules/no-any-schema.mjs";
|
|
8
9
|
import { noEmptyCustomSchema } from "./rules/no-empty-custom-schema.mjs";
|
|
10
|
+
import { noNumberSchemaWithFinite } from "./rules/no-number-schema-with-finite.mjs";
|
|
9
11
|
import { noNumberSchemaWithInt } from "./rules/no-number-schema-with-int.mjs";
|
|
12
|
+
import { noNumberSchemaWithIsFinite } from "./rules/no-number-schema-with-is-finite.mjs";
|
|
13
|
+
import { noNumberSchemaWithIsInt } from "./rules/no-number-schema-with-is-int.mjs";
|
|
14
|
+
import { noNumberSchemaWithSafe } from "./rules/no-number-schema-with-safe.mjs";
|
|
15
|
+
import { noNumberSchemaWithStep } from "./rules/no-number-schema-with-step.mjs";
|
|
10
16
|
import { noOptionalAndDefaultTogether } from "./rules/no-optional-and-default-together.mjs";
|
|
11
17
|
import { noStringSchemaWithUuid } from "./rules/no-string-schema-with-uuid.mjs";
|
|
12
18
|
import { noThrowInRefine } from "./rules/no-throw-in-refine.mjs";
|
|
@@ -32,10 +38,16 @@ const eslintPluginZod = {
|
|
|
32
38
|
"consistent-import-source": consistentImportSource,
|
|
33
39
|
"consistent-import": consistentImport,
|
|
34
40
|
"consistent-object-schema-type": consistentObjectSchemaType,
|
|
41
|
+
"consistent-schema-name": consistentSchemaName,
|
|
35
42
|
"consistent-schema-output-type-style": consistentSchemaOutputTypeStyle,
|
|
36
43
|
"no-any-schema": noAnySchema,
|
|
37
44
|
"no-empty-custom-schema": noEmptyCustomSchema,
|
|
45
|
+
"no-number-schema-with-finite": noNumberSchemaWithFinite,
|
|
38
46
|
"no-number-schema-with-int": noNumberSchemaWithInt,
|
|
47
|
+
"no-number-schema-with-is-finite": noNumberSchemaWithIsFinite,
|
|
48
|
+
"no-number-schema-with-is-int": noNumberSchemaWithIsInt,
|
|
49
|
+
"no-number-schema-with-safe": noNumberSchemaWithSafe,
|
|
50
|
+
"no-number-schema-with-step": noNumberSchemaWithStep,
|
|
39
51
|
"no-string-schema-with-uuid": noStringSchemaWithUuid,
|
|
40
52
|
"no-optional-and-default-together": noOptionalAndDefaultTogether,
|
|
41
53
|
"no-throw-in-refine": noThrowInRefine,
|
|
@@ -62,9 +74,15 @@ const recommendedConfig = {
|
|
|
62
74
|
rules: {
|
|
63
75
|
"zod/array-style": "error",
|
|
64
76
|
"zod/consistent-import": "error",
|
|
77
|
+
"zod/consistent-schema-name": "error",
|
|
65
78
|
"zod/no-any-schema": "error",
|
|
66
79
|
"zod/no-empty-custom-schema": "error",
|
|
80
|
+
"zod/no-number-schema-with-finite": "error",
|
|
67
81
|
"zod/no-number-schema-with-int": "error",
|
|
82
|
+
"zod/no-number-schema-with-is-finite": "error",
|
|
83
|
+
"zod/no-number-schema-with-is-int": "error",
|
|
84
|
+
"zod/no-number-schema-with-safe": "error",
|
|
85
|
+
"zod/no-number-schema-with-step": "error",
|
|
68
86
|
"zod/no-string-schema-with-uuid": "error",
|
|
69
87
|
"zod/no-optional-and-default-together": "error",
|
|
70
88
|
"zod/no-throw-in-refine": "error",
|
|
@@ -73,18 +91,17 @@ const recommendedConfig = {
|
|
|
73
91
|
"zod/prefer-meta-last": "error",
|
|
74
92
|
"zod/prefer-string-schema-with-trim": "error",
|
|
75
93
|
"zod/require-brand-type-parameter": "error",
|
|
76
|
-
"zod/require-error-message": "error"
|
|
77
|
-
"zod/require-schema-suffix": "error"
|
|
94
|
+
"zod/require-error-message": "error"
|
|
78
95
|
}
|
|
79
96
|
};
|
|
80
97
|
const recommendedConfigMini = {
|
|
81
98
|
...baseConfig,
|
|
82
99
|
rules: {
|
|
100
|
+
"zod/consistent-schema-name": "error",
|
|
83
101
|
"zod/no-any-schema": "error",
|
|
84
102
|
"zod/prefer-meta": "error",
|
|
85
103
|
"zod/require-brand-type-parameter": "error",
|
|
86
|
-
"zod/require-error-message": "error"
|
|
87
|
-
"zod/require-schema-suffix": "error"
|
|
104
|
+
"zod/require-error-message": "error"
|
|
88
105
|
}
|
|
89
106
|
};
|
|
90
107
|
var src_default = {
|
|
@@ -0,0 +1,76 @@
|
|
|
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/consistent-schema-name.ts
|
|
6
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = require_track_zod_schema_imports.createZodSchemaImportTrack("all");
|
|
7
|
+
const consistentSchemaName = require_create_plugin_rule.createZodPluginRule({
|
|
8
|
+
name: "consistent-schema-name",
|
|
9
|
+
meta: {
|
|
10
|
+
type: "suggestion",
|
|
11
|
+
docs: {
|
|
12
|
+
zodImportAllowedSource,
|
|
13
|
+
description: "Enforce a consistent naming convention for Zod schema variables"
|
|
14
|
+
},
|
|
15
|
+
messages: { invalidName: "Rename this Zod schema to \"{{expected}}\"" },
|
|
16
|
+
schema: [{
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
before: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "The required prefix for Zod schema variables"
|
|
22
|
+
},
|
|
23
|
+
after: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "The required suffix for Zod schema variables"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
additionalProperties: false
|
|
29
|
+
}]
|
|
30
|
+
},
|
|
31
|
+
defaultOptions: [{ after: "Schema" }],
|
|
32
|
+
create(context, [{ before = "", after = "" }]) {
|
|
33
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
|
|
34
|
+
return {
|
|
35
|
+
ImportDeclaration: importDeclarationListener,
|
|
36
|
+
VariableDeclarator(node) {
|
|
37
|
+
const initNode = node.init;
|
|
38
|
+
if (initNode?.type !== _typescript_eslint_utils.AST_NODE_TYPES.CallExpression || !detectZodSchemaRootNode(initNode)) return;
|
|
39
|
+
const chainMethods = collectZodChainMethods(initNode).map((it) => it.name);
|
|
40
|
+
if ([
|
|
41
|
+
"parse",
|
|
42
|
+
"parseAsync",
|
|
43
|
+
"safeParse",
|
|
44
|
+
"safeParseAsync",
|
|
45
|
+
"spa",
|
|
46
|
+
"encode",
|
|
47
|
+
"encodeAsync",
|
|
48
|
+
"decode",
|
|
49
|
+
"decodeAsync",
|
|
50
|
+
"safeEncode",
|
|
51
|
+
"safeEncodeAsync",
|
|
52
|
+
"safeDecode",
|
|
53
|
+
"safeDecodeAsync",
|
|
54
|
+
"codec",
|
|
55
|
+
"treeifyError",
|
|
56
|
+
"prettifyError",
|
|
57
|
+
"formatError",
|
|
58
|
+
"flattenError"
|
|
59
|
+
].some((it) => chainMethods.includes(it))) return;
|
|
60
|
+
if (node.id.type !== _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return;
|
|
61
|
+
const { name } = node.id;
|
|
62
|
+
const validPrefix = !before || name.startsWith(before);
|
|
63
|
+
const validSuffix = !after || name.endsWith(after);
|
|
64
|
+
if (validPrefix && validSuffix) return;
|
|
65
|
+
const expected = (validPrefix ? "" : before) + name + (validSuffix ? "" : after);
|
|
66
|
+
context.report({
|
|
67
|
+
node,
|
|
68
|
+
messageId: "invalidName",
|
|
69
|
+
data: { expected }
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
//#endregion
|
|
76
|
+
exports.consistentSchemaName = consistentSchemaName;
|
|
@@ -0,0 +1,75 @@
|
|
|
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/consistent-schema-name.ts
|
|
5
|
+
const { zodImportAllowedSource, trackZodSchemaImports } = createZodSchemaImportTrack("all");
|
|
6
|
+
const consistentSchemaName = createZodPluginRule({
|
|
7
|
+
name: "consistent-schema-name",
|
|
8
|
+
meta: {
|
|
9
|
+
type: "suggestion",
|
|
10
|
+
docs: {
|
|
11
|
+
zodImportAllowedSource,
|
|
12
|
+
description: "Enforce a consistent naming convention for Zod schema variables"
|
|
13
|
+
},
|
|
14
|
+
messages: { invalidName: "Rename this Zod schema to \"{{expected}}\"" },
|
|
15
|
+
schema: [{
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
before: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "The required prefix for Zod schema variables"
|
|
21
|
+
},
|
|
22
|
+
after: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: "The required suffix for Zod schema variables"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
additionalProperties: false
|
|
28
|
+
}]
|
|
29
|
+
},
|
|
30
|
+
defaultOptions: [{ after: "Schema" }],
|
|
31
|
+
create(context, [{ before = "", after = "" }]) {
|
|
32
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
|
|
33
|
+
return {
|
|
34
|
+
ImportDeclaration: importDeclarationListener,
|
|
35
|
+
VariableDeclarator(node) {
|
|
36
|
+
const initNode = node.init;
|
|
37
|
+
if (initNode?.type !== AST_NODE_TYPES.CallExpression || !detectZodSchemaRootNode(initNode)) return;
|
|
38
|
+
const chainMethods = collectZodChainMethods(initNode).map((it) => it.name);
|
|
39
|
+
if ([
|
|
40
|
+
"parse",
|
|
41
|
+
"parseAsync",
|
|
42
|
+
"safeParse",
|
|
43
|
+
"safeParseAsync",
|
|
44
|
+
"spa",
|
|
45
|
+
"encode",
|
|
46
|
+
"encodeAsync",
|
|
47
|
+
"decode",
|
|
48
|
+
"decodeAsync",
|
|
49
|
+
"safeEncode",
|
|
50
|
+
"safeEncodeAsync",
|
|
51
|
+
"safeDecode",
|
|
52
|
+
"safeDecodeAsync",
|
|
53
|
+
"codec",
|
|
54
|
+
"treeifyError",
|
|
55
|
+
"prettifyError",
|
|
56
|
+
"formatError",
|
|
57
|
+
"flattenError"
|
|
58
|
+
].some((it) => chainMethods.includes(it))) return;
|
|
59
|
+
if (node.id.type !== AST_NODE_TYPES.Identifier) return;
|
|
60
|
+
const { name } = node.id;
|
|
61
|
+
const validPrefix = !before || name.startsWith(before);
|
|
62
|
+
const validSuffix = !after || name.endsWith(after);
|
|
63
|
+
if (validPrefix && validSuffix) return;
|
|
64
|
+
const expected = (validPrefix ? "" : before) + name + (validSuffix ? "" : after);
|
|
65
|
+
context.report({
|
|
66
|
+
node,
|
|
67
|
+
messageId: "invalidName",
|
|
68
|
+
data: { expected }
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
//#endregion
|
|
75
|
+
export { consistentSchemaName };
|
|
@@ -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 };
|
|
@@ -8,6 +8,7 @@ const requireSchemaSuffix = require_create_plugin_rule.createZodPluginRule({
|
|
|
8
8
|
name: "require-schema-suffix",
|
|
9
9
|
meta: {
|
|
10
10
|
type: "suggestion",
|
|
11
|
+
deprecated: { message: "Use `zod/consistent-schema-name`" },
|
|
11
12
|
docs: {
|
|
12
13
|
zodImportAllowedSource,
|
|
13
14
|
description: "Require schema suffix when declaring a Zod schema"
|
|
@@ -7,6 +7,7 @@ const requireSchemaSuffix = createZodPluginRule({
|
|
|
7
7
|
name: "require-schema-suffix",
|
|
8
8
|
meta: {
|
|
9
9
|
type: "suggestion",
|
|
10
|
+
deprecated: { message: "Use `zod/consistent-schema-name`" },
|
|
10
11
|
docs: {
|
|
11
12
|
zodImportAllowedSource,
|
|
12
13
|
description: "Require schema suffix when declaring a Zod schema"
|
|
@@ -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
|
/**
|