UNPKG

@vendure/core

Version:

A modern, headless ecommerce framework

220 lines (216 loc) 8.52 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runMigrations = runMigrations; exports.revertLastMigration = revertLastMigration; exports.generateMigration = generateMigration; /* eslint-disable no-console */ const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const picocolors_1 = __importDefault(require("picocolors")); const typeorm_1 = require("typeorm"); const MysqlDriver_1 = require("typeorm/driver/mysql/MysqlDriver"); const StringUtils_1 = require("typeorm/util/StringUtils"); const bootstrap_1 = require("./bootstrap"); const config_helpers_1 = require("./config/config-helpers"); /** * @description * Runs any pending database migrations. See [TypeORM migration docs](https://typeorm.io/#/migrations) * for more information about the underlying migration mechanism. * * @docsCategory migration */ async function runMigrations(userConfig) { const config = await (0, bootstrap_1.preBootstrapConfig)(userConfig); const connection = await (0, typeorm_1.createConnection)(createConnectionOptions(config)); const migrationsRan = []; try { const migrations = await disableForeignKeysForSqLite(connection, () => connection.runMigrations({ transaction: 'each' })); for (const migration of migrations) { log(picocolors_1.default.green(`Successfully ran migration: ${migration.name}`)); migrationsRan.push(migration.name); } } catch (e) { log(picocolors_1.default.red('An error occurred when running migrations:')); log(e.message); if (isRunningFromVendureCli()) { throw e; } else { process.exitCode = 1; } } finally { await checkMigrationStatus(connection); await connection.close(); (0, config_helpers_1.resetConfig)(); } return migrationsRan; } async function checkMigrationStatus(connection) { const builderLog = await connection.driver.createSchemaBuilder().log(); if (builderLog.upQueries.length) { log(picocolors_1.default.yellow('Your database schema does not match your current configuration. Generate a new migration for the following changes:')); for (const query of builderLog.upQueries) { log(' - ' + picocolors_1.default.yellow(query.query)); } } } /** * @description * Reverts the last applied database migration. See [TypeORM migration docs](https://typeorm.io/#/migrations) * for more information about the underlying migration mechanism. * * @docsCategory migration */ async function revertLastMigration(userConfig) { const config = await (0, bootstrap_1.preBootstrapConfig)(userConfig); const connection = await (0, typeorm_1.createConnection)(createConnectionOptions(config)); try { await disableForeignKeysForSqLite(connection, () => connection.undoLastMigration({ transaction: 'each' })); } catch (e) { log(picocolors_1.default.red('An error occurred when reverting migration:')); log(e.message); if (isRunningFromVendureCli()) { throw e; } else { process.exitCode = 1; } } finally { await connection.close(); (0, config_helpers_1.resetConfig)(); } } /** * @description * Generates a new migration file based on any schema changes (e.g. adding or removing CustomFields). * See [TypeORM migration docs](https://typeorm.io/#/migrations) for more information about the * underlying migration mechanism. * * @docsCategory migration */ async function generateMigration(userConfig, options) { const config = await (0, bootstrap_1.preBootstrapConfig)(userConfig); const connection = await (0, typeorm_1.createConnection)(createConnectionOptions(config)); // TODO: This can hopefully be simplified if/when TypeORM exposes this CLI command directly. // See https://github.com/typeorm/typeorm/issues/4494 const sqlInMemory = await connection.driver.createSchemaBuilder().log(); const upSqls = []; const downSqls = []; let migrationName; // mysql is exceptional here because it uses ` character in to escape names in queries, that's why for mysql // we are using simple quoted string instead of template string syntax if (connection.driver instanceof MysqlDriver_1.MysqlDriver) { sqlInMemory.upQueries.forEach(upQuery => { upSqls.push(' await queryRunner.query("' + upQuery.query.replace(new RegExp('"', 'g'), '\\"') + '", ' + JSON.stringify(upQuery.parameters) + ');'); }); sqlInMemory.downQueries.forEach(downQuery => { downSqls.push(' await queryRunner.query("' + downQuery.query.replace(new RegExp('"', 'g'), '\\"') + '", ' + JSON.stringify(downQuery.parameters) + ');'); }); } else { sqlInMemory.upQueries.forEach(upQuery => { upSqls.push(' await queryRunner.query(`' + upQuery.query.replace(new RegExp('`', 'g'), '\\`') + '`, ' + JSON.stringify(upQuery.parameters) + ');'); }); sqlInMemory.downQueries.forEach(downQuery => { downSqls.push(' await queryRunner.query(`' + downQuery.query.replace(new RegExp('`', 'g'), '\\`') + '`, ' + JSON.stringify(downQuery.parameters) + ');'); }); } if (upSqls.length) { if (options.name) { const timestamp = new Date().getTime(); const filename = timestamp.toString() + '-' + options.name + '.ts'; const directory = options.outputDir; const fileContent = getTemplate(options.name, timestamp, upSqls, downSqls.reverse()); const outputPath = directory ? path_1.default.join(directory, filename) : path_1.default.join(process.cwd(), filename); await fs_extra_1.default.ensureFile(outputPath); fs_extra_1.default.writeFileSync(outputPath, fileContent); log(picocolors_1.default.green(`Migration ${picocolors_1.default.blue(outputPath)} has been generated successfully.`)); migrationName = outputPath; } } else { log(picocolors_1.default.yellow('No changes in database schema were found - cannot generate a migration.')); } await connection.close(); (0, config_helpers_1.resetConfig)(); return migrationName; } function createConnectionOptions(userConfig) { return Object.assign({ logging: ['query', 'error', 'schema'] }, userConfig.dbConnectionOptions, { subscribers: [], synchronize: false, migrationsRun: false, dropSchema: false, logger: 'advanced-console', }); } /** * There is a bug in TypeORM which causes db schema changes to fail with SQLite. This * is a work-around for the issue. * See https://github.com/typeorm/typeorm/issues/2576#issuecomment-499506647 */ async function disableForeignKeysForSqLite(connection, work) { const isSqLite = connection.options.type === 'sqlite' || connection.options.type === 'better-sqlite3'; if (isSqLite) { await connection.query('PRAGMA foreign_keys=OFF'); } const result = await work(); if (isSqLite) { await connection.query('PRAGMA foreign_keys=ON'); } return result; } /** * Gets contents of the migration file. */ function getTemplate(name, timestamp, upSqls, downSqls) { return `import {MigrationInterface, QueryRunner} from "typeorm"; export class ${(0, StringUtils_1.camelCase)(name, true)}${timestamp} implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<any> { ${upSqls.join(` `)} } public async down(queryRunner: QueryRunner): Promise<any> { ${downSqls.join(` `)} } } `; } function log(message) { // If running from within the Vendure CLI, we allow the CLI app // to handle the logging. if (isRunningFromVendureCli()) { return; } console.log(message); } function isRunningFromVendureCli() { return process.env.VENDURE_RUNNING_IN_CLI != null; } //# sourceMappingURL=migrate.js.map