Skip to content

Commit 00ed3c1

Browse files
committed
feat: check deps instead of installing + big improvements on hubDrizzle
1 parent ce58c22 commit 00ed3c1

File tree

12 files changed

+203
-131
lines changed

12 files changed

+203
-131
lines changed

playground/server/api/tests/db.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core'
22

33
export default defineEventHandler(async () => {
4-
const db0 = useDatabase()
4+
const db0 = useDatabase('db')
55

66
const _tables = await db0.sql`
77
SELECT
@@ -14,10 +14,9 @@ export default defineEventHandler(async () => {
1414
name NOT LIKE 'sqlite_%' and name NOT LIKE '_litestream_%' and name NOT LIKE '__drizzle%'
1515
;`
1616

17-
console.log(_tables.rows)
1817
const db = await useDrizzle()
1918

20-
const tables = await db.all(sql`
19+
const drizzleTables = await db.all(sql`
2120
SELECT
2221
name,
2322
type
@@ -29,18 +28,14 @@ export default defineEventHandler(async () => {
2928
;
3029
`)
3130

32-
const todos = sqliteTable('todos', {
33-
id: integer('id').primaryKey(),
34-
text: text('text')
35-
})
3631
// const inserted = await db.insert(todos).values({ text: 'hello' }).returning().get()
3732
// const todo = await db.select().from(todos).where(eq(todos.id, inserted.id)).get()
3833
// const updated = await db.update(todos).set({ text: 'Bonjour' }).where(eq(todos.id, inserted.id)).returning()
39-
const all = await db.select().from(todos).limit(3)
34+
const all = await db.select().from(tables.todos).limit(3)
4035
// const deleted = await db.delete(todos).where(eq(todos.id, all[0].id))
4136

4237
return {
43-
tables,
38+
drizzleTables,
4439
// todo,
4540
// inserted,
4641
// updated,

playground/server/utils/db.ts renamed to playground/server/utils/drizzle.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1+
12
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
23
// import { pgTable, text as pgText, boolean as pgBoolean, serial as pgSerial, timestamp as pgTimestamp } from 'drizzle-orm/pg-core'
34

4-
import { drizzle as drizzleSqlite } from 'drizzle-orm/better-sqlite3'
5-
// import { drizzle as drizzleD1 } from 'drizzle-orm/d1'
6-
// import { drizzle as drizzlePglite } from 'drizzle-orm/pglite'
7-
// import { drizzle as drizzlePostgres } from 'drizzle-orm/node-postgres'
8-
95
export { sql } from 'drizzle-orm'
106

117
const todos = sqliteTable('todos', {
@@ -27,12 +23,5 @@ export const tables = {
2723
}
2824

2925
export async function useDrizzle() {
30-
const database = hubDatabase()
31-
const instance = await database.getInstance()
32-
33-
if (import.meta.dev) {
34-
return drizzleSqlite(instance, { schema: tables })
35-
}
36-
37-
return drizzleSqlite(instance, { schema: tables })
26+
return hubDrizzle({ schema: tables })
3827
}

src/features/ai.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { addServerImportsDir, addServerScanDir, addTypeTemplate, logger } from '@nuxt/kit'
2-
import { ensureDependencyInstalled } from 'nypm'
32
import { logWhenReady } from '../features'
43
import { resolve } from '../module'
54

@@ -9,18 +8,14 @@ import type { HubConfig } from '../features'
98

109
const log = logger.withTag('nuxt:hub')
1110

12-
export async function setupAI(nuxt: Nuxt, hub: HubConfig) {
11+
export async function setupAI(nuxt: Nuxt, hub: HubConfig, deps: Record<string, string>) {
1312
const providerName = hub.ai === 'vercel' ? 'Vercel AI Gateway' : 'Workers AI Provider'
1413

15-
if (hub.ai === 'vercel') {
16-
await Promise.all([
17-
ensureDependencyInstalled('@ai-sdk/gateway')
18-
])
19-
} else if (hub.ai === 'cloudflare') {
20-
await Promise.all([
21-
ensureDependencyInstalled('workers-ai-provider')
22-
])
23-
} else {
14+
if (hub.ai === 'vercel' && !deps['@ai-sdk/gateway']) {
15+
return logWhenReady(nuxt, 'Please run `npx nypm i @ai-sdk/gateway` to use Vercel AI Gateway', 'error')
16+
} else if (hub.ai === 'cloudflare' && !deps['workers-ai-provider']) {
17+
return logWhenReady(nuxt, 'Please run `npx nypm i workers-ai-provider` to use Workers AI Provider', 'error')
18+
} else if (hub.ai && !['vercel', 'cloudflare'].includes(hub.ai)) {
2419
return logWhenReady(nuxt, `\`${hub.ai}\` is not a supported AI provider. Set \`hub.ai\` to \`'vercel'\` or \`'cloudflare'\` in your \`nuxt.config.ts\`. Learn more at https://hub.nuxt.com/docs/features/ai.`, 'error')
2520
}
2621

@@ -58,7 +53,7 @@ export async function setupAI(nuxt: Nuxt, hub: HubConfig) {
5853
logWhenReady(nuxt, `\`hubAI()\` configured with \`${providerName}\``)
5954
}
6055

61-
export async function setupProductionAI(nitro: Nitro, hub: HubConfig) {
56+
export async function setupProductionAI(nitro: Nitro, hub: HubConfig, _deps: Record<string, string>) {
6257
const preset = nitro.options.preset
6358
if (!preset) return
6459

src/features/blob.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ import type { Nitro, NitroOptions } from 'nitropack'
88
import type { HubConfig } from '../features'
99
import { resolve } from '../module'
1010

11-
import { ensureDependencyInstalled } from 'nypm'
12-
1311
const log = logger.withTag('nuxt:hub')
1412

15-
export function setupBlob(nuxt: Nuxt, hub: HubConfig) {
13+
export function setupBlob(nuxt: Nuxt, hub: HubConfig, _deps: Record<string, string>) {
1614
// Configure dev storage
1715
nuxt.options.nitro.devStorage ||= {}
1816

@@ -37,7 +35,7 @@ export function setupBlob(nuxt: Nuxt, hub: HubConfig) {
3735
logWhenReady(nuxt, `\`hubBlob()\` configured with \`${nuxt.options.nitro.devStorage.blob.driver}\` driver`)
3836
}
3937

40-
export async function setupProductionBlob(nitro: Nitro, _hub: HubConfig) {
38+
export async function setupProductionBlob(nitro: Nitro, _hub: HubConfig, deps: Record<string, string>) {
4139
const preset = nitro.options.preset
4240
if (!preset) return
4341

@@ -135,20 +133,14 @@ export async function setupProductionBlob(nitro: Nitro, _hub: HubConfig) {
135133

136134
if (blobConfig) {
137135
// check if driver dependencies are installed
138-
switch (blobConfig.driver) {
139-
case 'vercel-blob':
140-
await ensureDependencyInstalled('@vercel/blob')
141-
break
142-
case 's3':
143-
await ensureDependencyInstalled('aws4fetch')
144-
break
145-
case 'netlify-blobs':
146-
await ensureDependencyInstalled('@netlify/blobs')
147-
break
148-
case 'azure-storage-blob':
149-
await ensureDependencyInstalled('@azure/storage-blob')
150-
await ensureDependencyInstalled('@azure/identity')
151-
break
136+
if (blobConfig.driver === 'vercel-blob' && !deps['@vercel/blob']) {
137+
throw new Error('Please run `npx nypm i @vercel/blob` to use Vercel Blob')
138+
} else if (blobConfig.driver === 's3' && !deps['aws4fetch']) {
139+
throw new Error('Please run `npx nypm i aws4fetch` to use S3')
140+
} else if (blobConfig.driver === 'netlify-blobs' && !deps['@netlify/blobs']) {
141+
throw new Error('Please run `npx nypm i @netlify/blobs` to use Netlify Blobs')
142+
} else if (blobConfig.driver === 'azure-storage-blob' && (!deps['@azure/storage-blob'] || !deps['@azure/identity'])) {
143+
throw new Error('Please run `npx nypm i @azure/storage-blob @azure/identity` to use Azure Blob Storage')
152144
}
153145

154146
// set driver

src/features/cache.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { HubConfig } from '../features'
1212

1313
const log = logger.withTag('nuxt:hub')
1414

15-
export async function setupCache(nuxt: Nuxt, hub: HubConfig) {
15+
export async function setupCache(nuxt: Nuxt, hub: HubConfig, _deps: Record<string, string>) {
1616
// Configure dev storage
1717
nuxt.options.nitro.devStorage ||= {}
1818
nuxt.options.nitro.devStorage.cache = defu(nuxt.options.nitro.devStorage.cache, {
@@ -23,10 +23,10 @@ export async function setupCache(nuxt: Nuxt, hub: HubConfig) {
2323
// Add Server scanning
2424
addServerScanDir(resolve('runtime/cache/server'))
2525

26-
logWhenReady(nuxt, `Application cache configured with \`${nuxt.options.nitro.devStorage.cache.driver}\` driver`)
26+
logWhenReady(nuxt, `\`Hub cache\` configured with \`${nuxt.options.nitro.devStorage.cache.driver}\` driver`)
2727
}
2828

29-
export async function setupProductionCache(nitro: Nitro, _hub: HubConfig) {
29+
export async function setupProductionCache(nitro: Nitro, _hub: HubConfig, _deps: Record<string, string>) {
3030
const preset = nitro.options.preset
3131
if (!preset) return
3232

@@ -74,6 +74,6 @@ export async function setupProductionCache(nitro: Nitro, _hub: HubConfig) {
7474
if (cacheConfig) {
7575
nitro.options.storage ||= {}
7676
nitro.options.storage.cache = defu(nitro.options.storage?.cache, cacheConfig)
77-
log.info(`Application cache configured with \`${nitro.options.storage.cache.driver}\` driver`)
77+
log.info(`\`Hub cache\` configured with \`${nitro.options.storage.cache.driver}\` driver`)
7878
}
7979
}

src/features/database.ts

Lines changed: 34 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { mkdir, writeFile } from 'node:fs/promises'
22
import { defu } from 'defu'
33
import { join } from 'pathe'
4-
import { ensureDependencyInstalled } from 'nypm'
5-
import { addServerImportsDir, addServerScanDir, logger } from '@nuxt/kit'
4+
import { addServerImportsDir, addServerScanDir, addServerTemplate, addTypeTemplate, logger } from '@nuxt/kit'
65
import { copyDatabaseMigrationsToHubDir, copyDatabaseQueriesToHubDir } from '../runtime/database/server/utils/migrations/helpers'
76
import { logWhenReady } from '../features'
87
import { resolve } from '../module'
@@ -14,7 +13,7 @@ import type { HubConfig } from '../features'
1413

1514
const log = logger.withTag('nuxt:hub')
1615

17-
export async function setupDatabase(nuxt: Nuxt, hub: HubConfig) {
16+
export async function setupDatabase(nuxt: Nuxt, hub: HubConfig, deps: Record<string, string>) {
1817
// Configure dev storage
1918
if (typeof hub.database === 'string' && !['postgresql', 'sqlite', 'mysql'].includes(hub.database)) {
2019
return logWhenReady(nuxt, `Unknown database dialect set in hub.database: ${hub.database}`, 'error')
@@ -104,7 +103,8 @@ export async function setupDatabase(nuxt: Nuxt, hub: HubConfig) {
104103
path: join(hub.dir!, 'database/sqlite/db.sqlite3')
105104
}
106105
}
107-
} else if (dialect === 'mysql') {
106+
await mkdir(join(hub.dir!, 'database/sqlite'), { recursive: true })
107+
} else if (dialect === 'mysql') {
108108
if (!nuxt.options.nitro.devDatabase?.db?.connector) {
109109
logWhenReady(nuxt, '`hubDatabase()` configured with `MySQL` during local development is not supported yet. Please manually configure your development database in `nitro.devDatabase.db` in `nuxt.config.ts`. Learn more at https://hub.nuxt.com/docs/features/database.', 'warn')
110110
}
@@ -115,14 +115,14 @@ export async function setupDatabase(nuxt: Nuxt, hub: HubConfig) {
115115

116116
// Verify development database dependencies are installed
117117
const developmentDriver = nuxt.options.nitro.devDatabase?.db?.connector as ConnectorName
118-
if (developmentDriver === 'postgresql') {
119-
await ensureDependencyInstalled('pg')
120-
} else if (developmentDriver === 'pglite') {
121-
await ensureDependencyInstalled('@electric-sql/pglite')
122-
} else if (developmentDriver === 'mysql2') {
123-
await ensureDependencyInstalled('mysql2')
124-
} else if (developmentDriver === 'better-sqlite3') {
125-
await ensureDependencyInstalled('better-sqlite3')
118+
if (developmentDriver === 'postgresql' && !deps.pg) {
119+
logWhenReady(nuxt, 'Please run `npx nypm i pg` to use PostgreSQL as database.', 'error')
120+
} else if (developmentDriver === 'pglite' && !deps['@electric-sql/pglite']) {
121+
logWhenReady(nuxt, 'Please run `npx nypm i @electric-sql/pglite` to use PGlite as database.', 'error')
122+
} else if (developmentDriver === 'mysql2' && !deps.mysql2) {
123+
logWhenReady(nuxt, 'Please run `npx nypm i mysql2` to use MySQL as database.', 'error')
124+
} else if (developmentDriver === 'better-sqlite3' && !deps['better-sqlite3']) {
125+
logWhenReady(nuxt, 'Please run `npx nypm i better-sqlite3` to use SQLite as database.', 'error')
126126
}
127127

128128
// Enable Nitro database
@@ -145,22 +145,14 @@ export async function setupDatabase(nuxt: Nuxt, hub: HubConfig) {
145145
})
146146

147147
// Setup Drizzle ORM
148-
let isDrizzleOrmInstalled = false
149-
try {
150-
require.resolve('drizzle-orm', { paths: [nuxt.options.rootDir] })
151-
isDrizzleOrmInstalled = true
152-
} catch {
153-
// Ignore
154-
}
155-
156-
if (isDrizzleOrmInstalled) {
148+
if (deps['drizzle-orm']) {
157149
const connector = nuxt.options.nitro.devDatabase.db.connector as ConnectorName
158150
const dbConfig = nuxt.options.nitro.devDatabase.db.options
159151

160152
// @ts-expect-error not all connectors are supported
161153
const db0ToDrizzle: Record<ConnectorName, string> = {
162154
postgresql: 'node-postgres',
163-
pglite: 'pglite',
155+
pglite: 'pg-proxy',
164156
mysql2: 'mysql2',
165157
planetscale: 'planetscale-serverless',
166158
'better-sqlite3': 'better-sqlite3',
@@ -180,23 +172,29 @@ export async function setupDatabase(nuxt: Nuxt, hub: HubConfig) {
180172
let connectionConfig = dbConfig
181173
if (connector === 'postgresql' && dbConfig?.url) {
182174
connectionConfig = { connectionString: dbConfig.url, ...dbConfig.options }
175+
} else if (connector === 'better-sqlite3' && dbConfig?.path) {
176+
connectionConfig = { source: dbConfig.path, ...dbConfig.options }
183177
}
184178

185179
let drizzleOrmContent = `import { drizzle } from 'drizzle-orm/${db0ToDrizzle[connector]}'
186-
import type { DrizzleConfig } from 'drizzle-orm'
187180
188-
export function hubDrizzle<TSchema extends Record<string, unknown> = Record<string, never>>(options?: DrizzleConfig<TSchema>) {
181+
export function hubDrizzle(options) {
189182
return drizzle({
190183
...options,
191184
connection: ${JSON.stringify(connectionConfig)}
192185
})
193186
}`
187+
const drizzleOrmTypes = `import type { DrizzleConfig } from 'drizzle-orm'
188+
import { drizzle } from 'drizzle-orm/${db0ToDrizzle[connector]}'
189+
190+
declare module '#hub/drizzle-orm' {
191+
export function hubDrizzle<TSchema extends Record<string, unknown> = Record<string, never>>(options?: DrizzleConfig<TSchema>): ReturnType<typeof drizzle<TSchema>>
192+
}`
194193

195194
if (connector === 'pglite') {
196195
drizzleOrmContent = `import { drizzle } from 'drizzle-orm/pg-proxy'
197-
import type { DrizzleConfig } from 'drizzle-orm'
198196
199-
export function hubDrizzle<TSchema extends Record<string, unknown> = Record<string, never>>(options?: DrizzleConfig<TSchema>) {
197+
export function hubDrizzle(options) {
200198
return drizzle(async (sql, params, method) => {
201199
try {
202200
const rows = await $fetch<any[]>('/api/_hub/database/query', { method: 'POST', body: { sql, params, method } })
@@ -211,18 +209,19 @@ export function hubDrizzle<TSchema extends Record<string, unknown> = Record<stri
211209
}`
212210
}
213211

214-
// create hub directory in .nuxt if it doesn't exist
215-
const hubBuildDir = join(nuxt.options.buildDir, 'hub')
216-
await mkdir(hubBuildDir, { recursive: true })
217-
218-
const drizzleOrmPath = join(hubBuildDir, 'drizzle-orm.ts')
219-
await writeFile(drizzleOrmPath, drizzleOrmContent, 'utf-8')
220-
221-
nuxt.options.alias['#hub/drizzle-orm'] = drizzleOrmPath
212+
addServerTemplate({
213+
filename: '#hub/drizzle-orm',
214+
getContents: () => drizzleOrmContent
215+
})
216+
addTypeTemplate({
217+
filename: 'types/hub/drizzle-orm.d.ts',
218+
getContents: () => drizzleOrmTypes
219+
})
220+
addServerImportsDir(resolve('runtime/database/server/drizzle-utils'))
222221
}
223222
}
224223

225-
export async function setupProductionDatabase(nitro: Nitro, hub: HubConfig) {
224+
export async function setupProductionDatabase(nitro: Nitro, hub: HubConfig, deps: Record<string, string>) {
226225
const preset = nitro.options.preset
227226
if (!preset) return
228227

@@ -238,7 +237,6 @@ export async function setupProductionDatabase(nitro: Nitro, hub: HubConfig) {
238237

239238
switch (preset) {
240239
// Does your favourite cloud provider require special configuration? Feel free to open a PR to add zero-config support for other presets
241-
242240
case 'vercel': {
243241
if (dialect === true || dialect === 'postgresql') {
244242
databaseConfig = {
@@ -347,19 +345,6 @@ export async function setupProductionDatabase(nitro: Nitro, hub: HubConfig) {
347345
}
348346

349347
if (databaseConfig!) {
350-
// check if connector dependencies are installed
351-
switch (databaseConfig.connector) {
352-
case 'postgresql':
353-
await ensureDependencyInstalled('pg')
354-
break
355-
case 'better-sqlite3':
356-
await ensureDependencyInstalled('better-sqlite3')
357-
break
358-
case 'mysql2':
359-
await ensureDependencyInstalled('mysql2')
360-
break
361-
}
362-
363348
// set connector
364349
// @ts-expect-error temporarily set to empty object
365350
nitro.options.database ||= {}

src/features/kv.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { join } from 'pathe'
22
import { defu } from 'defu'
3-
import { ensureDependencyInstalled } from 'nypm'
43
import { addServerScanDir, addServerImportsDir, logger } from '@nuxt/kit'
54
import { logWhenReady } from '../features'
65
import { resolve } from '../module'
@@ -11,7 +10,7 @@ import type { HubConfig } from '../features'
1110

1211
const log = logger.withTag('nuxt:hub')
1312

14-
export function setupKV(nuxt: Nuxt, hub: HubConfig) {
13+
export function setupKV(nuxt: Nuxt, hub: HubConfig, _deps: Record<string, string>) {
1514
// Configure dev storage
1615
nuxt.options.nitro.devStorage ||= {}
1716
nuxt.options.nitro.devStorage.kv = defu(nuxt.options.nitro.devStorage.kv, {
@@ -28,7 +27,7 @@ export function setupKV(nuxt: Nuxt, hub: HubConfig) {
2827
logWhenReady(nuxt, `\`hubKV()\` configured with \`${driver}\` driver`)
2928
}
3029

31-
export async function setupProductionKV(nitro: Nitro, _hub: HubConfig) {
30+
export async function setupProductionKV(nitro: Nitro, _hub: HubConfig, deps: Record<string, string>) {
3231
const preset = nitro.options.preset
3332
if (!preset) return
3433

@@ -84,10 +83,8 @@ export async function setupProductionKV(nitro: Nitro, _hub: HubConfig) {
8483

8584
if (kvConfig) {
8685
// check if driver dependencies are installed
87-
switch (kvConfig.driver) {
88-
case 'redis':
89-
await ensureDependencyInstalled('ioredis')
90-
break
86+
if (kvConfig.driver === 'redis' && !deps['ioredis']) {
87+
throw new Error('Please run `npx nypm i ioredis` to use Redis KV storage')
9188
}
9289

9390
// set driver

0 commit comments

Comments
 (0)