Skip to content

Commit 7a9ca9e

Browse files
authoredFeb 18, 2025··
feat(zod): editor metadata (#3133)
1 parent 045d602 commit 7a9ca9e

File tree

5 files changed

+74
-19
lines changed

5 files changed

+74
-19
lines changed
 

‎docs/content.config.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ export default defineContentConfig({
6262
onboarding: z.object({
6363
title: z.string(),
6464
image: z.object({
65-
dark: z.string(),
66-
light: z.string(),
65+
dark: z.string().editor({ input: 'media' }),
66+
light: z.string().editor({ input: 'media' }),
6767
}),
6868
}),
6969
plans: z.object({
@@ -137,19 +137,19 @@ export default defineContentConfig({
137137
source: 'templates/*.md',
138138
schema: z.object({
139139
draft: z.boolean().default(false),
140-
slug: z.string(),
140+
slug: z.string().editor({ hidden: true }),
141141
subtitle: z.string(),
142142
baseDir: z.string(),
143143
branch: z.string(),
144144
category: z.enum(['docs', 'blog', 'minimal', 'saas']),
145145
demo: z.string(),
146146
licenseType: z.enum(['nuxt-ui-pro', 'free']),
147-
mainScreen: z.string(),
147+
mainScreen: z.string().editor({ input: 'media' }),
148148
name: z.string(),
149149
owner: z.string(),
150-
image1: z.string(),
151-
image2: z.string(),
152-
image3: z.string(),
150+
image1: z.string().editor({ input: 'media' }),
151+
image2: z.string().editor({ input: 'media' }),
152+
image3: z.string().editor({ input: 'media' }),
153153
}),
154154
}),
155155
posts: defineCollection({
@@ -170,7 +170,7 @@ export default defineContentConfig({
170170
category: z.enum(['studio', 'content']).optional(),
171171
date: z.date(),
172172
image: z.object({
173-
src: z.string(),
173+
src: z.string().editor({ input: 'media' }),
174174
alt: z.string(),
175175
}),
176176
}),

‎src/module.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { createParser } from './utils/content'
2828
import { installMDCModule } from './utils/mdc'
2929
import { findPreset } from './presets'
3030
import type { Manifest } from './types/manifest'
31-
import { setupPreview } from './utils/preview/module'
31+
import { setupPreview, shouldEnablePreview } from './utils/preview/module'
3232
import { parseSourceBase } from './utils/source'
3333
import { getLocalDatabase, refineDatabaseConfig, resolveDatabaseAdapter } from './utils/database'
3434

@@ -212,12 +212,7 @@ export default defineNuxtModule<ModuleOptions>({
212212
})
213213

214214
// Handle preview mode
215-
if (process.env.NUXT_CONTENT_PREVIEW_API || options.preview?.api) {
216-
// Only enable preview in production build or when explicitly enabled
217-
if (nuxt.options.dev === true && !options.preview?.dev) {
218-
return
219-
}
220-
215+
if (shouldEnablePreview(nuxt, options)) {
221216
await setupPreview(options, nuxt, resolver, manifest)
222217
}
223218
})

‎src/utils/preview/module.ts

+11
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,14 @@ export async function setupPreview(options: ModuleOptions, nuxt: Nuxt, resolver:
6161
include: manifest.components,
6262
})
6363
}
64+
65+
export function shouldEnablePreview(nuxt: Nuxt, options: ModuleOptions) {
66+
if (process.env.NUXT_CONTENT_PREVIEW_API || options.preview?.api) {
67+
// Only enable preview in production build or when explicitly enabled
68+
if (nuxt.options.dev === true && !options.preview?.dev) {
69+
return false
70+
}
71+
return true
72+
}
73+
return false
74+
}

‎src/utils/templates.ts

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
import { gzip } from 'node:zlib'
22
import { printNode, zodToTs } from 'zod-to-ts'
3-
import { zodToJsonSchema } from 'zod-to-json-schema'
3+
import { zodToJsonSchema, ignoreOverride } from 'zod-to-json-schema'
44
import type { NuxtTemplate } from '@nuxt/schema'
55
import { isAbsolute, join, relative } from 'pathe'
66
import { genDynamicImport } from 'knitwork'
77
import { pascalCase } from 'scule'
88
import type { Schema } from 'untyped'
9+
import { createDefu } from 'defu'
910
import type { CollectionInfo, ResolvedCollection } from '../types/collection'
1011
import type { Manifest } from '../types/manifest'
1112
import type { GitInfo } from './git'
1213
import { generateCollectionTableDefinition } from './collection'
1314

15+
const defu = createDefu((obj, key, value) => {
16+
if (Array.isArray(obj[key]) && Array.isArray(value)) {
17+
obj[key] = value
18+
return true
19+
}
20+
})
21+
1422
const compress = (text: string): Promise<string> => {
1523
return new Promise((resolve, reject) => gzip(text, (err, buff) => {
1624
if (err) {
@@ -186,7 +194,7 @@ export const previewTemplate = (collections: ResolvedCollection[], gitInfo: GitI
186194
source: collection.source?.filter(source => source.repository ? undefined : collection.source),
187195
type: collection.type,
188196
fields: collection.fields,
189-
schema: zodToJsonSchema(collection.extendedSchema, collection.name),
197+
schema: generateJsonSchema(collection),
190198
tableDefinition: generateCollectionTableDefinition(collection),
191199
}
192200
return acc
@@ -209,3 +217,23 @@ export const previewTemplate = (collections: ResolvedCollection[], gitInfo: GitI
209217
},
210218
write: true,
211219
})
220+
221+
function generateJsonSchema(collection: ResolvedCollection) {
222+
const jsonSchema = zodToJsonSchema(collection.extendedSchema, collection.name)
223+
const jsonSchemaWithEditorMeta = zodToJsonSchema(
224+
collection.extendedSchema,
225+
{
226+
name: collection.name,
227+
override: (def) => {
228+
if (def.editor) {
229+
return {
230+
editor: def.editor,
231+
} as never
232+
}
233+
234+
return ignoreOverride
235+
},
236+
})
237+
238+
return defu(jsonSchema, jsonSchemaWithEditorMeta)
239+
}

‎src/utils/zod.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
1-
import type { ZodType, ZodOptionalDef } from 'zod'
1+
import type { ZodOptionalDef, ZodType } from 'zod'
22
import { z as zod } from 'zod'
33

4+
declare module 'zod' {
5+
interface ZodTypeDef {
6+
editor?: EditorOptions
7+
}
8+
9+
interface ZodType {
10+
editor(options: EditorOptions): this
11+
}
12+
}
13+
14+
interface EditorOptions {
15+
input?: 'media' | 'icon' // Override the default input for the field
16+
hidden?: boolean // Do not display the field in the editor
17+
}
18+
419
export type ZodFieldType = 'ZodString' | 'ZodNumber' | 'ZodBoolean' | 'ZodDate' | 'ZodEnum'
520
export type SqlFieldType = 'VARCHAR' | 'INT' | 'BOOLEAN' | 'DATE' | 'TEXT'
621

22+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
23+
(zod.ZodType as any).prototype.editor = function (options: EditorOptions) {
24+
this._def.editor = { ...this._def.editor, ...options }
25+
return this
26+
}
27+
728
export const z = zod
829

930
export const ZodToSqlFieldTypes: Record<ZodFieldType, SqlFieldType> = {
@@ -21,7 +42,7 @@ export function getEnumValues<T extends Record<string, unknown>>(obj: T) {
2142
// Function to get the underlying Zod type
2243
export function getUnderlyingType(zodType: ZodType): ZodType {
2344
while ((zodType._def as ZodOptionalDef).innerType) {
24-
zodType = (zodType._def as ZodOptionalDef).innerType
45+
zodType = (zodType._def as ZodOptionalDef).innerType as ZodType
2546
}
2647
return zodType
2748
}

0 commit comments

Comments
 (0)
Please sign in to comment.