diff --git a/src/module.ts b/src/module.ts index bdf92b237..b6a0cd63f 100644 --- a/src/module.ts +++ b/src/module.ts @@ -30,7 +30,7 @@ import { createParser } from './utils/content' import { installMDCModule } from './utils/mdc' import { findPreset } from './presets' import type { Manifest } from './types/manifest' -import { setupPreview, shouldEnablePreview } from './utils/preview/module' +import { setupPreview, setupPreviewWithAPI, shouldEnablePreview } from './utils/preview/module' import { parseSourceBase } from './utils/source' import { databaseVersion, getLocalDatabase, refineDatabaseConfig, resolveDatabaseAdapter } from './utils/database' import type { ParsedContentFile } from './types' @@ -104,10 +104,10 @@ export default defineNuxtModule({ // Helpers are designed to be enviroment agnostic addImports([ - { name: 'queryCollection', from: resolver.resolve('./runtime/app') }, - { name: 'queryCollectionSearchSections', from: resolver.resolve('./runtime/app') }, - { name: 'queryCollectionNavigation', from: resolver.resolve('./runtime/app') }, - { name: 'queryCollectionItemSurroundings', from: resolver.resolve('./runtime/app') }, + { name: 'queryCollection', from: resolver.resolve('./runtime/client') }, + { name: 'queryCollectionSearchSections', from: resolver.resolve('./runtime/client') }, + { name: 'queryCollectionNavigation', from: resolver.resolve('./runtime/client') }, + { name: 'queryCollectionItemSurroundings', from: resolver.resolve('./runtime/client') }, ]) addServerImports([ { name: 'queryCollection', from: resolver.resolve('./runtime/nitro') }, @@ -202,6 +202,7 @@ export default defineNuxtModule({ if (hasNuxtModule('nuxt-llms')) { installModule(resolver.resolve('./features/llms')) } + await installMDCModule(options, nuxt) if (nuxt.options._prepare) { @@ -229,9 +230,12 @@ export default defineNuxtModule({ }) // Handle preview mode - if (shouldEnablePreview(nuxt, options)) { + if (hasNuxtModule('nuxt-studio')) { await setupPreview(options, nuxt, resolver, manifest) } + if (shouldEnablePreview(nuxt, options)) { + await setupPreviewWithAPI(options, nuxt, resolver, manifest) + } }) }, }) diff --git a/src/runtime/app.ts b/src/runtime/client.ts similarity index 100% rename from src/runtime/app.ts rename to src/runtime/client.ts diff --git a/src/runtime/components/ContentRenderer.vue b/src/runtime/components/ContentRenderer.vue index 6c45c4cf9..e2f03dcaf 100644 --- a/src/runtime/components/ContentRenderer.vue +++ b/src/runtime/components/ContentRenderer.vue @@ -74,7 +74,7 @@ const props = defineProps({ }, }) -const debug = import.meta.dev +const debug = import.meta.dev || import.meta.preview const body = computed(() => { let body = props.value.body || props.value diff --git a/src/runtime/plugins/preview-with-api.client.ts b/src/runtime/plugins/preview-with-api.client.ts new file mode 100644 index 000000000..ee833ee26 --- /dev/null +++ b/src/runtime/plugins/preview-with-api.client.ts @@ -0,0 +1,39 @@ +import type { PublicRuntimeConfig } from '@nuxt/content' +import { defineNuxtPlugin, useCookie, useRoute, useRuntimeConfig, getAppManifest } from '#imports' + +export default defineNuxtPlugin(async (nuxtApp) => { + const previewConfig: PublicRuntimeConfig['preview'] = useRuntimeConfig().public.preview || {} + const route = useRoute() + const previewToken = useCookie('previewToken', { sameSite: 'none', secure: true }) + + // Deprecated: will be removed in v4 + if (previewConfig.api) { + // Do not enable preview if preview token is missing in query params + if (Object.prototype.hasOwnProperty.call(route.query, 'preview') && !route.query.preview) { + return + } + + if (!route.query.preview && !previewToken.value) { + return + } + + if (route.query.preview) { + previewToken.value = String(route.query.preview) + } + + window.sessionStorage.setItem('previewToken', String(previewToken.value)) + // @ts-expect-error not exposed in runtimeConfig + window.sessionStorage.setItem('previewAPI', (typeof route.query.staging !== 'undefined' && previewConfig.stagingApi) ? previewConfig.stagingApi : previewConfig.api) + + // Disable prerendering for preview + const manifest = await getAppManifest() + manifest.prerendered = [] + + nuxtApp.hook('app:mounted', async () => { + await import('../internal/preview').then(({ mountPreviewUI, initIframeCommunication }) => { + mountPreviewUI() + initIframeCommunication() + }) + }) + } +}) diff --git a/src/runtime/plugins/preview.client.ts b/src/runtime/plugins/preview.client.ts index cfb55ca3b..537c9b34d 100644 --- a/src/runtime/plugins/preview.client.ts +++ b/src/runtime/plugins/preview.client.ts @@ -1,38 +1,13 @@ -import type { PublicRuntimeConfig } from '@nuxt/content' -import { defineNuxtPlugin, useCookie, useRoute, useRuntimeConfig, getAppManifest } from '#imports' - -export default defineNuxtPlugin(async (nuxtApp) => { - const previewConfig: PublicRuntimeConfig['preview'] = useRuntimeConfig().public.preview || {} - const route = useRoute() - const previewToken = useCookie('previewToken', { sameSite: 'none', secure: true }) - - if (previewConfig.api) { - // Do not enable preview if preview token is missing in query params - if (Object.prototype.hasOwnProperty.call(route.query, 'preview') && !route.query.preview) { - return - } - - if (!route.query.preview && !previewToken.value) { - return - } - - if (route.query.preview) { - previewToken.value = String(route.query.preview) - } - - window.sessionStorage.setItem('previewToken', String(previewToken.value)) - // @ts-expect-error not exposed in runtimeConfig - window.sessionStorage.setItem('previewAPI', (typeof route.query.staging !== 'undefined' && previewConfig.stagingApi) ? previewConfig.stagingApi : previewConfig.api) - - // Disable prerendering for preview - const manifest = await getAppManifest() - manifest.prerendered = [] - - nuxtApp.hook('app:mounted', async () => { - await import('../internal/preview').then(({ mountPreviewUI, initIframeCommunication }) => { - mountPreviewUI() - initIframeCommunication() - }) - }) +import { defineNuxtPlugin } from '#imports' + +export default defineNuxtPlugin(async () => { + return { + provide: { + content: { + loadLocalDatabase: () => { + return import('../internal/database.client').then(m => m.loadDatabaseAdapter) + }, + }, + }, } }) diff --git a/src/runtime/plugins/studio/preview.client.ts b/src/runtime/plugins/studio/preview.client.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 4baca9fe2..97be8f156 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -2,6 +2,10 @@ interface Window { sendNavigateMessageInPreview: (path: string, navigate: boolean) => void } +interface ImportMeta { + readonly preview?: boolean +} + declare module '#content/manifest' { const manifest: Record const checksums: Record diff --git a/src/types/schema.ts b/src/types/schema.ts index 37938f585..08ac1ee3c 100644 --- a/src/types/schema.ts +++ b/src/types/schema.ts @@ -14,6 +14,7 @@ export interface Draft07Definition { export interface Draft07DefinitionProperty { type?: string // missing type means any properties?: Record + items?: Draft07DefinitionProperty required?: string[] default?: unknown maxLength?: number diff --git a/src/utils/preview/module.ts b/src/utils/preview/module.ts index 50ba2378d..058046061 100644 --- a/src/utils/preview/module.ts +++ b/src/utils/preview/module.ts @@ -16,7 +16,26 @@ import type { ModuleOptions } from '../../types' import { previewTemplate } from '../templates' import type { Manifest } from '../../types/manifest' -export async function setupPreview(options: ModuleOptions, nuxt: Nuxt, resolver: Resolver, manifest: Manifest) { +export async function setupPreview(_options: ModuleOptions, nuxt: Nuxt, resolver: Resolver, manifest: Manifest) { + nuxt.hook('schema:resolved', (schema: Schema) => { + // Add preview templates once schema is resolved + const template = addTemplate(previewTemplate(manifest.collections, {} as GitInfo, schema)).dst + nuxt.options.nitro.alias ||= {} + nuxt.options.nitro.alias['#content/preview'] = template + nuxt.options.alias['#content/preview'] = template + }) + + // Add plugins + addPlugin(resolver.resolve('./runtime/plugins/preview.client')) + + // // Install dependencies + await installModule('nuxt-component-meta', { + globalsOnly: true, + include: manifest.components, + }) +} + +export async function setupPreviewWithAPI(options: ModuleOptions, nuxt: Nuxt, resolver: Resolver, manifest: Manifest) { const previewOptions = options.preview! const { resolve } = resolver @@ -41,7 +60,7 @@ export async function setupPreview(options: ModuleOptions, nuxt: Nuxt, resolver: }) // Add plugins - addPlugin(resolver.resolve('./runtime/plugins/preview.client')) + addPlugin(resolver.resolve('./runtime/plugins/preview-with-api.client')) // Register preview banner component addComponent({ name: 'ContentPreviewMode', filePath: resolver.resolve('./runtime/components/ContentPreviewMode.vue') })