bin/sequelize-auto
#!/usr/bin/env node
const SequelizeAuto = require('../');
const path = require('path');
const readline = require('readline');
const _ = require('lodash');
const argv = require('yargs')
.parserConfiguration({
"parse-numbers": false // disabled because of password field, other option can still be explicitly defined as number type
})
.usage(
'Usage: sequelize-auto -h <host> -d <database> -u <user> -x [password] -p [port] --dialect [dialect] -c [/path/to/config] -o [/path/to/models] -t [tableName]'
)
.option('host', {
description: 'IP/Hostname for the database.',
type: 'string',
alias: 'h'
})
.option('database', {
description: 'Database name.',
type: 'string',
alias: 'd'
})
.option('user', {
description: 'Username for database.',
type: 'string',
alias: 'u'
})
.option('pass', {
description: 'Password for database. If specified without providing a password, it will be requested interactively from the terminal.',
alias: 'x'
})
.option('port', {
description: 'Port number for database (not for sqlite). Ex: MySQL/MariaDB: 3306, Postgres: 5432, MSSQL: 1433',
type: 'number',
alias: 'p'
})
.option('config', {
description: 'Path to JSON file for Sequelize-Auto options and Sequelize\'s constructor "options" flag object as defined here: https://sequelize.org/api/v6/class/src/sequelize.js~sequelize#instance-constructor-constructor',
type: 'string',
alias: 'c'
})
.option('output', {
description: 'What directory to place the models.',
type: 'string',
alias: 'o'
})
.option('dialect', {
description: "The dialect/engine that you're using: postgres, mysql, sqlite, mssql",
type: 'string',
alias: 'e'
})
.option('additional', {
description: "Path to JSON file containing model options (for all tables). See the options: https://sequelize.org/api/v6/class/src/model.js~model#static-method-init",
type: 'string',
alias: 'a'
})
.option('indentation', {
description: 'Number of spaces to indent',
type: 'number'
})
.option('tables', {
description: 'Space-separated names of tables to import',
array: true,
type: 'string',
alias: 't'
})
.option('skipTables', {
description: 'Space-separated names of tables to skip',
array: true,
type: 'string',
alias: 'T'
})
.option('skipFields', {
description: 'Space-separated names of fields to skip',
array: true,
type: 'string',
alias: 'F'
})
.option('pkSuffixes', {
description: 'Space-separated names of primary key suffixes to trim (default is "id")',
array: true,
type: 'string'
})
.option('caseModel', {
description: 'Set case of model names: c|l|o|p|u \n c = camelCase \n l = lower_case \n o = original (default) \n p = PascalCase \n u = UPPER_CASE',
alias: 'cm'
})
.option('caseProp', {
description: 'Set case of property names: c|l|o|p|u',
alias: 'cp'
})
.option('caseFile', {
description: 'Set case of file names: c|l|o|p|u|k \n k = kebab-case',
alias: 'cf'
})
.option('noAlias', {
description: 'Avoid creating alias `as` property in relations',
type: 'boolean'
})
.option('noIndexes', {
description: 'Prevent writing index information in the models',
type: 'boolean'
})
.option('noInitModels', {
description: 'Prevent writing the init-models file',
type: 'boolean'
})
.option('noWrite', {
description: 'Prevent writing the models to disk',
type: 'boolean',
alias: 'n'
})
.option('schema', {
description: 'Database schema from which to retrieve tables',
type: 'string',
alias: 's'
})
.option('views', {
description: 'Include database views in generated models',
type: 'boolean',
alias: 'v'
})
.option('lang', {
description: 'Language for Model output: es5|es6|esm|ts \n es5 = ES5 CJS modules (default) \n es6 = ES6 CJS modules \n esm = ES6 ESM modules \n ts = TypeScript ',
type: 'string',
alias: 'l'
})
.option('useDefine', {
description: 'Use `sequelize.define` instead of `init` for es6|esm|ts',
type: 'boolean'
})
.option('singularize', {
description: 'Singularize model and file names from plural table names',
type: 'boolean',
alias: 'sg'
})
.check(argv => Boolean((argv.database && (argv.host || argv.dialect === 'sqlite')) || argv.config))
.argv;
function getDefaultPort(dialect) {
switch (dialect.toLowerCase()) {
case 'mssql':
return 1433;
case 'postgres':
return 5432;
default:
return 3306;
}
}
async function readPassword() {
let rl = readline.createInterface({
input: process.stdin,
terminal: true
});
process.stdout.write('Password: ');
let pwd = await new Promise(resolve => rl.question('', pwd => resolve(pwd)));
rl.close();
process.stdout.write('\n');
return pwd;
}
/* eslint-disable complexity, max-statements */
(async function() {
let password;
if (typeof argv.pass === 'boolean' && argv.pass) {
password = await readPassword();
} else if (typeof argv.pass === 'string') {
console.warn('Warning: using a password on the command line interface can be insecure.');
password = argv.pass;
}
const dir = !argv.noWrite && (argv.output || path.resolve(process.cwd() + '/models'));
/** @type {import('../types').AutoOptions} */
let configFile = {
spaces: true,
indentation: 2
};
if (argv.config) {
configFile = require(path.resolve(argv.config));
}
configFile.directory = configFile.directory || dir;
let additional = {};
if (argv.additional) {
additional = require(path.resolve(argv.additional));
} else if (configFile.additional) {
additional = configFile.additional;
}
configFile.additional = additional;
configFile.dialect = argv.dialect || configFile.dialect || 'mysql';
configFile.port = argv.port || configFile.port || getDefaultPort(configFile.dialect);
configFile.host = argv.host || configFile.host || 'localhost';
configFile.database = argv.database || configFile.database;
configFile.storage = configFile.storage || configFile.database;
configFile.tables = argv.tables || configFile.tables || null;
configFile.skipTables = argv.skipTables || configFile.skipTables || null;
configFile.skipFields = argv.skipFields || configFile.skipFields || null;
configFile.pkSuffixes = argv.pkSuffixes || configFile.pkSuffixes || null;
configFile.schema = argv.schema || configFile.schema;
configFile.lang = argv.lang || configFile.lang || 'es5';
configFile.caseModel = argv.caseModel || configFile.caseModel || 'o';
configFile.caseFile = argv.caseFile || configFile.caseFile || 'o';
configFile.caseProp = argv.caseProp || configFile.caseProp || 'o';
configFile.noAlias = argv.noAlias || configFile.noAlias || false;
configFile.noInitModels = argv.noInitModels || configFile.noInitModels || false;
configFile.noWrite = argv.noWrite || configFile.noWrite || false;
configFile.views = argv.views || configFile.views || false;
configFile.singularize = argv.singularize || configFile.singularize || false;
configFile.password = password || configFile.password || null;
configFile.username = argv.user || configFile.username;
configFile.useDefine = argv.useDefine || configFile.useDefine || false;
configFile.indentation = argv.indentation || configFile.indentation || 2;
configFile.noIndexes = argv.noIndexes || configFile.noIndexes || false;
console.log(_.omit(configFile, 'password'));
/** @type {import('../types').SequelizeAuto} */
const auto = new SequelizeAuto(configFile.database, configFile.username, configFile.password, configFile);
await auto.run();
console.log("Done!");
}()).catch(err => {
if (err.stack) {
console.error(err.stack);
} else if (err.message) {
console.error(err.message);
} else {
console.error(err);
}
process.exitCode = 1;
});