@reliverse/rse
Version:
@reliverse/rse is your all-in-one companion for bootstrapping and improving any kind of projects (especially web apps built with frameworks like Next.js) — whether you're kicking off something new or upgrading an existing app. It is also a little AI-power
121 lines (120 loc) • 3.7 kB
JavaScript
import { re } from "@reliverse/relico";
import { pathExists } from "@reliverse/relifso";
import {
confirmPrompt,
defineArgs,
defineCommand,
useSpinner
} from "@reliverse/rempts";
import { logger } from "better-auth";
import { getAdapter, getMigrations } from "better-auth/db";
import path from "path";
import { z } from "zod";
import { getConfig } from "../(utils)/get-config.js";
import { configPath } from "../consts.js";
export async function migrateAction(opts) {
const options = z.object({
cwd: z.string(),
config: z.string().optional(),
y: z.boolean().optional()
}).parse(opts);
const cwd = path.resolve(options.cwd);
if (!await pathExists(cwd)) {
logger.error(`The directory "${cwd}" does not exist.`);
process.exit(1);
}
const config = await getConfig({
cwd,
configPath: options.config
});
if (!config) {
logger.error(
"No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag."
);
return;
}
const db = await getAdapter(config);
if (!db) {
logger.error(
"Invalid database configuration. Make sure you're not using adapters. Migrate command only works with built-in Kysely adapter."
);
process.exit(1);
}
if (db.id !== "kysely") {
if (db.id === "prisma") {
logger.error(
"The migrate command only works with the built-in Kysely adapter. For Prisma, run `npx @better-auth/cli generate` to create the schema, then use Prisma's migrate or push to apply it."
);
process.exit(0);
}
if (db.id === "drizzle") {
logger.error(
"The migrate command only works with the built-in Kysely adapter. For Drizzle, run `npx @better-auth/cli generate` to create the schema, then use Drizzle's migrate or push to apply it."
);
process.exit(0);
}
logger.error("Migrate command isn't supported for this adapter.");
process.exit(1);
}
const spinner = useSpinner({ text: "preparing migration..." }).start();
const { toBeAdded, toBeCreated, runMigrations } = await getMigrations(config);
if (!toBeAdded.length && !toBeCreated.length) {
spinner.stop();
logger.info("\u{1F680} No migrations needed.");
process.exit(0);
}
spinner.stop();
logger.info("\u{1F511} The migration will affect the following:");
for (const table of [...toBeCreated, ...toBeAdded]) {
console.log(
"->",
re.magenta(Object.keys(table.fields).join(", ")),
re.white("fields on"),
re.yellow(table.table),
re.white("table.")
);
}
let migrate = options.y;
if (!migrate) {
const response = await confirmPrompt({
title: "migrate",
content: "Are you sure you want to run these migrations?",
defaultValue: false
});
migrate = response;
}
if (!migrate) {
logger.info("Migration cancelled.");
process.exit(0);
}
spinner?.start("migrating...");
await runMigrations();
spinner.stop();
logger.info("\u{1F680} migration was completed successfully!");
process.exit(0);
}
export default defineCommand({
meta: {
name: "migrate"
},
args: defineArgs({
cwd: {
type: "string",
description: "The working directory. defaults to the current directory.",
default: process.cwd()
},
config: {
type: "string",
description: "The path to the configuration file. defaults to the first configuration file found.",
default: configPath
},
y: {
type: "boolean",
description: "Automatically accept and run migrations without prompting",
default: false
}
}),
async run({ args }) {
await migrateAction(args);
}
});