UNPKG

@sqb/migrator

Version:

Database migrator for SQB

153 lines (152 loc) 6.64 kB
import glob from 'fast-glob'; import fs from 'fs/promises'; import path from 'path'; import { getCallingFilename } from './utils/get-calling-filename.js'; export function isSqlScriptMigrationTask(x) { return (typeof x === 'object' && (typeof x.script === 'string' || typeof x.script === 'function')); } export function isInsertDataMigrationTask(x) { return (typeof x === 'object' && typeof x.tableName === 'string' && Array.isArray(x.rows)); } export function isCustomMigrationTask(x) { return typeof x === 'object' && typeof x.fn === 'function'; } export var MigrationPackage; (function (MigrationPackage) { async function load(asyncConfig) { const baseDir = asyncConfig.baseDir || path.dirname(getCallingFilename(1)); const out = { ...asyncConfig, baseDir, migrations: [], }; if (!Array.isArray(asyncConfig.migrations)) { throw new TypeError('You must provide array of MigrationConfig in "migrations" property'); } if (asyncConfig.migrations?.length) { const srcMigrations = []; const trgMigrations = []; out.migrations = trgMigrations; let x; for (x of asyncConfig.migrations) { x = typeof x === 'function' ? await x() : x; if (typeof x === 'object' && x.tasks) srcMigrations.push(x); else if (typeof x === 'string') { srcMigrations.push(...(await loadMigrations(baseDir, x.replace(/\\/g, '/')))); } } srcMigrations.sort((a, b) => a.version - b.version); for (const migration of srcMigrations) { const trgMigration = { baseDir: '', ...migration, tasks: [], }; trgMigrations.push(trgMigration); const srcTasks = migration.tasks; trgMigration.tasks = []; for (const t of srcTasks) { if (typeof t === 'object') { trgMigration.tasks.push(t); } else if (typeof t === 'string') { let pattern = t.replace(/\\/g, '/'); pattern = path.resolve(path.join(baseDir, trgMigration.baseDir, pattern)); const files = await glob(pattern, { absolute: true, onlyFiles: true, }); files.sort(); for (const filename of files) { const ext = path.extname(filename).toLowerCase(); if (!path.basename(filename, ext).endsWith('.task')) continue; if (ext === '.sql') { const script = await fs.readFile(filename, 'utf-8'); trgMigration.tasks.push({ title: path.basename(filename, ext), filename, script, }); } else if (['.json', '.js', '.ts', '.cjs', '.mjs'].includes(ext)) { try { let json = ext === '.json' ? JSON.parse(await fs.readFile(filename, 'utf-8')) : await import(filename); if (typeof json !== 'object') continue; if (json.__esModule) json = json.default; if (json.script) { json.title = json.title || 'Run sql script'; json.filename = filename; trgMigration.tasks.push(json); continue; } if (json.tableName && json.rows) { json.title = json.title || 'Migrate data into ' + json.tableName; json.filename = filename; trgMigration.tasks.push(json); continue; } if (typeof json.fn === 'function') { json.title = json.title || 'Run custom function'; json.filename = filename; trgMigration.tasks.push(json); } } catch (e) { e.message = `Error in ${filename}\n` + e.message; throw e; } } } } } } } return out; } MigrationPackage.load = load; })(MigrationPackage || (MigrationPackage = {})); async function loadMigrations(baseDir, pattern) { const out = []; const files = await glob(path.join(baseDir, pattern), { absolute: true, onlyFiles: true, }); for (const filename of files) { const ext = path.extname(filename).toLowerCase(); if (path.basename(filename, ext) !== 'migration') continue; let json; if (['.js', '.ts', '.cjs', '.mjs', 'mts', 'cts'].includes(ext)) { json = await import(filename); if (json.default?.version) json = json.default; } else if (ext === '.json') { try { json = JSON.parse(await fs.readFile(filename, 'utf-8')); } catch (e) { e.message = `Error in ${filename}\n` + e.message; throw e; } } if (json && typeof json === 'object' && json.version && Array.isArray(json.tasks)) { json.baseDir = path.relative(baseDir, path.dirname(filename)); out.push(json); } } return out; }