Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6b2720b

Browse filesBrowse files
BellYunfarnabaz
andauthored
fix(content): support validator detection with pnpm enableGlobalVirtualStore (#3791)
Co-authored-by: Farnabaz <farnabaz@gmail.com>
1 parent fea3ffa commit 6b2720b
Copy full SHA for 6b2720b

3 files changed

+91-37Lines changed: 91 additions & 37 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/utils/context.ts‎

Copy file name to clipboard
+52-36Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,62 @@
1-
import { createContext } from 'unctx'
1+
import { getContext } from 'unctx'
2+
import type { Draft07 } from '../types'
23

34
type ContextKey = 'zod3' | 'zod4' | 'valibot' | 'unknown'
45

5-
const nuxtContentContext = {
6-
zod3: {
7-
toJSONSchema: (_schema: unknown, _name: string) => {
8-
throw new Error(
9-
'It seems you are using Zod version 3 for collection schema, but Zod is not installed, '
10-
+ 'Nuxt Content does not ship with zod, install `zod` and `zod-to-json-schema` and it will work.',
11-
)
6+
type SchemaHandler = { toJSONSchema: (schema: unknown, name: string) => Draft07 }
7+
8+
type NuxtContentContext = {
9+
zod3: SchemaHandler
10+
zod4: SchemaHandler
11+
valibot: SchemaHandler
12+
unknown: SchemaHandler
13+
set: (key: ContextKey, value: unknown) => void
14+
get: (key: ContextKey) => SchemaHandler
15+
}
16+
17+
const ctx = getContext<NuxtContentContext>('@nuxt/content:validators-context')
18+
19+
/**
20+
* Initialize the context if it hasn't been set yet.
21+
*/
22+
if (!ctx.tryUse()) {
23+
ctx.set({
24+
zod3: {
25+
toJSONSchema: (_schema: unknown, _name: string) => {
26+
throw new Error(
27+
'It seems you are using Zod version 3 for collection schema, but Zod is not installed, '
28+
+ 'Nuxt Content does not ship with zod, install `zod` and `zod-to-json-schema` and it will work.',
29+
)
30+
},
1231
},
13-
},
14-
zod4: {
15-
toJSONSchema: (_schema: unknown, _name: string) => {
16-
throw new Error(
17-
'It seems you are using Zod version 4 for collection schema, but Zod is not installed, '
18-
+ 'Nuxt Content does not ship with zod, install `zod` and it will work.',
19-
)
32+
zod4: {
33+
toJSONSchema: (_schema: unknown, _name: string) => {
34+
throw new Error(
35+
'It seems you are using Zod version 4 for collection schema, but Zod is not installed, '
36+
+ 'Nuxt Content does not ship with zod, install `zod` and it will work.',
37+
)
38+
},
2039
},
21-
},
22-
valibot: {
23-
toJSONSchema: (_schema: unknown, _name: string) => {
24-
throw new Error(
25-
'It seems you are using Valibot for collection schema, but Valibot is not installed, '
26-
+ 'Nuxt Content does not ship with valibot, install `valibot` and `@valibot/to-json-schema` and it will work.',
27-
)
40+
valibot: {
41+
toJSONSchema: (_schema: unknown, _name: string) => {
42+
throw new Error(
43+
'It seems you are using Valibot for collection schema, but Valibot is not installed, '
44+
+ 'Nuxt Content does not ship with valibot, install `valibot` and `@valibot/to-json-schema` and it will work.',
45+
)
46+
},
2847
},
29-
},
30-
unknown: {
31-
toJSONSchema: (_schema: unknown, _name: string) => {
32-
throw new Error('Unknown schema vendor')
48+
unknown: {
49+
toJSONSchema: (_schema: unknown, _name: string) => {
50+
throw new Error('Unknown schema vendor')
51+
},
3352
},
34-
},
35-
set: (key: ContextKey, value: unknown) => {
36-
nuxtContentContext[key] = value as typeof nuxtContentContext[ContextKey]
37-
},
38-
get: (key: ContextKey) => {
39-
return nuxtContentContext[key]
40-
},
53+
set(key: ContextKey, value: unknown) {
54+
(this as unknown as Record<ContextKey, unknown>)[key] = value
55+
},
56+
get(key: ContextKey) {
57+
return (this as unknown as Record<ContextKey, SchemaHandler>)[key]
58+
},
59+
})
4160
}
4261

43-
const ctx = createContext<typeof nuxtContentContext>()
44-
ctx.set(nuxtContentContext)
45-
4662
export default ctx.use
Collapse file

‎src/utils/dependencies.ts‎

Copy file name to clipboardExpand all lines: src/utils/dependencies.ts
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { addDependency } from 'nypm'
2+
import { resolvePackageJSON } from 'pkg-types'
23
import { logger } from './dev'
34
import nuxtContentContext from './context'
45
import { tryUseNuxt } from '@nuxt/kit'
56

67
export async function isPackageInstalled(packageName: string) {
8+
// Resolve relative to @nuxt/content's own location so the check survives
9+
// pnpm's `enableGlobalVirtualStore`, where dependencies declared by
10+
// @nuxt/content (e.g. zod) aren't reachable from the user's project root
11+
// and a plain dynamic import would fail.
712
try {
8-
await import(packageName)
13+
await resolvePackageJSON(packageName, { from: import.meta.url })
914
return true
1015
}
1116
catch {
Collapse file
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
2+
import { z } from 'zod'
3+
4+
const SINGLETON_KEY = Symbol.for('@nuxt/content:validators-context')
5+
6+
function resetValidatorsContext() {
7+
Reflect.deleteProperty(globalThis as Record<symbol, unknown>, SINGLETON_KEY)
8+
}
9+
10+
describe('validator registry', () => {
11+
beforeEach(() => {
12+
vi.resetModules()
13+
resetValidatorsContext()
14+
})
15+
16+
afterEach(() => {
17+
resetValidatorsContext()
18+
})
19+
20+
test('preserves initialized validators across duplicate module evaluations', async () => {
21+
const { initiateValidatorsContext } = await import('../../src/utils/dependencies.ts?deps=primary')
22+
await initiateValidatorsContext()
23+
24+
const { default: useContextA } = await import('../../src/utils/context.ts?ctx=a')
25+
const { default: useContextB } = await import('../../src/utils/context.ts?ctx=b')
26+
27+
const contextA = useContextA()
28+
const contextB = useContextB()
29+
30+
expect(contextA).toBe(contextB)
31+
expect(() => contextB.get('zod3').toJSONSchema(z.object({ title: z.string() }), '__SCHEMA__')).not.toThrow()
32+
})
33+
})

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.