diff --git a/Readme.md b/Readme.md index 3d66f9b..f6a2baf 100644 --- a/Readme.md +++ b/Readme.md @@ -230,6 +230,7 @@ Calls the callback with a `Set` based on the options passed. Options: - `set`: A set instance if you created your own - `stateStore`: A store instance to load and store migration state, or a string which is a path to the migration state file +- `migrations`: An object where keys are migration names and values are migration objects - `migrationsDirectory`: The path to the migrations directory - `filterFunction`: A filter function which will be called for each file found in the migrations directory - `sortFunction`: A sort function to ensure migration order diff --git a/index.d.ts b/index.d.ts index c9db032..7941e2b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,6 +3,7 @@ import type { EventEmitter } from "events"; type MigrationOptions = { set?: MigrationSet; stateStore?: string | FileStore; + migrations?: { [key: string]: { up: Function, down: Function } }; migrationsDirectory?: string; ignoreMissing?: boolean; filterFunction?: (migration: string) => boolean; diff --git a/index.js b/index.js index 793c765..0eb186b 100644 --- a/index.js +++ b/index.js @@ -53,6 +53,7 @@ exports.load = function (options, fn) { loadMigrationsIntoSet({ set, store, + migrations: opts.migrations, migrationsDirectory: opts.migrationsDirectory, filterFunction: opts.filterFunction, sortFunction: opts.sortFunction, diff --git a/lib/load-migrations.js b/lib/load-migrations.js index 004d1f3..6d3b463 100644 --- a/lib/load-migrations.js +++ b/lib/load-migrations.js @@ -7,6 +7,47 @@ const Migration = require('./migration') module.exports = loadMigrationsIntoSet +async function loadFromMigrationsDirectory (migrationsDirectory, filterFn) { + // Read migrations directory + let files = await fs.readdir(migrationsDirectory) + + // Filter out non-matching files + files = files.filter(filterFn) + + const migMap = {} + const promises = files.map(async function (file) { + // Try to load the migrations file + const filepath = path.join(migrationsDirectory, file) + let mod + try { + mod = require(filepath) + } catch (e) { + if (e.code === 'ERR_REQUIRE_ESM') { + mod = await import(url.pathToFileURL(filepath)) + } else { + throw e + } + } + + const migration = new Migration(file, mod.up, mod.down, mod.description) + migMap[file] = migration + return migration + }) + await Promise.all(promises) + return migMap +} + +function loadFromMigrations (migrations, filterFn) { + return Object + .keys(migrations) + .filter(filterFn) + .reduce((migMap, migrationName) => { + const mod = migrations[migrationName] + migMap[migrationName] = new Migration(migrationName, mod.up, mod.down, mod.description) + return migMap + }, {}) +} + function loadMigrationsIntoSet (options, fn) { // Process options, set and store are required, rest optional const opts = options || {} @@ -16,6 +57,7 @@ function loadMigrationsIntoSet (options, fn) { const set = opts.set const store = opts.store const ignoreMissing = !!opts.ignoreMissing + const migrations = opts.migrations const migrationsDirectory = path.resolve(opts.migrationsDirectory || 'migrations') const filterFn = opts.filterFunction || (() => true) const sortFn = opts.sortFunction || function (m1, m2) { @@ -30,33 +72,10 @@ function loadMigrationsIntoSet (options, fn) { // Set last run date on the set set.lastRun = state.lastRun || null - // Read migrations directory - let files = await fs.readdir(migrationsDirectory) - - // Filter out non-matching files - files = files.filter(filterFn) - // Create migrations, keep a lookup map for the next step - const migMap = {} - const promises = files.map(async function (file) { - // Try to load the migrations file - const filepath = path.join(migrationsDirectory, file) - let mod - try { - mod = require(filepath) - } catch (e) { - if (e.code === 'ERR_REQUIRE_ESM') { - mod = await import(url.pathToFileURL(filepath)) - } else { - return fn(e) - } - } - - const migration = new Migration(file, mod.up, mod.down, mod.description) - migMap[file] = migration - return migration - }) - let migrations = await Promise.all(promises) + const migMap = (migrations) + ? loadFromMigrations(migrations, filterFn) + : await loadFromMigrationsDirectory(migrationsDirectory, filterFn) // Fill in timestamp from state, or error if missing state.migrations && state.migrations.forEach(function (m) { @@ -69,11 +88,10 @@ function loadMigrationsIntoSet (options, fn) { migMap[m.title].timestamp = m.timestamp }) - // Sort the migrations by their title - migrations = migrations.sort(sortFn) - - // Add the migrations to the set - migrations.forEach(set.addMigration.bind(set)) + Object + .values(migMap) + .sort(sortFn) + .forEach(set.addMigration.bind(set)) // Successfully loaded fn()