eslint-plugin-zod 3.6.0 → 3.8.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
@@ -17,7 +17,10 @@
17
17
  [issuesBadge]: https://img.shields.io/github/issues/marcalexiei/eslint-plugin-zod.svg?style=for-the-badge
18
18
  [issuesURL]: https://github.com/marcalexiei/eslint-plugin-zod/issues
19
19
 
20
- [ESLint](https://eslint.org) plugin that adds custom linting rules to enforce best practices when using [Zod](https://github.com/colinhacks/zod)
20
+ [ESLint](https://eslint.org) plugin that adds custom linting rules to enforce best practices when using [Zod](https://github.com/colinhacks/zod).
21
+
22
+ It can also work with [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)!\
23
+ Find out more about [Oxlint's `jsPLugins`](https://oxc.rs/docs/guide/usage/linter/js-plugins.html).
21
24
 
22
25
  ## Rules
23
26
 
@@ -35,20 +38,21 @@ This plugin is primarily built for `zod`, so some rules are exclusive to `zod` a
35
38
 
36
39
  ### Universal rules (`zod` & `zod-mini`)
37
40
 
38
- | Name                          | Description | 💼 | 🔧 | 💡 | ❌ |
39
- | :--------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :---- | :-- | :-- | :-- |
40
- | [consistent-import](docs/rules/consistent-import.md) | Enforce a consistent import style for Zod | ✅ | 🔧 | | |
41
- | [consistent-import-source](docs/rules/consistent-import-source.md) | Enforce consistent source from Zod imports | | | 💡 | |
42
- | [consistent-object-schema-type](docs/rules/consistent-object-schema-type.md) | Enforce consistent usage of Zod schema methods | | | 💡 | |
43
- | [no-any-schema](docs/rules/no-any-schema.md) | Disallow usage of `z.any()` in Zod schemas | ✔️ | | 💡 | |
44
- | [no-empty-custom-schema](docs/rules/no-empty-custom-schema.md) | Disallow usage of `z.custom()` without arguments | ✅ | | | |
45
- | [no-unknown-schema](docs/rules/no-unknown-schema.md) | Disallow usage of `z.unknown()` in Zod schemas | | | | |
46
- | [prefer-meta](docs/rules/prefer-meta.md) | Enforce usage of `.meta()` over `.describe()` | ✔️ | 🔧 | | |
47
- | [prefer-namespace-import](docs/rules/prefer-namespace-import.md) | Enforce importing zod as a namespace import (`import * as z from 'zod'`) | | 🔧 | ||
48
- | [require-brand-type-parameter](docs/rules/require-brand-type-parameter.md) | Require type parameter on `.brand()` functions | ✔️ | | 💡 | |
49
- | [require-error-message](docs/rules/require-error-message.md) | Enforce that custom refinements include an error message | ✅ ✔️ | 🔧 | | |
50
- | [require-schema-suffix](docs/rules/require-schema-suffix.md) | Require schema suffix when declaring a Zod schema | ✅ ✔️ | | | |
51
- | [schema-error-property-style](docs/rules/schema-error-property-style.md) | Enforce consistent style for error messages in Zod schema validation (using ESQuery patterns) | | | | |
41
+ | Name                                | Description | 💼 | 🔧 | 💡 | ❌ |
42
+ | :--------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :---- | :-- | :-- | :-- |
43
+ | [consistent-import](docs/rules/consistent-import.md) | Enforce a consistent import style for Zod | ✅ | 🔧 | | |
44
+ | [consistent-import-source](docs/rules/consistent-import-source.md) | Enforce consistent source from Zod imports | | | 💡 | |
45
+ | [consistent-object-schema-type](docs/rules/consistent-object-schema-type.md) | Enforce consistent usage of Zod schema methods | | | 💡 | |
46
+ | [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
+ | [no-any-schema](docs/rules/no-any-schema.md) | Disallow usage of `z.any()` in Zod schemas | ✅ ✔️ | | 💡 | |
48
+ | [no-empty-custom-schema](docs/rules/no-empty-custom-schema.md) | Disallow usage of `z.custom()` without arguments | | | | |
49
+ | [no-unknown-schema](docs/rules/no-unknown-schema.md) | Disallow usage of `z.unknown()` in Zod schemas | | | | |
50
+ | [prefer-meta](docs/rules/prefer-meta.md) | Enforce usage of `.meta()` over `.describe()` | ✔️ | 🔧 | | |
51
+ | [prefer-namespace-import](docs/rules/prefer-namespace-import.md) | Enforce importing zod as a namespace import (`import * as z from 'zod'`) | | 🔧 | | ❌ |
52
+ | [require-brand-type-parameter](docs/rules/require-brand-type-parameter.md) | Require type parameter on `.brand()` functions | ✅ ✔️ | | 💡 | |
53
+ | [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
+ | [schema-error-property-style](docs/rules/schema-error-property-style.md) | Enforce consistent style for error messages in Zod schema validation (using ESQuery patterns) | | | | |
52
56
 
53
57
  ### `zod` exclusive rules
54
58
 
@@ -68,6 +72,8 @@ This plugin is primarily built for `zod`, so some rules are exclusive to `zod` a
68
72
 
69
73
  ## Installation
70
74
 
75
+ ### ESLint
76
+
71
77
  Install `eslint` and `eslint-plugin-zod` using your preferred package manager:
72
78
 
73
79
  ```shell
@@ -82,7 +88,7 @@ yarn add --dev eslint eslint-plugin-zod
82
88
  pnpm add --save-dev eslint eslint-plugin-zod
83
89
  ```
84
90
 
85
- ## Configuration
91
+ #### ESLint Configuration
86
92
 
87
93
  1. Import the plugin
88
94
 
@@ -110,6 +116,57 @@ export default defineConfig(
110
116
  );
111
117
  ```
112
118
 
119
+ ### Oxlint
120
+
121
+ Install `oxlint` and `eslint-plugin-zod` using your preferred package manager:
122
+
123
+ ```shell
124
+ npm i --save-dev oxlint eslint-plugin-zod
125
+ ```
126
+
127
+ ```shell
128
+ yarn add --dev oxlint eslint-plugin-zod
129
+ ```
130
+
131
+ ```shell
132
+ pnpm add --save-dev oxlint eslint-plugin-zod
133
+ ```
134
+
135
+ #### Oxlint Configuration
136
+
137
+ 1. Import the plugin
138
+
139
+ ```ts
140
+ import eslintPluginZod from 'eslint-plugin-zod';
141
+ ```
142
+
143
+ 2. Add `eslint-plugin-zod` to the `jsPlugins` key
144
+
145
+ ```ts
146
+ {
147
+ jsPlugins: ['eslint-plugin-zod'],
148
+ // ...
149
+ }
150
+ ```
151
+
152
+ 3. Add `eslintPluginZod.configs.recommended.rules` to your Oxlint config.\
153
+ Alternatively you can specify the rules manually
154
+
155
+ Here’s a minimal example using the flat config format:
156
+
157
+ ```ts
158
+ // oxlint.config.ts
159
+ import eslintPluginZod from 'eslint-plugin-zod';
160
+ import { defineConfig } from 'oxlint';
161
+
162
+ export default defineConfig({
163
+ jsPlugins: ['eslint-plugin-zod'],
164
+ rules: {
165
+ ...eslintPluginZod.configs.recommended.rules,
166
+ },
167
+ });
168
+ ```
169
+
113
170
  ## Zod peer dependency version
114
171
 
115
172
  `eslint-plugin-zod` is designed for projects that use `zod@^4`.
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@ 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_output_type_style = require("./rules/consistent-schema-output-type-style.cjs");
6
7
  const require_no_any_schema = require("./rules/no-any-schema.cjs");
7
8
  const require_no_empty_custom_schema = require("./rules/no-empty-custom-schema.cjs");
8
9
  const require_no_number_schema_with_int = require("./rules/no-number-schema-with-int.cjs");
@@ -31,6 +32,7 @@ const eslintPluginZod = {
31
32
  "consistent-import-source": require_consistent_import_source.consistentImportSource,
32
33
  "consistent-import": require_consistent_import.consistentImport,
33
34
  "consistent-object-schema-type": require_consistent_object_schema_type.consistentObjectSchemaType,
35
+ "consistent-schema-output-type-style": require_consistent_schema_output_type_style.consistentSchemaOutputTypeStyle,
34
36
  "no-any-schema": require_no_any_schema.noAnySchema,
35
37
  "no-empty-custom-schema": require_no_empty_custom_schema.noEmptyCustomSchema,
36
38
  "no-number-schema-with-int": require_no_number_schema_with_int.noNumberSchemaWithInt,
package/dist/index.mjs CHANGED
@@ -3,6 +3,7 @@ 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 { consistentSchemaOutputTypeStyle } from "./rules/consistent-schema-output-type-style.mjs";
6
7
  import { noAnySchema } from "./rules/no-any-schema.mjs";
7
8
  import { noEmptyCustomSchema } from "./rules/no-empty-custom-schema.mjs";
8
9
  import { noNumberSchemaWithInt } from "./rules/no-number-schema-with-int.mjs";
@@ -31,6 +32,7 @@ const eslintPluginZod = {
31
32
  "consistent-import-source": consistentImportSource,
32
33
  "consistent-import": consistentImport,
33
34
  "consistent-object-schema-type": consistentObjectSchemaType,
35
+ "consistent-schema-output-type-style": consistentSchemaOutputTypeStyle,
34
36
  "no-any-schema": noAnySchema,
35
37
  "no-empty-custom-schema": noEmptyCustomSchema,
36
38
  "no-number-schema-with-int": noNumberSchemaWithInt,
@@ -0,0 +1,70 @@
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-output-type-style.ts
6
+ const ZOD_SCHEMA_TYPE_STYLES = ["infer", "output"];
7
+ const { zodImportAllowedSource, trackZodSchemaImports } = require_track_zod_schema_imports.createZodSchemaImportTrack("all");
8
+ const consistentSchemaOutputTypeStyle = require_create_plugin_rule.createZodPluginRule({
9
+ name: "consistent-schema-output-type-style",
10
+ meta: {
11
+ type: "suggestion",
12
+ fixable: "code",
13
+ docs: {
14
+ zodImportAllowedSource,
15
+ description: "Enforce consistent use of z.infer or z.output for schema type inference"
16
+ },
17
+ messages: {
18
+ useInfer: "Use infer instead of output.",
19
+ useOutput: "Use output instead of infer."
20
+ },
21
+ schema: [{
22
+ type: "object",
23
+ properties: { style: {
24
+ description: "Decides which style to use for schema type inference",
25
+ type: "string",
26
+ enum: [...ZOD_SCHEMA_TYPE_STYLES]
27
+ } },
28
+ additionalProperties: false
29
+ }]
30
+ },
31
+ defaultOptions: [{ style: "output" }],
32
+ create(context, [{ style }]) {
33
+ const { importDeclarationListener, isZodNamespace, getNamedImportOriginal, getNamedImportLocal } = trackZodSchemaImports();
34
+ return {
35
+ ImportDeclaration: importDeclarationListener,
36
+ TSTypeReference(node) {
37
+ const { typeName } = node;
38
+ if (typeName.type === _typescript_eslint_utils.AST_NODE_TYPES.TSQualifiedName) {
39
+ const { left, right } = typeName;
40
+ if (left.type !== _typescript_eslint_utils.AST_NODE_TYPES.Identifier || !isZodNamespace(left.name)) return;
41
+ const usedStyle = right.name;
42
+ if (usedStyle !== "infer" && usedStyle !== "output" || usedStyle === style) return;
43
+ context.report({
44
+ node: right,
45
+ messageId: style === "infer" ? "useInfer" : "useOutput",
46
+ fix(fixer) {
47
+ return fixer.replaceText(right, style);
48
+ }
49
+ });
50
+ return;
51
+ }
52
+ if (typeName.type === _typescript_eslint_utils.AST_NODE_TYPES.Identifier) {
53
+ const originalName = getNamedImportOriginal(typeName.name);
54
+ if (originalName !== "infer" && originalName !== "output" || originalName === style) return;
55
+ const targetLocalName = getNamedImportLocal(style);
56
+ context.report({
57
+ node: typeName,
58
+ messageId: style === "infer" ? "useInfer" : "useOutput",
59
+ fix(fixer) {
60
+ if (!targetLocalName || targetLocalName === "infer") return null;
61
+ return fixer.replaceText(typeName, targetLocalName);
62
+ }
63
+ });
64
+ }
65
+ }
66
+ };
67
+ }
68
+ });
69
+ //#endregion
70
+ exports.consistentSchemaOutputTypeStyle = consistentSchemaOutputTypeStyle;
@@ -0,0 +1,69 @@
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-output-type-style.ts
5
+ const ZOD_SCHEMA_TYPE_STYLES = ["infer", "output"];
6
+ const { zodImportAllowedSource, trackZodSchemaImports } = createZodSchemaImportTrack("all");
7
+ const consistentSchemaOutputTypeStyle = createZodPluginRule({
8
+ name: "consistent-schema-output-type-style",
9
+ meta: {
10
+ type: "suggestion",
11
+ fixable: "code",
12
+ docs: {
13
+ zodImportAllowedSource,
14
+ description: "Enforce consistent use of z.infer or z.output for schema type inference"
15
+ },
16
+ messages: {
17
+ useInfer: "Use infer instead of output.",
18
+ useOutput: "Use output instead of infer."
19
+ },
20
+ schema: [{
21
+ type: "object",
22
+ properties: { style: {
23
+ description: "Decides which style to use for schema type inference",
24
+ type: "string",
25
+ enum: [...ZOD_SCHEMA_TYPE_STYLES]
26
+ } },
27
+ additionalProperties: false
28
+ }]
29
+ },
30
+ defaultOptions: [{ style: "output" }],
31
+ create(context, [{ style }]) {
32
+ const { importDeclarationListener, isZodNamespace, getNamedImportOriginal, getNamedImportLocal } = trackZodSchemaImports();
33
+ return {
34
+ ImportDeclaration: importDeclarationListener,
35
+ TSTypeReference(node) {
36
+ const { typeName } = node;
37
+ if (typeName.type === AST_NODE_TYPES.TSQualifiedName) {
38
+ const { left, right } = typeName;
39
+ if (left.type !== AST_NODE_TYPES.Identifier || !isZodNamespace(left.name)) return;
40
+ const usedStyle = right.name;
41
+ if (usedStyle !== "infer" && usedStyle !== "output" || usedStyle === style) return;
42
+ context.report({
43
+ node: right,
44
+ messageId: style === "infer" ? "useInfer" : "useOutput",
45
+ fix(fixer) {
46
+ return fixer.replaceText(right, style);
47
+ }
48
+ });
49
+ return;
50
+ }
51
+ if (typeName.type === AST_NODE_TYPES.Identifier) {
52
+ const originalName = getNamedImportOriginal(typeName.name);
53
+ if (originalName !== "infer" && originalName !== "output" || originalName === style) return;
54
+ const targetLocalName = getNamedImportLocal(style);
55
+ context.report({
56
+ node: typeName,
57
+ messageId: style === "infer" ? "useInfer" : "useOutput",
58
+ fix(fixer) {
59
+ if (!targetLocalName || targetLocalName === "infer") return null;
60
+ return fixer.replaceText(typeName, targetLocalName);
61
+ }
62
+ });
63
+ }
64
+ }
65
+ };
66
+ }
67
+ });
68
+ //#endregion
69
+ export { consistentSchemaOutputTypeStyle };
@@ -9,7 +9,8 @@ let _typescript_eslint_utils = require("@typescript-eslint/utils");
9
9
  */
10
10
  function trackZodSchemaImports(importAllowedSource) {
11
11
  const zodNamespaces = /* @__PURE__ */ new Set();
12
- const zodNamedImports = /* @__PURE__ */ new Set();
12
+ const zodNamedImports = /* @__PURE__ */ new Map();
13
+ const zodNamedImportsByOriginal = /* @__PURE__ */ new Map();
13
14
  function collectZodChainMethods(node) {
14
15
  const methods = [];
15
16
  let current = node;
@@ -42,12 +43,20 @@ function trackZodSchemaImports(importAllowedSource) {
42
43
  case _typescript_eslint_utils.AST_NODE_TYPES.ImportNamespaceSpecifier:
43
44
  zodNamespaces.add(spec.local.name);
44
45
  break;
45
- case _typescript_eslint_utils.AST_NODE_TYPES.ImportSpecifier:
46
- if ("name" in spec.imported && spec.imported.name === "z") zodNamespaces.add(spec.local.name);
47
- else zodNamedImports.add(spec.local.name);
46
+ case _typescript_eslint_utils.AST_NODE_TYPES.ImportSpecifier: {
47
+ const originalName = "name" in spec.imported ? spec.imported.name : spec.local.name;
48
+ if (originalName === "z") zodNamespaces.add(spec.local.name);
49
+ else {
50
+ zodNamedImports.set(spec.local.name, originalName);
51
+ zodNamedImportsByOriginal.set(originalName, spec.local.name);
52
+ }
48
53
  break;
54
+ }
49
55
  }
50
56
  },
57
+ isZodNamespace: (name) => zodNamespaces.has(name),
58
+ getNamedImportOriginal: (localName) => zodNamedImports.get(localName),
59
+ getNamedImportLocal: (originalName) => zodNamedImportsByOriginal.get(originalName),
51
60
  detectZodSchemaRootNode: (node) => require_detect_zod_schema_root_node.detectZodSchemaRootNode(node, zodNamespaces, zodNamedImports),
52
61
  collectZodChainMethods
53
62
  };
@@ -8,7 +8,8 @@ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
8
8
  */
9
9
  function trackZodSchemaImports(importAllowedSource) {
10
10
  const zodNamespaces = /* @__PURE__ */ new Set();
11
- const zodNamedImports = /* @__PURE__ */ new Set();
11
+ const zodNamedImports = /* @__PURE__ */ new Map();
12
+ const zodNamedImportsByOriginal = /* @__PURE__ */ new Map();
12
13
  function collectZodChainMethods(node) {
13
14
  const methods = [];
14
15
  let current = node;
@@ -41,12 +42,20 @@ function trackZodSchemaImports(importAllowedSource) {
41
42
  case AST_NODE_TYPES.ImportNamespaceSpecifier:
42
43
  zodNamespaces.add(spec.local.name);
43
44
  break;
44
- case AST_NODE_TYPES.ImportSpecifier:
45
- if ("name" in spec.imported && spec.imported.name === "z") zodNamespaces.add(spec.local.name);
46
- else zodNamedImports.add(spec.local.name);
45
+ case AST_NODE_TYPES.ImportSpecifier: {
46
+ const originalName = "name" in spec.imported ? spec.imported.name : spec.local.name;
47
+ if (originalName === "z") zodNamespaces.add(spec.local.name);
48
+ else {
49
+ zodNamedImports.set(spec.local.name, originalName);
50
+ zodNamedImportsByOriginal.set(originalName, spec.local.name);
51
+ }
47
52
  break;
53
+ }
48
54
  }
49
55
  },
56
+ isZodNamespace: (name) => zodNamespaces.has(name),
57
+ getNamedImportOriginal: (localName) => zodNamedImports.get(localName),
58
+ getNamedImportLocal: (originalName) => zodNamedImportsByOriginal.get(originalName),
50
59
  detectZodSchemaRootNode: (node) => detectZodSchemaRootNode(node, zodNamespaces, zodNamedImports),
51
60
  collectZodChainMethods
52
61
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-zod",
3
- "version": "3.6.0",
3
+ "version": "3.8.0",
4
4
  "type": "module",
5
5
  "description": "ESLint plugin that adds custom linting rules to enforce best practices when using Zod",
6
6
  "engines": {
@@ -27,6 +27,7 @@
27
27
  "eslint",
28
28
  "eslintplugin",
29
29
  "eslint-plugin",
30
+ "oxlint",
30
31
  "zod"
31
32
  ],
32
33
  "files": [
@@ -45,9 +46,16 @@
45
46
  },
46
47
  "peerDependencies": {
47
48
  "eslint": "^9 || ^10",
49
+ "oxlint": "^1.59.0",
48
50
  "zod": "^4"
49
51
  },
50
52
  "peerDependenciesMeta": {
53
+ "eslint": {
54
+ "optional": true
55
+ },
56
+ "oxlint": {
57
+ "optional": true
58
+ },
51
59
  "zod": {
52
60
  "optional": true
53
61
  }