UNPKG

4.4 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.migrate = exports.readMigrations = void 0;
4const fs = require("fs");
5const path = require("path");
6async function readMigrations(migrationPath) {
7 const migrationsPath = migrationPath || path.join(process.cwd(), 'migrations');
8 const location = path.resolve(migrationsPath);
9 // Get the list of migration files, for example:
10 // { id: 1, name: 'initial', filename: '001-initial.sql' }
11 // { id: 2, name: 'feature', filename: '002-feature.sql' }
12 const migrationFiles = await new Promise((resolve, reject) => {
13 fs.readdir(location, (err, files) => {
14 if (err) {
15 return reject(err);
16 }
17 resolve(files
18 .map(x => x.match(/^(\d+).(.*?)\.sql$/))
19 .filter(x => x !== null)
20 .map(x => ({ id: Number(x[1]), name: x[2], filename: x[0] }))
21 .sort((a, b) => Math.sign(a.id - b.id)));
22 });
23 });
24 if (!migrationFiles.length) {
25 throw new Error(`No migration files found in '${location}'.`);
26 }
27 // Get the list of migrations, for example:
28 // { id: 1, name: 'initial', filename: '001-initial.sql', up: ..., down: ... }
29 // { id: 2, name: 'feature', filename: '002-feature.sql', up: ..., down: ... }
30 return Promise.all(migrationFiles.map(migration => new Promise((resolve, reject) => {
31 const filename = path.join(location, migration.filename);
32 fs.readFile(filename, 'utf-8', (err, data) => {
33 if (err) {
34 return reject(err);
35 }
36 const [up, down] = data.split(/^--\s+?down\b/im);
37 const migrationData = migration;
38 migrationData.up = up.replace(/^-- .*?$/gm, '').trim(); // Remove comments
39 migrationData.down = down ? down.trim() : ''; // and trim whitespaces
40 resolve(migrationData);
41 });
42 })));
43}
44exports.readMigrations = readMigrations;
45/**
46 * Migrates database schema to the latest version
47 */
48async function migrate(db, config = {}) {
49 config.force = config.force || false;
50 config.table = config.table || 'migrations';
51 const { force, table } = config;
52 const migrations = config.migrations
53 ? config.migrations
54 : await readMigrations(config.migrationsPath);
55 // Create a database table for migrations meta data if it doesn't exist
56 await db.run(`CREATE TABLE IF NOT EXISTS "${table}" (
57 id INTEGER PRIMARY KEY,
58 name TEXT NOT NULL,
59 up TEXT NOT NULL,
60 down TEXT NOT NULL
61)`);
62 // Get the list of already applied migrations
63 let dbMigrations = await db.all(`SELECT id, name, up, down FROM "${table}" ORDER BY id ASC`);
64 // Undo migrations that exist only in the database but not in files,
65 // also undo the last migration if the `force` option is enabled.
66 const lastMigration = migrations[migrations.length - 1];
67 for (const migration of dbMigrations
68 .slice()
69 .sort((a, b) => Math.sign(b.id - a.id))) {
70 if (!migrations.some(x => x.id === migration.id) ||
71 (force && migration.id === lastMigration.id)) {
72 await db.run('BEGIN');
73 try {
74 await db.exec(migration.down);
75 await db.run(`DELETE FROM "${table}" WHERE id = ?`, migration.id);
76 await db.run('COMMIT');
77 dbMigrations = dbMigrations.filter(x => x.id !== migration.id);
78 }
79 catch (err) {
80 await db.run('ROLLBACK');
81 throw err;
82 }
83 }
84 else {
85 break;
86 }
87 }
88 // Apply pending migrations
89 const lastMigrationId = dbMigrations.length
90 ? dbMigrations[dbMigrations.length - 1].id
91 : 0;
92 for (const migration of migrations) {
93 if (migration.id > lastMigrationId) {
94 await db.run('BEGIN');
95 try {
96 await db.exec(migration.up);
97 await db.run(`INSERT INTO "${table}" (id, name, up, down) VALUES (?, ?, ?, ?)`, migration.id, migration.name, migration.up, migration.down);
98 await db.run('COMMIT');
99 }
100 catch (err) {
101 await db.run('ROLLBACK');
102 throw err;
103 }
104 }
105 }
106}
107exports.migrate = migrate;
108//# sourceMappingURL=migrate.js.map
\No newline at end of file