Skip to content

Commit

Permalink
add extend loader
Browse files Browse the repository at this point in the history
  • Loading branch information
fengmk2 committed Jun 15, 2024
1 parent afde200 commit 9c13225
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
},
"homepage": "https://github.com/eggjs/egg-core#readme",
"dependencies": {
"@eggjs/koa": "^2.18.1",
"@eggjs/koa": "^2.18.2",
"@eggjs/router": "^3.0.1",
"egg-logger": "^3.5.0",
"egg-path-matching": "^1.1.0",
Expand Down
1 change: 1 addition & 0 deletions src/egg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class EggCore extends KoaApplication {
BaseContextClass: typeof BaseContextClass;
Controller: typeof BaseContextClass;
Service: typeof BaseContextClass;
Helper?: typeof BaseContextClass;
lifecycle: Lifecycle;
loader: EggLoader;
#closePromise?: Promise<void>;
Expand Down
153 changes: 151 additions & 2 deletions src/loader/egg_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import homedir from 'node-homedir';
import type { Logger } from 'egg-logger';
import { readJSONSync } from 'utility';
import { extend } from 'extend2';
import { Request, Response, Context, Application } from '@eggjs/koa';
import { FileLoader, FileLoaderOptions } from './file_loader.js';
import { ContextLoader, ContextLoaderOptions } from './context_loader.js'
import utils from '../utils/index.js';
Expand All @@ -16,6 +17,13 @@ import type { EggCore } from '../egg.js';

const debug = debuglog('@eggjs/core:egg_loader');

const originalPrototypes: Record<string, any> = {
request: Request.prototype,
response: Response.prototype,
context: Context.prototype,
application: Application.prototype,
};

export interface EggAppInfo {
/** package.json */
pkg: Record<string, any>;
Expand Down Expand Up @@ -932,6 +940,149 @@ export class EggLoader {
}
/** end Config loader */

/** start Extend loader */
/**
* mixin Agent.prototype
* @function EggLoader#loadAgentExtend
* @since 1.0.0
*/
async loadAgentExtend() {
await this.loadExtend('agent', this.app);
}

/**
* mixin Application.prototype
* @function EggLoader#loadApplicationExtend
* @since 1.0.0
*/
async loadApplicationExtend() {
await this.loadExtend('application', this.app);
}

/**
* mixin Request.prototype
* @function EggLoader#loadRequestExtend
* @since 1.0.0
*/
async loadRequestExtend() {
await this.loadExtend('request', this.app.request);
}

/**
* mixin Response.prototype
* @function EggLoader#loadResponseExtend
* @since 1.0.0
*/
async loadResponseExtend() {
await this.loadExtend('response', this.app.response);
}

/**
* mixin Context.prototype
* @function EggLoader#loadContextExtend
* @since 1.0.0
*/
async loadContextExtend() {
await this.loadExtend('context', this.app.context);
}

/**
* mixin app.Helper.prototype
* @function EggLoader#loadHelperExtend
* @since 1.0.0
*/
async loadHelperExtend() {
if (this.app.Helper) {
await this.loadExtend('helper', this.app.Helper.prototype);
}
}

/**
* Find all extend file paths by name
* can be override in top level framework to support load `app/extends/{name}.js`
*
* @param {String} name - filename which may be `app/extend/{name}.js`
* @return {Array} filepaths extend file paths
* @private
*/
protected getExtendFilePaths(name: string): string[] {
return this.getLoadUnits().map(unit => path.join(unit.path, 'app/extend', name));
}

/**
* Loader app/extend/xx.js to `prototype`,
* @function loadExtend
* @param {String} name - filename which may be `app/extend/{name}.js`
* @param {Object} proto - prototype that mixed
* @since 1.0.0
*/
async loadExtend(name: string, proto: object) {
this.timing.start(`Load extend/${name}.js`);
// All extend files
const filepaths = this.getExtendFilePaths(name);
// if use mm.env and serverEnv is not unittest
const needUnittest = 'EGG_MOCK_SERVER_ENV' in process.env && this.serverEnv !== 'unittest';
for (const filepath of filepaths) {
filepaths.push(filepath + `.${this.serverEnv}`);
if (needUnittest) {
filepaths.push(filepath + '.unittest');
}
}

const mergeRecord = new Map();
for (let filepath of filepaths) {
filepath = this.resolveModule(filepath)!;
if (!filepath) {
continue;
}
if (filepath.endsWith('/index.js')) {
this.app.deprecate(`app/extend/${name}/index.js is deprecated, use app/extend/${name}.js instead`);
} else if (filepath.endsWith('/index.ts')) {
this.app.deprecate(`app/extend/${name}/index.ts is deprecated, use app/extend/${name}.ts instead`);
}

const ext = await this.requireFile(filepath);

const properties = Object.getOwnPropertyNames(ext)
.concat(Object.getOwnPropertySymbols(ext) as any[]);

for (const property of properties) {
if (mergeRecord.has(property)) {
debug('Property: "%s" already exists in "%s",it will be redefined by "%s"',
property, mergeRecord.get(property), filepath);
}

// Copy descriptor
let descriptor = Object.getOwnPropertyDescriptor(ext, property);
let originalDescriptor = Object.getOwnPropertyDescriptor(proto, property);
if (!originalDescriptor) {
// try to get descriptor from originalPrototypes
const originalProto = originalPrototypes[name];
if (originalProto) {
originalDescriptor = Object.getOwnPropertyDescriptor(originalProto, property);
}
}
if (originalDescriptor) {
// don't override descriptor
descriptor = {
...descriptor,
};
if (!descriptor.set && originalDescriptor.set) {
descriptor.set = originalDescriptor.set;
}
if (!descriptor.get && originalDescriptor.get) {
descriptor.get = originalDescriptor.get;
}
}
Object.defineProperty(proto, property, descriptor!);
mergeRecord.set(property, filepath);
}
debug('merge %j to %s from %s', Object.keys(ext), name, filepath);
}
this.timing.end(`Load extend/${name}.js`);
}
/** end Extend loader */

// Low Level API

/**
Expand Down Expand Up @@ -1124,14 +1275,12 @@ function isValidatePackageName(name: string) {
return true;
}


/**
* Mixin methods to EggLoader
* // ES6 Multiple Inheritance
* https://medium.com/@leocavalcante/es6-multiple-inheritance-73a3c66d2b6b
*/
// const loaders = [
// require('./mixin/config'),
// require('./mixin/extend'),
// require('./mixin/custom'),
// require('./mixin/service'),
Expand Down

0 comments on commit 9c13225

Please sign in to comment.