eslint-plugin-zod 4.2.2 → 4.4.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 CHANGED
@@ -50,13 +50,17 @@ Find out more about [Oxlint's `jsPLugins`](https://oxc.rs/docs/guide/usage/linte
50
50
  | [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()`. | ✅ | 🔧 | | |
51
51
  | [no-number-schema-with-step](docs/rules/no-number-schema-with-step.md) | Disallow deprecated `z.number().step()`. Use `.multipleOf()` instead. | ✅ | 🔧 | | |
52
52
  | [no-optional-and-default-together](docs/rules/no-optional-and-default-together.md) | Disallow using both `.optional()` and `.default()` on the same Zod schema | ✅ | 🔧 | | |
53
+ | [no-schema-with-is-nullable](docs/rules/no-schema-with-is-nullable.md) | Disallow deprecated `.isNullable()` on a Zod schema; use `safeParse(null).success` instead. | ✅ | | | |
54
+ | [no-schema-with-is-optional](docs/rules/no-schema-with-is-optional.md) | Disallow deprecated `.isOptional()` on a Zod schema; use `safeParse(undefined).success` instead. | ✅ | | | |
53
55
  | [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 | ✅ | 🔧 | | ❌ |
54
56
  | [no-throw-in-refine](docs/rules/no-throw-in-refine.md) | Disallow throwing errors directly inside Zod refine callbacks | ✅ | | | |
55
57
  | [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 | | | | |
56
58
  | [no-unknown-schema](docs/rules/no-unknown-schema.md) | Disallow usage of `z.unknown()` in Zod schemas | | | | |
57
59
  | [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. | ✅ | 🔧 | | |
60
+ | [prefer-loose-object](docs/rules/prefer-loose-object.md) | Prefer `z.looseObject()` over `z.object().passthrough()` and `z.object().loose()` | ✅ | 🔧 | | |
58
61
  | [prefer-meta](docs/rules/prefer-meta.md) | Enforce usage of `.meta()` over `.describe()` | ✅ | 🔧 | | |
59
62
  | [prefer-meta-last](docs/rules/prefer-meta-last.md) | Enforce `.meta()` as last method | ✅ | 🔧 | | |
63
+ | [prefer-strict-object](docs/rules/prefer-strict-object.md) | Prefer `z.strictObject()` over `z.object().strict()` | ✅ | 🔧 | | |
60
64
  | [prefer-string-schema-with-trim](docs/rules/prefer-string-schema-with-trim.md) | Enforce `z.string().trim()` to prevent accidental leading/trailing whitespace | ✅ | 🔧 | | |
61
65
  | [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 | ✅ | 🔧 | | |
62
66
  | [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 | ✅ | 🔧 | | |
@@ -106,10 +110,7 @@ import { defineConfig } from 'eslint/config';
106
110
  import eslint from '@eslint/js';
107
111
  import eslintPluginZod from 'eslint-plugin-zod';
108
112
 
109
- export default defineConfig(
110
- eslint.configs.recommended,
111
- eslintPluginZod.configs.recommended,
112
- );
113
+ export default defineConfig(eslint.configs.recommended, eslintPluginZod.configs.recommended);
113
114
  ```
114
115
 
115
116
  ### Oxlint
package/dist/index.cjs CHANGED
@@ -15,13 +15,17 @@ const require_no_number_schema_with_is_int = require("./rules/no-number-schema-w
15
15
  const require_no_number_schema_with_safe = require("./rules/no-number-schema-with-safe.cjs");
16
16
  const require_no_number_schema_with_step = require("./rules/no-number-schema-with-step.cjs");
17
17
  const require_no_optional_and_default_together = require("./rules/no-optional-and-default-together.cjs");
18
+ const require_no_schema_with_is_nullable = require("./rules/no-schema-with-is-nullable.cjs");
19
+ const require_no_schema_with_is_optional = require("./rules/no-schema-with-is-optional.cjs");
18
20
  const require_no_string_schema_with_uuid = require("./rules/no-string-schema-with-uuid.cjs");
19
21
  const require_no_throw_in_refine = require("./rules/no-throw-in-refine.cjs");
20
22
  const require_no_transform_in_record_key = require("./rules/no-transform-in-record-key.cjs");
21
23
  const require_no_unknown_schema = require("./rules/no-unknown-schema.cjs");
22
24
  const require_prefer_enum_over_literal_union = require("./rules/prefer-enum-over-literal-union.cjs");
25
+ const require_prefer_loose_object = require("./rules/prefer-loose-object.cjs");
23
26
  const require_prefer_meta_last = require("./rules/prefer-meta-last.cjs");
24
27
  const require_prefer_meta = require("./rules/prefer-meta.cjs");
28
+ const require_prefer_strict_object = require("./rules/prefer-strict-object.cjs");
25
29
  const require_prefer_string_schema_with_trim = require("./rules/prefer-string-schema-with-trim.cjs");
26
30
  const require_prefer_top_level_string_formats = require("./rules/prefer-top-level-string-formats.cjs");
27
31
  const require_prefer_trim_before_string_length_checks = require("./rules/prefer-trim-before-string-length-checks.cjs");
@@ -52,12 +56,16 @@ const eslintPluginZod = {
52
56
  "no-number-schema-with-step": require_no_number_schema_with_step.noNumberSchemaWithStep,
53
57
  "no-string-schema-with-uuid": require_no_string_schema_with_uuid.noStringSchemaWithUuid,
54
58
  "no-optional-and-default-together": require_no_optional_and_default_together.noOptionalAndDefaultTogether,
59
+ "no-schema-with-is-nullable": require_no_schema_with_is_nullable.noSchemaWithIsNullable,
60
+ "no-schema-with-is-optional": require_no_schema_with_is_optional.noSchemaWithIsOptional,
55
61
  "no-throw-in-refine": require_no_throw_in_refine.noThrowInRefine,
56
62
  "no-transform-in-record-key": require_no_transform_in_record_key.noTransformInRecordKey,
57
63
  "no-unknown-schema": require_no_unknown_schema.noUnknownSchema,
58
64
  "prefer-enum-over-literal-union": require_prefer_enum_over_literal_union.preferEnumOverLiteralUnion,
65
+ "prefer-loose-object": require_prefer_loose_object.preferLooseObject,
59
66
  "prefer-meta": require_prefer_meta.preferMeta,
60
67
  "prefer-meta-last": require_prefer_meta_last.preferMetaLast,
68
+ "prefer-strict-object": require_prefer_strict_object.preferStrictObject,
61
69
  "prefer-top-level-string-formats": require_prefer_top_level_string_formats.preferTopLevelStringFormats,
62
70
  "prefer-string-schema-with-trim": require_prefer_string_schema_with_trim.preferStringSchemaWithTrim,
63
71
  "prefer-trim-before-string-length-checks": require_prefer_trim_before_string_length_checks.preferTrimBeforeStringLengthChecks,
@@ -85,10 +93,14 @@ const recommendedConfig = {
85
93
  "zod/no-number-schema-with-step": "error",
86
94
  "zod/no-string-schema-with-uuid": "error",
87
95
  "zod/no-optional-and-default-together": "error",
96
+ "zod/no-schema-with-is-nullable": "error",
97
+ "zod/no-schema-with-is-optional": "error",
88
98
  "zod/no-throw-in-refine": "error",
89
99
  "zod/prefer-enum-over-literal-union": "error",
100
+ "zod/prefer-loose-object": "error",
90
101
  "zod/prefer-meta": "error",
91
102
  "zod/prefer-meta-last": "error",
103
+ "zod/prefer-strict-object": "error",
92
104
  "zod/prefer-top-level-string-formats": "error",
93
105
  "zod/prefer-string-schema-with-trim": "error",
94
106
  "zod/prefer-trim-before-string-length-checks": "error",
package/dist/index.mjs CHANGED
@@ -15,13 +15,17 @@ import { noNumberSchemaWithIsInt } from "./rules/no-number-schema-with-is-int.mj
15
15
  import { noNumberSchemaWithSafe } from "./rules/no-number-schema-with-safe.mjs";
16
16
  import { noNumberSchemaWithStep } from "./rules/no-number-schema-with-step.mjs";
17
17
  import { noOptionalAndDefaultTogether } from "./rules/no-optional-and-default-together.mjs";
18
+ import { noSchemaWithIsNullable } from "./rules/no-schema-with-is-nullable.mjs";
19
+ import { noSchemaWithIsOptional } from "./rules/no-schema-with-is-optional.mjs";
18
20
  import { noStringSchemaWithUuid } from "./rules/no-string-schema-with-uuid.mjs";
19
21
  import { noThrowInRefine } from "./rules/no-throw-in-refine.mjs";
20
22
  import { noTransformInRecordKey } from "./rules/no-transform-in-record-key.mjs";
21
23
  import { noUnknownSchema } from "./rules/no-unknown-schema.mjs";
22
24
  import { preferEnumOverLiteralUnion } from "./rules/prefer-enum-over-literal-union.mjs";
25
+ import { preferLooseObject } from "./rules/prefer-loose-object.mjs";
23
26
  import { preferMetaLast } from "./rules/prefer-meta-last.mjs";
24
27
  import { preferMeta } from "./rules/prefer-meta.mjs";
28
+ import { preferStrictObject } from "./rules/prefer-strict-object.mjs";
25
29
  import { preferStringSchemaWithTrim } from "./rules/prefer-string-schema-with-trim.mjs";
26
30
  import { preferTopLevelStringFormats } from "./rules/prefer-top-level-string-formats.mjs";
27
31
  import { preferTrimBeforeStringLengthChecks } from "./rules/prefer-trim-before-string-length-checks.mjs";
@@ -52,12 +56,16 @@ const eslintPluginZod = {
52
56
  "no-number-schema-with-step": noNumberSchemaWithStep,
53
57
  "no-string-schema-with-uuid": noStringSchemaWithUuid,
54
58
  "no-optional-and-default-together": noOptionalAndDefaultTogether,
59
+ "no-schema-with-is-nullable": noSchemaWithIsNullable,
60
+ "no-schema-with-is-optional": noSchemaWithIsOptional,
55
61
  "no-throw-in-refine": noThrowInRefine,
56
62
  "no-transform-in-record-key": noTransformInRecordKey,
57
63
  "no-unknown-schema": noUnknownSchema,
58
64
  "prefer-enum-over-literal-union": preferEnumOverLiteralUnion,
65
+ "prefer-loose-object": preferLooseObject,
59
66
  "prefer-meta": preferMeta,
60
67
  "prefer-meta-last": preferMetaLast,
68
+ "prefer-strict-object": preferStrictObject,
61
69
  "prefer-top-level-string-formats": preferTopLevelStringFormats,
62
70
  "prefer-string-schema-with-trim": preferStringSchemaWithTrim,
63
71
  "prefer-trim-before-string-length-checks": preferTrimBeforeStringLengthChecks,
@@ -85,10 +93,14 @@ const recommendedConfig = {
85
93
  "zod/no-number-schema-with-step": "error",
86
94
  "zod/no-string-schema-with-uuid": "error",
87
95
  "zod/no-optional-and-default-together": "error",
96
+ "zod/no-schema-with-is-nullable": "error",
97
+ "zod/no-schema-with-is-optional": "error",
88
98
  "zod/no-throw-in-refine": "error",
89
99
  "zod/prefer-enum-over-literal-union": "error",
100
+ "zod/prefer-loose-object": "error",
90
101
  "zod/prefer-meta": "error",
91
102
  "zod/prefer-meta-last": "error",
103
+ "zod/prefer-strict-object": "error",
92
104
  "zod/prefer-top-level-string-formats": "error",
93
105
  "zod/prefer-string-schema-with-trim": "error",
94
106
  "zod/prefer-trim-before-string-length-checks": "error",
@@ -0,0 +1,35 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
3
+ let _eslint_zod_utils = require("@eslint-zod/utils");
4
+ //#region src/rules/no-schema-with-is-nullable.ts
5
+ const { trackZodSchemaImports } = (0, _eslint_zod_utils.createZodSchemaImportTrack)(_eslint_zod_utils.zodImportScope);
6
+ const noSchemaWithIsNullable = require_create_plugin_rule.createZodPluginRule({
7
+ name: "no-schema-with-is-nullable",
8
+ meta: {
9
+ type: "problem",
10
+ docs: { description: "Disallow deprecated `.isNullable()` on a Zod schema; use `safeParse(null).success` instead." },
11
+ messages: { useSafeParse: "`.isNullable()` is deprecated. Try `schema.safeParse(null).success` instead." },
12
+ schema: []
13
+ },
14
+ defaultOptions: [],
15
+ create(context) {
16
+ const { importDeclarationListener, detectZodSchemaRootNode } = trackZodSchemaImports();
17
+ return {
18
+ ImportDeclaration: importDeclarationListener,
19
+ CallExpression(node) {
20
+ const zodSchemaMeta = detectZodSchemaRootNode(node);
21
+ if (!zodSchemaMeta) return;
22
+ const { methods } = zodSchemaMeta;
23
+ const isNullableIndex = methods.findIndex((it) => it === "isNullable");
24
+ if (isNullableIndex === -1) return;
25
+ if (methods.slice(0, isNullableIndex).some((method) => _eslint_zod_utils.ZOD_NON_SCHEMA_PRODUCING_METHODS.includes(method))) return;
26
+ context.report({
27
+ node,
28
+ messageId: "useSafeParse"
29
+ });
30
+ }
31
+ };
32
+ }
33
+ });
34
+ //#endregion
35
+ exports.noSchemaWithIsNullable = noSchemaWithIsNullable;
@@ -0,0 +1,34 @@
1
+ import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
+ import { ZOD_NON_SCHEMA_PRODUCING_METHODS, createZodSchemaImportTrack, zodImportScope } from "@eslint-zod/utils";
3
+ //#region src/rules/no-schema-with-is-nullable.ts
4
+ const { trackZodSchemaImports } = createZodSchemaImportTrack(zodImportScope);
5
+ const noSchemaWithIsNullable = createZodPluginRule({
6
+ name: "no-schema-with-is-nullable",
7
+ meta: {
8
+ type: "problem",
9
+ docs: { description: "Disallow deprecated `.isNullable()` on a Zod schema; use `safeParse(null).success` instead." },
10
+ messages: { useSafeParse: "`.isNullable()` is deprecated. Try `schema.safeParse(null).success` instead." },
11
+ schema: []
12
+ },
13
+ defaultOptions: [],
14
+ create(context) {
15
+ const { importDeclarationListener, detectZodSchemaRootNode } = trackZodSchemaImports();
16
+ return {
17
+ ImportDeclaration: importDeclarationListener,
18
+ CallExpression(node) {
19
+ const zodSchemaMeta = detectZodSchemaRootNode(node);
20
+ if (!zodSchemaMeta) return;
21
+ const { methods } = zodSchemaMeta;
22
+ const isNullableIndex = methods.findIndex((it) => it === "isNullable");
23
+ if (isNullableIndex === -1) return;
24
+ if (methods.slice(0, isNullableIndex).some((method) => ZOD_NON_SCHEMA_PRODUCING_METHODS.includes(method))) return;
25
+ context.report({
26
+ node,
27
+ messageId: "useSafeParse"
28
+ });
29
+ }
30
+ };
31
+ }
32
+ });
33
+ //#endregion
34
+ export { noSchemaWithIsNullable };
@@ -0,0 +1,35 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
3
+ let _eslint_zod_utils = require("@eslint-zod/utils");
4
+ //#region src/rules/no-schema-with-is-optional.ts
5
+ const { trackZodSchemaImports } = (0, _eslint_zod_utils.createZodSchemaImportTrack)(_eslint_zod_utils.zodImportScope);
6
+ const noSchemaWithIsOptional = require_create_plugin_rule.createZodPluginRule({
7
+ name: "no-schema-with-is-optional",
8
+ meta: {
9
+ type: "problem",
10
+ docs: { description: "Disallow deprecated `.isOptional()` on a Zod schema; use `safeParse(undefined).success` instead." },
11
+ messages: { useSafeParse: "`.isOptional()` is deprecated. Try `schema.safeParse(undefined).success` instead." },
12
+ schema: []
13
+ },
14
+ defaultOptions: [],
15
+ create(context) {
16
+ const { importDeclarationListener, detectZodSchemaRootNode } = trackZodSchemaImports();
17
+ return {
18
+ ImportDeclaration: importDeclarationListener,
19
+ CallExpression(node) {
20
+ const zodSchemaMeta = detectZodSchemaRootNode(node);
21
+ if (!zodSchemaMeta) return;
22
+ const { methods } = zodSchemaMeta;
23
+ const isOptionalIndex = methods.findIndex((it) => it === "isOptional");
24
+ if (isOptionalIndex === -1) return;
25
+ if (methods.slice(0, isOptionalIndex).some((method) => _eslint_zod_utils.ZOD_NON_SCHEMA_PRODUCING_METHODS.includes(method))) return;
26
+ context.report({
27
+ node,
28
+ messageId: "useSafeParse"
29
+ });
30
+ }
31
+ };
32
+ }
33
+ });
34
+ //#endregion
35
+ exports.noSchemaWithIsOptional = noSchemaWithIsOptional;
@@ -0,0 +1,34 @@
1
+ import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
+ import { ZOD_NON_SCHEMA_PRODUCING_METHODS, createZodSchemaImportTrack, zodImportScope } from "@eslint-zod/utils";
3
+ //#region src/rules/no-schema-with-is-optional.ts
4
+ const { trackZodSchemaImports } = createZodSchemaImportTrack(zodImportScope);
5
+ const noSchemaWithIsOptional = createZodPluginRule({
6
+ name: "no-schema-with-is-optional",
7
+ meta: {
8
+ type: "problem",
9
+ docs: { description: "Disallow deprecated `.isOptional()` on a Zod schema; use `safeParse(undefined).success` instead." },
10
+ messages: { useSafeParse: "`.isOptional()` is deprecated. Try `schema.safeParse(undefined).success` instead." },
11
+ schema: []
12
+ },
13
+ defaultOptions: [],
14
+ create(context) {
15
+ const { importDeclarationListener, detectZodSchemaRootNode } = trackZodSchemaImports();
16
+ return {
17
+ ImportDeclaration: importDeclarationListener,
18
+ CallExpression(node) {
19
+ const zodSchemaMeta = detectZodSchemaRootNode(node);
20
+ if (!zodSchemaMeta) return;
21
+ const { methods } = zodSchemaMeta;
22
+ const isOptionalIndex = methods.findIndex((it) => it === "isOptional");
23
+ if (isOptionalIndex === -1) return;
24
+ if (methods.slice(0, isOptionalIndex).some((method) => ZOD_NON_SCHEMA_PRODUCING_METHODS.includes(method))) return;
25
+ context.report({
26
+ node,
27
+ messageId: "useSafeParse"
28
+ });
29
+ }
30
+ };
31
+ }
32
+ });
33
+ //#endregion
34
+ export { noSchemaWithIsOptional };
@@ -0,0 +1,49 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
3
+ let _eslint_zod_utils = require("@eslint-zod/utils");
4
+ let _typescript_eslint_utils = require("@typescript-eslint/utils");
5
+ //#region src/rules/prefer-loose-object.ts
6
+ const { trackZodSchemaImports } = (0, _eslint_zod_utils.createZodSchemaImportTrack)(_eslint_zod_utils.zodImportScope);
7
+ const preferLooseObject = require_create_plugin_rule.createZodPluginRule({
8
+ name: "prefer-loose-object",
9
+ meta: {
10
+ type: "suggestion",
11
+ fixable: "code",
12
+ docs: { description: "Prefer `z.looseObject()` over `z.object().passthrough()` and `z.object().loose()`" },
13
+ messages: { preferLooseObject: "Use `z.looseObject()` instead of `.passthrough()` or `.loose()`." },
14
+ schema: []
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
19
+ return {
20
+ ImportDeclaration: importDeclarationListener,
21
+ CallExpression(node) {
22
+ const zodSchemaMeta = detectZodSchemaRootNode(node);
23
+ if (zodSchemaMeta?.schemaType !== "object") return;
24
+ const methods = collectZodChainMethods(zodSchemaMeta.node);
25
+ const looseMethod = methods.find((it) => it.name === "passthrough" || it.name === "loose");
26
+ if (!looseMethod) return;
27
+ context.report({
28
+ node: looseMethod.node,
29
+ messageId: "preferLooseObject",
30
+ fix(fixer) {
31
+ if (zodSchemaMeta.schemaDecl === "named") return null;
32
+ if (looseMethod.node.arguments.length !== 0) return null;
33
+ const objectMethod = methods.find((it) => it.name === "object");
34
+ if (!objectMethod) return null;
35
+ const { sourceCode } = context;
36
+ const replacementText = objectMethod.node.callee.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression ? `${sourceCode.getText(objectMethod.node.callee.object)}.looseObject` : "looseObject";
37
+ const fixes = [fixer.replaceText(objectMethod.node.callee, replacementText)];
38
+ const calleeProperty = looseMethod.node.callee.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression ? looseMethod.node.callee.property : looseMethod.node.callee;
39
+ const tokenBefore = sourceCode.getTokenBefore(calleeProperty);
40
+ if (tokenBefore?.value === ".") fixes.push(fixer.removeRange([tokenBefore.range[0], looseMethod.node.range[1]]));
41
+ return fixes;
42
+ }
43
+ });
44
+ }
45
+ };
46
+ }
47
+ });
48
+ //#endregion
49
+ exports.preferLooseObject = preferLooseObject;
@@ -0,0 +1,48 @@
1
+ import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
+ import { createZodSchemaImportTrack, zodImportScope } from "@eslint-zod/utils";
3
+ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
4
+ //#region src/rules/prefer-loose-object.ts
5
+ const { trackZodSchemaImports } = createZodSchemaImportTrack(zodImportScope);
6
+ const preferLooseObject = createZodPluginRule({
7
+ name: "prefer-loose-object",
8
+ meta: {
9
+ type: "suggestion",
10
+ fixable: "code",
11
+ docs: { description: "Prefer `z.looseObject()` over `z.object().passthrough()` and `z.object().loose()`" },
12
+ messages: { preferLooseObject: "Use `z.looseObject()` instead of `.passthrough()` or `.loose()`." },
13
+ schema: []
14
+ },
15
+ defaultOptions: [],
16
+ create(context) {
17
+ const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
18
+ return {
19
+ ImportDeclaration: importDeclarationListener,
20
+ CallExpression(node) {
21
+ const zodSchemaMeta = detectZodSchemaRootNode(node);
22
+ if (zodSchemaMeta?.schemaType !== "object") return;
23
+ const methods = collectZodChainMethods(zodSchemaMeta.node);
24
+ const looseMethod = methods.find((it) => it.name === "passthrough" || it.name === "loose");
25
+ if (!looseMethod) return;
26
+ context.report({
27
+ node: looseMethod.node,
28
+ messageId: "preferLooseObject",
29
+ fix(fixer) {
30
+ if (zodSchemaMeta.schemaDecl === "named") return null;
31
+ if (looseMethod.node.arguments.length !== 0) return null;
32
+ const objectMethod = methods.find((it) => it.name === "object");
33
+ if (!objectMethod) return null;
34
+ const { sourceCode } = context;
35
+ const replacementText = objectMethod.node.callee.type === AST_NODE_TYPES.MemberExpression ? `${sourceCode.getText(objectMethod.node.callee.object)}.looseObject` : "looseObject";
36
+ const fixes = [fixer.replaceText(objectMethod.node.callee, replacementText)];
37
+ const calleeProperty = looseMethod.node.callee.type === AST_NODE_TYPES.MemberExpression ? looseMethod.node.callee.property : looseMethod.node.callee;
38
+ const tokenBefore = sourceCode.getTokenBefore(calleeProperty);
39
+ if (tokenBefore?.value === ".") fixes.push(fixer.removeRange([tokenBefore.range[0], looseMethod.node.range[1]]));
40
+ return fixes;
41
+ }
42
+ });
43
+ }
44
+ };
45
+ }
46
+ });
47
+ //#endregion
48
+ export { preferLooseObject };
@@ -0,0 +1,49 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_create_plugin_rule = require("../utils/create-plugin-rule.cjs");
3
+ let _eslint_zod_utils = require("@eslint-zod/utils");
4
+ let _typescript_eslint_utils = require("@typescript-eslint/utils");
5
+ //#region src/rules/prefer-strict-object.ts
6
+ const { trackZodSchemaImports } = (0, _eslint_zod_utils.createZodSchemaImportTrack)(_eslint_zod_utils.zodImportScope);
7
+ const preferStrictObject = require_create_plugin_rule.createZodPluginRule({
8
+ name: "prefer-strict-object",
9
+ meta: {
10
+ type: "suggestion",
11
+ fixable: "code",
12
+ docs: { description: "Prefer `z.strictObject()` over `z.object().strict()`" },
13
+ messages: { preferStrictObject: "Use `z.strictObject()` instead of `.strict()`." },
14
+ schema: []
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
19
+ return {
20
+ ImportDeclaration: importDeclarationListener,
21
+ CallExpression(node) {
22
+ const zodSchemaMeta = detectZodSchemaRootNode(node);
23
+ if (zodSchemaMeta?.schemaType !== "object") return;
24
+ const methods = collectZodChainMethods(zodSchemaMeta.node);
25
+ const strictMethod = methods.find((it) => it.name === "strict");
26
+ if (!strictMethod) return;
27
+ context.report({
28
+ node: strictMethod.node,
29
+ messageId: "preferStrictObject",
30
+ fix(fixer) {
31
+ if (zodSchemaMeta.schemaDecl === "named") return null;
32
+ if (strictMethod.node.arguments.length !== 0) return null;
33
+ const objectMethod = methods.find((it) => it.name === "object");
34
+ if (!objectMethod) return null;
35
+ const { sourceCode } = context;
36
+ const replacementText = objectMethod.node.callee.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression ? `${sourceCode.getText(objectMethod.node.callee.object)}.strictObject` : "strictObject";
37
+ const fixes = [fixer.replaceText(objectMethod.node.callee, replacementText)];
38
+ const calleeProperty = strictMethod.node.callee.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression ? strictMethod.node.callee.property : strictMethod.node.callee;
39
+ const tokenBefore = sourceCode.getTokenBefore(calleeProperty);
40
+ if (tokenBefore?.value === ".") fixes.push(fixer.removeRange([tokenBefore.range[0], strictMethod.node.range[1]]));
41
+ return fixes;
42
+ }
43
+ });
44
+ }
45
+ };
46
+ }
47
+ });
48
+ //#endregion
49
+ exports.preferStrictObject = preferStrictObject;
@@ -0,0 +1,48 @@
1
+ import { createZodPluginRule } from "../utils/create-plugin-rule.mjs";
2
+ import { createZodSchemaImportTrack, zodImportScope } from "@eslint-zod/utils";
3
+ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
4
+ //#region src/rules/prefer-strict-object.ts
5
+ const { trackZodSchemaImports } = createZodSchemaImportTrack(zodImportScope);
6
+ const preferStrictObject = createZodPluginRule({
7
+ name: "prefer-strict-object",
8
+ meta: {
9
+ type: "suggestion",
10
+ fixable: "code",
11
+ docs: { description: "Prefer `z.strictObject()` over `z.object().strict()`" },
12
+ messages: { preferStrictObject: "Use `z.strictObject()` instead of `.strict()`." },
13
+ schema: []
14
+ },
15
+ defaultOptions: [],
16
+ create(context) {
17
+ const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods } = trackZodSchemaImports();
18
+ return {
19
+ ImportDeclaration: importDeclarationListener,
20
+ CallExpression(node) {
21
+ const zodSchemaMeta = detectZodSchemaRootNode(node);
22
+ if (zodSchemaMeta?.schemaType !== "object") return;
23
+ const methods = collectZodChainMethods(zodSchemaMeta.node);
24
+ const strictMethod = methods.find((it) => it.name === "strict");
25
+ if (!strictMethod) return;
26
+ context.report({
27
+ node: strictMethod.node,
28
+ messageId: "preferStrictObject",
29
+ fix(fixer) {
30
+ if (zodSchemaMeta.schemaDecl === "named") return null;
31
+ if (strictMethod.node.arguments.length !== 0) return null;
32
+ const objectMethod = methods.find((it) => it.name === "object");
33
+ if (!objectMethod) return null;
34
+ const { sourceCode } = context;
35
+ const replacementText = objectMethod.node.callee.type === AST_NODE_TYPES.MemberExpression ? `${sourceCode.getText(objectMethod.node.callee.object)}.strictObject` : "strictObject";
36
+ const fixes = [fixer.replaceText(objectMethod.node.callee, replacementText)];
37
+ const calleeProperty = strictMethod.node.callee.type === AST_NODE_TYPES.MemberExpression ? strictMethod.node.callee.property : strictMethod.node.callee;
38
+ const tokenBefore = sourceCode.getTokenBefore(calleeProperty);
39
+ if (tokenBefore?.value === ".") fixes.push(fixer.removeRange([tokenBefore.range[0], strictMethod.node.range[1]]));
40
+ return fixes;
41
+ }
42
+ });
43
+ }
44
+ };
45
+ }
46
+ });
47
+ //#endregion
48
+ export { preferStrictObject };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-zod",
3
- "version": "4.2.2",
3
+ "version": "4.4.0",
4
4
  "description": "ESLint plugin that adds custom linting rules to enforce best practices when using Zod",
5
5
  "keywords": [
6
6
  "eslint",
@@ -43,7 +43,7 @@
43
43
  "@eslint-zod/utils": "1.2.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@marcalexiei/prettier-config": "1.1.4",
46
+ "@marcalexiei/prettier-config": "2.0.0",
47
47
  "@types/esquery": "1.5.4",
48
48
  "@typescript-eslint/rule-tester": "8.59.2",
49
49
  "dedent": "1.7.2",