eslint-plugin-zod 4.12.0 → 4.13.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.
Files changed (30) hide show
  1. package/README.md +3 -0
  2. package/dist/index.cjs +6 -0
  3. package/dist/index.mjs +6 -0
  4. package/dist/rules/array-style.cjs +8 -4
  5. package/dist/rules/array-style.mjs +8 -4
  6. package/dist/rules/no-dynamic-schema-value.cjs +17 -0
  7. package/dist/rules/no-dynamic-schema-value.mjs +17 -0
  8. package/dist/rules/no-function-scoped-schema.cjs +17 -0
  9. package/dist/rules/no-function-scoped-schema.mjs +17 -0
  10. package/dist/rules/no-number-schema-with-finite.cjs +1 -1
  11. package/dist/rules/no-number-schema-with-finite.mjs +1 -1
  12. package/dist/rules/no-number-schema-with-step.cjs +1 -1
  13. package/dist/rules/no-number-schema-with-step.mjs +1 -1
  14. package/dist/rules/no-optional-and-default-together.cjs +1 -1
  15. package/dist/rules/no-optional-and-default-together.mjs +1 -1
  16. package/dist/rules/no-transform-in-record-key.cjs +1 -5
  17. package/dist/rules/no-transform-in-record-key.mjs +1 -5
  18. package/dist/rules/prefer-meta-last.cjs +1 -1
  19. package/dist/rules/prefer-meta-last.mjs +1 -1
  20. package/dist/rules/prefer-meta.cjs +1 -1
  21. package/dist/rules/prefer-meta.mjs +1 -1
  22. package/dist/rules/prefer-string-schema-with-trim.cjs +1 -1
  23. package/dist/rules/prefer-string-schema-with-trim.mjs +1 -1
  24. package/dist/rules/prefer-trim-before-string-length-checks.cjs +1 -1
  25. package/dist/rules/prefer-trim-before-string-length-checks.mjs +1 -1
  26. package/dist/rules/prefer-validate.cjs +21 -0
  27. package/dist/rules/prefer-validate.mjs +21 -0
  28. package/dist/utils/has-zod-runtime-import-root.cjs +21 -0
  29. package/dist/utils/has-zod-runtime-import-root.mjs +21 -0
  30. package/package.json +9 -7
package/README.md CHANGED
@@ -44,7 +44,9 @@ Find out more about [Oxlint's `jsPLugins`](https://oxc.rs/docs/guide/usage/linte
44
44
  | [no-coerce-boolean](docs/rules/no-coerce-boolean.md) | Disallow `z.coerce.boolean()` because it treats any non-empty string as `true`. | ✅ | | 💡 | |
45
45
  | [no-conflicting-checks](docs/rules/no-conflicting-checks.md) | Disallow check combinations that can never match, are redundant, or do not apply to the schema type | | | | |
46
46
  | [no-duplicate-schema-methods](docs/rules/no-duplicate-schema-methods.md) | Disallow calling the same schema method more than once in a single chain | ✅ | | | |
47
+ | [no-dynamic-schema-value](docs/rules/no-dynamic-schema-value.md) | Disallow non-static values passed as arguments in a Zod schema expression | | | | |
47
48
  | [no-empty-custom-schema](docs/rules/no-empty-custom-schema.md) | Disallow usage of `z.custom()` without arguments | ✅ | | | |
49
+ | [no-function-scoped-schema](docs/rules/no-function-scoped-schema.md) | Disallow constructing a Zod schema inside a function body | | | | |
48
50
  | [no-native-enum](docs/rules/no-native-enum.md) | Disallow deprecated `z.nativeEnum()` in favor of `z.enum()`. | ✅ | 🔧 | | |
49
51
  | [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. | ✅ | 🔧 | | |
50
52
  | [no-number-schema-with-int](docs/rules/no-number-schema-with-int.md) | Disallow usage of `z.number().int()` as it is considered legacy | ✅ | 🔧 | | |
@@ -73,6 +75,7 @@ Find out more about [Oxlint's `jsPLugins`](https://oxc.rs/docs/guide/usage/linte
73
75
  | [prefer-top-level-string-formats](docs/rules/prefer-top-level-string-formats.md) | Prefer top-level string format schemas over deprecated `z.string().<format>()` methods | ✅ | 🔧 | | |
74
76
  | [prefer-trim-before-string-length-checks](docs/rules/prefer-trim-before-string-length-checks.md) | Enforce `.trim()` is called before string length checks to ensure accurate validation | ✅ | 🔧 | | |
75
77
  | [prefer-tuple-over-array-length](docs/rules/prefer-tuple-over-array-length.md) | Prefer `z.tuple()` over a length-constrained `z.array()` so the length is preserved in the inferred type. | | 🔧 | | |
78
+ | [prefer-validate](docs/rules/prefer-validate.md) | Prefer boolean validation when only the success of parsing is used | | | 💡 | |
76
79
  | [require-brand-type-parameter](docs/rules/require-brand-type-parameter.md) | Require type parameter on `.brand()` functions | ✅ | | 💡 | |
77
80
  | [require-error-message](docs/rules/require-error-message.md) | Enforce that custom refinements include an error message | ✅ | 🔧 | | |
78
81
  | [schema-error-property-style](docs/rules/schema-error-property-style.md) | Enforce consistent style for error messages in Zod schema validation (using ESQuery patterns) | | | | |
package/dist/index.cjs CHANGED
@@ -9,7 +9,9 @@ const require_no_any_schema = require("./rules/no-any-schema.cjs");
9
9
  const require_no_coerce_boolean = require("./rules/no-coerce-boolean.cjs");
10
10
  const require_no_conflicting_checks = require("./rules/no-conflicting-checks.cjs");
11
11
  const require_no_duplicate_schema_methods = require("./rules/no-duplicate-schema-methods.cjs");
12
+ const require_no_dynamic_schema_value = require("./rules/no-dynamic-schema-value.cjs");
12
13
  const require_no_empty_custom_schema = require("./rules/no-empty-custom-schema.cjs");
14
+ const require_no_function_scoped_schema = require("./rules/no-function-scoped-schema.cjs");
13
15
  const require_no_native_enum = require("./rules/no-native-enum.cjs");
14
16
  const require_no_number_schema_with_finite = require("./rules/no-number-schema-with-finite.cjs");
15
17
  const require_no_number_schema_with_int = require("./rules/no-number-schema-with-int.cjs");
@@ -38,6 +40,7 @@ const require_prefer_string_schema_with_trim = require("./rules/prefer-string-sc
38
40
  const require_prefer_top_level_string_formats = require("./rules/prefer-top-level-string-formats.cjs");
39
41
  const require_prefer_trim_before_string_length_checks = require("./rules/prefer-trim-before-string-length-checks.cjs");
40
42
  const require_prefer_tuple_over_array_length = require("./rules/prefer-tuple-over-array-length.cjs");
43
+ const require_prefer_validate = require("./rules/prefer-validate.cjs");
41
44
  const require_require_brand_type_parameter = require("./rules/require-brand-type-parameter.cjs");
42
45
  const require_require_error_message = require("./rules/require-error-message.cjs");
43
46
  const require_schema_error_property_style = require("./rules/schema-error-property-style.cjs");
@@ -58,7 +61,9 @@ const eslintPluginZod = {
58
61
  "no-coerce-boolean": require_no_coerce_boolean.noCoerceBoolean,
59
62
  "no-conflicting-checks": require_no_conflicting_checks.noConflictingChecks,
60
63
  "no-duplicate-schema-methods": require_no_duplicate_schema_methods.noDuplicateSchemaMethods,
64
+ "no-dynamic-schema-value": require_no_dynamic_schema_value.noDynamicSchemaValue,
61
65
  "no-empty-custom-schema": require_no_empty_custom_schema.noEmptyCustomSchema,
66
+ "no-function-scoped-schema": require_no_function_scoped_schema.noFunctionScopedSchema,
62
67
  "no-native-enum": require_no_native_enum.noNativeEnum,
63
68
  "no-number-schema-with-finite": require_no_number_schema_with_finite.noNumberSchemaWithFinite,
64
69
  "no-number-schema-with-int": require_no_number_schema_with_int.noNumberSchemaWithInt,
@@ -87,6 +92,7 @@ const eslintPluginZod = {
87
92
  "prefer-string-schema-with-trim": require_prefer_string_schema_with_trim.preferStringSchemaWithTrim,
88
93
  "prefer-trim-before-string-length-checks": require_prefer_trim_before_string_length_checks.preferTrimBeforeStringLengthChecks,
89
94
  "prefer-tuple-over-array-length": require_prefer_tuple_over_array_length.preferTupleOverArrayLength,
95
+ "prefer-validate": require_prefer_validate.preferValidate,
90
96
  "require-brand-type-parameter": require_require_brand_type_parameter.requireBrandTypeParameter,
91
97
  "require-error-message": require_require_error_message.requireErrorMessage,
92
98
  "schema-error-property-style": require_schema_error_property_style.schemaErrorPropertyStyle
package/dist/index.mjs CHANGED
@@ -9,7 +9,9 @@ import { noAnySchema } from "./rules/no-any-schema.mjs";
9
9
  import { noCoerceBoolean } from "./rules/no-coerce-boolean.mjs";
10
10
  import { noConflictingChecks } from "./rules/no-conflicting-checks.mjs";
11
11
  import { noDuplicateSchemaMethods } from "./rules/no-duplicate-schema-methods.mjs";
12
+ import { noDynamicSchemaValue } from "./rules/no-dynamic-schema-value.mjs";
12
13
  import { noEmptyCustomSchema } from "./rules/no-empty-custom-schema.mjs";
14
+ import { noFunctionScopedSchema } from "./rules/no-function-scoped-schema.mjs";
13
15
  import { noNativeEnum } from "./rules/no-native-enum.mjs";
14
16
  import { noNumberSchemaWithFinite } from "./rules/no-number-schema-with-finite.mjs";
15
17
  import { noNumberSchemaWithInt } from "./rules/no-number-schema-with-int.mjs";
@@ -38,6 +40,7 @@ import { preferStringSchemaWithTrim } from "./rules/prefer-string-schema-with-tr
38
40
  import { preferTopLevelStringFormats } from "./rules/prefer-top-level-string-formats.mjs";
39
41
  import { preferTrimBeforeStringLengthChecks } from "./rules/prefer-trim-before-string-length-checks.mjs";
40
42
  import { preferTupleOverArrayLength } from "./rules/prefer-tuple-over-array-length.mjs";
43
+ import { preferValidate } from "./rules/prefer-validate.mjs";
41
44
  import { requireBrandTypeParameter } from "./rules/require-brand-type-parameter.mjs";
42
45
  import { requireErrorMessage } from "./rules/require-error-message.mjs";
43
46
  import { schemaErrorPropertyStyle } from "./rules/schema-error-property-style.mjs";
@@ -58,7 +61,9 @@ const eslintPluginZod = {
58
61
  "no-coerce-boolean": noCoerceBoolean,
59
62
  "no-conflicting-checks": noConflictingChecks,
60
63
  "no-duplicate-schema-methods": noDuplicateSchemaMethods,
64
+ "no-dynamic-schema-value": noDynamicSchemaValue,
61
65
  "no-empty-custom-schema": noEmptyCustomSchema,
66
+ "no-function-scoped-schema": noFunctionScopedSchema,
62
67
  "no-native-enum": noNativeEnum,
63
68
  "no-number-schema-with-finite": noNumberSchemaWithFinite,
64
69
  "no-number-schema-with-int": noNumberSchemaWithInt,
@@ -87,6 +92,7 @@ const eslintPluginZod = {
87
92
  "prefer-string-schema-with-trim": preferStringSchemaWithTrim,
88
93
  "prefer-trim-before-string-length-checks": preferTrimBeforeStringLengthChecks,
89
94
  "prefer-tuple-over-array-length": preferTupleOverArrayLength,
95
+ "prefer-validate": preferValidate,
90
96
  "require-brand-type-parameter": requireBrandTypeParameter,
91
97
  "require-error-message": requireErrorMessage,
92
98
  "schema-error-property-style": schemaErrorPropertyStyle
@@ -1,5 +1,7 @@
1
1
  const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
2
+ const require_has_zod_runtime_import_root = require("../utils/has-zod-runtime-import-root.cjs");
2
3
  let _eslint_zod_utils = require("@eslint-zod/utils");
4
+ let _typescript_eslint_utils = require("@typescript-eslint/utils");
3
5
  const arrayStyle = require_create_plugin_rule.createZodPluginRule({
4
6
  name: "array-style",
5
7
  meta: {
@@ -23,8 +25,9 @@ const arrayStyle = require_create_plugin_rule.createZodPluginRule({
23
25
  defaultOptions: [{ style: "function" }],
24
26
  create(context, [{ style }]) {
25
27
  const { sourceCode } = context;
26
- const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker();
28
+ const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker({ kind: "value" });
27
29
  return createSchemaVisitor({ onSchema(node, zodSchemaMeta) {
30
+ if (!require_has_zod_runtime_import_root.hasZodRuntimeImportRoot(node, sourceCode)) return;
28
31
  const { schemaDecl, schemaType } = zodSchemaMeta;
29
32
  if (style === "method") {
30
33
  if (schemaType === "array") {
@@ -34,9 +37,10 @@ const arrayStyle = require_create_plugin_rule.createZodPluginRule({
34
37
  messageId: "useMethod",
35
38
  fix(fixer) {
36
39
  const arrayCall = collectZodChainMethods(node).find((c) => c.name === "array");
37
- if (!arrayCall) return null;
38
- const arg = arrayCall.node.arguments.at(0);
39
- if (!arg) return null;
40
+ if (arrayCall?.node.arguments.length !== 1 || arrayCall.node.typeArguments || arrayCall.node.optional || arrayCall.node.callee.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && arrayCall.node.callee.optional) return null;
41
+ const [arg] = arrayCall.node.arguments;
42
+ if (arg.type !== _typescript_eslint_utils.AST_NODE_TYPES.Identifier && arg.type !== _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && arg.type !== _typescript_eslint_utils.AST_NODE_TYPES.CallExpression) return null;
43
+ if (sourceCode.getCommentsInside(arrayCall.node).some((comment) => comment.range[0] < arg.range[0] || comment.range[1] > arg.range[1])) return null;
40
44
  const argText = sourceCode.getText(arg);
41
45
  return fixer.replaceText(arrayCall.node, `${argText}.array()`);
42
46
  }
@@ -1,5 +1,7 @@
1
1
  import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
+ import { hasZodRuntimeImportRoot } from "../utils/has-zod-runtime-import-root.mjs";
2
3
  import { zodImportScope } from "@eslint-zod/utils";
4
+ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
3
5
  const arrayStyle = createZodPluginRule({
4
6
  name: "array-style",
5
7
  meta: {
@@ -23,8 +25,9 @@ const arrayStyle = createZodPluginRule({
23
25
  defaultOptions: [{ style: "function" }],
24
26
  create(context, [{ style }]) {
25
27
  const { sourceCode } = context;
26
- const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker();
28
+ const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker({ kind: "value" });
27
29
  return createSchemaVisitor({ onSchema(node, zodSchemaMeta) {
30
+ if (!hasZodRuntimeImportRoot(node, sourceCode)) return;
28
31
  const { schemaDecl, schemaType } = zodSchemaMeta;
29
32
  if (style === "method") {
30
33
  if (schemaType === "array") {
@@ -34,9 +37,10 @@ const arrayStyle = createZodPluginRule({
34
37
  messageId: "useMethod",
35
38
  fix(fixer) {
36
39
  const arrayCall = collectZodChainMethods(node).find((c) => c.name === "array");
37
- if (!arrayCall) return null;
38
- const arg = arrayCall.node.arguments.at(0);
39
- if (!arg) return null;
40
+ if (arrayCall?.node.arguments.length !== 1 || arrayCall.node.typeArguments || arrayCall.node.optional || arrayCall.node.callee.type === AST_NODE_TYPES.MemberExpression && arrayCall.node.callee.optional) return null;
41
+ const [arg] = arrayCall.node.arguments;
42
+ if (arg.type !== AST_NODE_TYPES.Identifier && arg.type !== AST_NODE_TYPES.MemberExpression && arg.type !== AST_NODE_TYPES.CallExpression) return null;
43
+ if (sourceCode.getCommentsInside(arrayCall.node).some((comment) => comment.range[0] < arg.range[0] || comment.range[1] > arg.range[1])) return null;
40
44
  const argText = sourceCode.getText(arg);
41
45
  return fixer.replaceText(arrayCall.node, `${argText}.array()`);
42
46
  }
@@ -0,0 +1,17 @@
1
+ const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
2
+ let _eslint_zod_utils = require("@eslint-zod/utils");
3
+ let _eslint_zod_utils_rule_builders_no_dynamic_schema_value = require("@eslint-zod/utils/rule-builders/no-dynamic-schema-value");
4
+ //#region src/rules/no-dynamic-schema-value.ts
5
+ const noDynamicSchemaValue = require_create_plugin_rule.createZodPluginRule({
6
+ name: "no-dynamic-schema-value",
7
+ meta: {
8
+ type: "suggestion",
9
+ docs: { description: "Disallow non-static values passed as arguments in a Zod schema expression" },
10
+ messages: { dynamicValue: "This value must be a literal or an imported binding to be hoistable by tools like `zod-compiler`." },
11
+ schema: []
12
+ },
13
+ defaultOptions: [],
14
+ create: (0, _eslint_zod_utils_rule_builders_no_dynamic_schema_value.buildNoDynamicSchemaValueCreate)(_eslint_zod_utils.zodImportScope)
15
+ });
16
+ //#endregion
17
+ exports.noDynamicSchemaValue = noDynamicSchemaValue;
@@ -0,0 +1,17 @@
1
+ import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
+ import { zodImportScope } from "@eslint-zod/utils";
3
+ import { buildNoDynamicSchemaValueCreate } from "@eslint-zod/utils/rule-builders/no-dynamic-schema-value";
4
+ //#region src/rules/no-dynamic-schema-value.ts
5
+ const noDynamicSchemaValue = createZodPluginRule({
6
+ name: "no-dynamic-schema-value",
7
+ meta: {
8
+ type: "suggestion",
9
+ docs: { description: "Disallow non-static values passed as arguments in a Zod schema expression" },
10
+ messages: { dynamicValue: "This value must be a literal or an imported binding to be hoistable by tools like `zod-compiler`." },
11
+ schema: []
12
+ },
13
+ defaultOptions: [],
14
+ create: buildNoDynamicSchemaValueCreate(zodImportScope)
15
+ });
16
+ //#endregion
17
+ export { noDynamicSchemaValue };
@@ -0,0 +1,17 @@
1
+ const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
2
+ let _eslint_zod_utils = require("@eslint-zod/utils");
3
+ let _eslint_zod_utils_rule_builders_no_function_scoped_schema = require("@eslint-zod/utils/rule-builders/no-function-scoped-schema");
4
+ //#region src/rules/no-function-scoped-schema.ts
5
+ const noFunctionScopedSchema = require_create_plugin_rule.createZodPluginRule({
6
+ name: "no-function-scoped-schema",
7
+ meta: {
8
+ type: "suggestion",
9
+ docs: { description: "Disallow constructing a Zod schema inside a function body" },
10
+ messages: { functionScopedSchema: "This schema is constructed inside a function and rebuilt on every call. Declare it once at module scope instead." },
11
+ schema: []
12
+ },
13
+ defaultOptions: [],
14
+ create: (0, _eslint_zod_utils_rule_builders_no_function_scoped_schema.buildNoFunctionScopedSchemaCreate)(_eslint_zod_utils.zodImportScope)
15
+ });
16
+ //#endregion
17
+ exports.noFunctionScopedSchema = noFunctionScopedSchema;
@@ -0,0 +1,17 @@
1
+ import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
+ import { zodImportScope } from "@eslint-zod/utils";
3
+ import { buildNoFunctionScopedSchemaCreate } from "@eslint-zod/utils/rule-builders/no-function-scoped-schema";
4
+ //#region src/rules/no-function-scoped-schema.ts
5
+ const noFunctionScopedSchema = createZodPluginRule({
6
+ name: "no-function-scoped-schema",
7
+ meta: {
8
+ type: "suggestion",
9
+ docs: { description: "Disallow constructing a Zod schema inside a function body" },
10
+ messages: { functionScopedSchema: "This schema is constructed inside a function and rebuilt on every call. Declare it once at module scope instead." },
11
+ schema: []
12
+ },
13
+ defaultOptions: [],
14
+ create: buildNoFunctionScopedSchemaCreate(zodImportScope)
15
+ });
16
+ //#endregion
17
+ export { noFunctionScopedSchema };
@@ -12,7 +12,7 @@ const noNumberSchemaWithFinite = require_create_plugin_rule.createZodPluginRule(
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({
17
17
  schemaType: "number",
18
18
  onSchema(node, zodSchemaMeta) {
@@ -12,7 +12,7 @@ const noNumberSchemaWithFinite = createZodPluginRule({
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({
17
17
  schemaType: "number",
18
18
  onSchema(node, zodSchemaMeta) {
@@ -12,7 +12,7 @@ const noNumberSchemaWithStep = require_create_plugin_rule.createZodPluginRule({
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({
17
17
  schemaType: "number",
18
18
  onSchema(node, zodSchemaMeta) {
@@ -12,7 +12,7 @@ const noNumberSchemaWithStep = createZodPluginRule({
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({
17
17
  schemaType: "number",
18
18
  onSchema(node, zodSchemaMeta) {
@@ -30,7 +30,7 @@ const noOptionalAndDefaultTogether = require_create_plugin_rule.createZodPluginR
30
30
  defaultOptions: [defaultOptions],
31
31
  create(context, [{ preferredMethod }]) {
32
32
  const { sourceCode } = context;
33
- const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker();
33
+ const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker({ kind: "value" });
34
34
  return createSchemaVisitor({ onSchema(node) {
35
35
  const methods = collectZodChainMethods(node);
36
36
  if (methods.length === 0) return;
@@ -30,7 +30,7 @@ const noOptionalAndDefaultTogether = createZodPluginRule({
30
30
  defaultOptions: [defaultOptions],
31
31
  create(context, [{ preferredMethod }]) {
32
32
  const { sourceCode } = context;
33
- const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker();
33
+ const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker({ kind: "value" });
34
34
  return createSchemaVisitor({ onSchema(node) {
35
35
  const methods = collectZodChainMethods(node);
36
36
  if (methods.length === 0) return;
@@ -1,6 +1,5 @@
1
1
  const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
2
2
  let _eslint_zod_utils = require("@eslint-zod/utils");
3
- let _typescript_eslint_utils = require("@typescript-eslint/utils");
4
3
  let _eslint_zod_utils_rule_builders_no_transform_in_record_key = require("@eslint-zod/utils/rule-builders/no-transform-in-record-key");
5
4
  //#region src/rules/no-transform-in-record-key.ts
6
5
  const TRANSFORM_METHODS = [
@@ -17,10 +16,7 @@ const noTransformInRecordKey = require_create_plugin_rule.createZodPluginRule({
17
16
  schema: []
18
17
  },
19
18
  defaultOptions: [],
20
- create: (0, _eslint_zod_utils_rule_builders_no_transform_in_record_key.buildNoTransformInRecordKeyCreate)(_eslint_zod_utils.zodImportScope, { findTransformNode(keySchema, { collectZodChainMethods }) {
21
- if (keySchema.type !== _typescript_eslint_utils.AST_NODE_TYPES.CallExpression) return null;
22
- return collectZodChainMethods(keySchema).some((method) => TRANSFORM_METHODS.includes(method.name)) ? keySchema : null;
23
- } })
19
+ create: (0, _eslint_zod_utils_rule_builders_no_transform_in_record_key.buildNoTransformInRecordKeyCreate)(_eslint_zod_utils.zodImportScope, TRANSFORM_METHODS)
24
20
  });
25
21
  //#endregion
26
22
  exports.noTransformInRecordKey = noTransformInRecordKey;
@@ -1,6 +1,5 @@
1
1
  import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
2
  import { ZOD_MUTATING_CHECK_NAMES, zodImportScope } from "@eslint-zod/utils";
3
- import { AST_NODE_TYPES } from "@typescript-eslint/utils";
4
3
  import { buildNoTransformInRecordKeyCreate } from "@eslint-zod/utils/rule-builders/no-transform-in-record-key";
5
4
  //#region src/rules/no-transform-in-record-key.ts
6
5
  const TRANSFORM_METHODS = [
@@ -17,10 +16,7 @@ const noTransformInRecordKey = createZodPluginRule({
17
16
  schema: []
18
17
  },
19
18
  defaultOptions: [],
20
- create: buildNoTransformInRecordKeyCreate(zodImportScope, { findTransformNode(keySchema, { collectZodChainMethods }) {
21
- if (keySchema.type !== AST_NODE_TYPES.CallExpression) return null;
22
- return collectZodChainMethods(keySchema).some((method) => TRANSFORM_METHODS.includes(method.name)) ? keySchema : null;
23
- } })
19
+ create: buildNoTransformInRecordKeyCreate(zodImportScope, TRANSFORM_METHODS)
24
20
  });
25
21
  //#endregion
26
22
  export { noTransformInRecordKey };
@@ -12,7 +12,7 @@ const preferMetaLast = require_create_plugin_rule.createZodPluginRule({
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({ onSchema(node) {
17
17
  const chain = collectZodChainMethods(node);
18
18
  const metaIndex = chain.findIndex((c) => c.name === "meta");
@@ -12,7 +12,7 @@ const preferMetaLast = createZodPluginRule({
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({ onSchema(node) {
17
17
  const chain = collectZodChainMethods(node);
18
18
  const metaIndex = chain.findIndex((c) => c.name === "meta");
@@ -12,7 +12,7 @@ const preferMeta = require_create_plugin_rule.createZodPluginRule({
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({ onSchema(node) {
17
17
  const describe = collectZodChainMethods(node).find((it) => it.name === "describe");
18
18
  if (!describe) return;
@@ -12,7 +12,7 @@ const preferMeta = createZodPluginRule({
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({ onSchema(node) {
17
17
  const describe = collectZodChainMethods(node).find((it) => it.name === "describe");
18
18
  if (!describe) return;
@@ -12,7 +12,7 @@ const preferStringSchemaWithTrim = require_create_plugin_rule.createZodPluginRul
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({
17
17
  schemaType: "string",
18
18
  onSchema(node, zodSchemaMeta) {
@@ -12,7 +12,7 @@ const preferStringSchemaWithTrim = createZodPluginRule({
12
12
  },
13
13
  defaultOptions: [],
14
14
  create(context) {
15
- const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker();
15
+ const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker({ kind: "value" });
16
16
  return createSchemaVisitor({
17
17
  schemaType: "string",
18
18
  onSchema(node, zodSchemaMeta) {
@@ -17,7 +17,7 @@ const preferTrimBeforeStringLengthChecks = require_create_plugin_rule.createZodP
17
17
  },
18
18
  defaultOptions: [],
19
19
  create(context) {
20
- const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker();
20
+ const { createSchemaVisitor, collectZodChainMethods } = _eslint_zod_utils.zodImportScope.createTracker({ kind: "value" });
21
21
  return createSchemaVisitor({
22
22
  schemaType: "string",
23
23
  onSchema(node, zodSchemaMeta) {
@@ -17,7 +17,7 @@ const preferTrimBeforeStringLengthChecks = createZodPluginRule({
17
17
  },
18
18
  defaultOptions: [],
19
19
  create(context) {
20
- const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker();
20
+ const { createSchemaVisitor, collectZodChainMethods } = zodImportScope.createTracker({ kind: "value" });
21
21
  return createSchemaVisitor({
22
22
  schemaType: "string",
23
23
  onSchema(node, zodSchemaMeta) {
@@ -0,0 +1,21 @@
1
+ const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
2
+ let _eslint_zod_utils = require("@eslint-zod/utils");
3
+ let _eslint_zod_utils_rule_builders_prefer_validate = require("@eslint-zod/utils/rule-builders/prefer-validate");
4
+ //#region src/rules/prefer-validate.ts
5
+ const preferValidate = require_create_plugin_rule.createZodPluginRule({
6
+ name: "prefer-validate",
7
+ meta: {
8
+ type: "suggestion",
9
+ hasSuggestions: true,
10
+ docs: { description: "Prefer boolean validation when only the success of parsing is used" },
11
+ messages: {
12
+ preferValidate: "Prefer `{{method}}` when only parse success is used.",
13
+ useValidate: "Replace success-only parsing with `{{method}}`."
14
+ },
15
+ schema: []
16
+ },
17
+ defaultOptions: [],
18
+ create: (0, _eslint_zod_utils_rule_builders_prefer_validate.buildPreferValidateCreate)(_eslint_zod_utils.zodImportScope, "schema-method")
19
+ });
20
+ //#endregion
21
+ exports.preferValidate = preferValidate;
@@ -0,0 +1,21 @@
1
+ import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
+ import { zodImportScope } from "@eslint-zod/utils";
3
+ import { buildPreferValidateCreate } from "@eslint-zod/utils/rule-builders/prefer-validate";
4
+ //#region src/rules/prefer-validate.ts
5
+ const preferValidate = createZodPluginRule({
6
+ name: "prefer-validate",
7
+ meta: {
8
+ type: "suggestion",
9
+ hasSuggestions: true,
10
+ docs: { description: "Prefer boolean validation when only the success of parsing is used" },
11
+ messages: {
12
+ preferValidate: "Prefer `{{method}}` when only parse success is used.",
13
+ useValidate: "Replace success-only parsing with `{{method}}`."
14
+ },
15
+ schema: []
16
+ },
17
+ defaultOptions: [],
18
+ create: buildPreferValidateCreate(zodImportScope, "schema-method")
19
+ });
20
+ //#endregion
21
+ export { preferValidate };
@@ -0,0 +1,21 @@
1
+ let _eslint_zod_utils = require("@eslint-zod/utils");
2
+ let _typescript_eslint_utils = require("@typescript-eslint/utils");
3
+ //#region src/utils/has-zod-runtime-import-root.ts
4
+ /**
5
+ * Whether the identifier a chain starts from resolves to a runtime zod import.
6
+ * Bails on a root that is shadowed, re-assigned, type-only, or imported from another source —
7
+ * every case where a fix would rewrite code that is not zod, or reference a binding erased at runtime.
8
+ */
9
+ function hasZodRuntimeImportRoot(node, sourceCode) {
10
+ let root = node;
11
+ while (root.type === _typescript_eslint_utils.AST_NODE_TYPES.CallExpression || root.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression) root = root.type === _typescript_eslint_utils.AST_NODE_TYPES.CallExpression ? root.callee : root.object;
12
+ if (root.type !== _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return false;
13
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(root), root);
14
+ if (variable?.defs.length !== 1) return false;
15
+ const [definition] = variable.defs;
16
+ if (definition.type !== _typescript_eslint_utils.TSESLint.Scope.DefinitionType.ImportBinding || definition.parent.type !== _typescript_eslint_utils.AST_NODE_TYPES.ImportDeclaration || definition.parent.importKind === "type" || !_eslint_zod_utils.zodImportScope.isAllowed(definition.parent.source.value)) return false;
17
+ const specifier = definition.node;
18
+ return specifier.type === _typescript_eslint_utils.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === _typescript_eslint_utils.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === _typescript_eslint_utils.AST_NODE_TYPES.ImportSpecifier && specifier.importKind !== "type";
19
+ }
20
+ //#endregion
21
+ exports.hasZodRuntimeImportRoot = hasZodRuntimeImportRoot;
@@ -0,0 +1,21 @@
1
+ import { zodImportScope } from "@eslint-zod/utils";
2
+ import { ASTUtils, AST_NODE_TYPES, TSESLint } from "@typescript-eslint/utils";
3
+ //#region src/utils/has-zod-runtime-import-root.ts
4
+ /**
5
+ * Whether the identifier a chain starts from resolves to a runtime zod import.
6
+ * Bails on a root that is shadowed, re-assigned, type-only, or imported from another source —
7
+ * every case where a fix would rewrite code that is not zod, or reference a binding erased at runtime.
8
+ */
9
+ function hasZodRuntimeImportRoot(node, sourceCode) {
10
+ let root = node;
11
+ while (root.type === AST_NODE_TYPES.CallExpression || root.type === AST_NODE_TYPES.MemberExpression) root = root.type === AST_NODE_TYPES.CallExpression ? root.callee : root.object;
12
+ if (root.type !== AST_NODE_TYPES.Identifier) return false;
13
+ const variable = ASTUtils.findVariable(sourceCode.getScope(root), root);
14
+ if (variable?.defs.length !== 1) return false;
15
+ const [definition] = variable.defs;
16
+ if (definition.type !== TSESLint.Scope.DefinitionType.ImportBinding || definition.parent.type !== AST_NODE_TYPES.ImportDeclaration || definition.parent.importKind === "type" || !zodImportScope.isAllowed(definition.parent.source.value)) return false;
17
+ const specifier = definition.node;
18
+ return specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === AST_NODE_TYPES.ImportSpecifier && specifier.importKind !== "type";
19
+ }
20
+ //#endregion
21
+ export { hasZodRuntimeImportRoot };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-zod",
3
- "version": "4.12.0",
3
+ "version": "4.13.0",
4
4
  "description": "ESLint plugin that adds custom linting rules to enforce best practices when using Zod",
5
5
  "keywords": [
6
6
  "eslint",
@@ -38,15 +38,16 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@typescript-eslint/utils": "^8.67.0",
42
- "@eslint-zod/utils": "3.3.0"
41
+ "@typescript-eslint/utils": "^8.70.0",
42
+ "@eslint-zod/utils": "5.0.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@typescript-eslint/rule-tester": "8.67.0",
45
+ "@arethetypeswrong/cli": "0.18.5",
46
+ "@typescript-eslint/rule-tester": "8.70.0",
46
47
  "dedent": "1.7.2",
47
- "eslint-doc-generator": "3.7.0",
48
- "tsdown": "0.22.14",
49
- "vitest": "4.1.11",
48
+ "eslint-doc-generator": "3.7.1",
49
+ "tsdown": "0.23.0",
50
+ "vitest": "5.0.0",
50
51
  "@eslint-zod/tooling": "0.0.0"
51
52
  },
52
53
  "peerDependencies": {
@@ -70,6 +71,7 @@
70
71
  },
71
72
  "scripts": {
72
73
  "build": "tsdown",
74
+ "lint:publish": "attw --pack .",
73
75
  "build:docs": "pnpm run build && eslint-doc-generator",
74
76
  "lint:docs": "eslint-doc-generator --check",
75
77
  "test": "vitest"