eslint-plugin-zod 3.7.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
@@ -38,20 +38,21 @@ This plugin is primarily built for `zod`, so some rules are exclusive to `zod` a
38
38
 
39
39
  ### Universal rules (`zod` & `zod-mini`)
40
40
 
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
- | [no-any-schema](docs/rules/no-any-schema.md) | Disallow usage of `z.any()` in Zod schemas | ✔️ | | 💡 | |
47
- | [no-empty-custom-schema](docs/rules/no-empty-custom-schema.md) | Disallow usage of `z.custom()` without arguments | ✅ | | | |
48
- | [no-unknown-schema](docs/rules/no-unknown-schema.md) | Disallow usage of `z.unknown()` in Zod schemas | | | | |
49
- | [prefer-meta](docs/rules/prefer-meta.md) | Enforce usage of `.meta()` over `.describe()` | ✔️ | 🔧 | | |
50
- | [prefer-namespace-import](docs/rules/prefer-namespace-import.md) | Enforce importing zod as a namespace import (`import * as z from 'zod'`) | | 🔧 | ||
51
- | [require-brand-type-parameter](docs/rules/require-brand-type-parameter.md) | Require type parameter on `.brand()` functions | ✔️ | | 💡 | |
52
- | [require-error-message](docs/rules/require-error-message.md) | Enforce that custom refinements include an error message | ✅ ✔️ | 🔧 | | |
53
- | [require-schema-suffix](docs/rules/require-schema-suffix.md) | Require schema suffix when declaring a Zod schema | ✅ ✔️ | | | |
54
- | [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) | | | | |
55
56
 
56
57
  ### `zod` exclusive rules
57
58
 
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.7.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": {