Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/content/docs/8.advanced/5.hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ export default defineNuxtConfig({
})
```

## `content:manifest`{lang="ts"}

This hook is called after the manifest is generated.

```ts
export default defineNuxtConfig({
hooks: {
'content:manifest'(ctx) {
// ...
// ctx.collections.push(resolveCollection('name', defineCollection()))
}
}
})
```

## Example Usage

```ts [nuxt.config.ts]
Expand Down
26 changes: 22 additions & 4 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ import { kebabCase, pascalCase } from 'scule'
import defu from 'defu'
import { version } from '../package.json'
import { generateCollectionInsert, generateCollectionTableDefinition } from './utils/collection'
import { componentsManifestTemplate, contentTypesTemplate, fullDatabaseRawDumpTemplate, manifestTemplate, moduleTemplates } from './utils/templates'
import {
componentsManifestTemplate,
contentTypesTemplate,
fullDatabaseRawDumpTemplate,
manifestTemplate,
moduleTemplates,
} from './utils/templates'
import type { ResolvedCollection } from './types/collection'
import type { ModuleOptions } from './types/module'
import { getContentChecksum, logger, chunks, NuxtContentHMRUnplugin } from './utils/dev'
Expand Down Expand Up @@ -120,6 +126,7 @@ export default defineNuxtModule<ModuleOptions>({

const { collections } = await loadContentConfig(nuxt, options)
manifest.collections = collections
await nuxt.callHook('content:manifest', manifest)

nuxt.options.vite.optimizeDeps = defu(nuxt.options.vite.optimizeDeps, {
exclude: ['@sqlite.org/sqlite-wasm'],
Expand Down Expand Up @@ -191,7 +198,10 @@ export default defineNuxtModule<ModuleOptions>({
const preset = findPreset(nuxt)
await preset.setupNitro(config, { manifest, resolver, moduleOptions: options, nuxt })

const resolveOptions = { resolver, sqliteConnector: options.experimental?.sqliteConnector || (options.experimental?.nativeSqlite ? 'native' : undefined) }
const resolveOptions = {
resolver,
sqliteConnector: options.experimental?.sqliteConnector || (options.experimental?.nativeSqlite ? 'native' : undefined),
}
config.alias ||= {}
config.alias['#content/adapter'] = await resolveDatabaseAdapter(config.runtimeConfig!.content!.database?.type || options.database.type, resolveOptions)
config.alias['#content/local-adapter'] = await resolveDatabaseAdapter(options._localDatabase!.type || 'sqlite', resolveOptions)
Expand Down Expand Up @@ -400,13 +410,20 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
// NOTE: all queries having the structure comment at the end, will be ignored at init if no
// structure changes are detected in the structureVersion
`${generateCollectionTableDefinition(infoCollection, { drop: false })} -- structure`,
...generateCollectionInsert(infoCollection, { id: `checksum_${collection.name}`, version, structureVersion, ready: false }).queries.map(row => `${row} -- meta`),
...generateCollectionInsert(infoCollection, {
id: `checksum_${collection.name}`,
version,
structureVersion,
ready: false,
}).queries.map(row => `${row} -- meta`),

// Insert queries for the collection
...collectionQueries,

// and finally when we are finished, we update the info table to say that the init is done
`UPDATE ${infoCollection.tableName} SET ready = true WHERE id = 'checksum_${collection.name}'; -- meta`,
`UPDATE ${infoCollection.tableName}
SET ready = true
WHERE id = 'checksum_${collection.name}'; -- meta`,
]
}

Expand Down Expand Up @@ -460,6 +477,7 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
}

const proseTags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', 'strong', 'em', 's', 'code', 'span', 'blockquote', 'pre', 'hr', 'img', 'ul', 'ol', 'li', 'table', 'thead', 'tbody', 'tr', 'th', 'td']

function getMappedTag(tag: string, additionalTags: Record<string, string> = {}) {
if (proseTags.includes(tag)) {
return `prose-${tag}`
Expand Down
2 changes: 2 additions & 0 deletions src/types/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ResolvedCollection } from './collection'
import type { ContentFile, ParsedContentFile } from './content'
import type { PathMetaOptions } from './path-meta'
import type { Manifest } from './manifest'

// Parser options interface
interface ParserOptions {
Expand Down Expand Up @@ -32,5 +33,6 @@ declare module '@nuxt/schema' {
interface NuxtHooks {
'content:file:beforeParse': (ctx: FileBeforeParseHook) => Promise<void> | void
'content:file:afterParse': (ctx: FileAfterParseHook) => Promise<void> | void
'content:manifest': (manifest: Manifest) => Promise<void> | void
}
}
2 changes: 1 addition & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { metaStandardSchema, pageStandardSchema, property } from './schema'
export { defineCollection, defineCollectionSource } from './collection'
export { resolveCollection, defineCollection, defineCollectionSource } from './collection'
export { defineContentConfig } from './config'
export { defineTransformer } from './content/transformers/utils'

Expand Down
37 changes: 37 additions & 0 deletions test/unit/hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { resolveCollection } from '../../src/utils/collection'
import { parseContent } from '../utils/content'
import type { FileAfterParseHook, FileBeforeParseHook } from '../../src/types'
import { initiateValidatorsContext } from '../../src/utils/dependencies'
import type { Manifest } from '../../src/types/manifest'

describe('Hooks', async () => {
await initiateValidatorsContext()
Expand Down Expand Up @@ -57,4 +58,40 @@ foo: 'bar'
expect(parsed.foo).toEqual('bar')
expect(parsed.bar).toEqual('foo')
})

it('content:manifest mutations are reflected in manifest', async () => {
const extraCollection = resolveCollection('injected', defineCollection({
type: 'data',
source: 'extra/**',
schema: z.object({
body: z.any(),
}),
}))!

const manifest: Manifest = {
checksumStructure: {},
checksum: {},
dump: {},
components: [],
collections: [collection],
}

// Simulate the module calling the hook
const nuxtMock = {
callHook(hook: string, ctx: Manifest) {
if (hook === 'content:manifest') {
ctx.collections.push(extraCollection)
}
},
}

nuxtMock.callHook('content:manifest', manifest)

// must be visible on original manifest object
expect(manifest.collections).toHaveLength(2)
// new collection is visible
expect(manifest.collections.find(c => c.name === 'injected')).toBeDefined()
// original collection still exists
expect(manifest.collections.find(c => c.name === 'hookTest')).toBeDefined()
})
})
Loading