diff --git a/src/utils/dependencies.ts b/src/utils/dependencies.ts index 2892d3ab4..93e04de1a 100644 --- a/src/utils/dependencies.ts +++ b/src/utils/dependencies.ts @@ -78,7 +78,11 @@ export async function initiateValidatorsContext() { nuxtContentContext().set('valibot', await import('./schema/valibot')) } if (await isPackageInstalled('zod')) { - nuxtContentContext().set('zod3', await import('./schema/zod3')) + // The zod3 converter statically imports `zod-to-json-schema`, so only + // register it when that package is actually reachable. + if (await isPackageInstalled('zod-to-json-schema')) { + nuxtContentContext().set('zod3', await import('./schema/zod3')) + } nuxtContentContext().set('zod4', await import('./schema/zod4')) } } diff --git a/src/utils/index.ts b/src/utils/index.ts index a4088891e..36427fa82 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -6,4 +6,4 @@ export { defineTransformer } from './content/transformers/utils' /** * This is only for backward compatibility with Zod v3. Consider using direct import from 'zod' instead. This will be removed in the next major version. */ -export { z } from './schema/zod3' +export { z } from './schema/zod3-legacy' diff --git a/src/utils/schema/zod3-legacy.ts b/src/utils/schema/zod3-legacy.ts new file mode 100644 index 000000000..026266f54 --- /dev/null +++ b/src/utils/schema/zod3-legacy.ts @@ -0,0 +1,20 @@ +import { z as zod } from 'zod' +import type { EditorOptions } from '../../types' + +declare module 'zod' { + interface ZodTypeDef { + editor?: EditorOptions + } + + interface ZodType { + editor(options: EditorOptions): this + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(zod.ZodType as any).prototype.editor = function (options: EditorOptions) { + this._def.editor = { ...this._def.editor, ...options } + return this +} + +export const z = zod diff --git a/src/utils/schema/zod3.ts b/src/utils/schema/zod3.ts index 17a789168..836cb286b 100644 --- a/src/utils/schema/zod3.ts +++ b/src/utils/schema/zod3.ts @@ -1,7 +1,7 @@ import { zodToJsonSchema, ignoreOverride } from 'zod-to-json-schema' -import { z as zod } from 'zod' +import type { ZodSchema } from 'zod' import { createDefu } from 'defu' -import type { Draft07, EditorOptions } from '../../types' +import type { Draft07 } from '../../types' const defu = createDefu((obj, key, value) => { if (Array.isArray(obj[key]) && Array.isArray(value)) { @@ -10,26 +10,8 @@ const defu = createDefu((obj, key, value) => { } }) -declare module 'zod' { - interface ZodTypeDef { - editor?: EditorOptions - } - - interface ZodType { - editor(options: EditorOptions): this - } -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -(zod.ZodType as any).prototype.editor = function (options: EditorOptions) { - this._def.editor = { ...this._def.editor, ...options } - return this -} - -export const z = zod - export function toJSONSchema(_schema: unknown, name: string): Draft07 { - const schema = _schema as zod.ZodSchema + const schema = _schema as ZodSchema const jsonSchema = zodToJsonSchema(schema, { name, $refStrategy: 'none', dateStrategy: 'format:date' }) as Draft07 const jsonSchemaWithEditorMeta = zodToJsonSchema( schema, diff --git a/test/unit/validatorLazyLoading.test.ts b/test/unit/validatorLazyLoading.test.ts new file mode 100644 index 000000000..b0fb77c2f --- /dev/null +++ b/test/unit/validatorLazyLoading.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, test, vi } from 'vitest' + +const converter = vi.hoisted(() => ({ loaded: vi.fn() })) + +vi.mock('zod-to-json-schema', async (importOriginal) => { + converter.loaded() + return await importOriginal() +}) + +describe('validator lazy loading', () => { + test('loads zod-to-json-schema only when the zod3 validator is initialized', async () => { + await import('../../src/utils/index.ts') + + expect(converter.loaded).not.toHaveBeenCalled() + + const { initiateValidatorsContext } = await import('../../src/utils/dependencies.ts') + await initiateValidatorsContext() + + expect(converter.loaded).toHaveBeenCalled() + }) +})