Skip to content
Open
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
52 changes: 34 additions & 18 deletions src/presets/nuxthub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,33 @@ import { provider } from 'std-env'
import { logger } from '../utils/dev'
import { definePreset } from '../utils/preset'
import type { Nuxt } from 'nuxt/schema'
import type { LibSQLDatabaseConfig, PGliteDatabaseConfig, SqliteDatabaseConfig } from '~/dist/module.mjs'
import type { D1DatabaseConfig, LibSQLDatabaseConfig, PGliteDatabaseConfig, PostgreSQLDatabaseConfig, SqliteDatabaseConfig } from '~/dist/module.mjs'
import cloudflarePreset from './cloudflare'
import nodePreset from './node'

type ContentDatabaseConfig = D1DatabaseConfig | SqliteDatabaseConfig | PostgreSQLDatabaseConfig | LibSQLDatabaseConfig | PGliteDatabaseConfig

// Map the resolved NuxtHub database config (`runtimeConfig.hub.db`) to a Nuxt Content database config
export function hubDatabaseToContentDatabase(hubDb: { driver: string, connection?: { url?: string, [key: string]: unknown } }): ContentDatabaseConfig | undefined {
if (hubDb.driver === 'd1') {
return { type: 'd1', bindingName: 'DB' }
}
if (['node-postgres', 'postgres-js', 'neon-http', 'postgres', 'postgresql'].includes(hubDb.driver)) {
return typeof hubDb.connection?.url === 'string' && hubDb.connection.url ? { type: 'postgresql', url: hubDb.connection.url } : undefined
}
if (['sqlite', 'better-sqlite3'].includes(hubDb.driver)) {
if (typeof hubDb.connection?.filename === 'string' && hubDb.connection.filename) {
return { type: 'sqlite', filename: hubDb.connection.filename }
}
const filename = typeof hubDb.connection?.url === 'string' ? hubDb.connection.url.replace(/^file:/, '') : ''
return filename ? { type: 'sqlite', filename } : undefined
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
if (['libsql', 'pglite'].includes(hubDb.driver)) {
return { type: hubDb.driver, ...hubDb.connection } as unknown as ContentDatabaseConfig
}
return undefined
}

export default definePreset({
name: 'nuxthub',
async setup(options, nuxt, config) {
Expand All @@ -21,15 +44,14 @@ export default definePreset({
if (nuxtOptions.hub?.database === true) {
options.database ||= { type: 'd1', bindingName: 'DB' }
}
else if (typeof nuxtOptions.hub?.db === 'string' && typeof hubDb === 'object') {
if (hubDb.driver === 'd1') {
options.database ||= { type: 'd1', bindingName: 'DB' }
}
else if (hubDb.driver === 'node-postgres') {
options.database ||= { type: 'postgresql', url: hubDb.connection.url as string }
// NuxtHub >= 0.10, `hub.db` can be a string or an object
else if (nuxtOptions.hub?.db && typeof hubDb === 'object' && !options.database) {
const database = hubDatabaseToContentDatabase(hubDb)
if (database) {
options.database = database
}
else {
options.database ||= { type: hubDb.driver as 'sqlite' | 'postgresql' | 'postgres' | 'libsql' | 'pglite', ...hubDb.connection } as unknown as SqliteDatabaseConfig | LibSQLDatabaseConfig | PGliteDatabaseConfig
logger.warn(`Nuxt Content cannot use the NuxtHub \`${hubDb.driver}\` database configuration, using the default database instead.`)
}
}
}
Expand Down Expand Up @@ -62,16 +84,10 @@ export default definePreset({
nitroConfig.runtimeConfig!.content!.database = { type: 'd1', bindingName: 'DB' }
}
}
else if (typeof nuxt.options.hub?.db === 'string' && typeof hubConfig.db === 'object') {
const hubDb = hubConfig.db as unknown as { driver: string, connection: object }
if (hubDb.driver === 'd1') {
nitroConfig.runtimeConfig!.content!.database ||= { type: 'd1', bindingName: 'DB' }
}
else if (hubDb.driver === 'node-postgres') {
nitroConfig.runtimeConfig!.content!.database ||= { type: 'postgresql', ...hubDb.connection }
}
else {
nitroConfig.runtimeConfig!.content!.database ||= { type: hubDb.driver, ...hubDb.connection }
else if (nuxt.options.hub?.db && typeof hubConfig.db === 'object') {
const database = hubDatabaseToContentDatabase(hubConfig.db as unknown as { driver: string, connection?: { url?: string } })
if (database) {
nitroConfig.runtimeConfig!.content!.database ||= database
}
}

Expand Down
150 changes: 150 additions & 0 deletions test/unit/nuxthubPreset.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { describe, expect, test, vi } from 'vitest'
import type { Nuxt } from '@nuxt/schema'
import type { Resolver } from '@nuxt/kit'
import type { NitroConfig } from 'nitropack'
import nuxthubPreset, { hubDatabaseToContentDatabase } from '../../src/presets/nuxthub'
import type { ModuleOptions } from '../../src/types/module'
import type { Manifest } from '../../src/types/manifest'

// `addTemplate` and `addServerHandler` require a live Nuxt context
vi.mock('@nuxt/kit', async (importOriginal) => {
const original = await importOriginal<typeof import('@nuxt/kit')>()
return {
...original,
addTemplate: vi.fn(() => ({ dst: '' })),
addServerHandler: vi.fn(),
}
})

const resolver = { resolve: (p: string) => p } as unknown as Resolver
const manifest = { collections: [], dump: {} } as unknown as Manifest
const opts = { resolver, manifest }

function createNuxt(hub: Record<string, unknown>, runtimeHub: Record<string, unknown>): Nuxt {
return {
options: {
dev: false,
hub,
nitro: { preset: 'node-server' },
runtimeConfig: { hub: runtimeHub },
},
} as unknown as Nuxt
}

describe('hubDatabaseToContentDatabase', () => {
test('maps d1 driver to d1 database', () => {
expect(hubDatabaseToContentDatabase({ driver: 'd1' })).toEqual({ type: 'd1', bindingName: 'DB' })
})

test('maps postgres drivers to postgresql database', () => {
for (const driver of ['node-postgres', 'postgres-js', 'neon-http', 'postgres', 'postgresql']) {
expect(hubDatabaseToContentDatabase({ driver, connection: { url: 'postgres://localhost' } }))
.toEqual({ type: 'postgresql', url: 'postgres://localhost' })
}
})

test('maps sqlite drivers to sqlite database', () => {
for (const driver of ['sqlite', 'better-sqlite3']) {
expect(hubDatabaseToContentDatabase({ driver, connection: { url: 'file:.data/hub/db/sqlite.db' } }))
.toEqual({ type: 'sqlite', filename: '.data/hub/db/sqlite.db' })
}
})

test('keeps an explicit sqlite filename', () => {
expect(hubDatabaseToContentDatabase({ driver: 'sqlite', connection: { filename: './contents.sqlite' } }))
.toEqual({ type: 'sqlite', filename: './contents.sqlite' })
})

test('maps libsql driver with its connection', () => {
expect(hubDatabaseToContentDatabase({ driver: 'libsql', connection: { url: 'file:.data/hub/db/sqlite.db' } }))
.toEqual({ type: 'libsql', url: 'file:.data/hub/db/sqlite.db' })
})

test('maps pglite driver with its connection', () => {
expect(hubDatabaseToContentDatabase({ driver: 'pglite', connection: { dataDir: '.data/hub/db/pglite' } }))
.toEqual({ type: 'pglite', dataDir: '.data/hub/db/pglite' })
})

test('returns undefined for unsupported drivers', () => {
expect(hubDatabaseToContentDatabase({ driver: 'mysql2', connection: { uri: 'mysql://localhost' } })).toBeUndefined()
})

test('returns undefined when required connection values are missing', () => {
expect(hubDatabaseToContentDatabase({ driver: 'postgres-js' })).toBeUndefined()
expect(hubDatabaseToContentDatabase({ driver: 'postgres-js', connection: { url: '' } })).toBeUndefined()
expect(hubDatabaseToContentDatabase({ driver: 'sqlite' })).toBeUndefined()
expect(hubDatabaseToContentDatabase({ driver: 'sqlite', connection: {} })).toBeUndefined()
})

test('returns undefined for non-string connection values', () => {
expect(hubDatabaseToContentDatabase({ driver: 'postgres-js', connection: { url: 123 as unknown as string } })).toBeUndefined()
expect(hubDatabaseToContentDatabase({ driver: 'sqlite', connection: { filename: true, url: 123 as unknown as string } })).toBeUndefined()
expect(hubDatabaseToContentDatabase({ driver: 'sqlite', connection: { filename: true, url: 'file:.data/hub/db/sqlite.db' } }))
.toEqual({ type: 'sqlite', filename: '.data/hub/db/sqlite.db' })
})
})

describe('nuxthub preset setup', () => {
const resolvedSqliteDb = { driver: 'libsql', connection: { url: 'file:.data/hub/db/sqlite.db' } }

test('maps the string form of hub.db', async () => {
const options = {} as ModuleOptions
await nuxthubPreset.setup!(options, createNuxt({ db: 'sqlite' }, { db: resolvedSqliteDb }), opts)

expect(options.database).toEqual({ type: 'libsql', url: 'file:.data/hub/db/sqlite.db' })
})

test('maps the object form of hub.db', async () => {
const options = {} as ModuleOptions
await nuxthubPreset.setup!(options, createNuxt({ db: { dialect: 'sqlite', applyMigrationsDuringBuild: false } }, { db: resolvedSqliteDb }), opts)

expect(options.database).toEqual({ type: 'libsql', url: 'file:.data/hub/db/sqlite.db' })
})

test('does not override an explicitly configured database', async () => {
const options = { database: { type: 'libsql', url: 'file:/tmp/sqlite.db' } } as ModuleOptions
await nuxthubPreset.setup!(options, createNuxt({ db: { dialect: 'sqlite' } }, { db: resolvedSqliteDb }), opts)

expect(options.database).toEqual({ type: 'libsql', url: 'file:/tmp/sqlite.db' })
})

test('leaves the database unset for unsupported drivers', async () => {
const options = {} as ModuleOptions
await nuxthubPreset.setup!(options, createNuxt({ db: { dialect: 'mysql' } }, { db: { driver: 'mysql2', connection: { uri: 'mysql://localhost' } } }), opts)

expect(options.database).toBeUndefined()
})

test('uses d1 with NuxtHub <= 0.9', async () => {
const options = {} as ModuleOptions
await nuxthubPreset.setup!(options, createNuxt({ database: true }, { database: true }), opts)

expect(options.database).toEqual({ type: 'd1', bindingName: 'DB' })
})
})

describe('nuxthub preset setupNitro', () => {
test('maps the object form of hub.db and rewrites local libsql to /tmp', async () => {
const nuxt = createNuxt(
{ db: { dialect: 'sqlite', applyMigrationsDuringBuild: false } },
{ db: { driver: 'libsql', connection: { url: 'file:.data/hub/db/sqlite.db' }, applyMigrationsDuringBuild: false } },
)
const nitroConfig = { runtimeConfig: { content: {} }, rootDir: '/' } as unknown as NitroConfig
await nuxthubPreset.setupNitro(nitroConfig, { ...opts, moduleOptions: {} as ModuleOptions, nuxt })

expect(nitroConfig.runtimeConfig!.content!.database).toEqual({ type: 'libsql', url: 'file:/tmp/sqlite.db' })
expect(nitroConfig.runtimeConfig!.content!.integrityCheck).toBe(true)
})

test('maps the string form of hub.db to the same database', async () => {
const nuxt = createNuxt(
{ db: 'sqlite' },
{ db: { driver: 'libsql', connection: { url: 'file:.data/hub/db/sqlite.db' }, applyMigrationsDuringBuild: false } },
)
const nitroConfig = { runtimeConfig: { content: {} }, rootDir: '/' } as unknown as NitroConfig
await nuxthubPreset.setupNitro(nitroConfig, { ...opts, moduleOptions: {} as ModuleOptions, nuxt })

expect(nitroConfig.runtimeConfig!.content!.database).toEqual({ type: 'libsql', url: 'file:/tmp/sqlite.db' })
expect(nitroConfig.runtimeConfig!.content!.integrityCheck).toBe(true)
})
})
Loading