Skip to content

Commit

Permalink
feat: resume
Browse files Browse the repository at this point in the history
  • Loading branch information
xuqingshan committed Sep 1, 2020
1 parent 0b66e25 commit ece96ee
Show file tree
Hide file tree
Showing 24 changed files with 1,262 additions and 1,147 deletions.
Binary file added drawing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added drawing2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
177 changes: 177 additions & 0 deletions node相关/eggsource/4.loadController的挂载.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
```js
loadController(opt) {
this.timing.start('Load Controller');
opt = Object.assign({
caseStyle: 'lower',
directory: path.join(this.options.baseDir, 'app/controller'),
initializer: (obj, opt) => {
if (is.function(obj) && !is.generatorFunction(obj) && !is.class(obj) && !is.asyncFunction(obj)) {
obj = obj(this.app);
}
if (is.class(obj)) {
obj.prototype.pathName = opt.pathName;
obj.prototype.fullPath = opt.path;
return wrapClass(obj);
}
if (is.object(obj)) {
return wrapObject(obj, opt.path);
}
// support generatorFunction for forward compatbility
if (is.generatorFunction(obj) || is.asyncFunction(obj)) {
return wrapObject({ 'module.exports': obj }, opt.path)['module.exports'];
}
return obj;
},
}, opt);
const controllerBase = opt.directory;

this.loadToApp(controllerBase, 'controller', opt);
this.options.logger.info('[egg:loader] Controller loaded: %s', controllerBase);
this.timing.end('Load Controller');
},
```

1. opt:
```js
opt = Object.assign({
caseStyle: 'lower',
directory: path.join(this.options.baseDir, 'app/controller'),
initializer: (obj, opt) => {
if (is.function(obj) && !is.generatorFunction(obj) && !is.class(obj) && !is.asyncFunction(obj)) {
obj = obj(this.app);
}
if (is.class(obj)) {
obj.prototype.pathName = opt.pathName;
obj.prototype.fullPath = opt.path;
return wrapClass(obj);
}
if (is.object(obj)) {
return wrapObject(obj, opt.path);
}
if (is.generatorFunction(obj) || is.asyncFunction(obj)) {
return wrapObject({ 'module.exports': obj }, opt.path)['module.exports'];
}
return obj;
},
}, opt);
// opt 是 undefined,这个opt操作初始化了一个opt对象,供后续的fileloader调用
```
2. new FileLoader(opt)的过程;
将opt复制一份挂载到 this.options上;

3. load过程:
先parse:
files = ['**/*.js'];
directories = ['app/controller'];
遍历app/controller下的所有文件
取出每个文件的:
properties, // app/service/foo/bar.js => [ 'foo', 'bar' ]
将路径解析成字符串;
pathName,// 文件路径;

exports = getExports(fullpath, this.options, pathName);
获取controller内容,执行initializer;
obj.prototype.pathName = opt.pathName;
obj.prototype.fullPath = opt.path;
return wrapClass(obj);

```js
// Get exports from filepath
// If exports is null/undefined, it will be ignored
function getExports(fullpath, { initializer, call, inject }, pathName) {
let exports = utils.loadFile(fullpath);
if (initializer) {
// { index1: classControllerMiddleware,
// index2: classControllerMiddleware
// }
exports = initializer(exports, { path: fullpath, pathName });
}
if (is.class(exports) || is.generatorFunction(exports) || is.asyncFunction(exports)) {
return exports;
}
if (call && is.function(exports)) {
exports = exports(inject);
if (exports != null) {
return exports;
}
}
return exports;
}
```

在wrapClass内部:

```js
// wrap the class, yield a object with middlewares
function wrapClass(Controller) {
let proto = Controller.prototype;
const ret = {};
// tracing the prototype chain
while (proto !== Object.prototype) {
const keys = Object.getOwnPropertyNames(proto);
for (const key of keys) {
// getOwnPropertyNames will return constructor
// that should be ignored
if (key === 'constructor') {
continue;
}
// skip getter, setter & non-function properties
const d = Object.getOwnPropertyDescriptor(proto, key);
// prevent to override sub method
if (is.function(d.value) && !ret.hasOwnProperty(key)) {
ret[key] = methodToMiddleware(Controller, key);
ret[key][FULLPATH] = Controller.prototype.fullPath + '#' + Controller.name + '.' + key + '()';
}
}
proto = Object.getPrototypeOf(proto);
}
return ret;

function methodToMiddleware(Controller, key) {
return function classControllerMiddleware(...args) {
const controller = new Controller(this);
if (!this.app.config.controller || !this.app.config.controller.supportParams) {
args = [ this ];
}
// fn.call(ctx, ...args);
return utils.callFn(controller[key], args, controller);
};
}
}

// 关键是这一句:
// ret[key] = methodToMiddleware(Controller, key);
// 将home.js里的所有方法放到ret[key]上。
```


挂载的关键是
```js
new FileLoader(opt).load();
```
整个过程是:

1. this.loadController();

2. this.loadToApp(controllerBase, 'controller', opt);

3. new FileLoader(opt).load();

4. this.parse();

5. this.load(); // 将controller里的各个方法挂到 appWorkerLoader.app.controller上

parse() 过程:

directories = this.options.directory; // controller 目录
找到该目录下的所有文件:
遍历每个文件取得
properties = getProperties(filepath, this.options);
pathName
exports = getExports(fullpath, this.options, pathName); // 取得controller文件比如home.js里的所有方法,
item.push({fullpath, properties, exports});
return item;

load() 过程:


33 changes: 33 additions & 0 deletions node相关/eggsource/5.loadRouter过程.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
this.loadFile(path.join(this.options.baseDir, 'app/router'));

router的初始化,发生在解构赋值的时候

```js app/router.js
const { router, controller } = app; // TODO: app的router属性什么时候加上的。
```

```js
class EggCore extends KoaApplication {
constructor(options = {}) {/**/}
/**
* get router
* @member {Router} EggCore#router
* @since 1.0.0
*/
get router() {
console.log('enter router')
if (this[ROUTER]) {
return this[ROUTER];
}
const router = this[ROUTER] = new Router({ sensitive: true }, this);
// register router middleware
this.beforeStart(() => {
this.use(router.middleware());
});
return router;
}
}
```

路由注册的时候,又引进来一个新的类Layer

158 changes: 158 additions & 0 deletions node相关/eggsource/FileLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
class FileLoader {

/**
* @class
* @param {Object} options - options
* @param {String|Array} options.directory - directories to be loaded
* @param {Object} options.target - attach the target object from loaded files
* @param {String} options.match - match the files when load, support glob, default to all js files
* @param {String} options.ignore - ignore the files when load, support glob
* @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`
* @param {Boolean} options.call - determine whether invoke when exports is function
* @param {Boolean} options.override - determine whether override the property when get the same name
* @param {Object} options.inject - an object that be the argument when invoke the function
* @param {Function} options.filter - a function that filter the exports which can be loaded
* @param {String|Function} options.caseStyle - set property's case when converting a filepath to property list.
*/
constructor(options) {
assert(options.directory, 'options.directory is required');
assert(options.target, 'options.target is required');
this.options = Object.assign({}, defaults, options);

// compatible old options _lowercaseFirst_
if (this.options.lowercaseFirst === true) {
deprecate('lowercaseFirst is deprecated, use caseStyle instead');
this.options.caseStyle = 'lower';
}
}

/**
* attach items to target object. Mapping the directory to properties.
* `app/controller/group/repository.js` => `target.group.repository`
* @return {Object} target
* @since 1.0.0
*/
load() {
const items = this.parse();
const target = this.options.target;
// debugger;
for (const item of items) {
debug('loading item %j', item);
// item { properties: [ 'a', 'b', 'c'], exports }
// => target.a.b.c = exports
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 && !is.primitive(obj)) {
obj[FULLPATH] = item.fullpath;
obj[EXPORTS] = true;
}
} else {
obj = target[property] || {};
}
target[property] = obj;
debug('loaded %s', properties);
return obj;
}, target);
}
return target;
}

/**
* Parse files from given directories, then return an items list, each item contains properties and exports.
*
* For example, parse `app/controller/group/repository.js`
*
* ```
* module.exports = app => {
* return class RepositoryController extends app.Controller {};
* }
* ```
*
* It returns a item
*
* ```
* {
* properties: [ 'group', 'repository' ],
* exports: app => { ... },
* }
* ```
*
* `Properties` is an array that contains the directory of a filepath.
*
* `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.
* @return {Array} items
* @since 1.0.0
* 文件加载的这个class 明天看吧。
*/
parse() {
let files = this.options.match;
if (!files) {
files = (process.env.EGG_TYPESCRIPT === 'true' && utils.extensions['.ts'])
? [ '**/*.(js|ts)', '!**/*.d.ts' ]
: [ '**/*.js' ];
} else {
files = Array.isArray(files) ? files : [ files ];
}

let ignore = this.options.ignore;
if (ignore) {
ignore = Array.isArray(ignore) ? ignore : [ ignore ];
ignore = ignore.filter(f => !!f).map(f => '!' + f);
files = files.concat(ignore);
}

let directories = this.options.directory;
if (!Array.isArray(directories)) {
directories = [ directories ];
}

const filter = is.function(this.options.filter) ? this.options.filter : null;
const items = [];
debug('parsing %j', directories);
for (const directory of directories) {
// debugger globby 之后跳到了路由文件,怎么做到的呢。TODO: 也算进步
const filepaths = globby.sync(files, { cwd: directory });
for (const filepath of filepaths) {
const fullpath = path.join(directory, filepath);
if (!fs.statSync(fullpath).isFile()) continue;
// get properties
// app/service/foo/bar.js => [ 'foo', 'bar' ]
const properties = getProperties(filepath, this.options);
// app/service/foo/bar.js => service.foo.bar
const pathName = directory.split(/[/\\]/).slice(-1) + '.' + properties.join('.');
// get exports from the file
const exports = getExports(fullpath, this.options, pathName);

// ignore exports when it's null or false returned by filter function
if (exports == null || (filter && filter(exports) === false)) continue;

// set properties of class
if (is.class(exports)) {
exports.prototype.pathName = pathName;
exports.prototype.fullPath = fullpath;
}

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

return items;
}

}

/**
* 两个方法:load 和 parse。
*
* load时,将
*
* parse时,将 controller/home.js
* 挂载到appWorkerLoader.app.controller上
*/
2 changes: 1 addition & 1 deletion node相关/eggsource/evtEmit.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ let eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners)
eventEmitter.emit('connection');
eventEmitter.removeListener('connection', listen1);
console.log(eventEmitter.listenerCount('connection'))
console.log(eventEmitter.listenerCount('connection'))
Loading

0 comments on commit ece96ee

Please sign in to comment.