mythix 2.5.4 → 2.5.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mythix",
3
- "version": "2.5.4",
3
+ "version": "2.5.5",
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.3",
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"
@@ -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
  }
@@ -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>;
@@ -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 { CMDed } = require('cmded');
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
- // eslint-disable-next-line new-cap
113
- let commandContext = CMDed((context) => {
114
- let { $, Types, store, scope } = context;
174
+ let helpShown = false;
115
175
 
116
- // Parse these even though they are no longer needed
117
- // so that we ensure they are "consumed".
118
- $('--config', Types.STRING({
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
- $('--runtime', Types.STRING());
180
+ helpShown = true;
123
181
 
124
- $('-e', Types.STRING(), { name: 'environment' });
125
- $('--env', Types.STRING(), { name: 'environment' });
182
+ showHelp(subHelp);
183
+ };
126
184
 
127
- let runner = null;
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
- if (typeof Klass.commandArguments === 'function') {
130
- let result = (Klass.commandArguments() || {});
131
- runner = result.runner;
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
- return scope(commandName, (context) => {
135
- if (typeof runner === 'function')
136
- return runner(context);
196
+ $('--runtime', Types.STRING());
137
197
 
138
- return true;
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 loadCommands(applicationCommandsPath, skip) {
229
- const getCommandFiles = (commandsPath) => {
230
- try {
231
- return walkDir(commandsPath, {
232
- filter: (fullFileName, fileName, stats) => {
233
- if (fileName.match(/^_/))
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
- if (stats.isFile() && !fileNameWithoutExtension(fileName).match(/-command$/))
237
- return false;
321
+ if (fileName.match(/^_/))
322
+ return false;
238
323
 
239
- return true;
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
- console.error(error);
247
- throw error;
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(Path.resolve(__dirname));
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([ commandPath ], argv);
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
+ })();