UNPKG

@launchql/migrate

Version:
92 lines (91 loc) 3.77 kB
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`); };