pg-flyway
Version:
Migration tool for PostgreSQL database, NodeJS version of Java migration tool - flyway (not wrapper for https://flywaydb.org/documentation/commandline)
156 lines (155 loc) • 7.98 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Migration = exports.CALLBACK_KEYS = void 0;
const tslib_1 = require("tslib");
const crc_32_1 = tslib_1.__importDefault(require("crc-32"));
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
exports.CALLBACK_KEYS = [
'beforeMigrate', // Before Migrate runs
'beforeRepeatables', // Before all repeatable migrations during Migrate
'beforeEachMigrate', // Before every single migration during Migrate
'beforeEachMigrateStatement', // Before every single statement of a migration during Migrate
'afterEachMigrateStatement', // After every single successful statement of a migration during Migrate
'afterEachMigrateStatementError', // After every single failed statement of a migration during Migrate
'afterEachMigrate', // After every single successful migration during Migrate
'afterEachMigrateError', // After every single failed migration during Migrate
'afterMigrate', // After successful Migrate runs
'afterMigrateApplied', // After successful Migrate runs where at least one migration has been applied
'afterVersioned', // After all versioned migrations during Migrate
'afterMigrateError', // After failed Migrate runs
];
class Migration {
static fromStatements({ statements }) {
const m = new Migration();
m.statements = statements;
return m;
}
constructor(filepath, sqlMigrationSeparator, sqlMigrationStatementSeparator, sqlMigrationSuffix, location) {
this.filepath = filepath;
this.sqlMigrationSeparator = sqlMigrationSeparator;
this.sqlMigrationStatementSeparator = sqlMigrationStatementSeparator;
this.sqlMigrationSuffix = sqlMigrationSuffix;
this.location = location;
/**
* https://documentation.red-gate.com/fd/migrations-184127470.html
*
* Versioned migrations have a version, a description and a checksum.
* The version must be unique. The description is purely informative for you to be able to remember what each migration does.
* The checksum is there to detect accidental changes.
* Versioned migrations are the most common type of migration. They are applied in order exactly once.
*/
this.versioned = false;
this.undo = false;
/**
* https://documentation.red-gate.com/fd/migrations-184127470.html
*
* Repeatable migrations have a description and a checksum, but no version.
* Instead of being run just once, they are (re-)applied every time their checksum changes.
* Within a single migration run, repeatable migrations are always applied last, after all pending versioned migrations have been executed.
* Repeatable migrations are applied in the alphanumeric order of their description.
*/
this.repeatable = false;
/**
* https://documentation.red-gate.com/fd/callback-concept-184127466.html
*
* While migrations are sufficient for most needs, there are certain situations that require you to execute the same action over and over again.
* This could be recompiling procedures, updating materialized views and many other types of housekeeping.
* For this reason, Flyway offers you the possibility to hook into its lifecycle by using Callbacks.
* In order to use these callbacks, name a script after the callback name (ie. afterMigrate.sql) and
* drop it alongside your other scripts in your migrations folder.
* Flyway will then invoke it when the execution event criteria listed below is met.
*/
this.callback = {};
this.statements = [];
this.statementLines = [];
if (this.filepath) {
this.filedir = (0, node_path_1.dirname)(this.filepath);
this.filename = (0, node_path_1.basename)(this.filepath);
if (this.filename.startsWith('V')) {
this.versioned = true;
if (this.sqlMigrationSeparator) {
const parts = this.filename.split(this.sqlMigrationSeparator);
this.versionedVersion = +parts[0].trim().substring(1);
if (this.sqlMigrationSuffix && parts[1]) {
this.name = parts[1].split(this.sqlMigrationSuffix)[0];
}
}
}
if (this.filename.startsWith('U')) {
this.undo = true;
if (this.sqlMigrationSeparator) {
const parts = this.filename.split(this.sqlMigrationSeparator);
this.undoVersion = +parts[0].trim().substring(1);
if (this.sqlMigrationSuffix && parts[1]) {
this.name = parts[1].split(this.sqlMigrationSuffix)[0];
}
}
}
if (this.filename.startsWith('R')) {
this.repeatable = true;
if (this.sqlMigrationSeparator) {
const parts = this.filename.split(this.sqlMigrationSeparator);
if (this.sqlMigrationSuffix && parts[1]) {
this.name = parts[1].split(this.sqlMigrationSuffix)[0];
}
}
}
const callback = exports.CALLBACK_KEYS.find((key) => this.filename?.startsWith(key));
if (callback) {
this.callback = { [callback]: true };
if (this.sqlMigrationSeparator) {
const parts = this.filename.split(this.sqlMigrationSeparator);
if (this.sqlMigrationSuffix && (parts[1] || parts[0])) {
this.name = (parts[1] || parts[0]).split(this.sqlMigrationSuffix)[0];
}
}
}
this.version = this.versionedVersion || this.undoVersion;
const location = this.location?.startsWith('./') ? this.location.substring(2) : this.location;
if (location) {
this.script = (0, node_path_1.resolve)(this.filepath).replace((0, node_path_1.resolve)(location) + node_path_1.sep, '');
if (!(0, node_fs_1.existsSync)(this.script)) {
this.script = this.filepath.replace(location + node_path_1.sep, '');
}
}
else {
this.script = this.filepath.replace(location + node_path_1.sep, '');
}
}
}
async fill(fileContent) {
const fileContentArray = fileContent.split('\n');
this.statements = [];
let statement = [];
for (let index = 0; index < fileContentArray.length; index++) {
const line = index + 1;
if (fileContentArray[index] !== this.sqlMigrationStatementSeparator &&
index != fileContentArray.length - 1) {
statement.push(fileContentArray[index]);
}
else {
if (fileContentArray[index] !== this.sqlMigrationStatementSeparator) {
statement.push(fileContentArray[index]);
}
this.statements = [...this.statements, statement.join('\n')];
this.statementLines = [...this.statementLines, line];
statement = [];
}
}
this.filechecksum = crc_32_1.default.bstr(this.stripBom(fileContent.split('\n').join('').toString()));
return this;
}
stripBom(string) {
if (typeof string !== 'string') {
throw new TypeError(`Expected a string, got ${typeof string}`);
}
// Catches EFBBBF (UTF-8 BOM) because the buffer-to-string
// conversion translates it to FEFF (UTF-16 BOM).
if (string.charCodeAt(0) === 0xfeff) {
return string.slice(1);
}
return string;
}
}
exports.Migration = Migration;