Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bold-lines-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-zod-x': patch
---

fix: `detectZodSchemaRootNode` doesn't detect named `z` import
31 changes: 22 additions & 9 deletions src/rules/array-style.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,30 @@ const ruleTester = new RuleTester();
ruleTester.run('array-style (function)', arrayStyle, {
valid: [
{
name: 'default option',
name: 'namespace import',
code: dedent`
import * as z from 'zod';
z.array(z.string());
`,
},
{
name: '`function` option',
name: 'named import',
code: dedent`
import * as z from 'zod';
z.array(z.string());
import { array, string } from 'zod';
array(string());
`,
},
{
name: 'named',
name: 'named z import',
code: dedent`
import { array, string } from 'zod';
array(string());
import { z } from 'zod';
z.array(z.string());
`,
},
],
invalid: [
{
name: 'namespace',
name: 'namespace import',
code: dedent`
import * as z from 'zod';
z.string().array();
Expand All @@ -43,7 +43,7 @@ ruleTester.run('array-style (function)', arrayStyle, {
`,
},
{
name: 'named',
name: 'named import',
code: dedent`
import { string } from 'zod';
string().array();
Expand All @@ -52,6 +52,19 @@ ruleTester.run('array-style (function)', arrayStyle, {
errors: [{ messageId: 'useFunction' }],
output: null,
},
{
// https://github.com/marcalexiei/eslint-plugin-zod-x/issues/174
name: 'named z import',
code: dedent`
import { z } from 'zod';
z.string().array();
`,
errors: [{ messageId: 'useFunction' }],
output: dedent`
import { z } from 'zod';
z.array(z.string());
`,
},
{
name: 'with method',
code: dedent`
Expand Down
6 changes: 3 additions & 3 deletions src/rules/array-style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ export const arrayStyle = ESLintUtils.RuleCreator(getRuleURL)<
return;
}

const { importType, schemaType } = zodSchema;
const { schemaDecl, schemaType } = zodSchema;

if (style === 'method') {
// match all z.array(z.string()) and convert them into
// z.string().array()
if (schemaType === 'array') {
if (importType === 'namespace') {
if (schemaDecl === 'namespace') {
context.report({
node,
messageId: 'useMethod',
Expand Down Expand Up @@ -105,7 +105,7 @@ export const arrayStyle = ESLintUtils.RuleCreator(getRuleURL)<
// if there is a param the array has already a schema inside
node.arguments.length === 0
) {
if (importType === 'namespace') {
if (schemaDecl === 'namespace') {
context.report({
node,
messageId: 'useFunction',
Expand Down
20 changes: 20 additions & 0 deletions src/rules/consistent-import-source.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const ruleTester = new RuleTester();

ruleTester.run('consistent-import-source', consistentImportSource, {
valid: [
{ code: 'import * as z from "zod"' },
{ code: 'import { z } from "zod"' },
{ code: 'import z from "zod"' },
{
code: 'import z from "zod"',
Expand All @@ -26,6 +28,24 @@ ruleTester.run('consistent-import-source', consistentImportSource, {
],
invalid: [
{
name: 'namespace',
code: 'import * as z from "zod/v4"',
errors: [
{
messageId: 'sourceNotAllowed',
data: { source: 'zod/v4', sources: '"zod"' },
suggestions: [
{
messageId: 'replaceSource',
data: { valid: 'zod', invalid: 'zod/v4' },
output: 'import * as z from "zod"',
},
],
},
],
},
{
name: 'default',
code: 'import z from "zod"',
options: [{ sources: ['zod/v4'] }],
errors: [
Expand Down
11 changes: 9 additions & 2 deletions src/rules/consistent-object-schema-type.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,26 @@ const ruleTester = new RuleTester();
ruleTester.run('consistent-object-schema-type', consistentObjectSchemaType, {
valid: [
{
name: 'correct usage',
name: 'namespace import',
code: dedent`
import * as z from 'zod';
z.object({})
`,
},
{
name: 'correct usage (named)',
name: 'named import',
code: dedent`
import { object } from 'zod';
object({})
`,
},
{
name: 'named z import',
code: dedent`
import { z } from 'zod';
z.object({})
`,
},
{
name: 'nested',
options: [{ allow: ['looseObject', 'strictObject'] }],
Expand Down
51 changes: 51 additions & 0 deletions src/rules/no-any-schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ ruleTester.run('no-any-schema', noAnySchema, {
const schema = z.string();
`,
},
{
// https://github.com/marcalexiei/eslint-plugin-zod-x/issues/174
name: 'named z import',
code: dedent`
import { z } from 'zod';
const schema = z.string();
`,
},
{
name: 'nested schema declaration',
code: dedent`
Expand Down Expand Up @@ -48,6 +56,48 @@ ruleTester.run('no-any-schema', noAnySchema, {
},
],
},
{
name: 'named z import',
code: dedent`
import { z } from 'zod';
const schema = z.any();
`,
errors: [
{
messageId: 'noZAny',
suggestions: [
{
messageId: 'useUnknown',
output: dedent`
import { z } from 'zod';
const schema = z.unknown();
`,
},
],
},
],
},
{
name: 'named z import with rename',
code: dedent`
import { z as pippo } from 'zod';
const schema = pippo.any();
`,
errors: [
{
messageId: 'noZAny',
suggestions: [
{
messageId: 'useUnknown',
output: dedent`
import { z as pippo } from 'zod';
const schema = pippo.unknown();
`,
},
],
},
],
},
{
name: 'named import',
code: dedent`
Expand Down Expand Up @@ -79,6 +129,7 @@ ruleTester.run('no-any-schema', noAnySchema, {
},
{
// https://github.com/marcalexiei/eslint-plugin-zod-x/issues/143
name: 'should correctly fix any schema with chained method',
code: dedent`
import * as z from 'zod';
export const aSchema = z.any().refine((value) => value)
Expand Down
18 changes: 13 additions & 5 deletions src/rules/no-empty-custom-schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ const ruleTester = new RuleTester();
ruleTester.run('no-empty-custom-schema', noEmptyCustomSchema, {
valid: [
{
name: 'valid usage',
name: 'namespace',
code: dedent`
import * as z from 'zod';
z.custom((val) => typeof val === "string" ? /^\\d+px$/.test(val) : false);
`,
},
{
name: 'valid usage (named)',
name: 'named',
code: dedent`
import { custom } from 'zod';
custom((val) => typeof val === "string" ? /^\\d+px$/.test(val) : false);
`,
},
{
name: 'valid usage (named renamed)',
name: 'named renamed',
code: dedent`
import { custom as zCustom } from 'zod';
zCustom((val) => typeof val === "string" ? /^\\d+px$/.test(val) : false);
Expand All @@ -49,21 +49,29 @@ ruleTester.run('no-empty-custom-schema', noEmptyCustomSchema, {
],
invalid: [
{
name: 'invalid usage',
name: 'namespace',
code: dedent`
import * as z from 'zod';
z.custom();
`,
errors: [{ messageId: 'noEmptyCustomSchema' }],
},
{
name: 'invalid usage (named)',
name: 'named',
code: dedent`
import { custom } from 'zod';
custom();
`,
errors: [{ messageId: 'noEmptyCustomSchema' }],
},
{
name: 'named z',
code: dedent`
import { z } from 'zod';
z.custom();
`,
errors: [{ messageId: 'noEmptyCustomSchema' }],
},
{
name: 'type without function',
code: dedent`
Expand Down
27 changes: 23 additions & 4 deletions src/rules/no-number-schema-with-int.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,26 @@ const ruleTester = new RuleTester();
ruleTester.run('no-number-schema-with-int', noNumberSchemaWithInt, {
valid: [
{
name: 'valid usage',
name: 'namespace import',
code: dedent`
import * as z from 'zod';
z.int();
`,
},
{
name: 'valid usage (named)',
name: 'named import',
code: dedent`
import int from 'zod';
int();
`,
},
{
name: 'named z import',
code: dedent`
import { z } from 'zod';
z.int();
`,
},
{
name: 'standard + chain method',
code: dedent`
Expand Down Expand Up @@ -49,7 +56,7 @@ ruleTester.run('no-number-schema-with-int', noNumberSchemaWithInt, {
],
invalid: [
{
name: 'number + int',
name: 'number + int (namespace import)',
code: `
import * as z from 'zod';
z.number().int();
Expand All @@ -61,14 +68,26 @@ ruleTester.run('no-number-schema-with-int', noNumberSchemaWithInt, {
`,
},
{
name: 'number + int (named)',
name: 'number + int (named import)',
code: `
import { number } from 'zod';
number().int();
`,
errors: [{ messageId: 'removeNumber' }],
output: null,
},
{
name: 'number + int (named z import)',
code: `
import { z } from 'zod';
z.number().int();
`,
errors: [{ messageId: 'removeNumber' }],
output: `
import { z } from 'zod';
z.int();
`,
},
{
name: 'number + int + other method',
code: `
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-number-schema-with-int.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const noNumberSchemaWithInt = ESLintUtils.RuleCreator(getRuleURL)({
}

// If it's a named import usage (e.g. `import { number } from 'zod'`), report but do not fix.
if (zodSchemaMeta.importType === 'named') {
if (zodSchemaMeta.schemaDecl === 'named') {
context.report({
node,
messageId: 'removeNumber',
Expand Down
17 changes: 17 additions & 0 deletions src/rules/no-optional-and-default-together.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ ruleTester.run(
string().default("Hello World")
`,
},
{
name: 'schema with only default (named z)',
code: dedent`
import { z } from 'zod';
z.string().default("Hello World")
`,
},
{
name: 'schema with only optional',
code: dedent`
Expand Down Expand Up @@ -106,6 +113,16 @@ ruleTester.run(
errors: [{ messageId: 'noOptionalAndDefaultTogether' }],
output: null,
},
{
name: 'optional then default - preferredMethod: none (explicit)',
code: dedent`
import { z } from 'zod';
z.string().optional().default("Hello World")
`,
options: [{ preferredMethod: 'none' }],
errors: [{ messageId: 'noOptionalAndDefaultTogether' }],
output: null,
},
{
name: 'optional then default - preferredMethod: none (explicit)',
code: dedent`
Expand Down
Loading