eslint-plugin-zod 3.0.2 β 3.2.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 +24 -21
- package/dist/index.cjs +6 -1
- package/dist/index.js +6 -1
- package/dist/rules/consistent-import.cjs +178 -0
- package/dist/rules/consistent-import.d.cts +11 -0
- package/dist/rules/consistent-import.d.ts +11 -0
- package/dist/rules/consistent-import.js +175 -0
- package/dist/rules/no-number-schema-with-int.cjs +10 -15
- package/dist/rules/no-number-schema-with-int.js +10 -15
- package/dist/rules/no-string-schema-with-uuid.cjs +63 -0
- package/dist/rules/no-string-schema-with-uuid.d.cts +4 -0
- package/dist/rules/no-string-schema-with-uuid.d.ts +4 -0
- package/dist/rules/no-string-schema-with-uuid.js +60 -0
- package/dist/rules/prefer-namespace-import.cjs +3 -0
- package/dist/rules/prefer-namespace-import.d.cts +1 -1
- package/dist/rules/prefer-namespace-import.d.ts +1 -1
- package/dist/rules/prefer-namespace-import.js +3 -0
- package/dist/utils/build-zod-chain-replacement-fix.cjs +24 -0
- package/dist/utils/build-zod-chain-replacement-fix.d.cts +12 -0
- package/dist/utils/build-zod-chain-replacement-fix.d.ts +12 -0
- package/dist/utils/build-zod-chain-replacement-fix.js +21 -0
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -26,27 +26,30 @@
|
|
|
26
26
|
πΌ Configurations enabled in.\
|
|
27
27
|
β
Set in the `recommended` configuration.\
|
|
28
28
|
π§ Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
|
|
29
|
-
π‘ Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
| [
|
|
35
|
-
| [consistent-
|
|
36
|
-
| [
|
|
37
|
-
| [
|
|
38
|
-
| [no-
|
|
39
|
-
| [no-
|
|
40
|
-
| [no-
|
|
41
|
-
| [no-
|
|
42
|
-
| [
|
|
43
|
-
| [
|
|
44
|
-
| [
|
|
45
|
-
| [prefer-
|
|
46
|
-
| [
|
|
47
|
-
| [
|
|
48
|
-
| [
|
|
49
|
-
| [
|
|
29
|
+
π‘ Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).\
|
|
30
|
+
β Deprecated.
|
|
31
|
+
|
|
32
|
+
| NameΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description | πΌ | π§ | π‘ | β |
|
|
33
|
+
| :--------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :-- | :-- | :-- | :-- |
|
|
34
|
+
| [array-style](docs/rules/array-style.md) | Enforce consistent Zod array style | β
| π§ | | |
|
|
35
|
+
| [consistent-import](docs/rules/consistent-import.md) | Enforce a consistent import style for Zod | β
| π§ | | |
|
|
36
|
+
| [consistent-import-source](docs/rules/consistent-import-source.md) | Enforce consistent source from Zod imports | | | π‘ | |
|
|
37
|
+
| [consistent-object-schema-type](docs/rules/consistent-object-schema-type.md) | Enforce consistent usage of Zod schema methods | | | π‘ | |
|
|
38
|
+
| [no-any-schema](docs/rules/no-any-schema.md) | Disallow usage of `z.any()` in Zod schemas | β
| | π‘ | |
|
|
39
|
+
| [no-empty-custom-schema](docs/rules/no-empty-custom-schema.md) | Disallow usage of `z.custom()` without arguments | β
| | | |
|
|
40
|
+
| [no-number-schema-with-int](docs/rules/no-number-schema-with-int.md) | Disallow usage of `z.number().int()` as it is considered legacy | β
| π§ | | |
|
|
41
|
+
| [no-optional-and-default-together](docs/rules/no-optional-and-default-together.md) | Disallow using both `.optional()` and `.default()` on the same Zod schema | β
| π§ | | |
|
|
42
|
+
| [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 | β
| π§ | | |
|
|
43
|
+
| [no-throw-in-refine](docs/rules/no-throw-in-refine.md) | Disallow throwing errors directly inside Zod refine callbacks | β
| | | |
|
|
44
|
+
| [no-unknown-schema](docs/rules/no-unknown-schema.md) | Disallow usage of `z.unknown()` in Zod schemas | | | | |
|
|
45
|
+
| [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. | β
| π§ | | |
|
|
46
|
+
| [prefer-meta](docs/rules/prefer-meta.md) | Enforce usage of `.meta()` over `.describe()` | β
| π§ | | |
|
|
47
|
+
| [prefer-meta-last](docs/rules/prefer-meta-last.md) | Enforce `.meta()` as last method | β
| π§ | | |
|
|
48
|
+
| [prefer-namespace-import](docs/rules/prefer-namespace-import.md) | Enforce importing zod as a namespace import (`import * as z from 'zod'`) | | π§ | | β |
|
|
49
|
+
| [require-brand-type-parameter](docs/rules/require-brand-type-parameter.md) | Require type parameter on `.brand()` functions | β
| | π‘ | |
|
|
50
|
+
| [require-error-message](docs/rules/require-error-message.md) | Enforce that custom refinements include an error message | β
| π§ | | |
|
|
51
|
+
| [require-schema-suffix](docs/rules/require-schema-suffix.md) | Require schema suffix when declaring a Zod schema | β
| | | |
|
|
52
|
+
| [schema-error-property-style](docs/rules/schema-error-property-style.md) | Enforce consistent style for error messages in Zod schema validation (using ESQuery patterns) | | | | |
|
|
50
53
|
|
|
51
54
|
<!-- end auto-generated rules list -->
|
|
52
55
|
|
package/dist/index.cjs
CHANGED
|
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const meta_js_1 = require("./meta.cjs");
|
|
4
4
|
const array_style_js_1 = require("./rules/array-style.cjs");
|
|
5
5
|
const consistent_import_source_js_1 = require("./rules/consistent-import-source.cjs");
|
|
6
|
+
const consistent_import_js_1 = require("./rules/consistent-import.cjs");
|
|
6
7
|
const consistent_object_schema_type_js_1 = require("./rules/consistent-object-schema-type.cjs");
|
|
7
8
|
const no_any_schema_js_1 = require("./rules/no-any-schema.cjs");
|
|
8
9
|
const no_empty_custom_schema_js_1 = require("./rules/no-empty-custom-schema.cjs");
|
|
9
10
|
const no_number_schema_with_int_js_1 = require("./rules/no-number-schema-with-int.cjs");
|
|
10
11
|
const no_optional_and_default_together_js_1 = require("./rules/no-optional-and-default-together.cjs");
|
|
12
|
+
const no_string_schema_with_uuid_js_1 = require("./rules/no-string-schema-with-uuid.cjs");
|
|
11
13
|
const no_throw_in_refine_js_1 = require("./rules/no-throw-in-refine.cjs");
|
|
12
14
|
const no_unknown_schema_js_1 = require("./rules/no-unknown-schema.cjs");
|
|
13
15
|
const prefer_enum_over_literal_union_js_1 = require("./rules/prefer-enum-over-literal-union.cjs");
|
|
@@ -26,10 +28,12 @@ const eslintPluginZod = {
|
|
|
26
28
|
rules: {
|
|
27
29
|
'array-style': array_style_js_1.arrayStyle,
|
|
28
30
|
'consistent-import-source': consistent_import_source_js_1.consistentImportSource,
|
|
31
|
+
'consistent-import': consistent_import_js_1.consistentImport,
|
|
29
32
|
'consistent-object-schema-type': consistent_object_schema_type_js_1.consistentObjectSchemaType,
|
|
30
33
|
'no-any-schema': no_any_schema_js_1.noAnySchema,
|
|
31
34
|
'no-empty-custom-schema': no_empty_custom_schema_js_1.noEmptyCustomSchema,
|
|
32
35
|
'no-number-schema-with-int': no_number_schema_with_int_js_1.noNumberSchemaWithInt,
|
|
36
|
+
'no-string-schema-with-uuid': no_string_schema_with_uuid_js_1.noStringSchemaWithUuid,
|
|
33
37
|
'no-optional-and-default-together': no_optional_and_default_together_js_1.noOptionalAndDefaultTogether,
|
|
34
38
|
'no-throw-in-refine': no_throw_in_refine_js_1.noThrowInRefine,
|
|
35
39
|
'no-unknown-schema': no_unknown_schema_js_1.noUnknownSchema,
|
|
@@ -51,15 +55,16 @@ const recommendedConfig = {
|
|
|
51
55
|
},
|
|
52
56
|
rules: {
|
|
53
57
|
'zod/array-style': 'error',
|
|
58
|
+
'zod/consistent-import': 'error',
|
|
54
59
|
'zod/no-any-schema': 'error',
|
|
55
60
|
'zod/no-empty-custom-schema': 'error',
|
|
56
61
|
'zod/no-number-schema-with-int': 'error',
|
|
62
|
+
'zod/no-string-schema-with-uuid': 'error',
|
|
57
63
|
'zod/no-optional-and-default-together': 'error',
|
|
58
64
|
'zod/no-throw-in-refine': 'error',
|
|
59
65
|
'zod/prefer-enum-over-literal-union': 'error',
|
|
60
66
|
'zod/prefer-meta': 'error',
|
|
61
67
|
'zod/prefer-meta-last': 'error',
|
|
62
|
-
'zod/prefer-namespace-import': 'error',
|
|
63
68
|
'zod/require-brand-type-parameter': 'error',
|
|
64
69
|
'zod/require-error-message': 'error',
|
|
65
70
|
'zod/require-schema-suffix': 'error',
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { PLUGIN_NAME, PLUGIN_VERSION } from "./meta.js";
|
|
2
2
|
import { arrayStyle } from "./rules/array-style.js";
|
|
3
3
|
import { consistentImportSource } from "./rules/consistent-import-source.js";
|
|
4
|
+
import { consistentImport } from "./rules/consistent-import.js";
|
|
4
5
|
import { consistentObjectSchemaType } from "./rules/consistent-object-schema-type.js";
|
|
5
6
|
import { noAnySchema } from "./rules/no-any-schema.js";
|
|
6
7
|
import { noEmptyCustomSchema } from "./rules/no-empty-custom-schema.js";
|
|
7
8
|
import { noNumberSchemaWithInt } from "./rules/no-number-schema-with-int.js";
|
|
8
9
|
import { noOptionalAndDefaultTogether } from "./rules/no-optional-and-default-together.js";
|
|
10
|
+
import { noStringSchemaWithUuid } from "./rules/no-string-schema-with-uuid.js";
|
|
9
11
|
import { noThrowInRefine } from "./rules/no-throw-in-refine.js";
|
|
10
12
|
import { noUnknownSchema } from "./rules/no-unknown-schema.js";
|
|
11
13
|
import { preferEnumOverLiteralUnion } from "./rules/prefer-enum-over-literal-union.js";
|
|
@@ -24,10 +26,12 @@ const eslintPluginZod = {
|
|
|
24
26
|
rules: {
|
|
25
27
|
'array-style': arrayStyle,
|
|
26
28
|
'consistent-import-source': consistentImportSource,
|
|
29
|
+
'consistent-import': consistentImport,
|
|
27
30
|
'consistent-object-schema-type': consistentObjectSchemaType,
|
|
28
31
|
'no-any-schema': noAnySchema,
|
|
29
32
|
'no-empty-custom-schema': noEmptyCustomSchema,
|
|
30
33
|
'no-number-schema-with-int': noNumberSchemaWithInt,
|
|
34
|
+
'no-string-schema-with-uuid': noStringSchemaWithUuid,
|
|
31
35
|
'no-optional-and-default-together': noOptionalAndDefaultTogether,
|
|
32
36
|
'no-throw-in-refine': noThrowInRefine,
|
|
33
37
|
'no-unknown-schema': noUnknownSchema,
|
|
@@ -49,15 +53,16 @@ const recommendedConfig = {
|
|
|
49
53
|
},
|
|
50
54
|
rules: {
|
|
51
55
|
'zod/array-style': 'error',
|
|
56
|
+
'zod/consistent-import': 'error',
|
|
52
57
|
'zod/no-any-schema': 'error',
|
|
53
58
|
'zod/no-empty-custom-schema': 'error',
|
|
54
59
|
'zod/no-number-schema-with-int': 'error',
|
|
60
|
+
'zod/no-string-schema-with-uuid': 'error',
|
|
55
61
|
'zod/no-optional-and-default-together': 'error',
|
|
56
62
|
'zod/no-throw-in-refine': 'error',
|
|
57
63
|
'zod/prefer-enum-over-literal-union': 'error',
|
|
58
64
|
'zod/prefer-meta': 'error',
|
|
59
65
|
'zod/prefer-meta-last': 'error',
|
|
60
|
-
'zod/prefer-namespace-import': 'error',
|
|
61
66
|
'zod/require-brand-type-parameter': 'error',
|
|
62
67
|
'zod/require-error-message': 'error',
|
|
63
68
|
'zod/require-schema-suffix': 'error',
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.consistentImport = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const meta_js_1 = require("../meta.cjs");
|
|
6
|
+
const is_zod_import_source_js_1 = require("../utils/is-zod-import-source.cjs");
|
|
7
|
+
const IMPORT_SYNTAXES = ['namespace', 'named'];
|
|
8
|
+
function isGroupFirstImportKindValidForSyntax(group, syntax) {
|
|
9
|
+
const { hasOnlyTypeImports, nodes } = group;
|
|
10
|
+
const [firstImportNode] = nodes;
|
|
11
|
+
const { specifiers, importKind } = firstImportNode;
|
|
12
|
+
if (specifiers.length !== 1) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const [specifier] = specifiers;
|
|
16
|
+
const isValidForSyntax = syntax === 'named'
|
|
17
|
+
? specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
18
|
+
specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
19
|
+
specifier.imported.name === 'z'
|
|
20
|
+
: specifier.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier;
|
|
21
|
+
if (!isValidForSyntax) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
if (hasOnlyTypeImports) {
|
|
25
|
+
return importKind === 'type';
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
function shouldIdentifierBeRenamed(node) {
|
|
30
|
+
if (node.parent.type === utils_1.AST_NODE_TYPES.ImportSpecifier) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (node.parent.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
34
|
+
node.parent.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
35
|
+
node.parent.object.name !== node.name) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
function getNamespaceAliasNameFrom(node) {
|
|
41
|
+
if (node.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier ||
|
|
42
|
+
node.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
43
|
+
return node.local.name;
|
|
44
|
+
}
|
|
45
|
+
if (node.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
46
|
+
node.imported.name === 'z') {
|
|
47
|
+
return node.local.name;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
exports.consistentImport = utils_1.ESLintUtils.RuleCreator(meta_js_1.getRuleURL)({
|
|
52
|
+
name: 'consistent-import',
|
|
53
|
+
meta: {
|
|
54
|
+
type: 'problem',
|
|
55
|
+
docs: {
|
|
56
|
+
description: 'Enforce a consistent import style for Zod',
|
|
57
|
+
},
|
|
58
|
+
fixable: 'code',
|
|
59
|
+
messages: {
|
|
60
|
+
changeImportSyntax: 'Use a {{syntax}} import for Zod.',
|
|
61
|
+
removeDuplicate: 'Remove duplicate Zod import; Zod is already imported.',
|
|
62
|
+
convertUsage: 'Update Zod usage to match the {{syntax}} import syntax.',
|
|
63
|
+
},
|
|
64
|
+
schema: [
|
|
65
|
+
{
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
syntax: {
|
|
69
|
+
description: 'Specifies the import syntax to use for Zod.',
|
|
70
|
+
type: 'string',
|
|
71
|
+
enum: IMPORT_SYNTAXES,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
additionalProperties: false,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
defaultOptions: [{ syntax: 'namespace' }],
|
|
79
|
+
create(context, [options]) {
|
|
80
|
+
const { syntax } = options;
|
|
81
|
+
const { sourceCode } = context;
|
|
82
|
+
const importGroups = {};
|
|
83
|
+
return {
|
|
84
|
+
ImportDeclaration(node) {
|
|
85
|
+
const { source, importKind } = node;
|
|
86
|
+
if (!(0, is_zod_import_source_js_1.isZodImportSource)(source.value)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!importGroups[source.value]) {
|
|
90
|
+
importGroups[source.value] = {
|
|
91
|
+
hasOnlyTypeImports: true,
|
|
92
|
+
nodes: [],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (importGroups[source.value].hasOnlyTypeImports &&
|
|
96
|
+
importKind === 'value') {
|
|
97
|
+
importGroups[source.value].hasOnlyTypeImports = false;
|
|
98
|
+
}
|
|
99
|
+
importGroups[source.value].nodes.push(node);
|
|
100
|
+
},
|
|
101
|
+
'Program:exit': function () {
|
|
102
|
+
let namespaceAliasNameIndex = 0;
|
|
103
|
+
for (const importGroup of Object.values(importGroups)) {
|
|
104
|
+
const { hasOnlyTypeImports, nodes } = importGroup;
|
|
105
|
+
const [firstImportNode, ...othersImportNodes] = nodes;
|
|
106
|
+
const nodesWithVariablesToUpdate = [];
|
|
107
|
+
let namespaceAliasName = null;
|
|
108
|
+
for (const specifier of nodes.flatMap((it) => it.specifiers)) {
|
|
109
|
+
if (!namespaceAliasName) {
|
|
110
|
+
namespaceAliasName = getNamespaceAliasNameFrom(specifier);
|
|
111
|
+
if (namespaceAliasName) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
nodesWithVariablesToUpdate.push(specifier);
|
|
116
|
+
}
|
|
117
|
+
if (!namespaceAliasName) {
|
|
118
|
+
namespaceAliasName = 'z';
|
|
119
|
+
if (namespaceAliasNameIndex > 0) {
|
|
120
|
+
namespaceAliasName = `z${namespaceAliasNameIndex}`;
|
|
121
|
+
namespaceAliasNameIndex += 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const isFirstImportValid = isGroupFirstImportKindValidForSyntax(importGroup, syntax);
|
|
125
|
+
if (!isFirstImportValid) {
|
|
126
|
+
context.report({
|
|
127
|
+
node: firstImportNode,
|
|
128
|
+
messageId: 'changeImportSyntax',
|
|
129
|
+
data: { syntax },
|
|
130
|
+
fix(fixer) {
|
|
131
|
+
const importTypeKeyword = hasOnlyTypeImports ? 'type ' : '';
|
|
132
|
+
let importSpecifier;
|
|
133
|
+
if (syntax === 'named') {
|
|
134
|
+
if (namespaceAliasName === 'z') {
|
|
135
|
+
importSpecifier = '{ z }';
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
importSpecifier = `{ z as ${namespaceAliasName} }`;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
importSpecifier = `* as ${namespaceAliasName}`;
|
|
143
|
+
}
|
|
144
|
+
const newImportText = `import ${importTypeKeyword}${importSpecifier} from ${firstImportNode.source.raw};`;
|
|
145
|
+
return fixer.replaceText(firstImportNode, newImportText);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const allVariables = nodesWithVariablesToUpdate.flatMap((it) => sourceCode.getDeclaredVariables(it));
|
|
150
|
+
const allReferences = allVariables.flatMap((it) => it.references);
|
|
151
|
+
for (const ref of allReferences) {
|
|
152
|
+
const { identifier } = ref;
|
|
153
|
+
if (shouldIdentifierBeRenamed(identifier)) {
|
|
154
|
+
context.report({
|
|
155
|
+
node: identifier,
|
|
156
|
+
messageId: 'convertUsage',
|
|
157
|
+
data: { syntax },
|
|
158
|
+
fix(fixer) {
|
|
159
|
+
const newId = `${namespaceAliasName}.${identifier.name}`;
|
|
160
|
+
return fixer.replaceText(identifier, newId);
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (const extraImport of othersImportNodes) {
|
|
166
|
+
context.report({
|
|
167
|
+
node: extraImport,
|
|
168
|
+
messageId: 'removeDuplicate',
|
|
169
|
+
fix(fixer) {
|
|
170
|
+
return fixer.removeRange(extraImport.range);
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
declare const IMPORT_SYNTAXES: readonly ["namespace", "named"];
|
|
3
|
+
type ImportSyntax = (typeof IMPORT_SYNTAXES)[number];
|
|
4
|
+
interface Options {
|
|
5
|
+
syntax: ImportSyntax;
|
|
6
|
+
}
|
|
7
|
+
type MessageIds = 'changeImportSyntax' | 'removeDuplicate' | 'convertUsage';
|
|
8
|
+
export declare const consistentImport: ESLintUtils.RuleModule<MessageIds, [Options], unknown, ESLintUtils.RuleListener> & {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
declare const IMPORT_SYNTAXES: readonly ["namespace", "named"];
|
|
3
|
+
type ImportSyntax = (typeof IMPORT_SYNTAXES)[number];
|
|
4
|
+
interface Options {
|
|
5
|
+
syntax: ImportSyntax;
|
|
6
|
+
}
|
|
7
|
+
type MessageIds = 'changeImportSyntax' | 'removeDuplicate' | 'convertUsage';
|
|
8
|
+
export declare const consistentImport: ESLintUtils.RuleModule<MessageIds, [Options], unknown, ESLintUtils.RuleListener> & {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getRuleURL } from "../meta.js";
|
|
3
|
+
import { isZodImportSource } from "../utils/is-zod-import-source.js";
|
|
4
|
+
const IMPORT_SYNTAXES = ['namespace', 'named'];
|
|
5
|
+
function isGroupFirstImportKindValidForSyntax(group, syntax) {
|
|
6
|
+
const { hasOnlyTypeImports, nodes } = group;
|
|
7
|
+
const [firstImportNode] = nodes;
|
|
8
|
+
const { specifiers, importKind } = firstImportNode;
|
|
9
|
+
if (specifiers.length !== 1) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const [specifier] = specifiers;
|
|
13
|
+
const isValidForSyntax = syntax === 'named'
|
|
14
|
+
? specifier.type === AST_NODE_TYPES.ImportSpecifier &&
|
|
15
|
+
specifier.imported.type === AST_NODE_TYPES.Identifier &&
|
|
16
|
+
specifier.imported.name === 'z'
|
|
17
|
+
: specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier;
|
|
18
|
+
if (!isValidForSyntax) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (hasOnlyTypeImports) {
|
|
22
|
+
return importKind === 'type';
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
function shouldIdentifierBeRenamed(node) {
|
|
27
|
+
if (node.parent.type === AST_NODE_TYPES.ImportSpecifier) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (node.parent.type === AST_NODE_TYPES.MemberExpression &&
|
|
31
|
+
node.parent.object.type === AST_NODE_TYPES.Identifier &&
|
|
32
|
+
node.parent.object.name !== node.name) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
function getNamespaceAliasNameFrom(node) {
|
|
38
|
+
if (node.type === AST_NODE_TYPES.ImportDefaultSpecifier ||
|
|
39
|
+
node.type === AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
40
|
+
return node.local.name;
|
|
41
|
+
}
|
|
42
|
+
if (node.imported.type === AST_NODE_TYPES.Identifier &&
|
|
43
|
+
node.imported.name === 'z') {
|
|
44
|
+
return node.local.name;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
export const consistentImport = ESLintUtils.RuleCreator(getRuleURL)({
|
|
49
|
+
name: 'consistent-import',
|
|
50
|
+
meta: {
|
|
51
|
+
type: 'problem',
|
|
52
|
+
docs: {
|
|
53
|
+
description: 'Enforce a consistent import style for Zod',
|
|
54
|
+
},
|
|
55
|
+
fixable: 'code',
|
|
56
|
+
messages: {
|
|
57
|
+
changeImportSyntax: 'Use a {{syntax}} import for Zod.',
|
|
58
|
+
removeDuplicate: 'Remove duplicate Zod import; Zod is already imported.',
|
|
59
|
+
convertUsage: 'Update Zod usage to match the {{syntax}} import syntax.',
|
|
60
|
+
},
|
|
61
|
+
schema: [
|
|
62
|
+
{
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
syntax: {
|
|
66
|
+
description: 'Specifies the import syntax to use for Zod.',
|
|
67
|
+
type: 'string',
|
|
68
|
+
enum: IMPORT_SYNTAXES,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
defaultOptions: [{ syntax: 'namespace' }],
|
|
76
|
+
create(context, [options]) {
|
|
77
|
+
const { syntax } = options;
|
|
78
|
+
const { sourceCode } = context;
|
|
79
|
+
const importGroups = {};
|
|
80
|
+
return {
|
|
81
|
+
ImportDeclaration(node) {
|
|
82
|
+
const { source, importKind } = node;
|
|
83
|
+
if (!isZodImportSource(source.value)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!importGroups[source.value]) {
|
|
87
|
+
importGroups[source.value] = {
|
|
88
|
+
hasOnlyTypeImports: true,
|
|
89
|
+
nodes: [],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (importGroups[source.value].hasOnlyTypeImports &&
|
|
93
|
+
importKind === 'value') {
|
|
94
|
+
importGroups[source.value].hasOnlyTypeImports = false;
|
|
95
|
+
}
|
|
96
|
+
importGroups[source.value].nodes.push(node);
|
|
97
|
+
},
|
|
98
|
+
'Program:exit': function () {
|
|
99
|
+
let namespaceAliasNameIndex = 0;
|
|
100
|
+
for (const importGroup of Object.values(importGroups)) {
|
|
101
|
+
const { hasOnlyTypeImports, nodes } = importGroup;
|
|
102
|
+
const [firstImportNode, ...othersImportNodes] = nodes;
|
|
103
|
+
const nodesWithVariablesToUpdate = [];
|
|
104
|
+
let namespaceAliasName = null;
|
|
105
|
+
for (const specifier of nodes.flatMap((it) => it.specifiers)) {
|
|
106
|
+
if (!namespaceAliasName) {
|
|
107
|
+
namespaceAliasName = getNamespaceAliasNameFrom(specifier);
|
|
108
|
+
if (namespaceAliasName) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
nodesWithVariablesToUpdate.push(specifier);
|
|
113
|
+
}
|
|
114
|
+
if (!namespaceAliasName) {
|
|
115
|
+
namespaceAliasName = 'z';
|
|
116
|
+
if (namespaceAliasNameIndex > 0) {
|
|
117
|
+
namespaceAliasName = `z${namespaceAliasNameIndex}`;
|
|
118
|
+
namespaceAliasNameIndex += 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const isFirstImportValid = isGroupFirstImportKindValidForSyntax(importGroup, syntax);
|
|
122
|
+
if (!isFirstImportValid) {
|
|
123
|
+
context.report({
|
|
124
|
+
node: firstImportNode,
|
|
125
|
+
messageId: 'changeImportSyntax',
|
|
126
|
+
data: { syntax },
|
|
127
|
+
fix(fixer) {
|
|
128
|
+
const importTypeKeyword = hasOnlyTypeImports ? 'type ' : '';
|
|
129
|
+
let importSpecifier;
|
|
130
|
+
if (syntax === 'named') {
|
|
131
|
+
if (namespaceAliasName === 'z') {
|
|
132
|
+
importSpecifier = '{ z }';
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
importSpecifier = `{ z as ${namespaceAliasName} }`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
importSpecifier = `* as ${namespaceAliasName}`;
|
|
140
|
+
}
|
|
141
|
+
const newImportText = `import ${importTypeKeyword}${importSpecifier} from ${firstImportNode.source.raw};`;
|
|
142
|
+
return fixer.replaceText(firstImportNode, newImportText);
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const allVariables = nodesWithVariablesToUpdate.flatMap((it) => sourceCode.getDeclaredVariables(it));
|
|
147
|
+
const allReferences = allVariables.flatMap((it) => it.references);
|
|
148
|
+
for (const ref of allReferences) {
|
|
149
|
+
const { identifier } = ref;
|
|
150
|
+
if (shouldIdentifierBeRenamed(identifier)) {
|
|
151
|
+
context.report({
|
|
152
|
+
node: identifier,
|
|
153
|
+
messageId: 'convertUsage',
|
|
154
|
+
data: { syntax },
|
|
155
|
+
fix(fixer) {
|
|
156
|
+
const newId = `${namespaceAliasName}.${identifier.name}`;
|
|
157
|
+
return fixer.replaceText(identifier, newId);
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
for (const extraImport of othersImportNodes) {
|
|
163
|
+
context.report({
|
|
164
|
+
node: extraImport,
|
|
165
|
+
messageId: 'removeDuplicate',
|
|
166
|
+
fix(fixer) {
|
|
167
|
+
return fixer.removeRange(extraImport.range);
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
});
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.noNumberSchemaWithInt = void 0;
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const meta_js_1 = require("../meta.cjs");
|
|
6
|
+
const build_zod_chain_replacement_fix_js_1 = require("../utils/build-zod-chain-replacement-fix.cjs");
|
|
6
7
|
const track_zod_schema_imports_js_1 = require("../utils/track-zod-schema-imports.cjs");
|
|
7
8
|
exports.noNumberSchemaWithInt = utils_1.ESLintUtils.RuleCreator(meta_js_1.getRuleURL)({
|
|
8
9
|
name: 'no-number-schema-with-int',
|
|
@@ -29,13 +30,11 @@ exports.noNumberSchemaWithInt = utils_1.ESLintUtils.RuleCreator(meta_js_1.getRul
|
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
const methods = collectZodChainMethods(node);
|
|
32
|
-
const numberIndex = methods.findIndex((m) => m.name === 'number');
|
|
33
33
|
const intIndex = methods.findIndex((m) => m.name === 'int');
|
|
34
|
-
if (
|
|
34
|
+
if (intIndex === -1) {
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
|
-
const
|
|
38
|
-
const intNode = methods[intIndex].node;
|
|
37
|
+
const numberIndex = methods.findIndex((m) => m.name === 'number');
|
|
39
38
|
if (zodSchemaMeta.schemaDecl === 'named') {
|
|
40
39
|
context.report({
|
|
41
40
|
node,
|
|
@@ -47,18 +46,14 @@ exports.noNumberSchemaWithInt = utils_1.ESLintUtils.RuleCreator(meta_js_1.getRul
|
|
|
47
46
|
node,
|
|
48
47
|
messageId: 'removeNumber',
|
|
49
48
|
fix(fixer) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const fullText = sourceCode.getText(m.node);
|
|
58
|
-
return fullText.slice(objText.length);
|
|
49
|
+
return (0, build_zod_chain_replacement_fix_js_1.buildZodChainReplacementFix)({
|
|
50
|
+
sourceCode,
|
|
51
|
+
fixer,
|
|
52
|
+
methods,
|
|
53
|
+
fromIndex: numberIndex,
|
|
54
|
+
toIndex: intIndex,
|
|
55
|
+
toMethodName: 'int',
|
|
59
56
|
});
|
|
60
|
-
const replacement = `${prefixText}.int()${betweenSuffixes.join('')}`;
|
|
61
|
-
return fixer.replaceTextRange([numberNode.range[0], intNode.range[1]], replacement);
|
|
62
57
|
},
|
|
63
58
|
});
|
|
64
59
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
2
|
import { getRuleURL } from "../meta.js";
|
|
3
|
+
import { buildZodChainReplacementFix } from "../utils/build-zod-chain-replacement-fix.js";
|
|
3
4
|
import { trackZodSchemaImports } from "../utils/track-zod-schema-imports.js";
|
|
4
5
|
export const noNumberSchemaWithInt = ESLintUtils.RuleCreator(getRuleURL)({
|
|
5
6
|
name: 'no-number-schema-with-int',
|
|
@@ -26,13 +27,11 @@ export const noNumberSchemaWithInt = ESLintUtils.RuleCreator(getRuleURL)({
|
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
const methods = collectZodChainMethods(node);
|
|
29
|
-
const numberIndex = methods.findIndex((m) => m.name === 'number');
|
|
30
30
|
const intIndex = methods.findIndex((m) => m.name === 'int');
|
|
31
|
-
if (
|
|
31
|
+
if (intIndex === -1) {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
const
|
|
35
|
-
const intNode = methods[intIndex].node;
|
|
34
|
+
const numberIndex = methods.findIndex((m) => m.name === 'number');
|
|
36
35
|
if (zodSchemaMeta.schemaDecl === 'named') {
|
|
37
36
|
context.report({
|
|
38
37
|
node,
|
|
@@ -44,18 +43,14 @@ export const noNumberSchemaWithInt = ESLintUtils.RuleCreator(getRuleURL)({
|
|
|
44
43
|
node,
|
|
45
44
|
messageId: 'removeNumber',
|
|
46
45
|
fix(fixer) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const fullText = sourceCode.getText(m.node);
|
|
55
|
-
return fullText.slice(objText.length);
|
|
46
|
+
return buildZodChainReplacementFix({
|
|
47
|
+
sourceCode,
|
|
48
|
+
fixer,
|
|
49
|
+
methods,
|
|
50
|
+
fromIndex: numberIndex,
|
|
51
|
+
toIndex: intIndex,
|
|
52
|
+
toMethodName: 'int',
|
|
56
53
|
});
|
|
57
|
-
const replacement = `${prefixText}.int()${betweenSuffixes.join('')}`;
|
|
58
|
-
return fixer.replaceTextRange([numberNode.range[0], intNode.range[1]], replacement);
|
|
59
54
|
},
|
|
60
55
|
});
|
|
61
56
|
},
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noStringSchemaWithUuid = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const meta_js_1 = require("../meta.cjs");
|
|
6
|
+
const build_zod_chain_replacement_fix_js_1 = require("../utils/build-zod-chain-replacement-fix.cjs");
|
|
7
|
+
const track_zod_schema_imports_js_1 = require("../utils/track-zod-schema-imports.cjs");
|
|
8
|
+
exports.noStringSchemaWithUuid = utils_1.ESLintUtils.RuleCreator(meta_js_1.getRuleURL)({
|
|
9
|
+
name: 'no-string-schema-with-uuid',
|
|
10
|
+
meta: {
|
|
11
|
+
fixable: 'code',
|
|
12
|
+
type: 'problem',
|
|
13
|
+
docs: {
|
|
14
|
+
description: 'Disallow usage of `z.string().uuid()` in favor of the dedicated `z.uuid()` schema',
|
|
15
|
+
url: 'https://zod.dev/api#uuids',
|
|
16
|
+
},
|
|
17
|
+
messages: {
|
|
18
|
+
useUuid: '`z.string().uuid()` is redundant. Use `z.uuid()` instead.',
|
|
19
|
+
},
|
|
20
|
+
schema: [],
|
|
21
|
+
},
|
|
22
|
+
defaultOptions: [],
|
|
23
|
+
create(context) {
|
|
24
|
+
const { sourceCode } = context;
|
|
25
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods, } = (0, track_zod_schema_imports_js_1.trackZodSchemaImports)();
|
|
26
|
+
return {
|
|
27
|
+
ImportDeclaration: importDeclarationListener,
|
|
28
|
+
CallExpression(node) {
|
|
29
|
+
const zodSchemaMeta = detectZodSchemaRootNode(node);
|
|
30
|
+
if ((zodSchemaMeta === null || zodSchemaMeta === void 0 ? void 0 : zodSchemaMeta.schemaType) !== 'string') {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const methods = collectZodChainMethods(node);
|
|
34
|
+
const uuidIndex = methods.findIndex((m) => m.name === 'uuid');
|
|
35
|
+
if (uuidIndex === -1) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const stringIndex = methods.findIndex((m) => m.name === 'string');
|
|
39
|
+
if (zodSchemaMeta.schemaDecl === 'named') {
|
|
40
|
+
context.report({
|
|
41
|
+
node,
|
|
42
|
+
messageId: 'useUuid',
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
context.report({
|
|
47
|
+
node,
|
|
48
|
+
messageId: 'useUuid',
|
|
49
|
+
fix(fixer) {
|
|
50
|
+
return (0, build_zod_chain_replacement_fix_js_1.buildZodChainReplacementFix)({
|
|
51
|
+
sourceCode,
|
|
52
|
+
fixer,
|
|
53
|
+
methods,
|
|
54
|
+
fromIndex: stringIndex,
|
|
55
|
+
toIndex: uuidIndex,
|
|
56
|
+
toMethodName: 'uuid',
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import { getRuleURL } from "../meta.js";
|
|
3
|
+
import { buildZodChainReplacementFix } from "../utils/build-zod-chain-replacement-fix.js";
|
|
4
|
+
import { trackZodSchemaImports } from "../utils/track-zod-schema-imports.js";
|
|
5
|
+
export const noStringSchemaWithUuid = ESLintUtils.RuleCreator(getRuleURL)({
|
|
6
|
+
name: 'no-string-schema-with-uuid',
|
|
7
|
+
meta: {
|
|
8
|
+
fixable: 'code',
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Disallow usage of `z.string().uuid()` in favor of the dedicated `z.uuid()` schema',
|
|
12
|
+
url: 'https://zod.dev/api#uuids',
|
|
13
|
+
},
|
|
14
|
+
messages: {
|
|
15
|
+
useUuid: '`z.string().uuid()` is redundant. Use `z.uuid()` instead.',
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
},
|
|
19
|
+
defaultOptions: [],
|
|
20
|
+
create(context) {
|
|
21
|
+
const { sourceCode } = context;
|
|
22
|
+
const { importDeclarationListener, detectZodSchemaRootNode, collectZodChainMethods, } = trackZodSchemaImports();
|
|
23
|
+
return {
|
|
24
|
+
ImportDeclaration: importDeclarationListener,
|
|
25
|
+
CallExpression(node) {
|
|
26
|
+
const zodSchemaMeta = detectZodSchemaRootNode(node);
|
|
27
|
+
if ((zodSchemaMeta === null || zodSchemaMeta === void 0 ? void 0 : zodSchemaMeta.schemaType) !== 'string') {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const methods = collectZodChainMethods(node);
|
|
31
|
+
const uuidIndex = methods.findIndex((m) => m.name === 'uuid');
|
|
32
|
+
if (uuidIndex === -1) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const stringIndex = methods.findIndex((m) => m.name === 'string');
|
|
36
|
+
if (zodSchemaMeta.schemaDecl === 'named') {
|
|
37
|
+
context.report({
|
|
38
|
+
node,
|
|
39
|
+
messageId: 'useUuid',
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
context.report({
|
|
44
|
+
node,
|
|
45
|
+
messageId: 'useUuid',
|
|
46
|
+
fix(fixer) {
|
|
47
|
+
return buildZodChainReplacementFix({
|
|
48
|
+
sourceCode,
|
|
49
|
+
fixer,
|
|
50
|
+
methods,
|
|
51
|
+
fromIndex: stringIndex,
|
|
52
|
+
toIndex: uuidIndex,
|
|
53
|
+
toMethodName: 'uuid',
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
});
|
|
@@ -46,6 +46,9 @@ exports.preferNamespaceImport = utils_1.ESLintUtils.RuleCreator(meta_js_1.getRul
|
|
|
46
46
|
name: 'prefer-namespace-import',
|
|
47
47
|
meta: {
|
|
48
48
|
type: 'suggestion',
|
|
49
|
+
deprecated: {
|
|
50
|
+
message: "Use `zod/consistent-import` with `{ syntax: 'namespace' }`",
|
|
51
|
+
},
|
|
49
52
|
docs: {
|
|
50
53
|
description: "Enforce importing zod as a namespace import (`import * as z from 'zod'`)",
|
|
51
54
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
export declare const preferNamespaceImport: ESLintUtils.RuleModule<"
|
|
2
|
+
export declare const preferNamespaceImport: ESLintUtils.RuleModule<"removeDuplicate" | "convertUsage" | "useNamespace", [], unknown, ESLintUtils.RuleListener> & {
|
|
3
3
|
name: string;
|
|
4
4
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
export declare const preferNamespaceImport: ESLintUtils.RuleModule<"
|
|
2
|
+
export declare const preferNamespaceImport: ESLintUtils.RuleModule<"removeDuplicate" | "convertUsage" | "useNamespace", [], unknown, ESLintUtils.RuleListener> & {
|
|
3
3
|
name: string;
|
|
4
4
|
};
|
|
@@ -43,6 +43,9 @@ export const preferNamespaceImport = ESLintUtils.RuleCreator(getRuleURL)({
|
|
|
43
43
|
name: 'prefer-namespace-import',
|
|
44
44
|
meta: {
|
|
45
45
|
type: 'suggestion',
|
|
46
|
+
deprecated: {
|
|
47
|
+
message: "Use `zod/consistent-import` with `{ syntax: 'namespace' }`",
|
|
48
|
+
},
|
|
46
49
|
docs: {
|
|
47
50
|
description: "Enforce importing zod as a namespace import (`import * as z from 'zod'`)",
|
|
48
51
|
},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildZodChainReplacementFix = buildZodChainReplacementFix;
|
|
4
|
+
function buildZodChainReplacementFix(opts) {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
const { sourceCode, fixer, methods, fromIndex, toIndex, toMethodName } = opts;
|
|
7
|
+
const fromNode = (_a = methods[fromIndex]) === null || _a === void 0 ? void 0 : _a.node;
|
|
8
|
+
const toNode = (_b = methods[toIndex]) === null || _b === void 0 ? void 0 : _b.node;
|
|
9
|
+
if (!fromNode || !toNode) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const fromCallee = fromNode.callee;
|
|
13
|
+
const prefixObj = fromCallee.object;
|
|
14
|
+
const prefixText = sourceCode.getText(prefixObj);
|
|
15
|
+
const methodsBetween = methods.slice(fromIndex + 1, toIndex);
|
|
16
|
+
const betweenSuffixes = methodsBetween.map((m) => {
|
|
17
|
+
const betweenCallee = m.node.callee;
|
|
18
|
+
const objText = sourceCode.getText(betweenCallee.object);
|
|
19
|
+
const fullText = sourceCode.getText(m.node);
|
|
20
|
+
return fullText.slice(objText.length);
|
|
21
|
+
});
|
|
22
|
+
const replacement = `${prefixText}.${toMethodName}()${betweenSuffixes.join('')}`;
|
|
23
|
+
return fixer.replaceTextRange([fromNode.range[0], toNode.range[1]], replacement);
|
|
24
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
export declare function buildZodChainReplacementFix(opts: {
|
|
3
|
+
sourceCode: TSESLint.SourceCode;
|
|
4
|
+
fixer: TSESLint.RuleFixer;
|
|
5
|
+
methods: Array<{
|
|
6
|
+
name: string;
|
|
7
|
+
node: TSESTree.CallExpression;
|
|
8
|
+
}>;
|
|
9
|
+
fromIndex: number;
|
|
10
|
+
toIndex: number;
|
|
11
|
+
toMethodName: string;
|
|
12
|
+
}): TSESLint.RuleFix | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
export declare function buildZodChainReplacementFix(opts: {
|
|
3
|
+
sourceCode: TSESLint.SourceCode;
|
|
4
|
+
fixer: TSESLint.RuleFixer;
|
|
5
|
+
methods: Array<{
|
|
6
|
+
name: string;
|
|
7
|
+
node: TSESTree.CallExpression;
|
|
8
|
+
}>;
|
|
9
|
+
fromIndex: number;
|
|
10
|
+
toIndex: number;
|
|
11
|
+
toMethodName: string;
|
|
12
|
+
}): TSESLint.RuleFix | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function buildZodChainReplacementFix(opts) {
|
|
2
|
+
var _a, _b;
|
|
3
|
+
const { sourceCode, fixer, methods, fromIndex, toIndex, toMethodName } = opts;
|
|
4
|
+
const fromNode = (_a = methods[fromIndex]) === null || _a === void 0 ? void 0 : _a.node;
|
|
5
|
+
const toNode = (_b = methods[toIndex]) === null || _b === void 0 ? void 0 : _b.node;
|
|
6
|
+
if (!fromNode || !toNode) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const fromCallee = fromNode.callee;
|
|
10
|
+
const prefixObj = fromCallee.object;
|
|
11
|
+
const prefixText = sourceCode.getText(prefixObj);
|
|
12
|
+
const methodsBetween = methods.slice(fromIndex + 1, toIndex);
|
|
13
|
+
const betweenSuffixes = methodsBetween.map((m) => {
|
|
14
|
+
const betweenCallee = m.node.callee;
|
|
15
|
+
const objText = sourceCode.getText(betweenCallee.object);
|
|
16
|
+
const fullText = sourceCode.getText(m.node);
|
|
17
|
+
return fullText.slice(objText.length);
|
|
18
|
+
});
|
|
19
|
+
const replacement = `${prefixText}.${toMethodName}()${betweenSuffixes.join('')}`;
|
|
20
|
+
return fixer.replaceTextRange([fromNode.range[0], toNode.range[1]], replacement);
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-zod",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.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": {
|
|
@@ -62,18 +62,20 @@
|
|
|
62
62
|
"@marcalexiei/prettier-config": "1.1.4",
|
|
63
63
|
"@types/esquery": "1.5.4",
|
|
64
64
|
"@types/node": "24.10.1",
|
|
65
|
-
"@typescript-eslint/rule-tester": "8.
|
|
66
|
-
"@vitest/eslint-plugin": "1.6.
|
|
65
|
+
"@typescript-eslint/rule-tester": "8.56.1",
|
|
66
|
+
"@vitest/eslint-plugin": "1.6.9",
|
|
67
67
|
"dedent": "1.7.1",
|
|
68
68
|
"eslint": "9.39.2",
|
|
69
|
-
"eslint-doc-generator": "3.0
|
|
70
|
-
"eslint-
|
|
71
|
-
"eslint-plugin-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
69
|
+
"eslint-doc-generator": "3.2.0",
|
|
70
|
+
"eslint-import-resolver-typescript": "4.4.4",
|
|
71
|
+
"eslint-plugin-eslint-plugin": "7.3.1",
|
|
72
|
+
"eslint-plugin-import-x": "4.16.1",
|
|
73
|
+
"eslint-plugin-n": "17.24.0",
|
|
74
|
+
"knip": "5.85.0",
|
|
75
|
+
"prettier": "3.8.1",
|
|
74
76
|
"typescript": "5.9.3",
|
|
75
|
-
"typescript-eslint": "8.
|
|
76
|
-
"vitest": "4.0.
|
|
77
|
+
"typescript-eslint": "8.56.1",
|
|
78
|
+
"vitest": "4.0.18",
|
|
77
79
|
"zshy": "0.7.0"
|
|
78
80
|
},
|
|
79
81
|
"scripts": {
|