mythix 2.5.4 → 2.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/application.js +5 -5
- package/src/cli/cli-utils.d.ts +14 -0
- package/src/cli/cli-utils.js +167 -70
- package/src/cli/command-executor.js +31 -0
- package/src/cli/deploy-command.js +949 -0
- package/src/cli/generators/generate-command.js +149 -0
- package/src/cli/generators/migration-generator.js +209 -0
- package/src/cli/{migrations/migrate-command.js → migrate-command.js} +2 -2
- package/src/models/model-utils.js +1 -1
- package/src/modules/database-module.js +1 -1
- package/src/tasks/task-utils.js +2 -2
- package/src/utils/file-utils.js +10 -1
- package/src/utils/test-utils.js +1 -2
- package/src/cli/migrations/makemigrations-command.js +0 -1200
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mythix",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.6",
|
|
4
4
|
"description": "Mythix is a NodeJS web-app framework",
|
|
5
5
|
"main": "src/index",
|
|
6
6
|
"scripts": {
|
|
@@ -29,10 +29,11 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@types/events": "^3.0.0",
|
|
31
31
|
"chokidar": "^3.5.3",
|
|
32
|
-
"cmded": "^1.2.
|
|
32
|
+
"cmded": "^1.2.5",
|
|
33
33
|
"express": "^4.18.1",
|
|
34
34
|
"express-busboy": "github:th317erd/express-busboy#0754a570d7979097b31e48655b80d3fcd628d4e4",
|
|
35
35
|
"form-data": "^4.0.0",
|
|
36
|
+
"micromatch": "^4.0.5",
|
|
36
37
|
"mythix-orm": "^1.6.3",
|
|
37
38
|
"nife": "^1.12.1",
|
|
38
39
|
"prompts": "^2.4.2"
|
package/src/application.js
CHANGED
|
@@ -151,6 +151,11 @@ class Application extends EventEmitter {
|
|
|
151
151
|
opts.tempPath = Path.resolve(OS.tmpdir(), this.getApplicationName().replace(/[^\w-]/g, ''), ('' + process.pid));
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
getApplicationName() {
|
|
155
|
+
let options = this.getOptions();
|
|
156
|
+
return options.appName || this.constructor.APP_NAME;
|
|
157
|
+
}
|
|
158
|
+
|
|
154
159
|
getTempPath() {
|
|
155
160
|
let options = this.getOptions();
|
|
156
161
|
return options.tempPath;
|
|
@@ -262,11 +267,6 @@ class Application extends EventEmitter {
|
|
|
262
267
|
return this;
|
|
263
268
|
}
|
|
264
269
|
|
|
265
|
-
getApplicationName() {
|
|
266
|
-
let options = this.getOptions();
|
|
267
|
-
return options.appName;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
270
|
getRoutes() {
|
|
271
271
|
throw new Error('Error: child application expected to implement "getRoutes" method');
|
|
272
272
|
}
|
package/src/cli/cli-utils.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HelpInterface, Runner } from 'cmded';
|
|
2
|
+
import { Stats } from 'fs';
|
|
2
3
|
import { ConnectionBase } from 'mythix-orm';
|
|
3
4
|
import { Application, ApplicationClass, ApplicationOptions } from '../application';
|
|
4
5
|
import { GenericObject } from '../interfaces/common';
|
|
@@ -8,6 +9,8 @@ export declare type CommandClass = typeof CommandBase;
|
|
|
8
9
|
|
|
9
10
|
export declare type CommandClasses = { [ key: string ]: CommandClass };
|
|
10
11
|
|
|
12
|
+
export declare type FileFilterCallback = (fullFileName: string, fileName: string, stats?: Stats) => boolean;
|
|
13
|
+
|
|
11
14
|
export declare class CommandBase {
|
|
12
15
|
declare public static commands: GenericObject;
|
|
13
16
|
declare public static commandName: string;
|
|
@@ -24,6 +27,14 @@ export declare class CommandBase {
|
|
|
24
27
|
getApplication(): Application;
|
|
25
28
|
getLogger(): Logger;
|
|
26
29
|
getDBConnection(): ConnectionBase;
|
|
30
|
+
|
|
31
|
+
spawnCommand(
|
|
32
|
+
command: string,
|
|
33
|
+
args: Array<string>,
|
|
34
|
+
options?: GenericObject
|
|
35
|
+
): Promise<{ stdout: string, stderr: string, error: any, code: number }>;
|
|
36
|
+
|
|
37
|
+
getCommandFiles(filterFunc?: FileFilterCallback): Array<string>;
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
export declare interface DefineCommandContext {
|
|
@@ -61,3 +72,6 @@ export declare function executeCommand(
|
|
|
61
72
|
CommandKlass: CommandClass,
|
|
62
73
|
argv: Array<string>
|
|
63
74
|
): void;
|
|
75
|
+
|
|
76
|
+
export declare function getInternalCommandsPath(): string;
|
|
77
|
+
export declare function getCommandFiles(commandsPath: string, filterFunc?: FileFilterCallback): Array<string>;
|
package/src/cli/cli-utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/* global process, __dirname */
|
|
3
|
+
/* global process, __dirname, Buffer */
|
|
4
4
|
|
|
5
5
|
const Path = require('path');
|
|
6
6
|
const FileSystem = require('fs');
|
|
@@ -12,7 +12,10 @@ const {
|
|
|
12
12
|
fileNameWithoutExtension,
|
|
13
13
|
} = require('../utils/file-utils');
|
|
14
14
|
|
|
15
|
-
const {
|
|
15
|
+
const {
|
|
16
|
+
CMDed,
|
|
17
|
+
showHelp,
|
|
18
|
+
} = require('cmded');
|
|
16
19
|
|
|
17
20
|
class CommandBase {
|
|
18
21
|
constructor(application, options) {
|
|
@@ -49,6 +52,81 @@ class CommandBase {
|
|
|
49
52
|
let application = this.getApplication();
|
|
50
53
|
return application.getDBConnection();
|
|
51
54
|
}
|
|
55
|
+
|
|
56
|
+
spawnCommand(command, args, options) {
|
|
57
|
+
const { spawn } = require('child_process');
|
|
58
|
+
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
try {
|
|
61
|
+
let childProcess = spawn(
|
|
62
|
+
command,
|
|
63
|
+
args,
|
|
64
|
+
Object.assign({ shell: true }, options || {}, {
|
|
65
|
+
env: Object.assign({}, process.env, (options || {}).env || {}),
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
let output = [];
|
|
70
|
+
let errors = [];
|
|
71
|
+
|
|
72
|
+
childProcess.stdout.on('data', (data) => {
|
|
73
|
+
output.push(data);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
childProcess.stderr.on('data', (data) => {
|
|
77
|
+
errors.push(data);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
childProcess.on('error', (error) => {
|
|
81
|
+
reject({
|
|
82
|
+
stdout: Buffer.concat(output).toString('utf8'),
|
|
83
|
+
stderr: Buffer.concat(errors).toString('utf8'),
|
|
84
|
+
code: 1,
|
|
85
|
+
error,
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
childProcess.on('close', (code) => {
|
|
90
|
+
if (code !== 0) {
|
|
91
|
+
let error = Buffer.concat(errors).toString('utf8');
|
|
92
|
+
|
|
93
|
+
reject({
|
|
94
|
+
stdout: Buffer.concat(output).toString('utf8'),
|
|
95
|
+
stderr: error,
|
|
96
|
+
error: error,
|
|
97
|
+
code,
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
resolve({
|
|
101
|
+
stdout: Buffer.concat(output).toString('utf8'),
|
|
102
|
+
stderr: Buffer.concat(errors).toString('utf8'),
|
|
103
|
+
error: null,
|
|
104
|
+
code,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
} catch (error) {
|
|
109
|
+
reject({
|
|
110
|
+
stdout: '',
|
|
111
|
+
stderr: '',
|
|
112
|
+
code: 1,
|
|
113
|
+
error,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getCommandFiles(filterFunc) {
|
|
120
|
+
let application = this.getApplication();
|
|
121
|
+
let applicationOptions = application.getOptions();
|
|
122
|
+
let internalCommands = getCommandFiles(getInternalCommandsPath(), filterFunc);
|
|
123
|
+
let externalCommands = [];
|
|
124
|
+
|
|
125
|
+
if (Nife.isNotEmpty(applicationOptions.commandsPath))
|
|
126
|
+
externalCommands = getCommandFiles(applicationOptions.commandsPath, filterFunc);
|
|
127
|
+
|
|
128
|
+
return [].concat(internalCommands, externalCommands);
|
|
129
|
+
}
|
|
52
130
|
}
|
|
53
131
|
|
|
54
132
|
let loadingAllCommandsInProgress = false;
|
|
@@ -68,22 +146,6 @@ function defineCommand(_commandName, definer, _parent) {
|
|
|
68
146
|
let commandName = _commandName.toLowerCase();
|
|
69
147
|
let parent = _parent;
|
|
70
148
|
|
|
71
|
-
let doExecuteCommand = process.env['MYTHIX_EXECUTE_COMMAND'];
|
|
72
|
-
let executeImmediately = false;
|
|
73
|
-
|
|
74
|
-
// Is this command script being executed directly?
|
|
75
|
-
// If so, make certain to load all commands.
|
|
76
|
-
// This is required, so that commands that inherit
|
|
77
|
-
// from other commands will continue to work.
|
|
78
|
-
if (doExecuteCommand === commandName && !loadingAllCommandsInProgress) {
|
|
79
|
-
executeImmediately = true;
|
|
80
|
-
|
|
81
|
-
let mythixCommandPath = process.env['MYTHIX_COMMAND_PATH'];
|
|
82
|
-
let mythixApplicationCommandsPath = process.env['MYTHIX_APPLICATION_COMMANDS'];
|
|
83
|
-
if (mythixCommandPath && mythixApplicationCommandsPath)
|
|
84
|
-
loadCommands(mythixApplicationCommandsPath, [ mythixCommandPath ]);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
149
|
if (Nife.instanceOf(parent, 'string')) {
|
|
88
150
|
if (!CommandBase.commands[parent])
|
|
89
151
|
throw new Error(`Can not find parent class for command "${commandName}": No such parent class "${parent}" found`);
|
|
@@ -109,36 +171,66 @@ function defineCommand(_commandName, definer, _parent) {
|
|
|
109
171
|
// "executeCommand" below, which spawns a node process that
|
|
110
172
|
// targets this command script.
|
|
111
173
|
Klass.execute = async function() {
|
|
112
|
-
|
|
113
|
-
let commandContext = CMDed((context) => {
|
|
114
|
-
let { $, Types, store, scope } = context;
|
|
174
|
+
let helpShown = false;
|
|
115
175
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
format: Path.resolve,
|
|
120
|
-
})) || store({ config: (Nife.isNotEmpty(process.env.MYTHIX_CONFIG_PATH)) ? Path.resolve(process.env.MYTHIX_CONFIG_PATH) : Path.join(process.env.PWD, '.mythix-config.js') });
|
|
176
|
+
const customShowHelp = (subHelp) => {
|
|
177
|
+
if (helpShown)
|
|
178
|
+
return;
|
|
121
179
|
|
|
122
|
-
|
|
180
|
+
helpShown = true;
|
|
123
181
|
|
|
124
|
-
|
|
125
|
-
|
|
182
|
+
showHelp(subHelp);
|
|
183
|
+
};
|
|
126
184
|
|
|
127
|
-
|
|
185
|
+
const getArgumentsContext = async (application) => {
|
|
186
|
+
// eslint-disable-next-line new-cap
|
|
187
|
+
return await CMDed(async (context) => {
|
|
188
|
+
let { $, Types, store, scope } = context;
|
|
128
189
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
190
|
+
// Parse these even though they are no longer needed
|
|
191
|
+
// so that we ensure they are "consumed".
|
|
192
|
+
$('--config', Types.STRING({
|
|
193
|
+
format: Path.resolve,
|
|
194
|
+
})) || store({ config: (Nife.isNotEmpty(process.env.MYTHIX_CONFIG_PATH)) ? Path.resolve(process.env.MYTHIX_CONFIG_PATH) : Path.join(process.env.PWD, '.mythix-config') });
|
|
133
195
|
|
|
134
|
-
|
|
135
|
-
if (typeof runner === 'function')
|
|
136
|
-
return runner(context);
|
|
196
|
+
$('--runtime', Types.STRING());
|
|
137
197
|
|
|
138
|
-
|
|
198
|
+
$('-e', Types.STRING(), { name: 'environment' });
|
|
199
|
+
$('--env', Types.STRING(), { name: 'environment' });
|
|
200
|
+
|
|
201
|
+
if (!application)
|
|
202
|
+
return true;
|
|
203
|
+
|
|
204
|
+
store('executing', true);
|
|
205
|
+
store('mythixApplication', application);
|
|
206
|
+
|
|
207
|
+
// Consume the command name
|
|
208
|
+
$(commandName, ({ fetch, showHelp }) => {
|
|
209
|
+
if (fetch('help', false))
|
|
210
|
+
showHelp(commandName);
|
|
211
|
+
|
|
212
|
+
return true;
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
let runner = null;
|
|
216
|
+
if (typeof Klass.commandArguments === 'function') {
|
|
217
|
+
let result = ((await Klass.commandArguments(application, 'runner')) || {});
|
|
218
|
+
runner = result.runner;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return await scope(commandName, async (context) => {
|
|
222
|
+
if (typeof runner === 'function')
|
|
223
|
+
return await runner(context);
|
|
224
|
+
|
|
225
|
+
return true;
|
|
226
|
+
});
|
|
227
|
+
}, {
|
|
228
|
+
helpArgPattern: null,
|
|
229
|
+
showHelp: customShowHelp,
|
|
139
230
|
});
|
|
140
|
-
}
|
|
231
|
+
};
|
|
141
232
|
|
|
233
|
+
let commandContext = await getArgumentsContext();
|
|
142
234
|
if (!commandContext)
|
|
143
235
|
return;
|
|
144
236
|
|
|
@@ -178,9 +270,11 @@ function defineCommand(_commandName, definer, _parent) {
|
|
|
178
270
|
if (doStartApplication)
|
|
179
271
|
await application.start();
|
|
180
272
|
|
|
273
|
+
commandContext = await getArgumentsContext(application);
|
|
274
|
+
|
|
181
275
|
let commandOptions = commandContext[commandName] || {};
|
|
182
276
|
let commandInstance = new Klass(application, commandOptions);
|
|
183
|
-
let result = await commandInstance.execute.call(commandInstance, commandOptions);
|
|
277
|
+
let result = await commandInstance.execute.call(commandInstance, commandOptions, commandContext);
|
|
184
278
|
|
|
185
279
|
await application.stop(result || 0);
|
|
186
280
|
} catch (error) {
|
|
@@ -193,14 +287,6 @@ function defineCommand(_commandName, definer, _parent) {
|
|
|
193
287
|
|
|
194
288
|
CommandBase.commands[commandName] = Klass;
|
|
195
289
|
|
|
196
|
-
// If this command file was loaded directly, and it was requested
|
|
197
|
-
// that we execute it, then do so right now
|
|
198
|
-
if (executeImmediately) {
|
|
199
|
-
Klass.execute().then(() => {}, (error) => {
|
|
200
|
-
console.log(error);
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
290
|
return Klass;
|
|
205
291
|
}
|
|
206
292
|
|
|
@@ -225,35 +311,42 @@ function loadCommand(name) {
|
|
|
225
311
|
return CommandKlass;
|
|
226
312
|
}
|
|
227
313
|
|
|
228
|
-
function
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return false;
|
|
314
|
+
function getCommandFiles(commandsPath, filterFunc) {
|
|
315
|
+
try {
|
|
316
|
+
return walkDir(commandsPath, {
|
|
317
|
+
filter: (fullFileName, fileName, stats) => {
|
|
318
|
+
if (typeof filterFunc === 'function')
|
|
319
|
+
return filterFunc(fullFileName, fileName, stats);
|
|
235
320
|
|
|
236
|
-
|
|
237
|
-
|
|
321
|
+
if (fileName.match(/^_/))
|
|
322
|
+
return false;
|
|
238
323
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
});
|
|
242
|
-
} catch (error) {
|
|
243
|
-
if (error.code === 'ENOENT')
|
|
244
|
-
return [];
|
|
324
|
+
if (stats.isFile() && !fileNameWithoutExtension(fileName).match(/-command$/))
|
|
325
|
+
return false;
|
|
245
326
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
}
|
|
327
|
+
return true;
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
} catch (error) {
|
|
331
|
+
if (error.code === 'ENOENT')
|
|
332
|
+
return [];
|
|
250
333
|
|
|
334
|
+
console.error(error);
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function getInternalCommandsPath() {
|
|
340
|
+
return Path.resolve(__dirname);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function loadCommands(applicationCommandsPath, skip) {
|
|
251
344
|
if (loadingAllCommandsInProgress)
|
|
252
345
|
return;
|
|
253
346
|
|
|
254
347
|
loadingAllCommandsInProgress = true;
|
|
255
348
|
|
|
256
|
-
let mythixCommandFiles = getCommandFiles(
|
|
349
|
+
let mythixCommandFiles = getCommandFiles(getInternalCommandsPath());
|
|
257
350
|
let applicationCommandFiles = getCommandFiles(applicationCommandsPath);
|
|
258
351
|
let allCommandFiles = [].concat(mythixCommandFiles, applicationCommandFiles);
|
|
259
352
|
|
|
@@ -261,7 +354,6 @@ function loadCommands(applicationCommandsPath, skip) {
|
|
|
261
354
|
if (skip && skip.indexOf(commandPath) >= 0)
|
|
262
355
|
return;
|
|
263
356
|
|
|
264
|
-
|
|
265
357
|
loadCommand(commandPath);
|
|
266
358
|
});
|
|
267
359
|
|
|
@@ -324,6 +416,9 @@ function loadMythixConfig(_mythixConfigPath, _appRootPath) {
|
|
|
324
416
|
try {
|
|
325
417
|
if (FileSystem.existsSync(configPath)) {
|
|
326
418
|
let mythixConfig = require(configPath);
|
|
419
|
+
if (mythixConfig.__esModule)
|
|
420
|
+
mythixConfig = mythixConfig['default'];
|
|
421
|
+
|
|
327
422
|
if (!mythixConfig.appRootPath)
|
|
328
423
|
mythixConfig.appRootPath = appRootPath;
|
|
329
424
|
|
|
@@ -377,7 +472,7 @@ async function executeCommand(_config, appOptions, commandContext, CommandKlass,
|
|
|
377
472
|
let commandsPath = appOptions.commandsPath;
|
|
378
473
|
let runtime = commandContext.runtime || config.runtime || process.env.MYTHIX_RUNTIME || 'node';
|
|
379
474
|
let runtimeArguments = ((CommandKlass.runtimeArguments || {})[runtime]) || [];
|
|
380
|
-
let args = runtimeArguments.concat([
|
|
475
|
+
let args = runtimeArguments.concat([ Path.resolve(__dirname, 'command-executor.js') ], argv);
|
|
381
476
|
|
|
382
477
|
let code = await spawnCommand(
|
|
383
478
|
args,
|
|
@@ -407,6 +502,8 @@ async function executeCommand(_config, appOptions, commandContext, CommandKlass,
|
|
|
407
502
|
module.exports = {
|
|
408
503
|
CommandBase,
|
|
409
504
|
loadMythixConfig,
|
|
505
|
+
getInternalCommandsPath,
|
|
506
|
+
getCommandFiles,
|
|
410
507
|
loadCommand,
|
|
411
508
|
loadCommands,
|
|
412
509
|
defineCommand,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* global process */
|
|
4
|
+
|
|
5
|
+
const Nife = require('nife');
|
|
6
|
+
const {
|
|
7
|
+
CommandBase,
|
|
8
|
+
loadCommands,
|
|
9
|
+
} = require('./cli-utils');
|
|
10
|
+
|
|
11
|
+
(async function() {
|
|
12
|
+
let commandName = process.env['MYTHIX_EXECUTE_COMMAND'];
|
|
13
|
+
if (Nife.isEmpty(commandName))
|
|
14
|
+
return;
|
|
15
|
+
|
|
16
|
+
// First load all commands
|
|
17
|
+
let mythixCommandPath = process.env['MYTHIX_COMMAND_PATH'];
|
|
18
|
+
let mythixApplicationCommandsPath = process.env['MYTHIX_APPLICATION_COMMANDS'];
|
|
19
|
+
if (mythixCommandPath && mythixApplicationCommandsPath)
|
|
20
|
+
loadCommands(mythixApplicationCommandsPath);
|
|
21
|
+
|
|
22
|
+
let Klass = CommandBase.commands[commandName];
|
|
23
|
+
if (!Klass || typeof Klass.execute !== 'function')
|
|
24
|
+
return;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await Klass.execute();
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(error);
|
|
30
|
+
}
|
|
31
|
+
})();
|