Skip to content

Commit

Permalink
feat: integrate with NuxtHub database queries (#3019)
Browse files Browse the repository at this point in the history
  • Loading branch information
farnabaz authored Jan 23, 2025
1 parent 5822e8a commit 7b480c5
Show file tree
Hide file tree
Showing 16 changed files with 180 additions and 154 deletions.
37 changes: 5 additions & 32 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 41 additions & 44 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mkdir, stat } from 'node:fs/promises'
import { stat } from 'node:fs/promises'
import {
defineNuxtModule,
createResolver,
Expand All @@ -13,15 +13,15 @@ import {
import type { Nuxt } from '@nuxt/schema'
import type { ModuleOptions as MDCModuleOptions } from '@nuxtjs/mdc'
import { hash } from 'ohash'
import { join, dirname, isAbsolute } from 'pathe'
import { join, isAbsolute } from 'pathe'
import htmlTags from '@nuxtjs/mdc/runtime/parser/utils/html-tags-list'
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 type { ResolvedCollection } from './types/collection'
import type { ModuleOptions, SqliteDatabaseConfig } from './types/module'
import type { ModuleOptions } from './types/module'
import { getContentChecksum, logger, watchContents, chunks, watchComponents, startSocketServer } from './utils/dev'
import { loadContentConfig } from './utils/config'
import { createParser } from './utils/content'
Expand All @@ -30,7 +30,7 @@ import { findPreset } from './presets'
import type { Manifest } from './types/manifest'
import { setupPreview } from './utils/preview/module'
import { parseSourceBase } from './utils/source'
import { getLocalDatabase, resolveDatabaseAdapter } from './utils/sqlite'
import { getLocalDatabase, refineDatabaseConfig, resolveDatabaseAdapter } from './utils/database'

// Export public utils
export * from './utils'
Expand Down Expand Up @@ -92,16 +92,16 @@ export default defineNuxtModule<ModuleOptions>({
}

// Create local database
options._localDatabase!.filename = isAbsolute(options._localDatabase!.filename)
? options._localDatabase!.filename
: join(nuxt.options.rootDir, options._localDatabase!.filename)
await mkdir(dirname(options._localDatabase!.filename), { recursive: true }).catch(() => {})
await refineDatabaseConfig(options._localDatabase, nuxt)
if (options._localDatabase?.type === 'sqlite') {
options._localDatabase!.filename = isAbsolute(options._localDatabase!.filename)
? options._localDatabase!.filename
: join(nuxt.options.rootDir, options._localDatabase!.filename)
}

// Create sql database
if ((options.database as SqliteDatabaseConfig).filename) {
(options.database as SqliteDatabaseConfig).filename = (options.database as SqliteDatabaseConfig).filename
await mkdir(dirname((options.database as SqliteDatabaseConfig).filename), { recursive: true }).catch(() => {})
}
await refineDatabaseConfig(options.database, nuxt)

const { collections } = await loadContentConfig(nuxt)
manifest.collections = collections

Expand All @@ -113,13 +113,14 @@ export default defineNuxtModule<ModuleOptions>({
version,
database: options.database,
localDatabase: options._localDatabase!,
integrityCheck: true,
} as never

nuxt.options.vite.optimizeDeps ||= {}
nuxt.options.vite.optimizeDeps.exclude ||= []
nuxt.options.vite.optimizeDeps.exclude.push('@sqlite.org/sqlite-wasm')
nuxt.options.vite.optimizeDeps.include ||= []
nuxt.options.vite.optimizeDeps.include.push('scule')
nuxt.options.vite.optimizeDeps.include.push('@nuxt/content > scule')

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

config.alias ||= {}
config.alias['#content/adapter'] = resolveDatabaseAdapter(config.runtimeConfig!.content!.database?.type || options.database.type, resolver)
Expand All @@ -174,6 +175,14 @@ export default defineNuxtModule<ModuleOptions>({
route: '/api/content/:collection/query',
handler: resolver.resolve('./runtime/api/query.post'),
})

// Handle HMR changes
if (nuxt.options.dev) {
addPlugin({ src: resolver.resolve('./runtime/plugins/websocket.dev'), mode: 'client' })
await watchComponents(nuxt)
const socket = await startSocketServer(nuxt, options, manifest)
await watchContents(nuxt, options, manifest, socket)
}
})

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

const dumpGeneratePromise = processCollectionItems(nuxt, manifest.collections, options)
.then((fest) => {
manifest.checksum = fest.checksum
manifest.dump = fest.dump
manifest.components = fest.components

return updateTemplates({
filter: template => [
moduleTemplates.fullRawDump,
moduleTemplates.fullCompressedDump,
moduleTemplates.manifest,
moduleTemplates.components,
].includes(template.filename),
})
})

// Generate collections and sql dump to update templates local database
// `app:templates` is triggered for all environments
nuxt.hook('app:templates', async () => {
await dumpGeneratePromise
})

dumpGeneratePromise.then(async () => {
// Handle HMR changes
if (nuxt.options.dev) {
addPlugin({ src: resolver.resolve('./runtime/plugins/websocket.dev'), mode: 'client' })
await watchComponents(nuxt)
const socket = await startSocketServer(nuxt, options, manifest)
await watchContents(nuxt, options, manifest, socket)
}
// `modules:done` is triggered for all environments
nuxt.hook('modules:done', async () => {
const fest = await processCollectionItems(nuxt, manifest.collections, options)

// Update manifest
manifest.checksum = fest.checksum
manifest.dump = fest.dump
manifest.components = fest.components

await updateTemplates({
filter: template => [
moduleTemplates.fullRawDump,
moduleTemplates.fullCompressedDump,
moduleTemplates.manifest,
moduleTemplates.components,
].includes(template.filename),
})

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

const configHash = hash({
Expand Down
1 change: 1 addition & 0 deletions src/presets/cloudflare-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { definePreset } from '../utils/preset'
import { logger } from '../utils/dev'

export default definePreset({
name: 'cloudflare-pages',
async setupNitro(nitroConfig, { manifest, resolver }) {
if (nitroConfig.runtimeConfig?.content?.database?.type === 'sqlite') {
logger.warn('Deploying to Cloudflare Pages requires using D1 database, switching to D1 database with binding `DB`.')
Expand Down
6 changes: 6 additions & 0 deletions src/presets/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import type { Nuxt } from '@nuxt/schema'
import { hasNuxtModule } from '@nuxt/kit'
import cloudflare from './cloudflare-pages'
import vercel from './vercel'
import node from './node'
import nuxthub from './nuxthub'

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

if (hasNuxtModule('@nuxthub/core', nuxt)) {
return nuxthub
}

if (preset === 'cloudflare-pages') {
return cloudflare
}
Expand Down
1 change: 1 addition & 0 deletions src/presets/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { fullDatabaseCompressedDumpTemplate } from '../utils/templates'
import { definePreset } from '../utils/preset'

export default definePreset({
name: 'node',
setupNitro(nitroConfig, { manifest, resolver }) {
nitroConfig.publicAssets ||= []
nitroConfig.alias = nitroConfig.alias || {}
Expand Down
20 changes: 20 additions & 0 deletions src/presets/nuxthub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { mkdir, writeFile } from 'node:fs/promises'
import { resolve } from 'pathe'
import { definePreset } from '../utils/preset'
import cfPreset from './cloudflare-pages'

export default definePreset({
name: 'nuxthub',
async setupNitro(nitroConfig, options) {
await cfPreset.setupNitro(nitroConfig, options)

if (nitroConfig.dev === false) {
// Write SQL dump to database queries when not in dev mode
const sql = Object.values(options.manifest.dump).map(value => value.join('\n')).join('\n')
await mkdir(resolve(nitroConfig.rootDir, '.data/hub/database/queries'), { recursive: true })
await writeFile(resolve(nitroConfig.rootDir, '.data/hub/database/queries/content-database.sql'), sql)
// Disable integrity check in production for performance
nitroConfig.runtimeConfig.content.integrityCheck = false
}
},
})
1 change: 1 addition & 0 deletions src/presets/vercel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { logger } from '../utils/dev'
import nodePreset from './node'

export default definePreset({
name: 'vercel',
async setupNitro(nitroConfig, options) {
if (nitroConfig.runtimeConfig?.content?.database?.type === 'sqlite') {
logger.warn('Deploying sqlite database to Vercel is not possible, switching to Postgres database with `POSTGRES_URL`.')
Expand Down
4 changes: 3 additions & 1 deletion src/runtime/api/query.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export default eventHandler(async (event) => {
const collection = getRouterParam(event, 'collection')!

const conf = useRuntimeConfig().content as RuntimeConfig['content']
await checkAndImportDatabaseIntegrity(event, collection, conf)
if (conf.integrityCheck) {
await checkAndImportDatabaseIntegrity(event, collection, conf)
}

return loadDatabaseAdapter(conf).all(sql)
})
Loading

0 comments on commit 7b480c5

Please sign in to comment.