|
import fs from "node:fs/promises"; |
|
import path from "node:path"; |
|
import { exec } from "node:child_process"; |
|
import { |
|
intro, |
|
outro, |
|
log, |
|
select, |
|
text, |
|
spinner, |
|
isCancel, |
|
confirm, |
|
} from "@clack/prompts"; |
|
import toml from "toml"; |
|
import parseArgv from "tiny-parse-argv"; |
|
|
|
const args = parseArgv(process.argv.slice(2)); |
|
const command = args._[0]; |
|
|
|
const projectRoot = path.resolve(); |
|
|
|
const asyncExec = (command: string) => |
|
new Promise<string>((resolve, reject) => { |
|
exec(command, (error, stdout, stderr) => { |
|
if (error) { |
|
reject(stderr); |
|
} else { |
|
resolve(stdout); |
|
} |
|
}); |
|
}); |
|
|
|
intro("D1 Prisma Migrate CLI"); |
|
|
|
if (args.help || !command) { |
|
switch (command) { |
|
case "create": |
|
log.message(`migrate create |
|
Create a new migration |
|
Options: |
|
-n, --name - The name of the migration |
|
-d, --database - The name of the D1 database |
|
--create-only - Only create the migration file, do not apply it |
|
--schema - Custom path to the Prisma schema |
|
-h, --help - Show this help message`); |
|
break; |
|
case "apply": |
|
log.message(`migrate apply |
|
Apply pending migrations |
|
Options: |
|
-d, --database - The name of the D1 database |
|
--remote - Apply migrations to your remote database |
|
--schema - Custom path to the Prisma schema |
|
-h, --help - Show this help message`); |
|
break; |
|
default: |
|
log.message(`migrate <command> |
|
Commands: |
|
create - Create a new migration |
|
apply - Apply pending migrations |
|
Options: |
|
-h, --help - Show this help message`); |
|
break; |
|
} |
|
process.exit(0); |
|
} |
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation> |
|
let wranglerConfig: any; |
|
|
|
// Check wrangler.toml to see what D1 namespaces are used |
|
try { |
|
const wranglerToml = await fs.readFile("wrangler.toml", "utf-8"); |
|
wranglerConfig = toml.parse(wranglerToml); |
|
} catch { |
|
log.error("Could not read wrangler.toml"); |
|
process.exit(1); |
|
} |
|
|
|
const databases: { value: string; label: string }[] = |
|
wranglerConfig.d1_databases?.map((db: { database_name: string }) => ({ |
|
value: db.database_name, |
|
label: db.database_name, |
|
})) || []; |
|
|
|
let database = args.d || args.database || databases[0]?.value; |
|
|
|
if (command === "create") { |
|
const database = await getDatabase(); |
|
|
|
const migrationName = |
|
args.name || |
|
args.n || |
|
(await text({ |
|
message: "What is the name of the migration?", |
|
validate: (input) => { |
|
if (input.length === 0) { |
|
return "Migration name cannot be empty"; |
|
} |
|
}, |
|
})); |
|
|
|
if (isCancel(migrationName)) { |
|
process.exit(1); |
|
} |
|
|
|
const s = spinner(); |
|
s.start("Creating migration"); |
|
const result = await asyncExec( |
|
`npx wrangler d1 migrations create ${database} ${migrationName}` |
|
); |
|
|
|
s.stop("Creating migration"); |
|
|
|
const migrationPath = result |
|
.trim() |
|
.split("\n") |
|
.find((line) => line.endsWith(".sql")); |
|
|
|
if (!migrationPath) { |
|
log.error("Could not find migration path"); |
|
process.exit(1); |
|
} |
|
|
|
s.start("Generating migration diff from Prisma schema"); |
|
|
|
await asyncExec( |
|
`npx prisma migrate diff --script --from-local-d1 --to-schema-datamodel ${ |
|
args.schema || "./prisma/schema.prisma" |
|
} >> ${migrationPath}` |
|
); |
|
|
|
s.stop("Generating migration diff from Prisma schema"); |
|
|
|
if (args["create-only"]) { |
|
outro(`Migration created at ${migrationPath.replace(projectRoot, ".")}`); |
|
process.exit(); |
|
} |
|
} |
|
|
|
if (command === "apply" || command === "create") { |
|
const database = await getDatabase(); |
|
|
|
const s = spinner(); |
|
s.start("Applying migrations"); |
|
|
|
await asyncExec( |
|
`npx wrangler d1 migrations apply ${database} ${ |
|
args.remote ? "--remote" : "--local" |
|
}` |
|
); |
|
|
|
s.stop("Applying migrations"); |
|
|
|
s.start("Generating Prisma client"); |
|
|
|
await asyncExec( |
|
`npx prisma generate ${args.schema ? `--schema ${args.schema}` : ""}` |
|
); |
|
|
|
s.stop("Generating Prisma client"); |
|
|
|
outro("Migrations applied"); |
|
} |
|
|
|
async function getDatabase() { |
|
if (databases.length === 0) { |
|
log.error("No D1 databases found in wrangler.toml"); |
|
process.exit(1); |
|
} |
|
|
|
database = |
|
database || |
|
(await select({ |
|
message: "Select a database", |
|
options: databases, |
|
initialValue: databases[0].value, |
|
})); |
|
|
|
if (isCancel(database)) { |
|
process.exit(1); |
|
} |
|
return database; |
|
} |
I've edited the script since it was not working using the
--from-local-d1flag and also I was using wrangler.jsonc instead of toml.The way the script generate the migrations is quite similar to the original but, instead of getting the schema using --from-local-d1, I had to: