Skip to content

Commit 2c6fbbf

Browse files
whxaxespopomore
authored andcommitted
feat: loader support custom extension (#156)
* feat: loader support custom extension * feat: remove require.extensions * refactor: refactor to require.extensions * test: add unittest for ts * chore: remove tsconfig.json * test: add d.ts * test: test loadCustomApp and loadCustomAgent * test: add more test for custom extend * fix: spelling mistake * feat: add typescript options * docs: update typescript opt to docs * chore: update comment * test: add more unittest for ts * refactor: code optimization * chore: update error msg * test: change beforeEach/afterEach to before/after * feat: add ts check in loadFile * feat: move resolveModule to egg_loader * fix: lint fix * refactor: code optimization
1 parent 9370f86 commit 2c6fbbf

36 files changed

Lines changed: 278 additions & 40 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ coverage
33
.logs
44
npm-debug.log
55
.vscode
6-
.DS_Store
6+
.DS_Store
7+
yarn.lock

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ EggLoader can easily load files or directories in your [egg] project. In additio
7878
- {String} baseDir - current directory of application
7979
- {Object} app - instance of egg application
8080
- {Object} plugins - merge plugins for test
81+
- {Boolean} typescript - whether support typescript
8182
- {Logger} logger - logger instance,default is console
8283

8384
### High Level APIs
@@ -226,7 +227,8 @@ Param | Type | Description
226227
-------------- | -------------- | ------------------------
227228
directory | `String/Array` | directories to be loaded
228229
target | `Object` | attach the target object from loaded files
229-
match | `String/Array` | match the files when load, default to `**/*.js`
230+
match | `String/Array` | match the files when load, default to `**/*.js`(if typescript was true, default to `[ '**/*.(js|ts)', '!**/*.d.ts' ]`)
231+
typescript | `Boolean` | whether support typescript
230232
ignore | `String/Array` | ignore the files when load
231233
initializer | `Function` | 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`
232234
caseStyle | `String/Function` | set property's case when converting a filepath to property list.

lib/egg.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class EggCore extends KoaApplication {
2828
* @param {Object} options - options
2929
* @param {String} [options.baseDir=process.cwd()] - the directory of application
3030
* @param {String} [options.type=application|agent] - whether it's running in app worker or agent worker
31+
* @param {Boolean} [options.typescript] - whether support typescript
3132
* @param {Object} [options.plugins] - custom plugins
3233
* @since 1.0.0
3334
*/
@@ -119,6 +120,7 @@ class EggCore extends KoaApplication {
119120
baseDir: options.baseDir,
120121
app: this,
121122
plugins: options.plugins,
123+
typescript: options.typescript,
122124
logger: this.console,
123125
serverScope: options.serverScope,
124126
});

lib/loader/egg_loader.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class EggLoader {
1818
* @constructor
1919
* @param {Object} options - options
2020
* @param {String} options.baseDir - the directory of application
21+
* @param {Boolean} options.typescript - whether support typescript
2122
* @param {EggCore} options.app - Application instance
2223
* @param {Logger} options.logger - logger
2324
* @param {Object} [options.plugins] - custom plugins
@@ -282,7 +283,7 @@ class EggLoader {
282283
* @since 1.0.0
283284
*/
284285
loadFile(filepath, ...inject) {
285-
if (!fs.existsSync(filepath)) {
286+
if (!filepath || !fs.existsSync(filepath)) {
286287
return null;
287288
}
288289

@@ -354,6 +355,7 @@ class EggLoader {
354355
directory,
355356
target,
356357
inject: this.app,
358+
typescript: this.options.typescript,
357359
}, opt);
358360
new FileLoader(opt).load();
359361
}
@@ -370,6 +372,7 @@ class EggLoader {
370372
directory,
371373
property,
372374
inject: this.app,
375+
typescript: this.options.typescript,
373376
}, opt);
374377
new ContextLoader(opt).load();
375378
}
@@ -391,14 +394,29 @@ class EggLoader {
391394
}
392395

393396
getTypeFiles(filename) {
394-
const files = [ `${filename}.default.js` ];
395-
if (this.serverScope) files.push(`${filename}.${this.serverScope}.js`);
397+
const files = [ `${filename}.default` ];
398+
if (this.serverScope) files.push(`${filename}.${this.serverScope}`);
396399
if (this.serverEnv === 'default') return files;
397400

398-
files.push(`${filename}.${this.serverEnv}.js`);
399-
if (this.serverScope) files.push(`${filename}.${this.serverScope}_${this.serverEnv}.js`);
401+
files.push(`${filename}.${this.serverEnv}`);
402+
if (this.serverScope) files.push(`${filename}.${this.serverScope}_${this.serverEnv}`);
400403
return files;
401404
}
405+
406+
resolveModule(filepath) {
407+
let fullPath;
408+
try {
409+
fullPath = require.resolve(filepath);
410+
} catch (e) {
411+
return undefined;
412+
}
413+
414+
if (!this.options.typescript && fullPath.endsWith('.ts')) {
415+
return undefined;
416+
}
417+
418+
return fullPath;
419+
}
402420
}
403421

404422
/**

lib/loader/file_loader.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const defaults = {
1515
directory: null,
1616
target: null,
1717
match: undefined,
18+
typescript: false,
1819
ignore: undefined,
1920
lowercaseFirst: false,
2021
caseStyle: 'camel',
@@ -37,6 +38,7 @@ class FileLoader {
3738
* @param {String|Array} options.directory - directories to be loaded
3839
* @param {Object} options.target - attach the target object from loaded files
3940
* @param {String} options.match - match the files when load, support glob, default to all js files
41+
* @param {Boolean} options.typescript - whether support typescript, default to false
4042
* @param {String} options.ignore - ignore the files when load, support glob
4143
* @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`
4244
* @param {Boolean} options.call - determine whether invoke when exports is function
@@ -48,6 +50,9 @@ class FileLoader {
4850
constructor(options) {
4951
assert(options.directory, 'options.directory is required');
5052
assert(options.target, 'options.target is required');
53+
if (options.typescript) {
54+
assert(require.extensions['.ts'], '`require.extensions` should contains `.ts` while `options.typescript` was true');
55+
}
5156
this.options = Object.assign({}, defaults, options);
5257

5358
// compatible old options _lowercaseFirst_
@@ -120,8 +125,14 @@ class FileLoader {
120125
* @since 1.0.0
121126
*/
122127
parse() {
123-
let files = this.options.match || [ '**/*.js' ];
124-
files = Array.isArray(files) ? files : [ files ];
128+
let files = this.options.match;
129+
if (!files) {
130+
files = this.options.typescript
131+
? [ '**/*.(js|ts)', '!**/*.d.ts' ]
132+
: [ '**/*.js' ];
133+
} else {
134+
files = Array.isArray(files) ? files : [ files ];
135+
}
125136

126137
let ignore = this.options.ignore;
127138
if (ignore) {

lib/loader/mixin/config.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

33
const debug = require('debug')('egg-core:config');
4-
const fs = require('fs');
54
const path = require('path');
65
const extend = require('extend2');
76
const assert = require('assert');
@@ -55,8 +54,8 @@ module.exports = {
5554

5655
_preloadAppConfig() {
5756
const names = [
58-
'config.default.js',
59-
`config.${this.serverEnv}.js`,
57+
'config.default',
58+
`config.${this.serverEnv}`,
6059
];
6160
const target = {};
6261
for (const filename of names) {
@@ -70,12 +69,13 @@ module.exports = {
7069
const isPlugin = type === 'plugin';
7170
const isApp = type === 'app';
7271

73-
let filepath = path.join(dirpath, 'config', filename);
72+
let filepath = this.resolveModule(path.join(dirpath, 'config', filename));
7473
// let config.js compatible
75-
if (filename === 'config.default.js' && !fs.existsSync(filepath)) {
76-
filepath = path.join(dirpath, 'config/config.js');
74+
if (filename === 'config.default' && !filepath) {
75+
filepath = this.resolveModule(path.join(dirpath, 'config/config'));
7776
}
7877
const config = this.loadFile(filepath, this.appInfo, extraInject);
78+
7979
if (!config) return null;
8080

8181
if (isPlugin || isApp) {

lib/loader/mixin/custom.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ module.exports = {
2222
*/
2323
loadCustomApp() {
2424
this.getLoadUnits()
25-
.forEach(unit => this.loadFile(path.join(unit.path, 'app.js')));
25+
.forEach(unit => this.loadFile(this.resolveModule(path.join(unit.path, 'app'))));
2626
},
2727

2828
/**
2929
* Load agent.js, same as {@link EggLoader#loadCustomApp}
3030
*/
3131
loadCustomAgent() {
3232
this.getLoadUnits()
33-
.forEach(unit => this.loadFile(path.join(unit.path, 'agent.js')));
33+
.forEach(unit => this.loadFile(this.resolveModule(path.join(unit.path, 'agent'))));
3434
},
3535

3636
};

lib/loader/mixin/extend.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const debug = require('debug')('egg-core:extend');
44
const deprecate = require('depd')('egg');
55
const path = require('path');
6-
const utils = require('../../utils');
76

87
const originalPrototypes = {
98
request: require('koa/lib/request'),
@@ -96,21 +95,21 @@ module.exports = {
9695
const isAddUnittest = 'EGG_MOCK_SERVER_ENV' in process.env && this.serverEnv !== 'unittest';
9796
for (let i = 0, l = filepaths.length; i < l; i++) {
9897
const filepath = filepaths[i];
99-
filepaths.push(filepath + `.${this.serverEnv}.js`);
100-
if (isAddUnittest) filepaths.push(filepath + '.unittest.js');
98+
filepaths.push(filepath + `.${this.serverEnv}`);
99+
if (isAddUnittest) filepaths.push(filepath + '.unittest');
101100
}
102101

103102
const mergeRecord = new Map();
104103
for (let filepath of filepaths) {
105-
filepath = utils.resolveModule(filepath);
104+
filepath = this.resolveModule(filepath);
106105
if (!filepath) {
107106
continue;
108107
} else if (filepath.endsWith('/index.js')) {
109108
// TODO: remove support at next version
110109
deprecate(`app/extend/${name}/index.js is deprecated, use app/extend/${name}.js instead`);
111110
}
112111

113-
const ext = utils.loadFile(filepath);
112+
const ext = this.loadFile(filepath);
114113

115114
const properties = Object.getOwnPropertyNames(ext)
116115
.concat(Object.getOwnPropertySymbols(ext));

lib/loader/mixin/plugin.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ module.exports = {
5656
*/
5757
loadPlugin() {
5858
// loader plugins from application
59-
const appPlugins = this.readPluginConfigs(path.join(this.options.baseDir, 'config/plugin.default.js'));
59+
const appPlugins = this.readPluginConfigs(path.join(this.options.baseDir, 'config/plugin.default'));
6060
debug('Loaded app plugins: %j', Object.keys(appPlugins));
6161

6262
// loader plugins from framework
63-
const eggPluginConfigPaths = this.eggPaths.map(eggPath => path.join(eggPath, 'config/plugin.default.js'));
63+
const eggPluginConfigPaths = this.eggPaths.map(eggPath => path.join(eggPath, 'config/plugin.default'));
6464
const eggPlugins = this.readPluginConfigs(eggPluginConfigPaths);
6565
debug('Loaded egg plugins: %j', Object.keys(eggPlugins));
6666

@@ -159,20 +159,22 @@ module.exports = {
159159
}
160160

161161
const plugins = {};
162-
for (let configPath of newConfigPaths) {
162+
for (const configPath of newConfigPaths) {
163+
let filepath = this.resolveModule(configPath);
164+
163165
// let plugin.js compatible
164-
if (configPath.endsWith('plugin.default.js') && !fs.existsSync(configPath)) {
165-
configPath = configPath.replace(/plugin\.default\.js$/, 'plugin.js');
166+
if (configPath.endsWith('plugin.default') && !filepath) {
167+
filepath = this.resolveModule(configPath.replace(/plugin\.default$/, 'plugin'));
166168
}
167169

168-
if (!fs.existsSync(configPath)) {
170+
if (!filepath) {
169171
continue;
170172
}
171173

172-
const config = loadFile(configPath);
174+
const config = loadFile(filepath);
173175

174176
for (const name in config) {
175-
this.normalizePluginConfig(config, name, configPath);
177+
this.normalizePluginConfig(config, name, filepath);
176178
}
177179

178180
this._extendPlugins(plugins, config);

lib/loader/mixin/router.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ module.exports = {
1212
*/
1313
loadRouter() {
1414
// 加载 router.js
15-
this.loadFile(path.join(this.options.baseDir, 'app/router.js'));
15+
this.loadFile(this.resolveModule(path.join(this.options.baseDir, 'app/router')));
1616
},
1717
};

0 commit comments

Comments
 (0)