diff --git a/cli/commands/db/generate.mjs b/cli/commands/db/generate.mjs index e408f5fdf..1ad9c1651 100644 --- a/cli/commands/db/generate.mjs +++ b/cli/commands/db/generate.mjs @@ -31,7 +31,13 @@ export default defineCommand({ type: 'boolean', description: 'Show verbose output.', required: false - } + }, + dialect: { + type: 'enum', + options: ["sqlite", "postgresql", "mysql", "all"], + description: 'Database dialect, one of postgresql mysql sqlite', + required: false + }, }, async run({ args }) { if (args.verbose) { @@ -39,27 +45,52 @@ export default defineCommand({ consola.level = 4 } const cwd = args.cwd ? resolve(process.cwd(), args.cwd) : process.cwd() - const options = { + let options = { stdout: 'pipe', stderr: 'pipe', preferLocal: true, - cwd + cwd, + env: {} } - consola.info('Ensuring database schema is generated...') - await execa(options)`nuxt prepare` - const alias = await getTsconfigAliases(cwd) - await buildDatabaseSchema(join(options.cwd, '.nuxt'), { relativeDir: cwd, alias }) - consola.info('Generating database migrations...') - const { stderr } = await execa({ - ...options, - stdin: 'inherit', - stdout: 'inherit' - })`drizzle-kit generate --config=./.nuxt/hub/db/drizzle.config.ts${args.custom ? ' --custom' : ''}${args.name ? ` --name=${args.name}` : ''}` - // Drizzle-kit does not exit with an error code when there is an error, so we need to check the stderr - if (stderr) { - consola.error(stderr) - process.exit(1) + + async function GenerateMigration(dialect) { + if (dialect) + options.env = { + ...process.env, + NUXT_HUB_DB: dialect + } + consola.info(`Ensuring database schema is generated${dialect ? ' for ' + dialect : ''}...`) + await execa(options)`nuxt prepare` + const alias = await getTsconfigAliases(cwd) + await buildDatabaseSchema(join(options.cwd, '.nuxt'), { relativeDir: cwd, alias }) + consola.info(`Generating database migrations${dialect ? ' for ' + dialect : ''}...`) + const { stderr } = await execa({ + ...options, + stdin: 'inherit', + stdout: 'inherit' + })`drizzle-kit generate --config=./.nuxt/hub/db/drizzle.config.ts${args.custom ? ' --custom' : ''}${args.name ? ` --name=${args.name}` : ''}` + // Drizzle-kit does not exit with an error code when there is an error, so we need to check the stderr + if (stderr) { + consola.error(stderr) + process.exit(1) + } + delete options.env.NUXT_HUB_DB } + + if (args.dialect === "all") { + for (const _dialect of ["sqlite", "postgresql", "mysql"]) { + await GenerateMigration(_dialect) + } + } else { + await GenerateMigration(args.dialect) + } + + + if (args.dialect) { + consola.info('Resetting .nuxt with original db dialect') + await execa(options)`nuxt prepare` + } + consola.success('Database migrations generated successfully.') consola.info('Run `npx nuxt dev` or run `npx nuxt db migrate` to apply the migrations.') } diff --git a/src/db/setup.ts b/src/db/setup.ts index 514910bb3..8dea4a36c 100644 --- a/src/db/setup.ts +++ b/src/db/setup.ts @@ -7,6 +7,7 @@ import { addServerImports, addTemplate, addServerPlugin, addTypeTemplate, getLay import { resolve, resolvePath, logWhenReady, addWranglerBinding } from '../utils' import { copyDatabaseMigrationsToHubDir, copyDatabaseQueriesToHubDir, copyDatabaseAssets, applyBuildTimeMigrations, getDatabaseSchemaPathMetadata, buildDatabaseSchema } from './lib' import { cloudflareHooks } from '../hosting/cloudflare' +import { stringifyJSON5 } from "confbox" import type { Nuxt } from '@nuxt/schema' import type { HubConfig, ResolvedHubConfig, ResolvedDatabaseConfig } from '@nuxthub/core' @@ -39,6 +40,8 @@ export { db, schema } * Resolve database configuration from string or object format */ export async function resolveDatabaseConfig(nuxt: Nuxt, hub: HubConfig): Promise { + // Env explicitly set db. Used by cli db generate command + if(process.env.NUXT_HUB_DB) hub.db = process.env.NUXT_HUB_DB if (!hub.db) return false let config = typeof hub.db === 'string' ? { dialect: hub.db } : hub.db @@ -635,16 +638,23 @@ export const db: ReturnType> async function setupDatabaseConfig(nuxt: Nuxt, hub: ResolvedHubConfig) { // generate drizzle.config.ts in .nuxt/hub/db/drizzle.config.ts - const { dialect, casing } = hub.db as ResolvedDatabaseConfig - const casingConfig = casing ? `\n casing: '${casing}',` : '' + const { dialect, casing, drizzle } = hub.db as ResolvedDatabaseConfig + addTemplate({ filename: 'hub/db/drizzle.config.ts', write: true, getContents: () => `import { defineConfig } from 'drizzle-kit' -export default defineConfig({ - dialect: '${dialect}',${casingConfig} - schema: '${relative(nuxt.options.rootDir, resolve(nuxt.options.buildDir, 'hub/db/schema.mjs'))}', - out: '${relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, `server/db/migrations/${dialect}`))}' +export default defineConfig(${ + stringifyJSON5( + defu(drizzle, + casing && { casing }, + { + dialect, + schema: relative(nuxt.options.rootDir, resolve(nuxt.options.buildDir, 'hub/db/schema.mjs')), + out: relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, `server/db/migrations/${dialect}`)) + } + ) + ) });` }) } diff --git a/src/types/config.ts b/src/types/config.ts index c0edaa201..0dcd7df5b 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -3,6 +3,7 @@ import type { FSDriverOptions } from '@nuxthub/core/blob/drivers/fs' import type { S3DriverOptions } from '@nuxthub/core/blob/drivers/s3' import type { VercelDriverOptions } from '@nuxthub/core/blob/drivers/vercel-blob' import type { CloudflareDriverOptions } from '@nuxthub/core/blob/drivers/cloudflare-r2' +import type { Config as DrizzleConfig } from "drizzle-kit" export interface HubConfig { blob: boolean | BlobConfig @@ -224,6 +225,15 @@ export type DatabaseConfig = { * @see https://orm.drizzle.team/docs/read-replicas */ replicas?: string[] + /** + * Drizzle config that will be passed down to the generated drizzle.config.ts + * + * @see https://orm.drizzle.team/docs/drizzle-config-file + */ + drizzle?: Pick< + DrizzleConfig, + "entities" | "schemaFilter" | "tablesFilter" | "extensionsFilters" + >; } export type ResolvedDatabaseConfig = DatabaseConfig & {