Skip to content

Commit

Permalink
feat: migrations can be provided as a map of loaded modules closes #203
Browse files Browse the repository at this point in the history
  • Loading branch information
polad authored and wesleytodd committed Jan 31, 2024
1 parent 3e73112 commit 6abebb1
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 31 deletions.
1 change: 1 addition & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ exports.load = function (options, fn) {
loadMigrationsIntoSet({
set,
store,
migrations: opts.migrations,
migrationsDirectory: opts.migrationsDirectory,
filterFunction: opts.filterFunction,
sortFunction: opts.sortFunction,
Expand Down
80 changes: 49 additions & 31 deletions lib/load-migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 || {}
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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()
Expand Down

0 comments on commit 6abebb1

Please sign in to comment.