sequelize-automate
Version:
Automatically generate bare sequelize models from your database.
158 lines (136 loc) • 5.27 kB
JavaScript
/* eslint-disable max-len */
const assert = require('assert');
const _ = require('lodash');
const Sequelize = require('sequelize');
const debug = require('debug')('sequelize-automate');
const { getModelDefinitions } = require('./util/definition');
const generate = require('./generate');
const { write } = require('./util/write');
class Automate {
constructor(dbOptions, options) {
debug('sequelize-automate constructor');
const defaultOptions = {
type: 'js', // Which code style want to generate, supported: js/ts/egg/midway. Default is `js`.
camelCase: false, // Model name camel case. Default is false.
fileNameCamelCase: false, // Model file name camel case. Default is false.
dir: 'models', // What directory to place the models. Default is `models`.
typesDir: null, // What directory to place the models' definitions (for typescript), default is the same with dir.
emptyDir: false, // Remove all files in `dir` and `typesDir` directories before generate models.
tables: null, // Use these tables, Example: ['user'], default is null.
skipTables: null, // Skip these tables. Example: ['user'], default is null.
tsNoCheck: false, // Whether add `@ts-nocheck` to model files, default is false.
};
// https://sequelize.org/master/class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor
this.dbOptions = dbOptions || {};
this.options = _.assign({}, defaultOptions, options);
// default `options.typesDir` is the same with `options.dir`
this.options.typesDir = this.options.typesDir || this.options.dir;
const supportTypes = ['js', 'ts', 'egg', 'midway', '@ali/midway'];
assert(supportTypes.includes(this.options.type), 'type not support');
assert(_.isBoolean(this.options.camelCase), 'Invalid params camelCase');
assert(_.isBoolean(this.options.fileNameCamelCase), 'Invalid params fileNameCamelCase');
assert(_.isString(this.options.dir), 'Invalid params dir');
assert(_.isString(this.options.typesDir), 'Invalid params typesDir');
assert(_.isBoolean(this.options.emptyDir), 'Invalid params cleanDir');
assert(_.isNull(this.options.tables) || _.isArray(this.options.tables), 'Invalid params table');
assert(_.isNull(this.options.skipTables) || _.isArray(this.options.skipTables), 'invalid params table');
assert(_.isBoolean(this.options.tsNoCheck), 'Invalid params tsNoCheck');
this.sequelize = new Sequelize(this.dbOptions);
this.queryInterface = this.sequelize.getQueryInterface();
}
async getTableNames({ tables, skipTables }) {
// TODO: check all dialects https://github.com/sequelize/sequelize/issues/11451
const tableNames = await this.queryInterface.showAllTables();
const allTables = _.map(tableNames, (tableName) => (
_.isPlainObject(tableName) ? tableName.tableName : tableName
));
if (_.isArray(tables)) {
// Fix: https://github.com/nodejh/sequelize-automate/issues/19
// tables.map((table) => assert(allTables.includes(table), `Table: ${table} not exist.`));
return tables;
}
if (_.isArray(skipTables)) {
skipTables.map((table) => assert(allTables.includes(table), `Table: ${table} not exist.`));
return _.difference(allTables, skipTables);
}
return allTables;
}
/**
* Get all tables
*/
async getTables() {
const { options } = this;
const tableNames = await this.getTableNames({
tables: options.tables,
skipTables: options.skipTables,
});
debug('tableNames: ', tableNames);
const tableStructures = await Promise.all(tableNames.map(
(tableName) => this.queryInterface.describeTable(tableName),
));
const tableIndexes = await Promise.all(tableNames.map(
(tableName) => this.queryInterface.showIndex(tableName),
));
const tableForeignKeys = await Promise.all(tableNames.map(
(tableName) => this.queryInterface.getForeignKeyReferencesForTable(tableName),
));
const tables = {};
tableNames.forEach((tableName, i) => {
tables[tableName] = {
structures: tableStructures[i],
indexes: tableIndexes[i],
foreignKeys: tableForeignKeys[i],
};
});
this.sequelize.close();
debug('sequelize close');
return tables;
}
async getDefinitions() {
const {
tables,
skipTables,
camelCase,
fileNameCamelCase,
} = this.options;
const allTables = await this.getTables({
tables,
skipTables,
});
const definitions = getModelDefinitions(allTables, {
camelCase,
fileNameCamelCase,
dialect: this.dbOptions.dialect,
});
debug('get model definitions');
return definitions;
}
async run() {
const {
type,
tables,
skipTables,
camelCase,
fileNameCamelCase,
tsNoCheck,
dir,
typesDir,
emptyDir,
} = this.options;
const definitions = await this.getDefinitions({
tables,
skipTables,
camelCase,
fileNameCamelCase,
});
const codes = generate(definitions, {
type,
tsNoCheck,
});
if (dir) {
await write(codes, { dir, typesDir, emptyDir });
}
return codes;
}
}
module.exports = Automate;