Skip to content

Commit

Permalink
refactor: implement Loader instead of loading
Browse files Browse the repository at this point in the history
  • Loading branch information
popomore committed Jul 30, 2016
1 parent 9ddef9d commit 81c590c
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 19 deletions.
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');
}

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
128 changes: 128 additions & 0 deletions lib/loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
'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}' with ${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;
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);
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) {
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) {
return s.substring(1).toUpperCase();
});
if (lowercaseFirst) {
result = result[0].toLowerCase() + result.substring(1);
}
return result;
});
}

function getExports(fullpath, initializer, isCall, inject) {
let exports = interopRequire(fullpath);

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

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);

// 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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@
"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"
}
}
2 changes: 1 addition & 1 deletion test/load_proxy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('test/load_proxy.test.js', function() {
it('should throw when dulplicate', function() {
(function() {
utils.createApp('proxy-override');
}).should.throw('can\'t overwrite property queryProxy');
}).should.throw(/^can't overwrite property 'queryProxy'/);
});

describe('subdir', function() {
Expand Down
2 changes: 1 addition & 1 deletion test/load_service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('test/load_service.test.js', function() {
it('should throw when dulplicate', function() {
(function() {
utils.createApp('service-override');
}).should.throw('can\'t overwrite property foo');
}).should.throw(/^can't overwrite property 'foo'/);
});

it('should check es6', function() {
Expand Down

0 comments on commit 81c590c

Please sign in to comment.