Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions lib/base_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ const fs = require('fs');
const path = require('path');
const assert = require('assert');
const isFunction = require('is-type-of').function;
const loading = require('loading');
const interopRequire = require('interop-require');
const debug = require('debug')('egg:loader');
const Loader = require('./loader');

class EggLoader {

Expand Down Expand Up @@ -129,10 +129,8 @@ class EggLoader {
opt = Object.assign({ lowercaseFirst: true }, opt);
const controllerBase = path.join(this.options.baseDir, 'app/controller');

delete app.controller;
app.controller = {};

loading(controllerBase, opt).into(app, 'controller');
this.loadToApp(controllerBase, 'controller', opt);
app.controllers = app.controller;
app.coreLogger.info('[egg:loader] Controller loaded: %s', controllerBase);
}

Expand Down Expand Up @@ -285,6 +283,20 @@ class EggLoader {
throw new Error('Can not get appname from package.json');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing name from package.json ?

}

loadTo(directory, target, opt) {
opt = Object.assign({}, {
directory,
target,
inject: this.app,
}, opt);
new Loader(opt).load();
}

loadToApp(directory, field, opt) {
const target = this.app[field] = {};
this.loadTo(directory, target, opt);
}

}

/**
Expand Down
135 changes: 135 additions & 0 deletions lib/loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
'use strict';

const assert = require('assert');
const fs = require('fs');
const debug = require('debug')('egg-loader:loader');
const path = require('path');
const globby = require('globby');
const interopRequire = require('interop-require');
const is = require('is-type-of');
const FULLPATH = Symbol('EGG_LOADER_ITEM_FULLPATH');

const defaults = {
directory: null,
target: null,
ignore: undefined,
lowercaseFirst: false,
initializer: null,
call: true,
override: false,
inject: undefined,
};

class Loader {

constructor(options) {
assert(options.directory, 'options.directory is required');
assert(options.target, 'options.target is required');
this.options = Object.assign({}, defaults, options);
}

load() {
const items = this.parse();
const target = this.options.target;
for (const item of items) {
debug('loading item %j', item);
item.properties.reduce((target, property, index) => {
let obj;
const properties = item.properties.slice(0, index + 1).join('.');
if (index === item.properties.length - 1) {
if (property in target) {
if (!this.options.override) throw new Error(`can't overwrite property '${properties}' from ${target[property][FULLPATH]} by ${item.fullpath}`);
}
obj = item.exports;
if (obj) obj[FULLPATH] = item.fullpath;
} else {
obj = target[property] || {};
}
target[property] = obj;
debug('loaded %s', properties);
return obj;
}, target);
}
return target;
}

parse() {
const files = [ '**/*.js' ];
if (typeof this.options.ignore === 'string') {
files.push('!' + this.options.ignore);
}

let directories = this.options.directory;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以直接 const directories = [].concat(this.options.directory || []);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不是一个意思吧,directory 如果非数组会有问题吧。

if (!Array.isArray(directories)) {
directories = [ directories ];
}

const items = [];
debug('parsing %j', directories);
for (const directory of directories) {
const filepaths = globby.sync(files, { cwd: directory });
for (const filepath of filepaths) {
const fullpath = path.join(directory, filepath);
if (!fs.statSync(fullpath).isFile()) {
continue;
}
const properties = getProperties(filepath, this.options.lowercaseFirst);
const exports = getExports(fullpath, this.options.initializer, this.options.call, this.options.inject);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的 exports 和 config 那里是不是换个变量名,毕竟和 commonjs 的 exports 全局变量同名了,万一不小心~

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里应该不影响吧,作用域关系

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是不影响, 只是个人习惯不喜欢变量名覆盖, 一不小心就~
先这样吧.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

es6 完全没有这个问题。

if (exports == null) continue;
items.push({ fullpath, properties, exports });
debug('parse %s, properties %j, export %j', fullpath, properties, exports);
}
}

return items;
}

}

module.exports = Loader;

// a/b/c.js => ['a', 'b', 'c']
function getProperties(filepath, lowercaseFirst) {
return filepath
.replace('.js', '')
.split('/')
.map(function(property) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

property =>

if (!/^[a-z][a-z0-9_-]*$/i.test(property)) {
throw new Error(`${property} is not match 'a-z0-9_-' in ${filepath}`);
}
let result = property.replace(/[_-][a-z]/ig, function(s) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const result

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个应该是 let

return s.substring(1).toUpperCase();
});
if (lowercaseFirst) {
result = result[0].toLowerCase() + result.substring(1);
}
return result;
});
}

function getExports(fullpath, initializer, isCall, inject) {
let exports;
try {
exports = interopRequire(fullpath);
} catch (err) {
err.message = 'load file: ' + fullpath + ', error: ' + err.message;
throw err;
}

if (initializer) {
exports = initializer(exports);
}

if (is.class(exports) || is.generatorFunction(exports)) {
return exports;
}

if (isCall && is.function(exports)) {
exports = exports(inject);
if (exports != null) {
return exports;
}
}

return exports;
}
4 changes: 1 addition & 3 deletions lib/middleware_loader.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const join = require('path').join;
const loading = require('loading');
const is = require('is-type-of');
const debug = require('debug')('egg:loader:middleware');
const inspect = require('util').inspect;
Expand Down Expand Up @@ -36,8 +35,7 @@ module.exports = {
}, opt);
const middlewarePaths = this.loadDirs().map(dir => join(dir, 'app/middleware'));

delete app.middlewares;
loading(middlewarePaths, opt).into(app, 'middlewares');
this.loadToApp(middlewarePaths, 'middlewares', opt);

app.coreLogger.info('Use coreMiddleware order: %j', this.config.coreMiddleware);
app.coreLogger.info('Use appMiddleware order: %j', this.config.appMiddleware);
Expand Down
4 changes: 1 addition & 3 deletions lib/proxy_loader.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const join = require('path').join;
const loading = require('loading');
const classLoader = Symbol('classLoader');
const utils = require('./utils');

Expand All @@ -21,8 +20,7 @@ module.exports = {
opt = Object.assign({ call: true, lowercaseFirst: true }, opt);
const arr = this.loadDirs().map(dir => join(dir, 'app/proxy'));
// load proxy classes to app.proxyClasses
delete app.proxyClasses;
loading(arr, opt).into(app, 'proxyClasses');
this.loadToApp(arr, 'proxyClasses', opt);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这样 proxy 就支持多级了?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个还不支持,这里只是切换 loading

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

喔, 手机上不方便看, 看到 service 和 proxy 貌似现在 loader 差不多.


// this.proxy.demoQuery.getUser(uid)
Object.defineProperty(app.context, 'proxy', {
Expand Down
6 changes: 2 additions & 4 deletions lib/service_loader.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const path = require('path');
const loading = require('loading');
const utils = require('./utils');
const classLoader = Symbol('classLoader');

Expand All @@ -18,15 +17,14 @@ module.exports = {
*/
loadService(opt) {
const app = this.app;
opt = Object.assign({ call: false, lowercaseFirst: true }, opt);
const servicePaths = this.loadDirs().map(dir => {
const servicePath = path.join(dir, 'app/service');
return servicePath;
});

// 载入到 app.serviceClasses
delete app.serviceClasses;
loading(servicePaths, opt).into(app, 'serviceClasses');
opt = Object.assign({ call: false, lowercaseFirst: true }, opt);
this.loadToApp(servicePaths, 'serviceClasses', opt);

/**
* 可以访问到当前应用配置的所有 service,
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,16 @@
"koa": "1",
"koa-router": "4",
"mm": "1",
"pedding": "^1.0.0",
"should": "9",
"supertest": "1"
},
"dependencies": {
"debug": "^2.2.0",
"depd": "^1.1.0",
"extend": "^3.0.0",
"globby": "^6.0.0",
"interop-require": "^1.0.0",
"is-type-of": "^1.0.0",
"loading": "^1.12.0"
"is-type-of": "^1.0.0"
}
}
}
32 changes: 32 additions & 0 deletions test/fixtures/load_dirs/babel/UserProxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

var _temporalUndefined = {};

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

var UserProxy = _temporalUndefined;

function _temporalAssertDefined(val, name, undef) { if (val === undef) { throw new ReferenceError(name + ' is not defined - temporal dead zone'); } return true; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

UserProxy = (function () {
function UserProxy() {
_classCallCheck(this, _temporalAssertDefined(UserProxy, 'UserProxy', _temporalUndefined) && UserProxy);

this.user = {
name: 'xiaochen.gaoxc'
};
}

_createClass(_temporalAssertDefined(UserProxy, 'UserProxy', _temporalUndefined) && UserProxy, [{
key: 'getUser',
value: function getUser() {
return this.user;
}
}]);

return _temporalAssertDefined(UserProxy, 'UserProxy', _temporalUndefined) && UserProxy;
})();

module.exports = _temporalAssertDefined(UserProxy, 'UserProxy', _temporalUndefined) && UserProxy;
15 changes: 15 additions & 0 deletions test/fixtures/load_dirs/class/UserProxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

class UserProxy {
constructor() {
this.user = {
name: 'xiaochen.gaoxc',
};
}

getUser() {
return this.user;
}
}

module.exports = UserProxy;
9 changes: 9 additions & 0 deletions test/fixtures/load_dirs/dao/TestClass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

module.exports = class TestClass {
constructor() {
this.user = {
name: 'kai.fangk',
};
}
}
9 changes: 9 additions & 0 deletions test/fixtures/load_dirs/dao/testFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

module.exports = function(obj) {
return {
user: {
name: 'kai.fangk',
},
};
};
11 changes: 11 additions & 0 deletions test/fixtures/load_dirs/dao/testReturnFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

module.exports = function(obj) {
return function() {
return {
user: {
name: 'kai.fangk',
},
}
};
};
Empty file.
Empty file.
Empty file.
Empty file.
6 changes: 6 additions & 0 deletions test/fixtures/load_dirs/es6_module/mod.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

Object.defineProperty(exports, "__esModule", { value: true });
exports.default = function() {
return { a: 1 };
};
3 changes: 3 additions & 0 deletions test/fixtures/load_dirs/ignore/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function() {
return { a: 1 };
};
4 changes: 4 additions & 0 deletions test/fixtures/load_dirs/ignore/util/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
method1: function() {
}
};
1 change: 1 addition & 0 deletions test/fixtures/load_dirs/ignore/util/b/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { b: 2 };
5 changes: 5 additions & 0 deletions test/fixtures/load_dirs/lowercase/SomeClass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
exports.getByName = function (name, callback) {
setTimeout(function () {
callback(null, {name: name});
}, 1);
};
5 changes: 5 additions & 0 deletions test/fixtures/load_dirs/lowercase/SomeDir/SomeSubClass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
exports.getByName = function (name, callback) {
setTimeout(function () {
callback(null, {name: name});
}, 1);
};
1 change: 1 addition & 0 deletions test/fixtures/load_dirs/middlewares/app/m1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = function () {};
1 change: 1 addition & 0 deletions test/fixtures/load_dirs/middlewares/app/m2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = function () {};
Empty file.
Empty file.
1 change: 1 addition & 0 deletions test/fixtures/load_dirs/middlewares/default/dm1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = function () {};
1 change: 1 addition & 0 deletions test/fixtures/load_dirs/middlewares/default/dm2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = function () {};
1 change: 1 addition & 0 deletions test/fixtures/load_dirs/middlewares/default/session.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = function () {};
Loading