diff --git a/src/utils/config.ts b/src/utils/config.ts index 8cb279ac7..c726b6d0a 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -1,22 +1,22 @@ import { loadConfig, watchConfig, createDefineConfig } from 'c12' import { relative } from 'pathe' +import { hasNuxtModule, useNuxt } from '@nuxt/kit' import type { Nuxt } from '@nuxt/schema' import type { DefinedCollection, ModuleOptions } from '../types' import { defineCollection, resolveCollections } from './collection' import { logger } from './dev' +import { resolveStudioCollection } from './studio' type NuxtContentConfig = { collections: Record } -const defaultConfig: NuxtContentConfig = { - collections: { - content: defineCollection({ - type: 'page', - source: '**/*', - }), - }, -} +const createDefaultCollections = (): NuxtContentConfig['collections'] => ({ + content: defineCollection({ + type: 'page', + source: '**/*', + }), +}) export const defineContentConfig = createDefineConfig() @@ -68,7 +68,14 @@ export async function loadContentConfig(nuxt: Nuxt, options?: ModuleOptions) { logger.warn('No content configuration found, falling back to default collection. In order to have full control over your collections, create the config file in project root. See: https://content.nuxt.com/docs/getting-started/installation') } - const collections = resolveCollections(hasNoCollections ? defaultConfig.collections : collectionsConfig) + const finalCollectionsConfig = hasNoCollections ? createDefaultCollections() : collectionsConfig + + // If nuxt-studio is installed, automatically configure studio collection + if (hasNuxtModule('nuxt-studio', nuxt || useNuxt())) { + resolveStudioCollection(nuxt, finalCollectionsConfig) + } + + const collections = resolveCollections(finalCollectionsConfig) return { collections } } diff --git a/src/utils/studio.ts b/src/utils/studio.ts new file mode 100644 index 000000000..a3114f6da --- /dev/null +++ b/src/utils/studio.ts @@ -0,0 +1,68 @@ +import { z } from 'zod' +import type { Nuxt } from '@nuxt/schema' +import type { DefinedCollection } from '../types' +import { defineCollection } from './collection' + +interface StudioAIConfig { + apiKey?: string + context?: { + title?: string + description?: string + tone?: string + style?: string + collection?: { + name?: string + folder?: string + } + } +} + +const DEFAULT_STUDIO_COLLECTION_NAME = 'studio' +const DEFAULT_STUDIO_COLLECTION_FOLDER = '.studio' + +/** + * Resolves studio collection configuration when nuxt-studio is installed. + * Automatically creates a studio collection and adds exclude patterns to other collections. + */ +export function resolveStudioCollection( + nuxt: Nuxt, + collectionsConfig: Record, +): void { + /* @ts-expect-error - studio is not typed */ + const studioAIConfig: StudioAIConfig = nuxt.options.studio?.ai || {} + if (!studioAIConfig.apiKey) { + return + } + + const studioCollectionName = studioAIConfig.context?.collection?.name || DEFAULT_STUDIO_COLLECTION_NAME + const studioFolder = studioAIConfig.context?.collection?.folder || DEFAULT_STUDIO_COLLECTION_FOLDER + const studioPattern = `${studioFolder}/**` + + // Add studio collection if it doesn't exist + if (!collectionsConfig[studioCollectionName]) { + collectionsConfig[studioCollectionName] = defineCollection({ + type: 'data', + source: studioPattern, + schema: z.object({ + rawbody: z.string(), + }), + }) + } + + // Add exclude pattern to all existing collections except studio + for (const [name, collection] of Object.entries(collectionsConfig)) { + if (name === studioCollectionName || !collection.source) { + continue + } + + // Add exclude pattern to each source + for (const source of collection.source) { + if (!source.exclude) { + source.exclude = [] + } + if (!source.exclude.includes(studioPattern)) { + source.exclude.push(studioPattern) + } + } + } +} diff --git a/test/basic.test.ts b/test/basic.test.ts index bbf7db191..0610dff5e 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -34,7 +34,7 @@ describe('basic', async () => { describe('`content.config.ts`', async () => { test('Default collection is defined', async () => { const rootDir = resolver.resolve('./fixtures/basic') - const config = await loadContentConfig({ options: { _layers: [{ config: { rootDir } }] } } as Nuxt) + const config = await loadContentConfig({ options: { _installedModules: [], modules: [], _layers: [{ config: { rootDir } }] } } as Nuxt) // Pages collection + info collection expect(config.collections.length).toBe(2) diff --git a/test/csv.test.ts b/test/csv.test.ts index 5ab44332d..1bc2368c5 100644 --- a/test/csv.test.ts +++ b/test/csv.test.ts @@ -32,7 +32,7 @@ describe('csv single-file collection', async () => { describe('`content.config.ts`', async () => { test('single-file csv source is resolved', async () => { const rootDir = resolver.resolve('./fixtures/csv') - const config = await loadContentConfig({ options: { _layers: [{ config: { rootDir } }] } } as Nuxt) + const config = await loadContentConfig({ options: { _installedModules: [], modules: [], _layers: [{ config: { rootDir } }] } } as Nuxt) expect(config.collections.map(c => c.name)).toContain('people') diff --git a/test/empty.test.ts b/test/empty.test.ts index 575b9ef1d..32da74913 100644 --- a/test/empty.test.ts +++ b/test/empty.test.ts @@ -37,7 +37,7 @@ describe('empty', async () => { }) test('Default collection is defined', async () => { const rootDir = resolver.resolve('./fixtures/empty') - const config = await loadContentConfig({ options: { _layers: [{ config: { rootDir } }] } } as Nuxt) + const config = await loadContentConfig({ options: { _installedModules: [], modules: [], _layers: [{ config: { rootDir } }] } } as Nuxt) // Pages collection + info collection expect(config.collections.length).toBe(2)