import type { DrizzleSnapshotJSON } from 'drizzle-kit/api' import type { CreateMigration } from 'payload' import fs from 'fs' import { createRequire } from 'module' import path from 'path' import { getPredefinedMigration, writeMigrationIndex } from 'payload' import prompts from 'prompts' import { fileURLToPath } from 'url' import type { SQLiteAdapter } from './types.js' import { defaultDrizzleSnapshot } from './defaultSnapshot.js' import { getMigrationTemplate } from './getMigrationTemplate.js' const require = createRequire(import.meta.url) export const createMigration: CreateMigration = async function createMigration( this: SQLiteAdapter, { file, forceAcceptWarning, migrationName, payload }, ) { const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) const dir = payload.db.migrationDir if (!fs.existsSync(dir)) { fs.mkdirSync(dir) } const { generateSQLiteDrizzleJson, generateSQLiteMigration } = require('drizzle-kit/api') const drizzleJsonAfter = await generateSQLiteDrizzleJson(this.schema) const [yyymmdd, hhmmss] = new Date().toISOString().split('T') const formattedDate = yyymmdd.replace(/\D/g, '') const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '') let imports: string = '' let downSQL: string let upSQL: string ;({ downSQL, imports, upSQL } = await getPredefinedMigration({ dirname, file, migrationName, payload, })) const timestamp = `${formattedDate}_${formattedTime}` const name = migrationName || file?.split('/').slice(2).join('/') const fileName = `${timestamp}${name ? `_${name.replace(/\W/g, '_')}` : ''}` const filePath = `${dir}/${fileName}` let drizzleJsonBefore = defaultDrizzleSnapshot as any if (!upSQL) { // Get latest migration snapshot const latestSnapshot = fs .readdirSync(dir) .filter((file) => file.endsWith('.json')) .sort() .reverse()?.[0] if (latestSnapshot) { drizzleJsonBefore = JSON.parse( fs.readFileSync(`${dir}/${latestSnapshot}`, 'utf8'), ) as DrizzleSnapshotJSON } const sqlStatementsUp = await generateSQLiteMigration(drizzleJsonBefore, drizzleJsonAfter) const sqlStatementsDown = await generateSQLiteMigration(drizzleJsonAfter, drizzleJsonBefore) // need to create tables as separate statements const sqlExecute = 'await payload.db.drizzle.run(sql`' if (sqlStatementsUp?.length) { upSQL = sqlStatementsUp .map((statement) => `${sqlExecute}${statement?.replaceAll('`', '\\`')}\`)`) .join('\n') } if (sqlStatementsDown?.length) { downSQL = sqlStatementsDown .map((statement) => `${sqlExecute}${statement?.replaceAll('`', '\\`')}\`)`) .join('\n') } if (!upSQL?.length && !downSQL?.length && !forceAcceptWarning) { const { confirm: shouldCreateBlankMigration } = await prompts( { name: 'confirm', type: 'confirm', initial: false, message: 'No schema changes detected. Would you like to create a blank migration file?', }, { onCancel: () => { process.exit(0) }, }, ) if (!shouldCreateBlankMigration) { process.exit(0) } } // write schema fs.writeFileSync(`${filePath}.json`, JSON.stringify(drizzleJsonAfter, null, 2)) } // write migration fs.writeFileSync( `${filePath}.ts`, getMigrationTemplate({ downSQL: downSQL || ` // Migration code`, imports, upSQL: upSQL || ` // Migration code`, }), ) writeMigrationIndex({ migrationsDir: payload.db.migrationDir }) payload.logger.info({ msg: `Migration created at ${filePath}.ts` }) }