UNPKG

8.34 kBJavaScriptView Raw
1'use strict';
2
3const path = require('path');
4const glob = require('glob');
5const fs = require('fs-extra');
6
7const JSONSchemaSequelizer = require('../lib');
8
9function fixedName(value) {
10 return value
11 .replace(/([A-Z])/g, (_, $1) => `_${$1}`)
12 .replace(/\W+/g, '_')
13 .toLowerCase();
14}
15
16module.exports = (conn, config) => {
17 return Promise.resolve()
18 .then(() => {
19 const _cwd = process.cwd();
20 const _migrations = conn.sequelize.options.migrations || {};
21 const _baseDir = _migrations.directory || conn.sequelize.options.directory;
22
23 /* istanbul ignore else */
24 if (!fs.existsSync(_baseDir)) {
25 throw new Error(`Missing ${_baseDir} directory`);
26 }
27
28 const _allowed = typeof config.options.only === 'string'
29 ? String(config.options.only).split(',')
30 : [];
31
32 /* istanbul ignore else */
33 if (Array.isArray(config.options.only)) {
34 Array.prototype.push.apply(_allowed, config.options.only);
35 }
36
37 const _models = Object.keys(conn.models)
38 .filter(x => (_allowed.length ? _allowed.indexOf(x) !== -1 : true))
39 .map(x => conn.models[x]);
40
41 const _logger = config.logger || {};
42
43 _logger.error = _logger.error || console.error.bind(console);
44 _logger.message = _logger.message || console.log.bind(console);
45
46 const schemaFile = path.join(_baseDir, 'schema.js');
47 const schemaJson = path.join(_baseDir, 'schema.json');
48 const migrationsDir = path.join(_baseDir, 'migrations');
49 const migrationsFile = path.join(migrationsDir, 'index.json');
50
51 function upgrade() {
52 const fixedRefs = {};
53
54 Object.keys(conn.$refs).forEach(ref => {
55 /* istanbul ignore else */
56 if (!_models[ref]) {
57 fixedRefs[ref] = conn.$refs[ref].$schema;
58 }
59 });
60
61 _logger.message(`write ${path.relative(_cwd, schemaJson)}`);
62
63 fs.outputJsonSync(schemaJson,
64 JSONSchemaSequelizer.bundle(_models, fixedRefs,
65 typeof config.options.apply === 'string' && config.options.apply), { spaces: 2 });
66
67 _logger.message(`${_models.length} model${_models.length === 1 ? '' : 's'} exported`);
68 }
69
70 function reset() {
71 /* istanbul ignore else */
72 if (!fs.existsSync(schemaFile)) {
73 throw new Error(`Missing ${schemaFile} file`);
74 }
75
76 const migrations = glob.sync('*.js', { cwd: migrationsDir });
77
78 if (!_migrations.database) {
79 if (config.options.create) {
80 fs.outputJsonSync(migrationsFile, migrations, { spaces: 2 });
81 } else {
82 fs.outputFileSync(migrationsFile, '[]');
83 }
84 }
85
86 return Promise.resolve()
87 .then(() => {
88 _logger.message(`read ${path.relative(_cwd, schemaFile)}`);
89 })
90 .then(() => {
91 const instance = JSONSchemaSequelizer.migrate(conn.sequelize, require(schemaFile), true);
92 const method = config.options.create ? 'up' : 'down';
93
94 if (!instance[method]) {
95 throw new Error(`Missing ${method}() method!`);
96 }
97
98 return instance[method]();
99 })
100 .then(() => {
101 _logger.message(`${config.options.create ? 'applied' : 'reverted'} ${path.relative(_cwd, schemaFile)}`);
102 });
103 }
104
105 function write() {
106 const fulldate = [
107 new Date().getFullYear(),
108 `0${new Date().getMonth() + 1}`.substr(-2),
109 `0${new Date().getDate() + 1}`.substr(-2),
110 ].join('');
111
112 const dump = fs.existsSync(schemaJson)
113 ? fs.readJsonSync(schemaJson)
114 : {};
115
116 return JSONSchemaSequelizer.generate(dump || {}, _models, false, conn.sequelize.options.define)
117 .then(results => {
118 /* istanbul ignore else */
119 if (!results.length) {
120 _logger.message('Without changes');
121 return;
122 }
123
124 results.forEach((result, key) => {
125 /* istanbul ignore else */
126 if (!result.code) {
127 return;
128 }
129
130 const hourtime = [
131 `0${new Date().getHours()}`.substr(-2),
132 `0${new Date().getMinutes()}`.substr(-2),
133 `0${new Date().getSeconds()}`.substr(-2),
134 '.',
135 `000${new Date().getMilliseconds()}`.substr(-3),
136 ].join('');
137
138 const name = typeof config.options.make === 'string'
139 ? `_${fixedName(config.options.make)}`
140 : `_${result.code.indexOf('createTable') > -1 ? 'create' : 'update'}_${fixedName(result.model.tableName).replace(/^_/, '')}`;
141
142 const file = path.join(migrationsDir, `${fulldate}${hourtime}.${key}${name}.js`);
143 const src = path.relative(_cwd, file);
144
145 _logger.message(`write ${src}`);
146 fs.outputFileSync(file, result.code);
147 });
148 });
149 }
150
151 function check() {
152 let method = 'status';
153
154 const params = {};
155
156 ['up', 'down', 'prev', 'next'].forEach(key => {
157 /* istanbul ignore else */
158 if (config.options[key]) {
159 method = key;
160
161 /* istanbul ignore else */
162 if (typeof config.options[key] === 'string') {
163 params.migrations = params.migrations || [];
164 params.migrations.push(config.options[key]);
165 }
166 }
167 });
168
169 ['from', 'to'].forEach(key => {
170 /* istanbul ignore else */
171 if (typeof config.options[key] === 'string') {
172 params[key] = config.options[key];
173 }
174 });
175
176 /* istanbul ignore else */
177 if (Array.isArray(config.migrations) && config.migrations.length) {
178 params.migrations = params.migrations || [];
179 config.migrations.forEach(migration => {
180 params.migrations.push(migration);
181 });
182 }
183
184 return Promise.all([
185 config.options.apply
186 ? JSONSchemaSequelizer.generate({}, _models, true, conn.sequelize.options.define)
187 : null,
188 JSONSchemaSequelizer.migrate(conn.sequelize, {
189 database: _migrations.database,
190 configFile: migrationsFile,
191 baseDir: migrationsDir,
192 logging(message) {
193 _logger.message(message);
194 },
195 })[method](params),
196 ])
197 .then(results => {
198 const result = results[1];
199
200 /* istanbul ignore else */
201 if (results[0]) {
202 _logger.message(`write ${path.relative(_cwd, schemaFile)}`);
203 fs.outputFileSync(schemaFile, results[0].code);
204 }
205
206 if (!Array.isArray(result)) {
207 /* istanbul ignore else */
208 if (result.executed && result.executed.length === 0) {
209 _logger.message('No executed migrations');
210 }
211
212 /* istanbul ignore else */
213 if (result.pending && result.pending.length) {
214 _logger.message('Pending migrations:');
215
216 result.pending.forEach(x => {
217 _logger.message(`- ${x}`);
218 });
219 }
220
221 /* istanbul ignore else */
222 if (result.pending && result.pending.length === 0) {
223 _logger.message('No pending migrations');
224 }
225 } else if (!result.length) {
226 _logger.message('No changes were made');
227 } else {
228 _logger.message(`${result.length} migration${
229 result.length === 1 ? '' : 's'
230 } ${
231 result.length === 1 ? 'was' : 'were'
232 } ${
233 config.options.up || config.options.next ? 'applied' : 'reverted'
234 }`);
235 }
236 });
237 }
238
239 /* istanbul ignore else */
240 if (config.options.create || config.options.destroy) {
241 return reset();
242 }
243
244 /* istanbul ignore else */
245 if (config.options.apply) {
246 return (upgrade(), check());
247 }
248
249 /* istanbul ignore else */
250 if (config.options.make) {
251 return write();
252 }
253
254 return check();
255 })
256 .then(() => conn.close())
257 .then(() => process.exit());
258};