213 lines
5.4 KiB
JavaScript
213 lines
5.4 KiB
JavaScript
|
const path = require('path'),
|
||
|
_ = require('lodash'),
|
||
|
fs = require('fs'),
|
||
|
compareVer = require('compare-ver'),
|
||
|
resolve = require('util').promisify(require('resolve')),
|
||
|
debug = require('debug')('knex-migrator:utils'),
|
||
|
errors = require('./errors');
|
||
|
|
||
|
/**
|
||
|
* @description This helper function offers two ways of loading the knex-migrator configuration.
|
||
|
*
|
||
|
* 1. via JS object
|
||
|
* 2. via file location
|
||
|
*
|
||
|
* The expected format is:
|
||
|
*
|
||
|
* {
|
||
|
* database: Object,
|
||
|
* migrationPath: String,
|
||
|
* currentVersion: String
|
||
|
* }
|
||
|
*
|
||
|
* @param {Object} options
|
||
|
* @returns {*}
|
||
|
*/
|
||
|
module.exports.loadConfig = function loadConfig(options) {
|
||
|
if (options.knexMigratorConfig) {
|
||
|
return options.knexMigratorConfig;
|
||
|
}
|
||
|
|
||
|
const knexMigratorFilePath = options.knexMigratorFilePath || process.cwd();
|
||
|
|
||
|
try {
|
||
|
return require(path.join(path.resolve(knexMigratorFilePath), '/MigratorConfig.js'));
|
||
|
} catch (err) {
|
||
|
if (err.code === 'MODULE_NOT_FOUND') {
|
||
|
throw new errors.KnexMigrateError({
|
||
|
message: 'Please provide a file named MigratorConfig.js in your project root.',
|
||
|
help: 'Read through the README.md to see which values are expected.'
|
||
|
});
|
||
|
}
|
||
|
|
||
|
throw new errors.KnexMigrateError({err: err});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @description List all migration files from disk based on a path.
|
||
|
*
|
||
|
* @param absolutePath
|
||
|
* @returns {Array}
|
||
|
*/
|
||
|
exports.listFiles = function listFiles(absolutePath) {
|
||
|
let files = [];
|
||
|
|
||
|
try {
|
||
|
files = fs.readdirSync(absolutePath);
|
||
|
} catch (err) {
|
||
|
throw new errors.KnexMigrateError({
|
||
|
code: 'MIGRATION_PATH',
|
||
|
message: 'MigrationPath is wrong: ' + absolutePath
|
||
|
});
|
||
|
}
|
||
|
|
||
|
files = files.filter(function (file) {
|
||
|
// CASE: ignore dot files
|
||
|
return !file.match(/^\./);
|
||
|
});
|
||
|
|
||
|
debug(files);
|
||
|
return files;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @description Reads all migration files from disk based on a path.
|
||
|
* It returns an Array of migration files including it's up/down hooks, config and the name.
|
||
|
*
|
||
|
* @param absolutePath
|
||
|
* @returns {Array}
|
||
|
*/
|
||
|
exports.readTasks = function readTasks(absolutePath) {
|
||
|
let tasks = [];
|
||
|
|
||
|
const files = exports.listFiles(absolutePath);
|
||
|
|
||
|
_.each(files, function (file) {
|
||
|
let executeFn = require(path.join(absolutePath, file));
|
||
|
|
||
|
try {
|
||
|
tasks.push({
|
||
|
up: executeFn.up,
|
||
|
down: executeFn.down,
|
||
|
config: executeFn.config,
|
||
|
name: file
|
||
|
});
|
||
|
} catch (err) {
|
||
|
debug(err.message);
|
||
|
|
||
|
throw new errors.MigrationScript({
|
||
|
message: err.message,
|
||
|
help: 'Cannot load Migrationscript.',
|
||
|
context: file
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
debug(tasks);
|
||
|
return tasks;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @description Reads all version folders from disk in correct order.
|
||
|
*
|
||
|
* @param absolutePath
|
||
|
* @returns {*}
|
||
|
*/
|
||
|
exports.readVersionFolders = function readFolders(absolutePath) {
|
||
|
let folders = [],
|
||
|
toReturn = [];
|
||
|
|
||
|
try {
|
||
|
folders = fs.readdirSync(absolutePath);
|
||
|
} catch (err) {
|
||
|
throw new errors.KnexMigrateError({
|
||
|
message: 'MigrationPath is wrong: ' + absolutePath,
|
||
|
code: 'READ_FOLDERS'
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (!folders.length) {
|
||
|
return folders;
|
||
|
}
|
||
|
|
||
|
folders.forEach((folderToAdd) => {
|
||
|
let index = null;
|
||
|
|
||
|
// CASE: ignore dot files
|
||
|
if (folderToAdd.match(/^\./)) {
|
||
|
debug('Ignore Dotfile: ' + folderToAdd);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
toReturn.forEach((existingElement, _index) => {
|
||
|
if (index !== null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// CASE: folder to add is smaller, push before this element
|
||
|
if (compareVer.gt(folderToAdd, existingElement) === -1) {
|
||
|
index = _index;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
if (index === null) {
|
||
|
if (!toReturn.length) {
|
||
|
index = 0;
|
||
|
} else {
|
||
|
index = toReturn.length;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
toReturn.splice(index, 0, folderToAdd);
|
||
|
});
|
||
|
|
||
|
debug(toReturn);
|
||
|
return toReturn;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @description Auto detect local installation to avoid version incompatible behaviour
|
||
|
*/
|
||
|
exports.getKnexMigrator = function getKnexMigrator(options) {
|
||
|
options = options || {};
|
||
|
|
||
|
return resolve('knex-migrator', {basedir: options.path})
|
||
|
.then(function (localCLIPath) {
|
||
|
return require(localCLIPath);
|
||
|
})
|
||
|
.catch(function () {
|
||
|
return require('./');
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @description A helper function to figure out if a version is greater than another version.
|
||
|
*
|
||
|
* Valid versions are:
|
||
|
* - 1
|
||
|
* - 1.1
|
||
|
* - 1.1.0
|
||
|
*
|
||
|
* It's up to you which pattern you would like to use.
|
||
|
*
|
||
|
* @param options
|
||
|
* @returns {boolean}
|
||
|
*/
|
||
|
exports.isGreaterThanVersion = function isGreaterThanVersion(options) {
|
||
|
let greaterVersion = options.greaterVersion;
|
||
|
let smallerVersion = options.smallerVersion;
|
||
|
|
||
|
// CASE: are they semver like strings?
|
||
|
if (new RegExp(/\./g).test(greaterVersion) && new RegExp(/\./g).test(smallerVersion)) {
|
||
|
// -1 less than, 0 equal, 1 greater than
|
||
|
return compareVer.gt(greaterVersion, smallerVersion) === 1;
|
||
|
}
|
||
|
|
||
|
// CASE: must be numbers / number like strings
|
||
|
greaterVersion = Number(greaterVersion.toString());
|
||
|
smallerVersion = Number(smallerVersion.toString());
|
||
|
|
||
|
return greaterVersion > smallerVersion;
|
||
|
};
|