@launchql/migrate
Version:
PostgreSQL Migration Tools
92 lines (91 loc) • 3.77 kB
JavaScript
import { readFileSync, writeFileSync, mkdirSync, rmSync } from 'fs';
import { relative } from 'path';
import { deparse, parse } from 'pgsql-parser';
import { getExtensionName } from './extensions';
import { resolve, resolveWithPlan } from './resolve';
import { transformProps } from './transform';
import { Logger } from '@launchql/server-utils';
const log = new Logger('package');
const noop = () => undefined;
export const cleanTree = (tree) => {
return transformProps(tree, {
stmt_len: noop,
stmt_location: noop,
location: noop,
});
};
export const packageModule = (packageDir, { usePlan = true, extension = true } = {}) => {
const resolveFn = usePlan ? resolveWithPlan : resolve;
const sql = resolveFn(packageDir);
if (!sql?.trim()) {
log.warn(`⚠️ No SQL generated for module at ${packageDir}. Skipping.`);
return { sql: '' };
}
const extname = getExtensionName(packageDir);
try {
const parsed = parse(sql);
const stmts = parsed.reduce((m, node) => {
if (extension) {
if (node.RawStmt.stmt.hasOwnProperty('TransactionStmt'))
return m;
if (node.RawStmt.stmt.hasOwnProperty('CreateExtensionStmt'))
return m;
}
return [...m, node];
}, []);
const topLine = extension
? `\\echo Use "CREATE EXTENSION ${extname}" to load this file. \\quit\n`
: '';
const finalSql = deparse(stmts, {});
const tree1 = stmts;
const tree2 = parse(finalSql);
const results = {
sql: `${topLine}${finalSql}`,
};
const diff = JSON.stringify(cleanTree(tree1)) !== JSON.stringify(cleanTree(tree2));
if (diff) {
results.diff = true;
results.tree1 = JSON.stringify(cleanTree(tree1), null, 2);
results.tree2 = JSON.stringify(cleanTree(tree2), null, 2);
}
return results;
}
catch (e) {
log.error(`❌ Failed to parse SQL for ${packageDir}`);
console.error(e);
throw e;
}
};
export const writePackage = async ({ version, extension = true, usePlan = true, packageDir, }) => {
const pkgPath = `${packageDir}/package.json`;
const pkg = require(pkgPath);
const extname = await getExtensionName(packageDir);
const makePath = `${packageDir}/Makefile`;
const controlPath = `${packageDir}/${extname}.control`;
const sqlFileName = `${extname}--${version}.sql`;
const Makefile = readFileSync(makePath, 'utf-8');
const control = readFileSync(controlPath, 'utf-8');
const { sql, diff, tree1, tree2 } = packageModule(packageDir, {
extension,
usePlan,
});
const outPath = extension ? `${packageDir}/sql` : `${packageDir}/out`;
rmSync(outPath, { recursive: true, force: true });
mkdirSync(outPath, { recursive: true });
if (extension) {
writeFileSync(controlPath, control.replace(/default_version = '[0-9\.]+'/, `default_version = '${version}'`));
pkg.version = version;
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
const regex = new RegExp(`${extname}--[0-9.]+.sql`);
writeFileSync(makePath, Makefile.replace(regex, sqlFileName));
}
if (diff) {
log.warn(`⚠️ SQL diff exists! Review the ${relative(packageDir, outPath)}/ folder.`);
// Uncomment if needed:
// writeFileSync(`${outPath}/orig.${sqlFileName}.tree.json`, tree1);
// writeFileSync(`${outPath}/parsed.${sqlFileName}.tree.json`, tree2);
}
const writePath = `${outPath}/${sqlFileName}`;
writeFileSync(writePath, sql);
log.success(`${relative(packageDir, writePath)} written`);
};