Skip to content

Commit 7b480c5

Browse files
authored
feat: integrate with NuxtHub database queries (#3019)
1 parent 5822e8a commit 7b480c5

16 files changed

+180
-154
lines changed

pnpm-lock.yaml

+5-32
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/module.ts

+41-44
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { mkdir, stat } from 'node:fs/promises'
1+
import { stat } from 'node:fs/promises'
22
import {
33
defineNuxtModule,
44
createResolver,
@@ -13,15 +13,15 @@ import {
1313
import type { Nuxt } from '@nuxt/schema'
1414
import type { ModuleOptions as MDCModuleOptions } from '@nuxtjs/mdc'
1515
import { hash } from 'ohash'
16-
import { join, dirname, isAbsolute } from 'pathe'
16+
import { join, isAbsolute } from 'pathe'
1717
import htmlTags from '@nuxtjs/mdc/runtime/parser/utils/html-tags-list'
1818
import { kebabCase, pascalCase } from 'scule'
1919
import defu from 'defu'
2020
import { version } from '../package.json'
2121
import { generateCollectionInsert, generateCollectionTableDefinition } from './utils/collection'
2222
import { componentsManifestTemplate, contentTypesTemplate, fullDatabaseRawDumpTemplate, manifestTemplate, moduleTemplates } from './utils/templates'
2323
import type { ResolvedCollection } from './types/collection'
24-
import type { ModuleOptions, SqliteDatabaseConfig } from './types/module'
24+
import type { ModuleOptions } from './types/module'
2525
import { getContentChecksum, logger, watchContents, chunks, watchComponents, startSocketServer } from './utils/dev'
2626
import { loadContentConfig } from './utils/config'
2727
import { createParser } from './utils/content'
@@ -30,7 +30,7 @@ import { findPreset } from './presets'
3030
import type { Manifest } from './types/manifest'
3131
import { setupPreview } from './utils/preview/module'
3232
import { parseSourceBase } from './utils/source'
33-
import { getLocalDatabase, resolveDatabaseAdapter } from './utils/sqlite'
33+
import { getLocalDatabase, refineDatabaseConfig, resolveDatabaseAdapter } from './utils/database'
3434

3535
// Export public utils
3636
export * from './utils'
@@ -92,16 +92,16 @@ export default defineNuxtModule<ModuleOptions>({
9292
}
9393

9494
// Create local database
95-
options._localDatabase!.filename = isAbsolute(options._localDatabase!.filename)
96-
? options._localDatabase!.filename
97-
: join(nuxt.options.rootDir, options._localDatabase!.filename)
98-
await mkdir(dirname(options._localDatabase!.filename), { recursive: true }).catch(() => {})
95+
await refineDatabaseConfig(options._localDatabase, nuxt)
96+
if (options._localDatabase?.type === 'sqlite') {
97+
options._localDatabase!.filename = isAbsolute(options._localDatabase!.filename)
98+
? options._localDatabase!.filename
99+
: join(nuxt.options.rootDir, options._localDatabase!.filename)
100+
}
99101

100102
// Create sql database
101-
if ((options.database as SqliteDatabaseConfig).filename) {
102-
(options.database as SqliteDatabaseConfig).filename = (options.database as SqliteDatabaseConfig).filename
103-
await mkdir(dirname((options.database as SqliteDatabaseConfig).filename), { recursive: true }).catch(() => {})
104-
}
103+
await refineDatabaseConfig(options.database, nuxt)
104+
105105
const { collections } = await loadContentConfig(nuxt)
106106
manifest.collections = collections
107107

@@ -113,13 +113,14 @@ export default defineNuxtModule<ModuleOptions>({
113113
version,
114114
database: options.database,
115115
localDatabase: options._localDatabase!,
116+
integrityCheck: true,
116117
} as never
117118

118119
nuxt.options.vite.optimizeDeps ||= {}
119120
nuxt.options.vite.optimizeDeps.exclude ||= []
120121
nuxt.options.vite.optimizeDeps.exclude.push('@sqlite.org/sqlite-wasm')
121122
nuxt.options.vite.optimizeDeps.include ||= []
122-
nuxt.options.vite.optimizeDeps.include.push('scule')
123+
nuxt.options.vite.optimizeDeps.include.push('@nuxt/content > scule')
123124

124125
// Helpers are designed to be enviroment agnostic
125126
addImports([
@@ -163,7 +164,7 @@ export default defineNuxtModule<ModuleOptions>({
163164
// Load nitro preset and set db adapter
164165
nuxt.hook('nitro:config', async (config) => {
165166
const preset = findPreset(nuxt)
166-
await preset.setupNitro(config, { manifest, resolver })
167+
await preset.setupNitro(config, { manifest, resolver, moduleOptions: options })
167168

168169
config.alias ||= {}
169170
config.alias['#content/adapter'] = resolveDatabaseAdapter(config.runtimeConfig!.content!.database?.type || options.database.type, resolver)
@@ -174,6 +175,14 @@ export default defineNuxtModule<ModuleOptions>({
174175
route: '/api/content/:collection/query',
175176
handler: resolver.resolve('./runtime/api/query.post'),
176177
})
178+
179+
// Handle HMR changes
180+
if (nuxt.options.dev) {
181+
addPlugin({ src: resolver.resolve('./runtime/plugins/websocket.dev'), mode: 'client' })
182+
await watchComponents(nuxt)
183+
const socket = await startSocketServer(nuxt, options, manifest)
184+
await watchContents(nuxt, options, manifest, socket)
185+
}
177186
})
178187

179188
// Prerender database.sql routes for each collection to fetch dump
@@ -190,36 +199,24 @@ export default defineNuxtModule<ModuleOptions>({
190199
return
191200
}
192201

193-
const dumpGeneratePromise = processCollectionItems(nuxt, manifest.collections, options)
194-
.then((fest) => {
195-
manifest.checksum = fest.checksum
196-
manifest.dump = fest.dump
197-
manifest.components = fest.components
198-
199-
return updateTemplates({
200-
filter: template => [
201-
moduleTemplates.fullRawDump,
202-
moduleTemplates.fullCompressedDump,
203-
moduleTemplates.manifest,
204-
moduleTemplates.components,
205-
].includes(template.filename),
206-
})
207-
})
208-
209202
// Generate collections and sql dump to update templates local database
210-
// `app:templates` is triggered for all environments
211-
nuxt.hook('app:templates', async () => {
212-
await dumpGeneratePromise
213-
})
214-
215-
dumpGeneratePromise.then(async () => {
216-
// Handle HMR changes
217-
if (nuxt.options.dev) {
218-
addPlugin({ src: resolver.resolve('./runtime/plugins/websocket.dev'), mode: 'client' })
219-
await watchComponents(nuxt)
220-
const socket = await startSocketServer(nuxt, options, manifest)
221-
await watchContents(nuxt, options, manifest, socket)
222-
}
203+
// `modules:done` is triggered for all environments
204+
nuxt.hook('modules:done', async () => {
205+
const fest = await processCollectionItems(nuxt, manifest.collections, options)
206+
207+
// Update manifest
208+
manifest.checksum = fest.checksum
209+
manifest.dump = fest.dump
210+
manifest.components = fest.components
211+
212+
await updateTemplates({
213+
filter: template => [
214+
moduleTemplates.fullRawDump,
215+
moduleTemplates.fullCompressedDump,
216+
moduleTemplates.manifest,
217+
moduleTemplates.components,
218+
].includes(template.filename),
219+
})
223220

224221
// Handle preview mode
225222
if (process.env.NUXT_CONTENT_PREVIEW_API || options.preview?.api) {
@@ -237,7 +234,7 @@ export default defineNuxtModule<ModuleOptions>({
237234
async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollection[], options: ModuleOptions) {
238235
const collectionDump: Record<string, string[]> = {}
239236
const collectionChecksum: Record<string, string> = {}
240-
const db = await getLocalDatabase(options._localDatabase!.filename)
237+
const db = await getLocalDatabase(options._localDatabase)
241238
const databaseContents = await db.fetchDevelopmentCache()
242239

243240
const configHash = hash({

src/presets/cloudflare-pages.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { definePreset } from '../utils/preset'
55
import { logger } from '../utils/dev'
66

77
export default definePreset({
8+
name: 'cloudflare-pages',
89
async setupNitro(nitroConfig, { manifest, resolver }) {
910
if (nitroConfig.runtimeConfig?.content?.database?.type === 'sqlite') {
1011
logger.warn('Deploying to Cloudflare Pages requires using D1 database, switching to D1 database with binding `DB`.')

src/presets/index.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import type { Nuxt } from '@nuxt/schema'
2+
import { hasNuxtModule } from '@nuxt/kit'
23
import cloudflare from './cloudflare-pages'
34
import vercel from './vercel'
45
import node from './node'
6+
import nuxthub from './nuxthub'
57

68
export function findPreset(nuxt: Nuxt) {
79
const preset = nuxt.options.nitro.preset?.replace(/_/g, '-')
810

11+
if (hasNuxtModule('@nuxthub/core', nuxt)) {
12+
return nuxthub
13+
}
14+
915
if (preset === 'cloudflare-pages') {
1016
return cloudflare
1117
}

src/presets/node.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { fullDatabaseCompressedDumpTemplate } from '../utils/templates'
33
import { definePreset } from '../utils/preset'
44

55
export default definePreset({
6+
name: 'node',
67
setupNitro(nitroConfig, { manifest, resolver }) {
78
nitroConfig.publicAssets ||= []
89
nitroConfig.alias = nitroConfig.alias || {}

src/presets/nuxthub.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { mkdir, writeFile } from 'node:fs/promises'
2+
import { resolve } from 'pathe'
3+
import { definePreset } from '../utils/preset'
4+
import cfPreset from './cloudflare-pages'
5+
6+
export default definePreset({
7+
name: 'nuxthub',
8+
async setupNitro(nitroConfig, options) {
9+
await cfPreset.setupNitro(nitroConfig, options)
10+
11+
if (nitroConfig.dev === false) {
12+
// Write SQL dump to database queries when not in dev mode
13+
const sql = Object.values(options.manifest.dump).map(value => value.join('\n')).join('\n')
14+
await mkdir(resolve(nitroConfig.rootDir, '.data/hub/database/queries'), { recursive: true })
15+
await writeFile(resolve(nitroConfig.rootDir, '.data/hub/database/queries/content-database.sql'), sql)
16+
// Disable integrity check in production for performance
17+
nitroConfig.runtimeConfig.content.integrityCheck = false
18+
}
19+
},
20+
})

src/presets/vercel.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { logger } from '../utils/dev'
33
import nodePreset from './node'
44

55
export default definePreset({
6+
name: 'vercel',
67
async setupNitro(nitroConfig, options) {
78
if (nitroConfig.runtimeConfig?.content?.database?.type === 'sqlite') {
89
logger.warn('Deploying sqlite database to Vercel is not possible, switching to Postgres database with `POSTGRES_URL`.')

src/runtime/api/query.post.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ export default eventHandler(async (event) => {
99
const collection = getRouterParam(event, 'collection')!
1010

1111
const conf = useRuntimeConfig().content as RuntimeConfig['content']
12-
await checkAndImportDatabaseIntegrity(event, collection, conf)
12+
if (conf.integrityCheck) {
13+
await checkAndImportDatabaseIntegrity(event, collection, conf)
14+
}
1315

1416
return loadDatabaseAdapter(conf).all(sql)
1517
})

0 commit comments

Comments
 (0)