ee-core 1.5.2-beta.1 → 2.0.0-beta.1

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.
Files changed (72) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +2 -2
  3. package/addon/window/index.js +91 -91
  4. package/bin/tools.js +18 -18
  5. package/config/config.default.js +287 -280
  6. package/core/index.js +12 -12
  7. package/core/lib/ee.js +218 -218
  8. package/core/lib/loader/context_loader.js +106 -106
  9. package/core/lib/loader/ee_loader.js +461 -457
  10. package/core/lib/loader/file_loader.js +325 -325
  11. package/core/lib/loader/mixin/addon.js +32 -32
  12. package/core/lib/loader/mixin/config.js +135 -135
  13. package/core/lib/loader/mixin/controller.js +125 -124
  14. package/core/lib/loader/mixin/service.js +28 -28
  15. package/core/lib/utils/base_context_class.js +34 -34
  16. package/core/lib/utils/function.js +30 -0
  17. package/core/lib/utils/index.js +127 -127
  18. package/core/lib/utils/sequencify.js +59 -59
  19. package/core/lib/utils/timing.js +77 -77
  20. package/index.js +49 -49
  21. package/lib/appLoader.js +48 -53
  22. package/lib/application.js +85 -84
  23. package/lib/baseApp.js +114 -131
  24. package/lib/eeApp.js +325 -359
  25. package/{lib/constant.js → module/const/index.js} +12 -9
  26. package/{lib/httpclient.js → module/httpclient/index.js} +170 -136
  27. package/module/jobs/child/forkProcess.js +99 -0
  28. package/module/jobs/child/index.js +33 -0
  29. package/module/jobs/index.js +55 -0
  30. package/module/jobs/renderer/index.js +140 -0
  31. package/module/jobs/renderer/loadView.js +40 -0
  32. package/module/loader/index.js +78 -0
  33. package/module/log/index.js +53 -0
  34. package/module/log/logger.js +61 -0
  35. package/module/message/index.js +13 -0
  36. package/module/message/ipcMain.js +160 -0
  37. package/module/message/ipcRender.js +0 -0
  38. package/{lib → module}/socket/httpServer.js +142 -142
  39. package/{lib → module}/socket/io.js +23 -23
  40. package/{lib → module}/socket/ipcServer.js +106 -108
  41. package/{lib → module}/socket/socketClient.js +51 -50
  42. package/{lib → module}/socket/socketServer.js +77 -76
  43. package/module/socket/start.js +22 -0
  44. package/{lib → module}/storage/index.js +35 -34
  45. package/module/storage/jsondb/adapters/Base.js +14 -0
  46. package/module/storage/jsondb/adapters/FileSync.js +32 -0
  47. package/{lib/storage/lowdb → module/storage/jsondb}/main.js +42 -46
  48. package/{lib/storage/lowdbStorage.js → module/storage/jsondbStorage.js} +98 -99
  49. package/{lib → module}/storage/sqliteStorage.js +123 -127
  50. package/module/utils/copyto.js +161 -0
  51. package/module/utils/helper.js +117 -0
  52. package/module/utils/index.js +120 -0
  53. package/module/utils/json.js +72 -0
  54. package/module/utils/ps.js +175 -0
  55. package/{utils → module/utils}/wrap.js +35 -37
  56. package/package.json +44 -48
  57. package/tools/encrypt.js +274 -274
  58. package/tools/replaceDist.js +61 -61
  59. package/utils/index.js +128 -246
  60. package/lib/logger.js +0 -47
  61. package/lib/socket/start.js +0 -22
  62. package/lib/storage/lowdb/adapters/Base.js +0 -15
  63. package/lib/storage/lowdb/adapters/FileAsync.js +0 -41
  64. package/lib/storage/lowdb/adapters/FileSync.js +0 -39
  65. package/lib/storage/lowdb/adapters/LocalStorage.js +0 -20
  66. package/lib/storage/lowdb/adapters/Memory.js +0 -8
  67. package/lib/storage/lowdb/adapters/_stringify.js +0 -4
  68. package/lib/storage/lowdb/common.js +0 -33
  69. package/lib/storage/lowdb/fp.js +0 -23
  70. package/lib/storage/lowdb/is-promise.js +0 -6
  71. package/lib/storage/lowdb/nano.js +0 -5
  72. package/utils/common.js +0 -91
@@ -1,325 +1,325 @@
1
- 'use strict';
2
-
3
- const assert = require('assert');
4
- const fs = require('fs');
5
- const debug = require('debug')('ee-core:fileLoader');
6
- const path = require('path');
7
- const globby = require('globby');
8
- const is = require('is-type-of');
9
- const deprecate = require('depd')('ee');
10
- const utils = require('../utils');
11
- const FULLPATH = Symbol('EE_LOADER_ITEM_FULLPATH');
12
- const EXPORTS = Symbol('EE_LOADER_ITEM_EXPORTS');
13
-
14
- const defaults = {
15
- directory: null,
16
- target: null,
17
- match: undefined,
18
- ignore: undefined,
19
- lowercaseFirst: false,
20
- caseStyle: 'camel',
21
- initializer: null,
22
- call: true,
23
- override: false,
24
- inject: undefined,
25
- filter: null,
26
- loader: undefined,
27
- };
28
-
29
- /**
30
- * Load files from directory to target object.
31
- * @since 1.0.0
32
- */
33
- class FileLoader {
34
-
35
- /**
36
- * @class
37
- * @param {Object} options - options
38
- * @param {String|Array} options.directory - directories to be loaded
39
- * @param {Object} options.target - attach the target object from loaded files
40
- * @param {String} options.match - match the files when load, support glob, default to all js files
41
- * @param {String} options.ignore - ignore the files when load, support glob
42
- * @param {Function} options.initializer - custom file exports, receive two parameters, first is the inject object(if not js file, will be content buffer), second is an `options` object that contain `path`
43
- * @param {Boolean} options.call - determine whether invoke when exports is function
44
- * @param {Boolean} options.override - determine whether override the property when get the same name
45
- * @param {Object} options.inject - an object that be the argument when invoke the function
46
- * @param {Function} options.filter - a function that filter the exports which can be loaded
47
- * @param {String|Function} options.caseStyle - set property's case when converting a filepath to property list.
48
- * @param {Object} options.loader - an object that be the argument when invoke the function
49
- */
50
- constructor(options) {
51
- assert(options.directory, 'options.directory is required');
52
- assert(options.target, 'options.target is required');
53
- this.options = Object.assign({}, defaults, options);
54
-
55
- // compatible old options _lowercaseFirst_
56
- if (this.options.lowercaseFirst === true) {
57
- deprecate('lowercaseFirst is deprecated, use caseStyle instead');
58
- this.options.caseStyle = 'lower';
59
- }
60
- }
61
-
62
- /**
63
- * attach items to target object. Mapping the directory to properties.
64
- * `app/controller/group/repository.js` => `target.group.repository`
65
- * @return {Object} target
66
- * @since 1.0.0
67
- */
68
- load() {
69
- const items = this.parse();
70
- const target = this.options.target;
71
- for (const item of items) {
72
- // item { properties: [ 'a', 'b', 'c'], exports }
73
- // => target.a.b.c = exports
74
- item.properties.reduce((target, property, index) => {
75
- let obj;
76
- const properties = item.properties.slice(0, index + 1).join('.');
77
- if (index === item.properties.length - 1) {
78
- if (property in target) {
79
- if (!this.options.override) throw new Error(`can't overwrite property '${properties}' from ${target[property][FULLPATH]} by ${item.fullpath}`);
80
- }
81
- obj = item.exports;
82
- if (obj && !is.primitive(obj)) {
83
- obj[FULLPATH] = item.fullpath;
84
- obj[EXPORTS] = true;
85
- }
86
- } else {
87
- obj = target[property] || {};
88
- }
89
-
90
- target[property] = obj;
91
- debug('loaded %s', properties);
92
- return obj;
93
- }, target);
94
- }
95
-
96
- return target;
97
- }
98
-
99
- /**
100
- * Parse files from given directories, then return an items list, each item contains properties and exports.
101
- *
102
- * For example, parse `app/controller/group/repository.js`
103
- *
104
- * ```
105
- * module.exports = app => {
106
- * return class RepositoryController extends app.Controller {};
107
- * }
108
- * ```
109
- *
110
- * It returns a item
111
- *
112
- * ```
113
- * {
114
- * properties: [ 'group', 'repository' ],
115
- * exports: app => { ... },
116
- * }
117
- * ```
118
- *
119
- * `Properties` is an array that contains the directory of a filepath.
120
- *
121
- * `Exports` depends on type, if exports is a function, it will be called. if initializer is specified, it will be called with exports for customizing.
122
- * @return {Array} items
123
- * @since 1.0.0
124
- */
125
- parse() {
126
- let files = this.options.match;
127
- if (!files) {
128
- files = (process.env.EE_TYPESCRIPT === 'true' && utils.extensions['.ts'])
129
- ? [ '**/*.(js|ts)', '!**/*.d.ts' ]
130
- : [ '**/*.js', '**/*.jsc'];
131
- } else {
132
- files = Array.isArray(files) ? files : [ files ];
133
- }
134
-
135
- let ignore = this.options.ignore;
136
- if (ignore) {
137
- ignore = Array.isArray(ignore) ? ignore : [ ignore ];
138
- ignore = ignore.filter(f => !!f).map(f => '!' + f);
139
- files = files.concat(ignore);
140
- }
141
-
142
- let directories = this.options.directory;
143
- if (!Array.isArray(directories)) {
144
- directories = [ directories ];
145
- }
146
-
147
- const filter = is.function(this.options.filter) ? this.options.filter : null;
148
- const items = [];
149
- debug('parsing %j', directories);
150
-
151
- for (const directory of directories) {
152
- const filepaths = globby.sync(files, { cwd: directory });
153
- for (const filepath of filepaths) {
154
- const fullpath = path.join(directory, filepath);
155
- if (!fs.statSync(fullpath).isFile()) continue;
156
- // get properties
157
- // app/service/foo/bar.js => [ 'foo', 'bar' ]
158
- const properties = getProperties(filepath, this.options);
159
- // app/service/foo/bar.js => service.foo.bar
160
- const pathName = directory.split(/[/\\]/).slice(-1) + '.' + properties.join('.');
161
- // get exports from the file
162
- let exports = getExports(fullpath, this.options, pathName);
163
- // ignore exports when it's null or false returned by filter function
164
- if (exports == null || (filter && filter(exports) === false)) continue;
165
-
166
- // set properties of class
167
- if (is.class(exports) || utils.isBytecodeClass(exports)) {
168
- exports.prototype.pathName = pathName;
169
- exports.prototype.fullPath = fullpath;
170
- }
171
-
172
- items.push({ fullpath, properties, exports });
173
- debug('parse %s, properties %j, export %O', fullpath, properties, exports);
174
- }
175
- }
176
-
177
- return items;
178
- }
179
-
180
- /**
181
- * attach items to target object for addons.
182
- * `app/addon/group/index.js` => `target.group`
183
- * @return {Object} target
184
- * @since 1.0.0
185
- */
186
- loadAddons() {
187
- const items = [];
188
- const files = '*';
189
- const app = this.options.inject;
190
- const loader = this.options.loader;
191
- const target = this.options.target;
192
- let directories = this.options.directory;
193
-
194
- if (!Array.isArray(directories)) {
195
- directories = [ directories ];
196
- }
197
-
198
- // check addon
199
- const config = app.config.addons;
200
- let filterAddons = [];
201
- for (let key of Object.keys(config)) {
202
- if (!config[key].enable) {
203
- filterAddons.push(key);
204
- }
205
- }
206
-
207
- for (const directory of directories) {
208
- const addonpaths = globby.sync(files, { cwd: directory, deep: 1, onlyDirectories: true});
209
- for (const addonName of addonpaths) {
210
- if (filterAddons.includes(addonName)) continue;
211
-
212
- let fullpath = path.join(directory, addonName, 'index');
213
- fullpath = loader.resolveModule(fullpath);
214
- if (!fs.statSync(fullpath).isFile()) continue;
215
-
216
- let exports = getExports(fullpath, this.options, addonName);
217
- if (exports == null) continue;
218
-
219
- const properties = [addonName];
220
- if (is.class(exports) || utils.isBytecodeClass(exports)) {
221
- exports.prototype.pathName = addonName;
222
- exports.prototype.fullPath = fullpath;
223
- }
224
-
225
- items.push({ fullpath, properties, exports });
226
- }
227
-
228
- for (const item of items) {
229
- const property = item.properties[0];
230
- let obj = item.exports;
231
- if (obj && !is.primitive(obj)) {
232
- obj[FULLPATH] = item.fullpath;
233
- obj[EXPORTS] = true;
234
- }
235
-
236
- target[property] = obj;
237
- }
238
- }
239
-
240
- return target;
241
- }
242
- }
243
-
244
- module.exports = FileLoader;
245
- module.exports.EXPORTS = EXPORTS;
246
- module.exports.FULLPATH = FULLPATH;
247
-
248
- // convert file path to an array of properties
249
- // a/b/c.js => ['a', 'b', 'c']
250
- function getProperties(filepath, { caseStyle }) {
251
- // if caseStyle is function, return the result of function
252
- if (is.function(caseStyle)) {
253
- const result = caseStyle(filepath);
254
- assert(is.array(result), `caseStyle expect an array, but got ${result}`);
255
- return result;
256
- }
257
- // use default camelize
258
- return defaultCamelize(filepath, caseStyle);
259
- }
260
-
261
- // Get exports from filepath
262
- // If exports is null/undefined, it will be ignored
263
- function getExports(fullpath, { initializer, call, inject }, pathName) {
264
- let exports = utils.loadFile(fullpath);
265
-
266
- // process exports as you like
267
- if (initializer) {
268
- exports = initializer(exports, { path: fullpath, pathName });
269
- }
270
-
271
- // return exports when it's a class or generator
272
- //
273
- // module.exports = class Service {};
274
- // or
275
- // module.exports = function*() {}
276
- //new exports;
277
-
278
- if (is.class(exports) || is.generatorFunction(exports) || is.asyncFunction(exports) || utils.isBytecodeClass(exports)) {
279
- return exports;
280
- }
281
-
282
- // return exports after call when it's a function
283
- //
284
- // module.exports = function(app) {
285
- // return {};
286
- // }
287
- if (call && is.function(exports)) {
288
- exports = exports(inject);
289
- if (exports != null) {
290
- return exports;
291
- }
292
- }
293
-
294
- // return exports what is
295
- return exports;
296
- }
297
-
298
- function defaultCamelize(filepath, caseStyle) {
299
- const properties = filepath.substring(0, filepath.lastIndexOf('.')).split('/');
300
- return properties.map(property => {
301
- if (!/^[a-z][a-z0-9_-]*$/i.test(property)) {
302
- throw new Error(`${property} is not match 'a-z0-9_-' in ${filepath}`);
303
- }
304
-
305
- // use default camelize, will capitalize the first letter
306
- // foo_bar.js > FooBar
307
- // fooBar.js > FooBar
308
- // FooBar.js > FooBar
309
- // FooBar.js > FooBar
310
- // FooBar.js > fooBar (if lowercaseFirst is true)
311
- property = property.replace(/[_-][a-z]/ig, s => s.substring(1).toUpperCase());
312
- let first = property[0];
313
- switch (caseStyle) {
314
- case 'lower':
315
- first = first.toLowerCase();
316
- break;
317
- case 'upper':
318
- first = first.toUpperCase();
319
- break;
320
- case 'camel':
321
- default:
322
- }
323
- return first + property.substring(1);
324
- });
325
- }
1
+ 'use strict';
2
+
3
+ const assert = require('assert');
4
+ const fs = require('fs');
5
+ const debug = require('debug')('ee-core:fileLoader');
6
+ const path = require('path');
7
+ const globby = require('globby');
8
+ const is = require('is-type-of');
9
+ const deprecate = require('depd')('ee');
10
+ const utils = require('../utils');
11
+ const FULLPATH = Symbol('EE_LOADER_ITEM_FULLPATH');
12
+ const EXPORTS = Symbol('EE_LOADER_ITEM_EXPORTS');
13
+
14
+ const defaults = {
15
+ directory: null,
16
+ target: null,
17
+ match: undefined,
18
+ ignore: undefined,
19
+ lowercaseFirst: false,
20
+ caseStyle: 'camel',
21
+ initializer: null,
22
+ call: true,
23
+ override: false,
24
+ inject: undefined,
25
+ filter: null,
26
+ loader: undefined,
27
+ };
28
+
29
+ /**
30
+ * Load files from directory to target object.
31
+ * @since 1.0.0
32
+ */
33
+ class FileLoader {
34
+
35
+ /**
36
+ * @class
37
+ * @param {Object} options - options
38
+ * @param {String|Array} options.directory - directories to be loaded
39
+ * @param {Object} options.target - attach the target object from loaded files
40
+ * @param {String} options.match - match the files when load, support glob, default to all js files
41
+ * @param {String} options.ignore - ignore the files when load, support glob
42
+ * @param {Function} options.initializer - custom file exports, receive two parameters, first is the inject object(if not js file, will be content buffer), second is an `options` object that contain `path`
43
+ * @param {Boolean} options.call - determine whether invoke when exports is function
44
+ * @param {Boolean} options.override - determine whether override the property when get the same name
45
+ * @param {Object} options.inject - an object that be the argument when invoke the function
46
+ * @param {Function} options.filter - a function that filter the exports which can be loaded
47
+ * @param {String|Function} options.caseStyle - set property's case when converting a filepath to property list.
48
+ * @param {Object} options.loader - an object that be the argument when invoke the function
49
+ */
50
+ constructor(options) {
51
+ assert(options.directory, 'options.directory is required');
52
+ assert(options.target, 'options.target is required');
53
+ this.options = Object.assign({}, defaults, options);
54
+
55
+ // compatible old options _lowercaseFirst_
56
+ if (this.options.lowercaseFirst === true) {
57
+ deprecate('lowercaseFirst is deprecated, use caseStyle instead');
58
+ this.options.caseStyle = 'lower';
59
+ }
60
+ }
61
+
62
+ /**
63
+ * attach items to target object. Mapping the directory to properties.
64
+ * `app/controller/group/repository.js` => `target.group.repository`
65
+ * @return {Object} target
66
+ * @since 1.0.0
67
+ */
68
+ load() {
69
+ const items = this.parse();
70
+ const target = this.options.target;
71
+ for (const item of items) {
72
+ // item { properties: [ 'a', 'b', 'c'], exports }
73
+ // => target.a.b.c = exports
74
+ item.properties.reduce((target, property, index) => {
75
+ let obj;
76
+ const properties = item.properties.slice(0, index + 1).join('.');
77
+ if (index === item.properties.length - 1) {
78
+ if (property in target) {
79
+ if (!this.options.override) throw new Error(`can't overwrite property '${properties}' from ${target[property][FULLPATH]} by ${item.fullpath}`);
80
+ }
81
+ obj = item.exports;
82
+ if (obj && !is.primitive(obj)) {
83
+ obj[FULLPATH] = item.fullpath;
84
+ obj[EXPORTS] = true;
85
+ }
86
+ } else {
87
+ obj = target[property] || {};
88
+ }
89
+
90
+ target[property] = obj;
91
+ debug('loaded %s', properties);
92
+ return obj;
93
+ }, target);
94
+ }
95
+
96
+ return target;
97
+ }
98
+
99
+ /**
100
+ * Parse files from given directories, then return an items list, each item contains properties and exports.
101
+ *
102
+ * For example, parse `app/controller/group/repository.js`
103
+ *
104
+ * ```
105
+ * module.exports = app => {
106
+ * return class RepositoryController extends app.Controller {};
107
+ * }
108
+ * ```
109
+ *
110
+ * It returns a item
111
+ *
112
+ * ```
113
+ * {
114
+ * properties: [ 'group', 'repository' ],
115
+ * exports: app => { ... },
116
+ * }
117
+ * ```
118
+ *
119
+ * `Properties` is an array that contains the directory of a filepath.
120
+ *
121
+ * `Exports` depends on type, if exports is a function, it will be called. if initializer is specified, it will be called with exports for customizing.
122
+ * @return {Array} items
123
+ * @since 1.0.0
124
+ */
125
+ parse() {
126
+ let files = this.options.match;
127
+ if (!files) {
128
+ files = (process.env.EE_TYPESCRIPT === 'true' && utils.extensions['.ts'])
129
+ ? [ '**/*.(js|ts)', '!**/*.d.ts' ]
130
+ : [ '**/*.js', '**/*.jsc'];
131
+ } else {
132
+ files = Array.isArray(files) ? files : [ files ];
133
+ }
134
+
135
+ let ignore = this.options.ignore;
136
+ if (ignore) {
137
+ ignore = Array.isArray(ignore) ? ignore : [ ignore ];
138
+ ignore = ignore.filter(f => !!f).map(f => '!' + f);
139
+ files = files.concat(ignore);
140
+ }
141
+
142
+ let directories = this.options.directory;
143
+ if (!Array.isArray(directories)) {
144
+ directories = [ directories ];
145
+ }
146
+
147
+ const filter = is.function(this.options.filter) ? this.options.filter : null;
148
+ const items = [];
149
+ debug('parsing %j', directories);
150
+
151
+ for (const directory of directories) {
152
+ const filepaths = globby.sync(files, { cwd: directory });
153
+ for (const filepath of filepaths) {
154
+ const fullpath = path.join(directory, filepath);
155
+ if (!fs.statSync(fullpath).isFile()) continue;
156
+ // get properties
157
+ // app/service/foo/bar.js => [ 'foo', 'bar' ]
158
+ const properties = getProperties(filepath, this.options);
159
+ // app/service/foo/bar.js => service.foo.bar
160
+ const pathName = directory.split(/[/\\]/).slice(-1) + '.' + properties.join('.');
161
+ // get exports from the file
162
+ let exports = getExports(fullpath, this.options, pathName);
163
+ // ignore exports when it's null or false returned by filter function
164
+ if (exports == null || (filter && filter(exports) === false)) continue;
165
+
166
+ // set properties of class
167
+ if (is.class(exports) || utils.isBytecodeClass(exports)) {
168
+ exports.prototype.pathName = pathName;
169
+ exports.prototype.fullPath = fullpath;
170
+ }
171
+
172
+ items.push({ fullpath, properties, exports });
173
+ debug('parse %s, properties %j, export %O', fullpath, properties, exports);
174
+ }
175
+ }
176
+
177
+ return items;
178
+ }
179
+
180
+ /**
181
+ * attach items to target object for addons.
182
+ * `app/addon/group/index.js` => `target.group`
183
+ * @return {Object} target
184
+ * @since 1.0.0
185
+ */
186
+ loadAddons() {
187
+ const items = [];
188
+ const files = '*';
189
+ const app = this.options.inject;
190
+ const loader = this.options.loader;
191
+ const target = this.options.target;
192
+ let directories = this.options.directory;
193
+
194
+ if (!Array.isArray(directories)) {
195
+ directories = [ directories ];
196
+ }
197
+
198
+ // check addon
199
+ const config = app.config.addons;
200
+ let filterAddons = [];
201
+ for (let key of Object.keys(config)) {
202
+ if (!config[key].enable) {
203
+ filterAddons.push(key);
204
+ }
205
+ }
206
+
207
+ for (const directory of directories) {
208
+ const addonpaths = globby.sync(files, { cwd: directory, deep: 1, onlyDirectories: true});
209
+ for (const addonName of addonpaths) {
210
+ if (filterAddons.includes(addonName)) continue;
211
+
212
+ let fullpath = path.join(directory, addonName, 'index');
213
+ fullpath = loader.resolveModule(fullpath);
214
+ if (!fs.statSync(fullpath).isFile()) continue;
215
+
216
+ let exports = getExports(fullpath, this.options, addonName);
217
+ if (exports == null) continue;
218
+
219
+ const properties = [addonName];
220
+ if (is.class(exports) || utils.isBytecodeClass(exports)) {
221
+ exports.prototype.pathName = addonName;
222
+ exports.prototype.fullPath = fullpath;
223
+ }
224
+
225
+ items.push({ fullpath, properties, exports });
226
+ }
227
+
228
+ for (const item of items) {
229
+ const property = item.properties[0];
230
+ let obj = item.exports;
231
+ if (obj && !is.primitive(obj)) {
232
+ obj[FULLPATH] = item.fullpath;
233
+ obj[EXPORTS] = true;
234
+ }
235
+
236
+ target[property] = obj;
237
+ }
238
+ }
239
+
240
+ return target;
241
+ }
242
+ }
243
+
244
+ module.exports = FileLoader;
245
+ module.exports.EXPORTS = EXPORTS;
246
+ module.exports.FULLPATH = FULLPATH;
247
+
248
+ // convert file path to an array of properties
249
+ // a/b/c.js => ['a', 'b', 'c']
250
+ function getProperties(filepath, { caseStyle }) {
251
+ // if caseStyle is function, return the result of function
252
+ if (is.function(caseStyle)) {
253
+ const result = caseStyle(filepath);
254
+ assert(is.array(result), `caseStyle expect an array, but got ${result}`);
255
+ return result;
256
+ }
257
+ // use default camelize
258
+ return defaultCamelize(filepath, caseStyle);
259
+ }
260
+
261
+ // Get exports from filepath
262
+ // If exports is null/undefined, it will be ignored
263
+ function getExports(fullpath, { initializer, call, inject }, pathName) {
264
+ let exports = utils.loadFile(fullpath);
265
+
266
+ // process exports as you like
267
+ if (initializer) {
268
+ exports = initializer(exports, { path: fullpath, pathName });
269
+ }
270
+
271
+ // return exports when it's a class or generator
272
+ //
273
+ // module.exports = class Service {};
274
+ // or
275
+ // module.exports = function*() {}
276
+ //new exports;
277
+
278
+ if (is.class(exports) || is.generatorFunction(exports) || is.asyncFunction(exports) || utils.isBytecodeClass(exports)) {
279
+ return exports;
280
+ }
281
+
282
+ // return exports after call when it's a function
283
+ //
284
+ // module.exports = function(app) {
285
+ // return {};
286
+ // }
287
+ if (call && is.function(exports)) {
288
+ exports = exports(inject);
289
+ if (exports != null) {
290
+ return exports;
291
+ }
292
+ }
293
+
294
+ // return exports what is
295
+ return exports;
296
+ }
297
+
298
+ function defaultCamelize(filepath, caseStyle) {
299
+ const properties = filepath.substring(0, filepath.lastIndexOf('.')).split('/');
300
+ return properties.map(property => {
301
+ if (!/^[a-z][a-z0-9_-]*$/i.test(property)) {
302
+ throw new Error(`${property} is not match 'a-z0-9_-' in ${filepath}`);
303
+ }
304
+
305
+ // use default camelize, will capitalize the first letter
306
+ // foo_bar.js > FooBar
307
+ // fooBar.js > FooBar
308
+ // FooBar.js > FooBar
309
+ // FooBar.js > FooBar
310
+ // FooBar.js > fooBar (if lowercaseFirst is true)
311
+ property = property.replace(/[_-][a-z]/ig, s => s.substring(1).toUpperCase());
312
+ let first = property[0];
313
+ switch (caseStyle) {
314
+ case 'lower':
315
+ first = first.toLowerCase();
316
+ break;
317
+ case 'upper':
318
+ first = first.toUpperCase();
319
+ break;
320
+ case 'camel':
321
+ default:
322
+ }
323
+ return first + property.substring(1);
324
+ });
325
+ }