UNPKG

netlify

Version:

Netlify command line tool

135 lines 4.91 kB
import { readdir, mkdir, writeFile } from 'fs/promises'; import { join } from 'path'; import inquirer from 'inquirer'; import { log, logJson } from '../../utils/command-helpers.js'; export const generateSlug = (description) => { return description .toLowerCase() .trim() .replace(/[^a-z0-9\s_-]/g, '') .replace(/[\s_]+/g, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, ''); }; export const detectNumberingScheme = (existingNames) => { if (existingNames.length === 0) { return undefined; } const prefixes = existingNames.map((name) => name.split(/[_-]/)[0]); const allTimestamp = prefixes.every((p) => /^\d{14}$/.test(p)); if (allTimestamp) { return 'timestamp'; } const allSequential = prefixes.every((p) => /^\d{4}$/.test(p)); if (allSequential) { return 'sequential'; } return undefined; }; export const generateNextPrefix = (existingNames, scheme) => { if (scheme === 'timestamp') { const now = new Date(); const pad = (n, width = 2) => String(n).padStart(width, '0'); return [ now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()), pad(now.getHours()), pad(now.getMinutes()), pad(now.getSeconds()), ].join(''); } const prefixes = existingNames.map((name) => { const match = /^(\d{4})[_-]/.exec(name); return match ? parseInt(match[1], 10) : 0; }); const maxPrefix = prefixes.length > 0 ? Math.max(...prefixes) : 0; return String(maxPrefix + 1).padStart(4, '0'); }; const getExistingMigrationNames = async (migrationsDirectory) => { try { const entries = await readdir(migrationsDirectory, { withFileTypes: true }); return entries .filter((entry) => entry.isDirectory()) .map((entry) => entry.name) .sort(); } catch (error) { if (error.code === 'ENOENT') { return []; } throw error; } }; const DEFAULT_MIGRATIONS_PATH = 'netlify/db/migrations'; export const resolveMigrationsDirectory = (command) => { const configuredPath = command.netlify.config.db?.migrations?.path; if (configuredPath) { return configuredPath; } const projectRoot = command.netlify.site.root ?? command.project.root ?? command.project.baseDirectory; if (!projectRoot) { throw new Error('Could not determine the project root directory.'); } return join(projectRoot, DEFAULT_MIGRATIONS_PATH); }; export const migrationNew = async (options, command) => { const { json } = options; const migrationsDirectory = resolveMigrationsDirectory(command); const existingMigrations = await getExistingMigrationNames(migrationsDirectory); const detectedScheme = detectNumberingScheme(existingMigrations); let description = options.description; let scheme = options.scheme; if (!description) { const answers = await inquirer.prompt([ { type: 'input', name: 'description', message: 'What is the purpose of this migration?', validate: (input) => (input.trim().length > 0 ? true : 'Description cannot be empty'), }, ]); description = answers.description; } if (!scheme) { const answers = await inquirer.prompt([ { type: 'list', name: 'scheme', message: 'Numbering scheme:', choices: [ { name: 'Sequential (0001, 0002, ...)', value: 'sequential' }, { name: 'Timestamp (20260312143000)', value: 'timestamp' }, ], ...(detectedScheme && { default: detectedScheme }), }, ]); scheme = answers.scheme; } const slug = generateSlug(description); if (!slug) { throw new Error(`Description "${description}" produces an empty slug. Use a description with alphanumeric characters (e.g. "add users table").`); } const prefix = generateNextPrefix(existingMigrations, scheme); const folderName = `${prefix}_${slug}`; const folderPath = join(migrationsDirectory, folderName); const migrationFilePath = join(folderPath, 'migration.sql'); await mkdir(folderPath, { recursive: true }); await writeFile(migrationFilePath, `-- Write your migration SQL here -- -- Example: -- CREATE TABLE IF NOT EXISTS users ( -- id SERIAL PRIMARY KEY, -- name TEXT NOT NULL, -- created_at TIMESTAMP DEFAULT NOW() -- ); `, { flag: 'wx' }); if (json) { logJson({ path: folderPath, name: folderName }); } else { log(`Created migration: ${folderName}`); log(` ${join(folderPath, 'migration.sql')}`); } }; //# sourceMappingURL=migration-new.js.map