diff --git a/lib/base_loader.js b/lib/base_loader.js index dd3ddfac..9ab75658 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -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 { @@ -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); } @@ -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); + } + } /** diff --git a/lib/loader.js b/lib/loader.js new file mode 100644 index 00000000..a0ebf96d --- /dev/null +++ b/lib/loader.js @@ -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; + 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); + 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) { + 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; + 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; +} diff --git a/lib/middleware_loader.js b/lib/middleware_loader.js index 033b4ff1..e530c304 100644 --- a/lib/middleware_loader.js +++ b/lib/middleware_loader.js @@ -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; @@ -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); diff --git a/lib/proxy_loader.js b/lib/proxy_loader.js index 71eee077..c0c07ea7 100644 --- a/lib/proxy_loader.js +++ b/lib/proxy_loader.js @@ -1,7 +1,6 @@ 'use strict'; const join = require('path').join; -const loading = require('loading'); const classLoader = Symbol('classLoader'); const utils = require('./utils'); @@ -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', { diff --git a/lib/service_loader.js b/lib/service_loader.js index 1804ba10..a031de78 100644 --- a/lib/service_loader.js +++ b/lib/service_loader.js @@ -1,7 +1,6 @@ 'use strict'; const path = require('path'); -const loading = require('loading'); const utils = require('./utils'); const classLoader = Symbol('classLoader'); @@ -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, diff --git a/package.json b/package.json index fd52c156..3fd2521f 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "koa": "1", "koa-router": "4", "mm": "1", + "pedding": "^1.0.0", "should": "9", "supertest": "1" }, @@ -44,8 +45,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" } -} +} \ No newline at end of file diff --git a/test/fixtures/load_dirs/babel/UserProxy.js b/test/fixtures/load_dirs/babel/UserProxy.js new file mode 100644 index 00000000..43d20a85 --- /dev/null +++ b/test/fixtures/load_dirs/babel/UserProxy.js @@ -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; \ No newline at end of file diff --git a/test/fixtures/load_dirs/class/UserProxy.js b/test/fixtures/load_dirs/class/UserProxy.js new file mode 100644 index 00000000..48534130 --- /dev/null +++ b/test/fixtures/load_dirs/class/UserProxy.js @@ -0,0 +1,15 @@ +'use strict'; + +class UserProxy { + constructor() { + this.user = { + name: 'xiaochen.gaoxc', + }; + } + + getUser() { + return this.user; + } +} + +module.exports = UserProxy; diff --git a/test/fixtures/load_dirs/dao/TestClass.js b/test/fixtures/load_dirs/dao/TestClass.js new file mode 100644 index 00000000..aea1ecbc --- /dev/null +++ b/test/fixtures/load_dirs/dao/TestClass.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = class TestClass { + constructor() { + this.user = { + name: 'kai.fangk', + }; + } +} diff --git a/test/fixtures/load_dirs/dao/testFunction.js b/test/fixtures/load_dirs/dao/testFunction.js new file mode 100644 index 00000000..57d482dc --- /dev/null +++ b/test/fixtures/load_dirs/dao/testFunction.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = function(obj) { + return { + user: { + name: 'kai.fangk', + }, + }; +}; diff --git a/test/fixtures/load_dirs/dao/testReturnFunction.js b/test/fixtures/load_dirs/dao/testReturnFunction.js new file mode 100644 index 00000000..980f4967 --- /dev/null +++ b/test/fixtures/load_dirs/dao/testReturnFunction.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function(obj) { + return function() { + return { + user: { + name: 'kai.fangk', + }, + } + }; +}; diff --git a/test/fixtures/load_dirs/error/dotdir/dot.dir/a.js b/test/fixtures/load_dirs/error/dotdir/dot.dir/a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/error/underscore-dir/_underscore/a.js b/test/fixtures/load_dirs/error/underscore-dir/_underscore/a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/error/underscore-file-in-dir/dir/_a.js b/test/fixtures/load_dirs/error/underscore-file-in-dir/dir/_a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/error/underscore-file/_private.js b/test/fixtures/load_dirs/error/underscore-file/_private.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/es6_module/mod.js b/test/fixtures/load_dirs/es6_module/mod.js new file mode 100644 index 00000000..fc64c89c --- /dev/null +++ b/test/fixtures/load_dirs/es6_module/mod.js @@ -0,0 +1,6 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = function() { + return { a: 1 }; +}; diff --git a/test/fixtures/load_dirs/ignore/a.js b/test/fixtures/load_dirs/ignore/a.js new file mode 100644 index 00000000..d3ccb9a4 --- /dev/null +++ b/test/fixtures/load_dirs/ignore/a.js @@ -0,0 +1,3 @@ +module.exports = function() { + return { a: 1 }; +}; diff --git a/test/fixtures/load_dirs/ignore/util/a.js b/test/fixtures/load_dirs/ignore/util/a.js new file mode 100644 index 00000000..aa55c2a1 --- /dev/null +++ b/test/fixtures/load_dirs/ignore/util/a.js @@ -0,0 +1,4 @@ +module.exports = { + method1: function() { + } +}; diff --git a/test/fixtures/load_dirs/ignore/util/b/b.js b/test/fixtures/load_dirs/ignore/util/b/b.js new file mode 100644 index 00000000..f95f140a --- /dev/null +++ b/test/fixtures/load_dirs/ignore/util/b/b.js @@ -0,0 +1 @@ +module.exports = { b: 2 }; diff --git a/test/fixtures/load_dirs/lowercase/SomeClass.js b/test/fixtures/load_dirs/lowercase/SomeClass.js new file mode 100644 index 00000000..3dcf1151 --- /dev/null +++ b/test/fixtures/load_dirs/lowercase/SomeClass.js @@ -0,0 +1,5 @@ +exports.getByName = function (name, callback) { + setTimeout(function () { + callback(null, {name: name}); + }, 1); +}; \ No newline at end of file diff --git a/test/fixtures/load_dirs/lowercase/SomeDir/SomeSubClass.js b/test/fixtures/load_dirs/lowercase/SomeDir/SomeSubClass.js new file mode 100644 index 00000000..3dcf1151 --- /dev/null +++ b/test/fixtures/load_dirs/lowercase/SomeDir/SomeSubClass.js @@ -0,0 +1,5 @@ +exports.getByName = function (name, callback) { + setTimeout(function () { + callback(null, {name: name}); + }, 1); +}; \ No newline at end of file diff --git a/test/fixtures/load_dirs/middlewares/app/m1.js b/test/fixtures/load_dirs/middlewares/app/m1.js new file mode 100644 index 00000000..ea41b01d --- /dev/null +++ b/test/fixtures/load_dirs/middlewares/app/m1.js @@ -0,0 +1 @@ +module.exports = function () {}; diff --git a/test/fixtures/load_dirs/middlewares/app/m2.js b/test/fixtures/load_dirs/middlewares/app/m2.js new file mode 100644 index 00000000..ea41b01d --- /dev/null +++ b/test/fixtures/load_dirs/middlewares/app/m2.js @@ -0,0 +1 @@ +module.exports = function () {}; diff --git a/test/fixtures/load_dirs/middlewares/app/other/bar.js b/test/fixtures/load_dirs/middlewares/app/other/bar.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/middlewares/app/other/foo/ok.js b/test/fixtures/load_dirs/middlewares/app/other/foo/ok.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/middlewares/default/dm1.js b/test/fixtures/load_dirs/middlewares/default/dm1.js new file mode 100644 index 00000000..ea41b01d --- /dev/null +++ b/test/fixtures/load_dirs/middlewares/default/dm1.js @@ -0,0 +1 @@ +module.exports = function () {}; diff --git a/test/fixtures/load_dirs/middlewares/default/dm2.js b/test/fixtures/load_dirs/middlewares/default/dm2.js new file mode 100644 index 00000000..ea41b01d --- /dev/null +++ b/test/fixtures/load_dirs/middlewares/default/dm2.js @@ -0,0 +1 @@ +module.exports = function () {}; diff --git a/test/fixtures/load_dirs/middlewares/default/session.js b/test/fixtures/load_dirs/middlewares/default/session.js new file mode 100644 index 00000000..ea41b01d --- /dev/null +++ b/test/fixtures/load_dirs/middlewares/default/session.js @@ -0,0 +1 @@ +module.exports = function () {}; diff --git a/test/fixtures/load_dirs/overwrite_services/foo.js b/test/fixtures/load_dirs/overwrite_services/foo.js new file mode 100644 index 00000000..6e7b735d --- /dev/null +++ b/test/fixtures/load_dirs/overwrite_services/foo.js @@ -0,0 +1,21 @@ +/**! + * loading - test/fixtures/services/foo.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +exports.get = function (callback) { + setTimeout(function () { + callback(null, 'overwrite bar'); + }, 1); +}; diff --git a/test/fixtures/load_dirs/services/bar.js/aaa b/test/fixtures/load_dirs/services/bar.js/aaa new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/services/dir/abc.js b/test/fixtures/load_dirs/services/dir/abc.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/services/dir/service.js b/test/fixtures/load_dirs/services/dir/service.js new file mode 100644 index 00000000..a56b9112 --- /dev/null +++ b/test/fixtures/load_dirs/services/dir/service.js @@ -0,0 +1,6 @@ +module.exports = function (app) { + return { + load: true, + app: app + }; +}; diff --git a/test/fixtures/load_dirs/services/foo.js b/test/fixtures/load_dirs/services/foo.js new file mode 100644 index 00000000..d97b9f7b --- /dev/null +++ b/test/fixtures/load_dirs/services/foo.js @@ -0,0 +1,21 @@ +/**! + * loading - test/fixtures/services/foo.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +exports.get = function (callback) { + setTimeout(function () { + callback(null, 'bar'); + }, 1); +}; diff --git a/test/fixtures/load_dirs/services/foo_bar_hello.js b/test/fixtures/load_dirs/services/foo_bar_hello.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/services/foo_service.js b/test/fixtures/load_dirs/services/foo_service.js new file mode 100644 index 00000000..fcfdfc09 --- /dev/null +++ b/test/fixtures/load_dirs/services/foo_service.js @@ -0,0 +1,5 @@ +module.exports = function(obj) { + return { + a: 1 + }; +}; diff --git a/test/fixtures/load_dirs/services/hyphen-dir/a.js b/test/fixtures/load_dirs/services/hyphen-dir/a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/services/null.js b/test/fixtures/load_dirs/services/null.js new file mode 100644 index 00000000..ae041f4d --- /dev/null +++ b/test/fixtures/load_dirs/services/null.js @@ -0,0 +1,3 @@ +module.exports = function () { + return; +}; diff --git a/test/fixtures/load_dirs/services/tmp b/test/fixtures/load_dirs/services/tmp new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/services/underscore_dir/a.js b/test/fixtures/load_dirs/services/underscore_dir/a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/load_dirs/services/userProfile.js b/test/fixtures/load_dirs/services/userProfile.js new file mode 100644 index 00000000..39c4fc32 --- /dev/null +++ b/test/fixtures/load_dirs/services/userProfile.js @@ -0,0 +1,22 @@ +/**! + * loading - test/fixtures/services/userProfile.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.github.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + + +exports.getByName = function (name, callback) { + setTimeout(function () { + callback(null, {name: name}); + }, 1); +}; diff --git a/test/fixtures/load_dirs/syntax_error/error.js b/test/fixtures/load_dirs/syntax_error/error.js new file mode 100644 index 00000000..250dab53 --- /dev/null +++ b/test/fixtures/load_dirs/syntax_error/error.js @@ -0,0 +1,3 @@ +modulde.exports = function (a) { + yield a; +} diff --git a/test/load_proxy.test.js b/test/load_proxy.test.js index 006b8d5a..f1a00e4c 100644 --- a/test/load_proxy.test.js +++ b/test/load_proxy.test.js @@ -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() { diff --git a/test/load_service.test.js b/test/load_service.test.js index 0335304d..a0fa0350 100644 --- a/test/load_service.test.js +++ b/test/load_service.test.js @@ -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() { diff --git a/test/loader.test.js b/test/loader.test.js new file mode 100644 index 00000000..506fa5df --- /dev/null +++ b/test/loader.test.js @@ -0,0 +1,225 @@ +'use strict'; + +/** + * Module dependencies. + */ + +const should = require('should'); +const pedding = require('pedding'); +const path = require('path'); +const Loader = require('../lib/loader'); +const dirBase = path.join(__dirname, 'fixtures/load_dirs'); + +describe('test/loader.test.js', () => { + + it('should load files', done => { + const services = {}; + new Loader({ + directory: path.join(dirBase, 'services'), + target: services, + }).load(); + + services.should.have.keys('dir', 'foo', 'fooBarHello', 'fooService', 'hyphenDir', 'underscoreDir', 'userProfile'); + should.exists(services.dir.abc); + should.exists(services.dir.service); + should.exists(services.foo); + should.exists(services.fooBarHello); + should.exists(services.fooService); + should.exists(services.hyphenDir.a); + should.exists(services.underscoreDir.a); + should.exists(services.userProfile); + + done = pedding(2, done); + services.foo.get((err, v) => { + should.not.exist(err); + v.should.equal('bar'); + done(); + }); + services.userProfile.getByName('mk2', (err, user) => { + should.not.exist(err); + user.should.eql({ name: 'mk2' }); + done(); + }); + + services.dir.service.should.have.keys('load', 'app'); + services.dir.service.load.should.equal(true); + }); + + it('should not overwrite property', () => { + const app = { + services: { + foo: {}, + }, + }; + (function() { + new Loader({ + directory: path.join(dirBase, 'services'), + target: app.services, + }).load(); + }).should.throw(/^can't overwrite property 'foo'/); + }); + + it('should not overwrite property from loading', () => { + const app = { services: {} }; + (function() { + new Loader({ + directory: [ + path.join(dirBase, 'services'), + path.join(dirBase, 'overwrite_services'), + ], + target: app.services, + }).load(); + }).should.throw(/^can't overwrite property 'foo'/); + }); + + it('should overwrite property from loading', () => { + const app = { services: {} }; + new Loader({ + directory: [ + path.join(dirBase, 'services'), + path.join(dirBase, 'overwrite_services'), + ], + override: true, + target: app.services, + }).load(); + }); + + it('should loading without call function', () => { + const app = { services: {} }; + new Loader({ + directory: path.join(dirBase, 'services'), + target: app.services, + call: false, + }).load(); + app.services.fooService().should.eql({ a: 1 }); + }); + + it('should loading without call es6 class', () => { + const app = { services: {} }; + new Loader({ + directory: path.join(dirBase, 'class'), + target: app.services, + }).load(); + (function() { + app.services.UserProxy(); + }).should.throw(/cannot be invoked without 'new'/); + const instance = new app.services.UserProxy(); + instance.getUser().should.eql({ name: 'xiaochen.gaoxc' }); + }); + + it('should loading without call babel class', () => { + const app = { services: {} }; + new Loader({ + directory: path.join(dirBase, 'babel'), + target: app.services, + }).load(); + const instance = new app.services.UserProxy(); + instance.getUser().should.eql({ name: 'xiaochen.gaoxc' }); + }); + + it.skip('should only load property match the filers', () => { + const app = { middlewares: {} }; + new Loader({ + directory: [ + path.join(dirBase, 'middlewares/default'), + path.join(dirBase, 'middlewares/app'), + ], + target: app.middlewares, + call: false, + filters: [ 'm1', 'm2', 'dm1', 'dm2' ], + }).load(); + app.middlewares.should.have.keys('m1', 'm2', 'dm1', 'dm2'); + }); + + it('should support ignore', () => { + const app = { services: {} }; + new Loader({ + directory: path.join(dirBase, 'ignore'), + target: app.services, + ignore: 'util/**', + }).load(); + app.services.should.have.property('a', { a: 1 }); + }); + + it('should support lowercase first letter', () => { + const app = { services: {} }; + new Loader({ + directory: path.join(dirBase, 'lowercase'), + target: app.services, + lowercaseFirst: true, + }).load(); + app.services.should.have.properties('someClass', 'someDir'); + app.services.someDir.should.have.property('someSubClass'); + }); + + it('should support options.initializer with es6 class ', () => { + const app = { dao: {} }; + new Loader({ + directory: path.join(dirBase, 'dao'), + target: app.dao, + ignore: 'util/**', + initializer(exports) { + return new exports(app); + }, + }).load(); + app.dao.should.have.property('TestClass'); + app.dao.TestClass.user.should.eql({ name: 'kai.fangk' }); + app.dao.should.have.property('testFunction', { user: { name: 'kai.fangk' } }); + app.dao.should.have.property('testReturnFunction', { user: { name: 'kai.fangk' } }); + }); + + it('should pass es6 module', () => { + const app = { model: {} }; + new Loader({ + directory: path.join(dirBase, 'es6_module'), + target: app.model, + }).load(); + app.model.mod.should.eql({ a: 1 }); + }); + + it('should contain syntax error filepath', () => { + const app = { model: {} }; + (function() { + new Loader({ + directory: path.join(dirBase, 'syntax_error'), + target: app.model, + }).load(); + }).should.throw(/load file: .*?test\/fixtures\/load_dirs\/syntax_error\/error\.js, error:/); + }); + + it('should throw when directory contains dot', () => { + const mod = {}; + (function() { + new Loader({ + directory: path.join(dirBase, 'error/dotdir'), + target: mod, + }).load(); + }).should.throw('dot.dir is not match \'a-z0-9_-\' in dot.dir/a.js'); + }); + + it('should throw when directory contains dot', () => { + const mod = {}; + (function() { + new Loader({ + directory: path.join(dirBase, 'error/underscore-dir'), + target: mod, + }).load(); + }).should.throw('_underscore is not match \'a-z0-9_-\' in _underscore/a.js'); + (function() { + new Loader({ + directory: path.join(dirBase, 'error/underscore-file-in-dir'), + target: mod, + }).load(); + }).should.throw('_a is not match \'a-z0-9_-\' in dir/_a.js'); + }); + + it('should throw when file starts with underscore', () => { + const mod = {}; + (function() { + new Loader({ + directory: path.join(dirBase, 'error/underscore-file'), + target: mod, + }).load(); + }).should.throw('_private is not match \'a-z0-9_-\' in _private.js'); + }); +});