@eggjs/core 7.0.0-beta.35 → 7.0.0-beta.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base_context_class.d.ts +19 -14
- package/dist/base_context_class.js +39 -36
- package/dist/egg.d.ts +279 -277
- package/dist/egg.js +394 -426
- package/dist/index.d.ts +12 -12
- package/dist/index.js +12 -12
- package/dist/lifecycle.d.ts +79 -75
- package/dist/lifecycle.js +280 -306
- package/dist/loader/context_loader.d.ts +34 -30
- package/dist/loader/context_loader.js +76 -99
- package/dist/loader/egg_loader.d.ts +370 -366
- package/dist/loader/egg_loader.js +1169 -1567
- package/dist/loader/file_loader.d.ts +99 -95
- package/dist/loader/file_loader.js +190 -241
- package/dist/singleton.d.ts +31 -27
- package/dist/singleton.js +107 -117
- package/dist/types.d.ts +54 -51
- package/dist/utils/index.d.ts +16 -14
- package/dist/utils/index.js +96 -105
- package/dist/utils/sequencify.d.ts +13 -10
- package/dist/utils/sequencify.js +44 -58
- package/dist/utils/timing.d.ts +22 -19
- package/dist/utils/timing.js +85 -92
- package/package.json +35 -40
- package/dist/types.js +0 -2
|
@@ -1,1588 +1,1190 @@
|
|
|
1
|
-
import
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { debuglog, inspect } from 'node:util';
|
|
5
|
-
import { extend } from '@eggjs/extend2';
|
|
6
|
-
import { Request, Response, Application, Context as KoaContext } from '@eggjs/koa';
|
|
7
|
-
import { pathMatching } from '@eggjs/path-matching';
|
|
8
|
-
import { isESM, isSupportTypeScript } from '@eggjs/utils';
|
|
9
|
-
import { isAsyncFunction, isClass, isGeneratorFunction, isObject, isPromise } from 'is-type-of';
|
|
10
|
-
import { homedir } from 'node-homedir';
|
|
11
|
-
import { now, diff } from 'performance-ms';
|
|
12
|
-
import { register as tsconfigPathsRegister } from 'tsconfig-paths';
|
|
13
|
-
import { getParamNames, readJSONSync, readJSON, exists } from 'utility';
|
|
14
|
-
import utils, {} from "../utils/index.js";
|
|
1
|
+
import utils_default from "../utils/index.js";
|
|
15
2
|
import { sequencify } from "../utils/sequencify.js";
|
|
16
3
|
import { Timing } from "../utils/timing.js";
|
|
17
|
-
import { ContextLoader } from "./context_loader.js";
|
|
18
4
|
import { CaseStyle, FULLPATH, FileLoader } from "./file_loader.js";
|
|
19
|
-
|
|
5
|
+
import { ContextLoader } from "./context_loader.js";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { debuglog, inspect } from "node:util";
|
|
9
|
+
import { isESM, isSupportTypeScript } from "@eggjs/utils";
|
|
10
|
+
import assert from "node:assert";
|
|
11
|
+
import { Application, Context, Request, Response } from "@eggjs/koa";
|
|
12
|
+
import { isAsyncFunction, isClass, isGeneratorFunction, isObject, isPromise } from "is-type-of";
|
|
13
|
+
import { extend } from "@eggjs/extend2";
|
|
14
|
+
import { pathMatching } from "@eggjs/path-matching";
|
|
15
|
+
import { homedir } from "node-homedir";
|
|
16
|
+
import { diff, now } from "performance-ms";
|
|
17
|
+
import { register } from "tsconfig-paths";
|
|
18
|
+
import { exists, getParamNames, readJSON, readJSONSync } from "utility";
|
|
19
|
+
|
|
20
|
+
//#region src/loader/egg_loader.ts
|
|
21
|
+
const debug = debuglog("egg/core/loader/egg_loader");
|
|
20
22
|
const originalPrototypes = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
request: Request.prototype,
|
|
24
|
+
response: Response.prototype,
|
|
25
|
+
context: Context.prototype,
|
|
26
|
+
application: Application.prototype
|
|
27
|
+
};
|
|
28
|
+
var EggLoader = class {
|
|
29
|
+
#requiredCount = 0;
|
|
30
|
+
options;
|
|
31
|
+
timing;
|
|
32
|
+
pkg;
|
|
33
|
+
eggPaths;
|
|
34
|
+
serverEnv;
|
|
35
|
+
serverScope;
|
|
36
|
+
appInfo;
|
|
37
|
+
dirs;
|
|
38
|
+
/**
|
|
39
|
+
* @class
|
|
40
|
+
* @param {Object} options - options
|
|
41
|
+
* @param {String} options.baseDir - the directory of application
|
|
42
|
+
* @param {EggCore} options.app - Application instance
|
|
43
|
+
* @param {Logger} options.logger - logger
|
|
44
|
+
* @param {Object} [options.plugins] - custom plugins
|
|
45
|
+
* @since 1.0.0
|
|
46
|
+
*/
|
|
47
|
+
constructor(options) {
|
|
48
|
+
this.options = options;
|
|
49
|
+
assert(fs.existsSync(this.options.baseDir), `${this.options.baseDir} not exists`);
|
|
50
|
+
assert(this.options.app, "options.app is required");
|
|
51
|
+
assert(this.options.logger, "options.logger is required");
|
|
52
|
+
this.timing = this.app.timing || new Timing();
|
|
53
|
+
/**
|
|
54
|
+
* @member {Object} EggLoader#pkg
|
|
55
|
+
* @see {@link AppInfo#pkg}
|
|
56
|
+
* @since 1.0.0
|
|
57
|
+
*/
|
|
58
|
+
this.pkg = readJSONSync(path.join(this.options.baseDir, "package.json"));
|
|
59
|
+
if (process.env.EGG_TYPESCRIPT === "true" || this.pkg.egg && this.pkg.egg.typescript) {
|
|
60
|
+
const tsConfigFile = path.join(this.options.baseDir, "tsconfig.json");
|
|
61
|
+
if (fs.existsSync(tsConfigFile)) register({ cwd: this.options.baseDir });
|
|
62
|
+
else this.logger.info("[@eggjs/core/egg_loader] skip register \"tsconfig-paths\" because tsconfig.json not exists at %s", tsConfigFile);
|
|
63
|
+
}
|
|
64
|
+
debug("-------------------- type: %s --------------------", this.app.type);
|
|
65
|
+
/**
|
|
66
|
+
* All framework directories.
|
|
67
|
+
*
|
|
68
|
+
* You can extend Application of egg, the entry point is options.app,
|
|
69
|
+
*
|
|
70
|
+
* loader will find all directories from the prototype of Application,
|
|
71
|
+
* you should define `Symbol.for('egg#eggPath')` property.
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* // src/example.ts
|
|
75
|
+
* import { Application } from 'egg';
|
|
76
|
+
* class ExampleApplication extends Application {
|
|
77
|
+
* get [Symbol.for('egg#eggPath')]() {
|
|
78
|
+
* return baseDir;
|
|
79
|
+
* }
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
* @member {Array} EggLoader#eggPaths
|
|
83
|
+
* @see EggLoader#getEggPaths
|
|
84
|
+
* @since 1.0.0
|
|
85
|
+
*/
|
|
86
|
+
this.eggPaths = this.getEggPaths();
|
|
87
|
+
debug("Loaded eggPaths %j", this.eggPaths);
|
|
88
|
+
/**
|
|
89
|
+
* @member {String} EggLoader#serverEnv
|
|
90
|
+
* @see AppInfo#env
|
|
91
|
+
* @since 1.0.0
|
|
92
|
+
*/
|
|
93
|
+
this.serverEnv = this.getServerEnv();
|
|
94
|
+
debug("Loaded serverEnv %j", this.serverEnv);
|
|
95
|
+
/**
|
|
96
|
+
* @member {String} EggLoader#serverScope
|
|
97
|
+
* @see AppInfo#serverScope
|
|
98
|
+
*/
|
|
99
|
+
this.serverScope = options.serverScope ?? this.getServerScope();
|
|
100
|
+
/**
|
|
101
|
+
* @member {AppInfo} EggLoader#appInfo
|
|
102
|
+
* @since 1.0.0
|
|
103
|
+
*/
|
|
104
|
+
this.appInfo = this.getAppInfo();
|
|
105
|
+
}
|
|
106
|
+
get app() {
|
|
107
|
+
return this.options.app;
|
|
108
|
+
}
|
|
109
|
+
get lifecycle() {
|
|
110
|
+
return this.app.lifecycle;
|
|
111
|
+
}
|
|
112
|
+
get logger() {
|
|
113
|
+
return this.options.logger;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get {@link AppInfo#env}
|
|
117
|
+
* @returns {String} env
|
|
118
|
+
* @see AppInfo#env
|
|
119
|
+
* @private
|
|
120
|
+
* @since 1.0.0
|
|
121
|
+
*/
|
|
122
|
+
getServerEnv() {
|
|
123
|
+
let serverEnv = this.options.env;
|
|
124
|
+
const envPath = path.join(this.options.baseDir, "config/env");
|
|
125
|
+
if (!serverEnv && fs.existsSync(envPath)) serverEnv = fs.readFileSync(envPath, "utf8").trim();
|
|
126
|
+
if (!serverEnv && process.env.EGG_SERVER_ENV) serverEnv = process.env.EGG_SERVER_ENV;
|
|
127
|
+
if (serverEnv) serverEnv = serverEnv.trim();
|
|
128
|
+
else if (process.env.NODE_ENV === "test") serverEnv = "unittest";
|
|
129
|
+
else if (process.env.NODE_ENV === "production") serverEnv = "prod";
|
|
130
|
+
else serverEnv = "local";
|
|
131
|
+
return serverEnv;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get {@link AppInfo#scope}
|
|
135
|
+
* @returns {String} serverScope
|
|
136
|
+
* @private
|
|
137
|
+
*/
|
|
138
|
+
getServerScope() {
|
|
139
|
+
return process.env.EGG_SERVER_SCOPE ?? "";
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get {@link AppInfo#name}
|
|
143
|
+
* @returns {String} appname
|
|
144
|
+
* @private
|
|
145
|
+
* @since 1.0.0
|
|
146
|
+
*/
|
|
147
|
+
getAppname() {
|
|
148
|
+
if (this.pkg.name) {
|
|
149
|
+
debug("Loaded appname(%s) from package.json", this.pkg.name);
|
|
150
|
+
return this.pkg.name;
|
|
151
|
+
}
|
|
152
|
+
const pkg = path.join(this.options.baseDir, "package.json");
|
|
153
|
+
throw new Error(`name is required from ${pkg}`);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get home directory
|
|
157
|
+
* @returns {String} home directory
|
|
158
|
+
* @since 3.4.0
|
|
159
|
+
*/
|
|
160
|
+
getHomedir() {
|
|
161
|
+
return process.env.EGG_HOME || homedir() || "/home/admin";
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get app info
|
|
165
|
+
* @returns {AppInfo} appInfo
|
|
166
|
+
* @since 1.0.0
|
|
167
|
+
*/
|
|
168
|
+
getAppInfo() {
|
|
169
|
+
const env = this.serverEnv;
|
|
170
|
+
const scope = this.serverScope;
|
|
171
|
+
const home = this.getHomedir();
|
|
172
|
+
const baseDir = this.options.baseDir;
|
|
173
|
+
/**
|
|
174
|
+
* Meta information of the application
|
|
175
|
+
* @class AppInfo
|
|
176
|
+
*/
|
|
177
|
+
return {
|
|
178
|
+
name: this.getAppname(),
|
|
179
|
+
baseDir,
|
|
180
|
+
env,
|
|
181
|
+
scope,
|
|
182
|
+
HOME: home,
|
|
183
|
+
pkg: this.pkg,
|
|
184
|
+
root: env === "local" || env === "unittest" ? baseDir : home
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Get {@link EggLoader#eggPaths}
|
|
189
|
+
* @returns {Array} framework directories
|
|
190
|
+
* @see {@link EggLoader#eggPaths}
|
|
191
|
+
* @private
|
|
192
|
+
* @since 1.0.0
|
|
193
|
+
*/
|
|
194
|
+
getEggPaths() {
|
|
195
|
+
const EggCore = this.options.EggCoreClass;
|
|
196
|
+
let eggPaths = [];
|
|
197
|
+
if (this.app.customEggPaths) eggPaths = this.app.customEggPaths();
|
|
198
|
+
let proto = this.app;
|
|
199
|
+
while (proto) {
|
|
200
|
+
proto = Object.getPrototypeOf(proto);
|
|
201
|
+
if (proto === Object.prototype || proto === EggCore?.prototype) break;
|
|
202
|
+
let eggPath;
|
|
203
|
+
eggPath = Reflect.get(proto, Symbol.for("egg#eggPath"));
|
|
204
|
+
if (!eggPath) continue;
|
|
205
|
+
if (this.app.deprecate) this.app.deprecate("Symbol.for('egg#eggPath') is deprecated, please use \"override the `customEggPaths()` method\" instead");
|
|
206
|
+
assert(typeof eggPath === "string", "Symbol.for('egg#eggPath') should be string");
|
|
207
|
+
assert(fs.existsSync(eggPath), `${eggPath} not exists`);
|
|
208
|
+
const realpath = fs.realpathSync(eggPath);
|
|
209
|
+
if (!eggPaths.includes(realpath)) eggPaths.unshift(realpath);
|
|
210
|
+
}
|
|
211
|
+
return eggPaths;
|
|
212
|
+
}
|
|
213
|
+
/** start Plugin loader */
|
|
214
|
+
lookupDirs;
|
|
215
|
+
eggPlugins;
|
|
216
|
+
appPlugins;
|
|
217
|
+
customPlugins;
|
|
218
|
+
allPlugins;
|
|
219
|
+
orderPlugins;
|
|
220
|
+
/** enable plugins */
|
|
221
|
+
plugins;
|
|
222
|
+
/**
|
|
223
|
+
* Load config/plugin.js from {EggLoader#loadUnits}
|
|
224
|
+
*
|
|
225
|
+
* plugin.js is written below
|
|
226
|
+
*
|
|
227
|
+
* ```js
|
|
228
|
+
* {
|
|
229
|
+
* 'xxx-client': {
|
|
230
|
+
* enable: true,
|
|
231
|
+
* package: 'xxx-client',
|
|
232
|
+
* dep: [],
|
|
233
|
+
* env: [],
|
|
234
|
+
* },
|
|
235
|
+
* // short hand
|
|
236
|
+
* 'rds': false,
|
|
237
|
+
* 'depd': {
|
|
238
|
+
* enable: true,
|
|
239
|
+
* path: 'path/to/depd'
|
|
240
|
+
* }
|
|
241
|
+
* }
|
|
242
|
+
* ```
|
|
243
|
+
*
|
|
244
|
+
* If the plugin has path, Loader will find the module from it.
|
|
245
|
+
*
|
|
246
|
+
* Otherwise Loader will lookup follow the order by packageName
|
|
247
|
+
*
|
|
248
|
+
* 1. $APP_BASE/node_modules/${package}
|
|
249
|
+
* 2. $EGG_BASE/node_modules/${package}
|
|
250
|
+
*
|
|
251
|
+
* You can call `loader.plugins` that retrieve enabled plugins.
|
|
252
|
+
*
|
|
253
|
+
* ```js
|
|
254
|
+
* loader.plugins['xxx-client'] = {
|
|
255
|
+
* name: 'xxx-client', // the plugin name, it can be used in `dep`
|
|
256
|
+
* package: 'xxx-client', // the package name of plugin
|
|
257
|
+
* enable: true, // whether enabled
|
|
258
|
+
* path: 'path/to/xxx-client', // the directory of the plugin package
|
|
259
|
+
* dep: [], // the dependent plugins, you can use the plugin name
|
|
260
|
+
* env: [ 'local', 'unittest' ], // specify the serverEnv that only enable the plugin in it
|
|
261
|
+
* }
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* `loader.allPlugins` can be used when retrieve all plugins.
|
|
265
|
+
* @function EggLoader#loadPlugin
|
|
266
|
+
* @since 1.0.0
|
|
267
|
+
*/
|
|
268
|
+
async loadPlugin() {
|
|
269
|
+
this.timing.start("Load Plugin");
|
|
270
|
+
this.lookupDirs = this.getLookupDirs();
|
|
271
|
+
this.allPlugins = {};
|
|
272
|
+
this.eggPlugins = await this.loadEggPlugins();
|
|
273
|
+
this.appPlugins = await this.loadAppPlugins();
|
|
274
|
+
this.customPlugins = this.loadCustomPlugins();
|
|
275
|
+
this.#extendPlugins(this.allPlugins, this.eggPlugins);
|
|
276
|
+
this.#extendPlugins(this.allPlugins, this.appPlugins);
|
|
277
|
+
this.#extendPlugins(this.allPlugins, this.customPlugins);
|
|
278
|
+
const enabledPluginNames = [];
|
|
279
|
+
const plugins = {};
|
|
280
|
+
const env = this.serverEnv;
|
|
281
|
+
for (const name in this.allPlugins) {
|
|
282
|
+
const plugin = this.allPlugins[name];
|
|
283
|
+
plugin.path = this.getPluginPath(plugin);
|
|
284
|
+
if (!plugin.skipMerge) await this.#mergePluginConfig(plugin);
|
|
285
|
+
if (env && plugin.env.length > 0 && !plugin.env.includes(env)) {
|
|
286
|
+
this.logger.info("[egg/core] Plugin %o is disabled by env unmatched, require env(%o) but got env is %o", name, plugin.env, env);
|
|
287
|
+
plugin.enable = false;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
plugins[name] = plugin;
|
|
291
|
+
if (plugin.enable) enabledPluginNames.push(name);
|
|
292
|
+
}
|
|
293
|
+
this.orderPlugins = this.getOrderPlugins(plugins, enabledPluginNames, this.appPlugins);
|
|
294
|
+
const enablePlugins = {};
|
|
295
|
+
for (const plugin of this.orderPlugins) enablePlugins[plugin.name] = plugin;
|
|
296
|
+
debug("Loaded enable plugins: %j", Object.keys(enablePlugins));
|
|
297
|
+
/**
|
|
298
|
+
* Retrieve enabled plugins
|
|
299
|
+
* @member {Object} EggLoader#plugins
|
|
300
|
+
* @since 1.0.0
|
|
301
|
+
*/
|
|
302
|
+
this.plugins = enablePlugins;
|
|
303
|
+
this.timing.end("Load Plugin");
|
|
304
|
+
}
|
|
305
|
+
async loadAppPlugins() {
|
|
306
|
+
const appPlugins = await this.readPluginConfigs(path.join(this.options.baseDir, "config/plugin.default"));
|
|
307
|
+
debug("Loaded app plugins: %j", Object.keys(appPlugins).map((k) => `${k}:${appPlugins[k].enable}`));
|
|
308
|
+
return appPlugins;
|
|
309
|
+
}
|
|
310
|
+
async loadEggPlugins() {
|
|
311
|
+
const eggPluginConfigPaths = this.eggPaths.map((eggPath) => path.join(eggPath, "config/plugin.default"));
|
|
312
|
+
const eggPlugins = await this.readPluginConfigs(eggPluginConfigPaths);
|
|
313
|
+
debug("Loaded egg plugins: %j", Object.keys(eggPlugins).map((k) => `${k}:${eggPlugins[k].enable}`));
|
|
314
|
+
return eggPlugins;
|
|
315
|
+
}
|
|
316
|
+
loadCustomPlugins() {
|
|
317
|
+
let customPlugins = {};
|
|
318
|
+
const configPaths = [];
|
|
319
|
+
if (process.env.EGG_PLUGINS) try {
|
|
320
|
+
customPlugins = JSON.parse(process.env.EGG_PLUGINS);
|
|
321
|
+
configPaths.push("<process.env.EGG_PLUGINS>");
|
|
322
|
+
} catch (e) {
|
|
323
|
+
debug("parse EGG_PLUGINS failed, %s", e);
|
|
324
|
+
}
|
|
325
|
+
if (this.options.plugins) {
|
|
326
|
+
customPlugins = {
|
|
327
|
+
...customPlugins,
|
|
328
|
+
...this.options.plugins
|
|
329
|
+
};
|
|
330
|
+
configPaths.push("<options.plugins>");
|
|
331
|
+
}
|
|
332
|
+
if (customPlugins) {
|
|
333
|
+
const configPath = configPaths.join(" or ");
|
|
334
|
+
for (const name in customPlugins) this.#normalizePluginConfig(customPlugins, name, configPath);
|
|
335
|
+
debug("Loaded custom plugins: %o", customPlugins);
|
|
336
|
+
}
|
|
337
|
+
return customPlugins;
|
|
338
|
+
}
|
|
339
|
+
async readPluginConfigs(configPaths) {
|
|
340
|
+
if (!Array.isArray(configPaths)) configPaths = [configPaths];
|
|
341
|
+
const newConfigPaths = [];
|
|
342
|
+
for (const filename of this.getTypeFiles("plugin")) for (let configPath of configPaths) {
|
|
343
|
+
configPath = path.join(path.dirname(configPath), filename);
|
|
344
|
+
newConfigPaths.push(configPath);
|
|
345
|
+
}
|
|
346
|
+
const plugins = {};
|
|
347
|
+
for (const configPath of newConfigPaths) {
|
|
348
|
+
let filepath = this.resolveModule(configPath);
|
|
349
|
+
if (configPath.endsWith("plugin.default") && !filepath) filepath = this.resolveModule(configPath.replace(/plugin\.default$/, "plugin"));
|
|
350
|
+
if (!filepath) {
|
|
351
|
+
debug("[readPluginConfigs:ignore] plugin config not found %o", configPath);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const config = await utils_default.loadFile(filepath);
|
|
355
|
+
for (const name in config) this.#normalizePluginConfig(config, name, filepath);
|
|
356
|
+
this.#extendPlugins(plugins, config);
|
|
357
|
+
}
|
|
358
|
+
return plugins;
|
|
359
|
+
}
|
|
360
|
+
#normalizePluginConfig(plugins, name, configPath) {
|
|
361
|
+
const plugin = plugins[name];
|
|
362
|
+
if (typeof plugin === "boolean") {
|
|
363
|
+
plugins[name] = {
|
|
364
|
+
name,
|
|
365
|
+
enable: plugin,
|
|
366
|
+
dependencies: [],
|
|
367
|
+
optionalDependencies: [],
|
|
368
|
+
env: [],
|
|
369
|
+
from: configPath
|
|
370
|
+
};
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (!("enable" in plugin)) Reflect.set(plugin, "enable", true);
|
|
374
|
+
plugin.name = name;
|
|
375
|
+
plugin.dependencies = plugin.dependencies || [];
|
|
376
|
+
plugin.optionalDependencies = plugin.optionalDependencies || [];
|
|
377
|
+
plugin.env = plugin.env || [];
|
|
378
|
+
plugin.from = plugin.from || configPath;
|
|
379
|
+
depCompatible(plugin);
|
|
380
|
+
}
|
|
381
|
+
async #mergePluginConfig(plugin) {
|
|
382
|
+
let pkg;
|
|
383
|
+
let eggPluginConfig;
|
|
384
|
+
const pluginPackage = path.join(plugin.path, "package.json");
|
|
385
|
+
if (await utils_default.existsPath(pluginPackage)) {
|
|
386
|
+
pkg = await readJSON(pluginPackage);
|
|
387
|
+
eggPluginConfig = pkg.eggPlugin;
|
|
388
|
+
if (pkg.version) plugin.version = pkg.version;
|
|
389
|
+
plugin.path = await this.#formatPluginPathFromPackageJSON(plugin.path, pkg);
|
|
390
|
+
}
|
|
391
|
+
const logger = this.options.logger;
|
|
392
|
+
if (!eggPluginConfig) {
|
|
393
|
+
logger.warn("[@eggjs/core/egg_loader] pkg.eggPlugin is missing in %s, plugin: %j", pluginPackage, plugin);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (eggPluginConfig.name && eggPluginConfig.strict !== false && eggPluginConfig.name !== plugin.name) logger.warn(`[@eggjs/core/egg_loader] pluginName(${plugin.name}) is different from pluginConfigName(${eggPluginConfig.name})`);
|
|
397
|
+
depCompatible(eggPluginConfig);
|
|
398
|
+
for (const key of [
|
|
399
|
+
"dependencies",
|
|
400
|
+
"optionalDependencies",
|
|
401
|
+
"env"
|
|
402
|
+
]) {
|
|
403
|
+
const values = eggPluginConfig[key];
|
|
404
|
+
const existsValues = Reflect.get(plugin, key);
|
|
405
|
+
if (Array.isArray(values) && !existsValues?.length) Reflect.set(plugin, key, values);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
getOrderPlugins(allPlugins, enabledPluginNames, appPlugins) {
|
|
409
|
+
if (enabledPluginNames.length === 0) return [];
|
|
410
|
+
const result = sequencify(allPlugins, enabledPluginNames);
|
|
411
|
+
debug("Got plugins %j after sequencify", result);
|
|
412
|
+
if (result.sequence.length === 0) {
|
|
413
|
+
const err = /* @__PURE__ */ new Error(`sequencify plugins has problem, missing: [${result.missingTasks}], recursive: [${result.recursiveDependencies}]`);
|
|
414
|
+
for (const missName of result.missingTasks) {
|
|
415
|
+
const requires = [];
|
|
416
|
+
for (const name in allPlugins) if (allPlugins[name].dependencies.includes(missName)) requires.push(name);
|
|
417
|
+
err.message += `\n\t>> Plugin [${missName}] is disabled or missed, but is required by [${requires}]`;
|
|
418
|
+
}
|
|
419
|
+
err.name = "PluginSequencifyError";
|
|
420
|
+
throw err;
|
|
421
|
+
}
|
|
422
|
+
const implicitEnabledPlugins = [];
|
|
423
|
+
const requireMap = {};
|
|
424
|
+
for (const name of result.sequence) {
|
|
425
|
+
for (const depName of allPlugins[name].dependencies) {
|
|
426
|
+
if (!requireMap[depName]) requireMap[depName] = [];
|
|
427
|
+
requireMap[depName].push(name);
|
|
428
|
+
}
|
|
429
|
+
if (!allPlugins[name].enable) {
|
|
430
|
+
implicitEnabledPlugins.push(name);
|
|
431
|
+
allPlugins[name].enable = true;
|
|
432
|
+
allPlugins[name].implicitEnable = true;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
for (const [name, dependents] of Object.entries(requireMap)) allPlugins[name].dependents = dependents;
|
|
436
|
+
if (implicitEnabledPlugins.length > 0) {
|
|
437
|
+
let message = implicitEnabledPlugins.map((name) => ` - ${name} required by [${requireMap[name]}]`).join("\n");
|
|
438
|
+
this.options.logger.info(`Following plugins will be enabled implicitly.\n${message}`);
|
|
439
|
+
const disabledPlugins = implicitEnabledPlugins.filter((name) => appPlugins[name] && appPlugins[name].enable === false);
|
|
440
|
+
if (disabledPlugins.length > 0) {
|
|
441
|
+
message = disabledPlugins.map((name) => ` - ${name} required by [${requireMap[name]}]`).join("\n");
|
|
442
|
+
this.options.logger.warn(`Following plugins will be enabled implicitly that is disabled by application.\n${message}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return result.sequence.map((name) => allPlugins[name]);
|
|
446
|
+
}
|
|
447
|
+
getLookupDirs() {
|
|
448
|
+
const lookupDirs = /* @__PURE__ */ new Set();
|
|
449
|
+
lookupDirs.add(this.options.baseDir);
|
|
450
|
+
for (let i = this.eggPaths.length - 1; i >= 0; i--) {
|
|
451
|
+
const eggPath = this.eggPaths[i];
|
|
452
|
+
lookupDirs.add(eggPath);
|
|
453
|
+
}
|
|
454
|
+
lookupDirs.add(process.cwd());
|
|
455
|
+
return lookupDirs;
|
|
456
|
+
}
|
|
457
|
+
getPluginPath(plugin) {
|
|
458
|
+
if (plugin.path) return plugin.path;
|
|
459
|
+
if (plugin.package) assert(isValidatePackageName(plugin.package), `plugin ${plugin.name} invalid, use 'path' instead of package: "${plugin.package}"`);
|
|
460
|
+
return this.#resolvePluginPath(plugin);
|
|
461
|
+
}
|
|
462
|
+
#resolvePluginPath(plugin) {
|
|
463
|
+
const name = plugin.package || plugin.name;
|
|
464
|
+
try {
|
|
465
|
+
const pluginPkgFile = utils_default.resolvePath(`${name}/package.json`, { paths: [...this.lookupDirs] });
|
|
466
|
+
return path.dirname(pluginPkgFile);
|
|
467
|
+
} catch (err) {
|
|
468
|
+
debug("[resolvePluginPath] error: %o, plugin info: %o", err, plugin);
|
|
469
|
+
throw new Error(`Can not find plugin ${name} in "${[...this.lookupDirs].join(", ")}"`, { cause: err });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async #formatPluginPathFromPackageJSON(pluginPath, pluginPkg) {
|
|
473
|
+
let realPluginPath = pluginPath;
|
|
474
|
+
const exports = pluginPkg.eggPlugin?.exports;
|
|
475
|
+
if (exports) {
|
|
476
|
+
if (isESM) {
|
|
477
|
+
if (exports.import) realPluginPath = path.join(pluginPath, exports.import);
|
|
478
|
+
} else if (exports.require) realPluginPath = path.join(pluginPath, exports.require);
|
|
479
|
+
if (exports.typescript && isSupportTypeScript() && !await exists(realPluginPath)) {
|
|
480
|
+
realPluginPath = path.join(pluginPath, exports.typescript);
|
|
481
|
+
debug("[formatPluginPathFromPackageJSON] use typescript path %o", realPluginPath);
|
|
482
|
+
}
|
|
483
|
+
} else if (pluginPkg.exports?.["."] && pluginPkg.type === "module") {
|
|
484
|
+
let defaultExport = pluginPkg.exports["."];
|
|
485
|
+
if (typeof defaultExport === "string") realPluginPath = path.dirname(path.join(pluginPath, defaultExport));
|
|
486
|
+
else if (defaultExport?.import) {
|
|
487
|
+
if (typeof defaultExport.import === "string") realPluginPath = path.dirname(path.join(pluginPath, defaultExport.import));
|
|
488
|
+
else if (defaultExport.import.default) realPluginPath = path.dirname(path.join(pluginPath, defaultExport.import.default));
|
|
489
|
+
}
|
|
490
|
+
debug("[formatPluginPathFromPackageJSON] resolve plugin path from %o to %o, defaultExport: %o", pluginPath, realPluginPath, defaultExport);
|
|
491
|
+
}
|
|
492
|
+
return realPluginPath;
|
|
493
|
+
}
|
|
494
|
+
#extendPlugins(targets, plugins) {
|
|
495
|
+
if (!plugins) return;
|
|
496
|
+
for (const name in plugins) {
|
|
497
|
+
const plugin = plugins[name];
|
|
498
|
+
let targetPlugin = targets[name];
|
|
499
|
+
if (!targetPlugin) {
|
|
500
|
+
targetPlugin = {};
|
|
501
|
+
targets[name] = targetPlugin;
|
|
502
|
+
}
|
|
503
|
+
if (targetPlugin.package && targetPlugin.package === plugin.package) this.logger.warn("[@eggjs/core] plugin %s has been defined that is %j, but you define again in %s", name, targetPlugin, plugin.from);
|
|
504
|
+
if (plugin.path || plugin.package) {
|
|
505
|
+
delete targetPlugin.path;
|
|
506
|
+
delete targetPlugin.package;
|
|
507
|
+
}
|
|
508
|
+
for (const [prop, value] of Object.entries(plugin)) {
|
|
509
|
+
if (value === void 0) continue;
|
|
510
|
+
if (Reflect.get(targetPlugin, prop) && Array.isArray(value) && value.length === 0) continue;
|
|
511
|
+
Reflect.set(targetPlugin, prop, value);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
/** end Plugin loader */
|
|
516
|
+
/** start Config loader */
|
|
517
|
+
configMeta;
|
|
518
|
+
config;
|
|
519
|
+
/**
|
|
520
|
+
* Load config/config.js
|
|
521
|
+
*
|
|
522
|
+
* Will merge config.default.js 和 config.${env}.js
|
|
523
|
+
*
|
|
524
|
+
* @function EggLoader#loadConfig
|
|
525
|
+
* @since 1.0.0
|
|
526
|
+
*/
|
|
527
|
+
async loadConfig() {
|
|
528
|
+
this.timing.start("Load Config");
|
|
529
|
+
this.configMeta = {};
|
|
530
|
+
const target = {
|
|
531
|
+
middleware: [],
|
|
532
|
+
coreMiddleware: []
|
|
533
|
+
};
|
|
534
|
+
const appConfig = await this.#preloadAppConfig();
|
|
535
|
+
for (const filename of this.getTypeFiles("config")) for (const unit of this.getLoadUnits()) {
|
|
536
|
+
const isApp = unit.type === "app";
|
|
537
|
+
const config = await this.#loadConfig(unit.path, filename, isApp ? void 0 : appConfig, unit.type);
|
|
538
|
+
if (!config) continue;
|
|
539
|
+
debug("[loadConfig] Loaded config %s/%s, %j", unit.path, filename, config);
|
|
540
|
+
extend(true, target, config);
|
|
541
|
+
}
|
|
542
|
+
const envConfig = this.#loadConfigFromEnv();
|
|
543
|
+
if (envConfig) {
|
|
544
|
+
debug("[loadConfig] Loaded config from env, %j", envConfig);
|
|
545
|
+
extend(true, target, envConfig);
|
|
546
|
+
}
|
|
547
|
+
target.coreMiddleware = target.coreMiddleware || [];
|
|
548
|
+
target.coreMiddlewares = target.coreMiddleware;
|
|
549
|
+
target.appMiddleware = target.middleware || [];
|
|
550
|
+
target.appMiddlewares = target.appMiddleware;
|
|
551
|
+
this.config = target;
|
|
552
|
+
debug("[loadConfig] type: %s, all config: %o", this.app.type, this.config);
|
|
553
|
+
this.timing.end("Load Config");
|
|
554
|
+
}
|
|
555
|
+
async #preloadAppConfig() {
|
|
556
|
+
const names = ["config.default", `config.${this.serverEnv}`];
|
|
557
|
+
const target = {};
|
|
558
|
+
for (const filename of names) {
|
|
559
|
+
const config = await this.#loadConfig(this.options.baseDir, filename, void 0, "app");
|
|
560
|
+
if (!config) continue;
|
|
561
|
+
extend(true, target, config);
|
|
562
|
+
}
|
|
563
|
+
return target;
|
|
564
|
+
}
|
|
565
|
+
async #loadConfig(dirpath, filename, extraInject, type) {
|
|
566
|
+
const isPlugin = type === "plugin";
|
|
567
|
+
const isApp = type === "app";
|
|
568
|
+
let filepath = this.resolveModule(path.join(dirpath, "config", filename));
|
|
569
|
+
if (filename === "config.default" && !filepath) filepath = this.resolveModule(path.join(dirpath, "config/config"));
|
|
570
|
+
if (!filepath) return;
|
|
571
|
+
const config = await this.loadFile(filepath, this.appInfo, extraInject);
|
|
572
|
+
if (!config) return;
|
|
573
|
+
if (isPlugin || isApp) assert(!config.coreMiddleware, "Can not define coreMiddleware in app or plugin");
|
|
574
|
+
if (!isApp) assert(!config.middleware, "Can not define middleware in " + filepath);
|
|
575
|
+
this.#setConfigMeta(config, filepath);
|
|
576
|
+
return config;
|
|
577
|
+
}
|
|
578
|
+
#loadConfigFromEnv() {
|
|
579
|
+
const envConfigStr = process.env.EGG_APP_CONFIG;
|
|
580
|
+
if (!envConfigStr) return;
|
|
581
|
+
try {
|
|
582
|
+
const envConfig = JSON.parse(envConfigStr);
|
|
583
|
+
this.#setConfigMeta(envConfig, "<process.env.EGG_APP_CONFIG>");
|
|
584
|
+
return envConfig;
|
|
585
|
+
} catch {
|
|
586
|
+
this.options.logger.warn("[egg-loader] process.env.EGG_APP_CONFIG is not invalid JSON: %s", envConfigStr);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
#setConfigMeta(config, filepath) {
|
|
590
|
+
config = extend(true, {}, config);
|
|
591
|
+
this.#setConfig(config, filepath);
|
|
592
|
+
extend(true, this.configMeta, config);
|
|
593
|
+
}
|
|
594
|
+
#setConfig(obj, filepath) {
|
|
595
|
+
for (const key of Object.keys(obj)) {
|
|
596
|
+
const val = obj[key];
|
|
597
|
+
if (key === "console" && val && typeof val.Console === "function" && val.Console === console.Console) {
|
|
598
|
+
obj[key] = filepath;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (val && Object.getPrototypeOf(val) === Object.prototype && Object.keys(val).length > 0) {
|
|
602
|
+
this.#setConfig(val, filepath);
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
obj[key] = filepath;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
/** end Config loader */
|
|
609
|
+
/** start Extend loader */
|
|
610
|
+
/**
|
|
611
|
+
* mixin Agent.prototype
|
|
612
|
+
* @function EggLoader#loadAgentExtend
|
|
613
|
+
* @since 1.0.0
|
|
614
|
+
*/
|
|
615
|
+
async loadAgentExtend() {
|
|
616
|
+
await this.loadExtend("agent", this.app);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* mixin Application.prototype
|
|
620
|
+
* @function EggLoader#loadApplicationExtend
|
|
621
|
+
* @since 1.0.0
|
|
622
|
+
*/
|
|
623
|
+
async loadApplicationExtend() {
|
|
624
|
+
await this.loadExtend("application", this.app);
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* mixin Request.prototype
|
|
628
|
+
* @function EggLoader#loadRequestExtend
|
|
629
|
+
* @since 1.0.0
|
|
630
|
+
*/
|
|
631
|
+
async loadRequestExtend() {
|
|
632
|
+
await this.loadExtend("request", this.app.request);
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* mixin Response.prototype
|
|
636
|
+
* @function EggLoader#loadResponseExtend
|
|
637
|
+
* @since 1.0.0
|
|
638
|
+
*/
|
|
639
|
+
async loadResponseExtend() {
|
|
640
|
+
await this.loadExtend("response", this.app.response);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* mixin Context.prototype
|
|
644
|
+
* @function EggLoader#loadContextExtend
|
|
645
|
+
* @since 1.0.0
|
|
646
|
+
*/
|
|
647
|
+
async loadContextExtend() {
|
|
648
|
+
await this.loadExtend("context", this.app.context);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* mixin app.Helper.prototype
|
|
652
|
+
* @function EggLoader#loadHelperExtend
|
|
653
|
+
* @since 1.0.0
|
|
654
|
+
*/
|
|
655
|
+
async loadHelperExtend() {
|
|
656
|
+
if (this.app.Helper) await this.loadExtend("helper", this.app.Helper.prototype);
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Find all extend file paths by name
|
|
660
|
+
* can be override in top level framework to support load `app/extends/{name}.js`
|
|
661
|
+
*
|
|
662
|
+
* @param {String} name - filename which may be `app/extend/{name}.js`
|
|
663
|
+
* @returns {Array} filepaths extend file paths
|
|
664
|
+
* @private
|
|
665
|
+
*/
|
|
666
|
+
getExtendFilePaths(name) {
|
|
667
|
+
return this.getLoadUnits().map((unit) => path.join(unit.path, "app/extend", name));
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Loader app/extend/xx.js to `prototype`,
|
|
671
|
+
* @function loadExtend
|
|
672
|
+
* @param {String} name - filename which may be `app/extend/{name}.js`
|
|
673
|
+
* @param {Object} proto - prototype that mixed
|
|
674
|
+
* @since 1.0.0
|
|
675
|
+
*/
|
|
676
|
+
async loadExtend(name, proto) {
|
|
677
|
+
this.timing.start(`Load extend/${name}.js`);
|
|
678
|
+
const filepaths = this.getExtendFilePaths(name);
|
|
679
|
+
const needUnittest = "EGG_MOCK_SERVER_ENV" in process.env && this.serverEnv !== "unittest";
|
|
680
|
+
const length = filepaths.length;
|
|
681
|
+
for (let i = 0; i < length; i++) {
|
|
682
|
+
const filepath = filepaths[i];
|
|
683
|
+
filepaths.push(filepath + `.${this.serverEnv}`);
|
|
684
|
+
if (needUnittest) filepaths.push(filepath + ".unittest");
|
|
685
|
+
}
|
|
686
|
+
debug("loadExtend %s, filepaths: %j", name, filepaths);
|
|
687
|
+
const mergeRecord = /* @__PURE__ */ new Map();
|
|
688
|
+
for (const rawFilepath of filepaths) {
|
|
689
|
+
const filepath = this.resolveModule(rawFilepath);
|
|
690
|
+
if (!filepath) continue;
|
|
691
|
+
if (filepath.endsWith("/index.js")) this.app.deprecate(`app/extend/${name}/index.js is deprecated, use app/extend/${name}.js instead`);
|
|
692
|
+
else if (filepath.endsWith("/index.ts")) this.app.deprecate(`app/extend/${name}/index.ts is deprecated, use app/extend/${name}.ts instead`);
|
|
693
|
+
let ext = await this.requireFile(filepath);
|
|
694
|
+
if (isClass(ext)) ext = ext.prototype;
|
|
695
|
+
const properties = Object.getOwnPropertyNames(ext).concat(Object.getOwnPropertySymbols(ext)).filter((name$1) => name$1 !== "constructor");
|
|
696
|
+
for (const property of properties) {
|
|
697
|
+
if (mergeRecord.has(property)) debug("Property: \"%s\" already exists in \"%s\",it will be redefined by \"%s\"", property, mergeRecord.get(property), filepath);
|
|
698
|
+
let descriptor = Object.getOwnPropertyDescriptor(ext, property);
|
|
699
|
+
let originalDescriptor = Object.getOwnPropertyDescriptor(proto, property);
|
|
700
|
+
if (!originalDescriptor) {
|
|
701
|
+
const originalProto = originalPrototypes[name];
|
|
702
|
+
if (originalProto) originalDescriptor = Object.getOwnPropertyDescriptor(originalProto, property);
|
|
703
|
+
}
|
|
704
|
+
if (originalDescriptor) {
|
|
705
|
+
descriptor = { ...descriptor };
|
|
706
|
+
if (!descriptor.set && originalDescriptor.set) descriptor.set = originalDescriptor.set;
|
|
707
|
+
if (!descriptor.get && originalDescriptor.get) descriptor.get = originalDescriptor.get;
|
|
708
|
+
}
|
|
709
|
+
Object.defineProperty(proto, property, descriptor);
|
|
710
|
+
mergeRecord.set(property, filepath);
|
|
711
|
+
}
|
|
712
|
+
debug("merge %j to %s from %s", properties, name, filepath);
|
|
713
|
+
}
|
|
714
|
+
this.timing.end(`Load extend/${name}.js`);
|
|
715
|
+
}
|
|
716
|
+
/** end Extend loader */
|
|
717
|
+
/** start Custom loader */
|
|
718
|
+
/**
|
|
719
|
+
* load app.js
|
|
720
|
+
*
|
|
721
|
+
* @example
|
|
722
|
+
* - old:
|
|
723
|
+
*
|
|
724
|
+
* ```js
|
|
725
|
+
* module.exports = function(app) {
|
|
726
|
+
* doSomething();
|
|
727
|
+
* }
|
|
728
|
+
* ```
|
|
729
|
+
*
|
|
730
|
+
* - new:
|
|
731
|
+
*
|
|
732
|
+
* ```js
|
|
733
|
+
* module.exports = class Boot {
|
|
734
|
+
* constructor(app) {
|
|
735
|
+
* this.app = app;
|
|
736
|
+
* }
|
|
737
|
+
* configDidLoad() {
|
|
738
|
+
* doSomething();
|
|
739
|
+
* }
|
|
740
|
+
* }
|
|
741
|
+
* @since 1.0.0
|
|
742
|
+
*/
|
|
743
|
+
async loadCustomApp() {
|
|
744
|
+
await this.#loadBootHook("app");
|
|
745
|
+
this.lifecycle.triggerConfigWillLoad();
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Load agent.js, same as {@link EggLoader#loadCustomApp}
|
|
749
|
+
*/
|
|
750
|
+
async loadCustomAgent() {
|
|
751
|
+
await this.#loadBootHook("agent");
|
|
752
|
+
this.lifecycle.triggerConfigWillLoad();
|
|
753
|
+
}
|
|
754
|
+
loadBootHook() {}
|
|
755
|
+
async #loadBootHook(fileName) {
|
|
756
|
+
this.timing.start(`Load ${fileName}.js`);
|
|
757
|
+
for (const unit of this.getLoadUnits()) {
|
|
758
|
+
const bootFile = path.join(unit.path, fileName);
|
|
759
|
+
const bootFilePath = this.resolveModule(bootFile);
|
|
760
|
+
if (!bootFilePath) {
|
|
761
|
+
debug("[loadBootHook:ignore] %o not found", bootFile);
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
debug("[loadBootHook:success] %o => %o", bootFile, bootFilePath);
|
|
765
|
+
const bootHook = await this.requireFile(bootFilePath);
|
|
766
|
+
if (isClass(bootHook)) {
|
|
767
|
+
bootHook.prototype.fullPath = bootFilePath;
|
|
768
|
+
this.lifecycle.addBootHook(bootHook);
|
|
769
|
+
debug("[loadBootHook] add BootHookClass from %o", bootFilePath);
|
|
770
|
+
} else if (typeof bootHook === "function") {
|
|
771
|
+
this.lifecycle.addFunctionAsBootHook(bootHook, bootFilePath);
|
|
772
|
+
debug("[loadBootHook] add bootHookFunction from %o", bootFilePath);
|
|
773
|
+
} else this.options.logger.warn("[@eggjs/core/egg_loader] %s must exports a boot class", bootFilePath);
|
|
774
|
+
}
|
|
775
|
+
this.lifecycle.init();
|
|
776
|
+
this.timing.end(`Load ${fileName}.js`);
|
|
777
|
+
}
|
|
778
|
+
/** end Custom loader */
|
|
779
|
+
/** start Service loader */
|
|
780
|
+
/**
|
|
781
|
+
* Load app/service
|
|
782
|
+
* @function EggLoader#loadService
|
|
783
|
+
* @param {Object} options - LoaderOptions
|
|
784
|
+
* @since 1.0.0
|
|
785
|
+
*/
|
|
786
|
+
async loadService(options) {
|
|
787
|
+
this.timing.start("Load Service");
|
|
788
|
+
const servicePaths = this.getLoadUnits().map((unit) => path.join(unit.path, "app/service"));
|
|
789
|
+
options = {
|
|
790
|
+
call: true,
|
|
791
|
+
caseStyle: CaseStyle.lower,
|
|
792
|
+
fieldClass: "serviceClasses",
|
|
793
|
+
directory: servicePaths,
|
|
794
|
+
...options
|
|
795
|
+
};
|
|
796
|
+
debug("[loadService] options: %o", options);
|
|
797
|
+
await this.loadToContext(servicePaths, "service", options);
|
|
798
|
+
this.timing.end("Load Service");
|
|
799
|
+
}
|
|
800
|
+
/** end Service loader */
|
|
801
|
+
/** start Middleware loader */
|
|
802
|
+
/**
|
|
803
|
+
* Load app/middleware
|
|
804
|
+
*
|
|
805
|
+
* app.config.xx is the options of the middleware xx that has same name as config
|
|
806
|
+
*
|
|
807
|
+
* @function EggLoader#loadMiddleware
|
|
808
|
+
* @param {Object} opt - LoaderOptions
|
|
809
|
+
* @example
|
|
810
|
+
* ```js
|
|
811
|
+
* // app/middleware/status.js
|
|
812
|
+
* module.exports = function(options, app) {
|
|
813
|
+
* // options == app.config.status
|
|
814
|
+
* return async next => {
|
|
815
|
+
* await next();
|
|
816
|
+
* }
|
|
817
|
+
* }
|
|
818
|
+
* ```
|
|
819
|
+
* @since 1.0.0
|
|
820
|
+
*/
|
|
821
|
+
async loadMiddleware(opt) {
|
|
822
|
+
this.timing.start("Load Middleware");
|
|
823
|
+
const app = this.app;
|
|
824
|
+
const middlewarePaths = this.getLoadUnits().map((unit) => path.join(unit.path, "app/middleware"));
|
|
825
|
+
opt = {
|
|
826
|
+
call: false,
|
|
827
|
+
override: true,
|
|
828
|
+
caseStyle: CaseStyle.lower,
|
|
829
|
+
directory: middlewarePaths,
|
|
830
|
+
...opt
|
|
831
|
+
};
|
|
832
|
+
await this.loadToApp(middlewarePaths, "middlewares", opt);
|
|
833
|
+
debug("[loadMiddleware] middlewarePaths: %j", middlewarePaths);
|
|
834
|
+
for (const name in app.middlewares) Object.defineProperty(app.middleware, name, {
|
|
835
|
+
get() {
|
|
836
|
+
return app.middlewares[name];
|
|
837
|
+
},
|
|
838
|
+
enumerable: false,
|
|
839
|
+
configurable: false
|
|
840
|
+
});
|
|
841
|
+
this.options.logger.info("Use coreMiddleware order: %j", this.config.coreMiddleware);
|
|
842
|
+
this.options.logger.info("Use appMiddleware order: %j", this.config.appMiddleware);
|
|
843
|
+
const middlewareNames = this.config.coreMiddleware.concat(this.config.appMiddleware);
|
|
844
|
+
debug("[loadMiddleware] middlewareNames: %j", middlewareNames);
|
|
845
|
+
const middlewaresMap = /* @__PURE__ */ new Map();
|
|
846
|
+
for (const name of middlewareNames) {
|
|
847
|
+
const createMiddleware = app.middlewares[name];
|
|
848
|
+
if (!createMiddleware) throw new TypeError(`Middleware ${name} not found`);
|
|
849
|
+
if (middlewaresMap.has(name)) throw new TypeError(`Middleware ${name} redefined`);
|
|
850
|
+
middlewaresMap.set(name, true);
|
|
851
|
+
const options = this.config[name] || {};
|
|
852
|
+
let mw$1 = createMiddleware(options, app);
|
|
853
|
+
assert(typeof mw$1 === "function", `Middleware ${name} must be a function, but actual is ${inspect(mw$1)}`);
|
|
854
|
+
if (isGeneratorFunction(mw$1)) {
|
|
855
|
+
const fullpath = Reflect.get(createMiddleware, FULLPATH);
|
|
856
|
+
throw new TypeError(`Support for generators was removed, middleware: ${name}, fullpath: ${fullpath}`);
|
|
857
|
+
}
|
|
858
|
+
mw$1._name = name;
|
|
859
|
+
mw$1 = wrapMiddleware(mw$1, options);
|
|
860
|
+
if (mw$1) {
|
|
861
|
+
if (debug.enabled) mw$1 = debugMiddlewareWrapper(mw$1);
|
|
862
|
+
app.use(mw$1);
|
|
863
|
+
debug("[loadMiddleware] Use middleware: %s with options: %j", name, options);
|
|
864
|
+
this.options.logger.info("[@eggjs/core/egg_loader] Use middleware: %s", name);
|
|
865
|
+
} else this.options.logger.info("[@eggjs/core/egg_loader] Disable middleware: %s", name);
|
|
866
|
+
}
|
|
867
|
+
this.options.logger.info("[@eggjs/core/egg_loader] Loaded middleware from %j", middlewarePaths);
|
|
868
|
+
this.timing.end("Load Middleware");
|
|
869
|
+
const mw = this.app.router.middleware();
|
|
870
|
+
Reflect.set(mw, "_name", "routerMiddleware");
|
|
871
|
+
this.app.use(mw);
|
|
872
|
+
}
|
|
873
|
+
/** end Middleware loader */
|
|
874
|
+
/** start Controller loader */
|
|
875
|
+
/**
|
|
876
|
+
* Load app/controller
|
|
877
|
+
* @param {Object} opt - LoaderOptions
|
|
878
|
+
* @since 1.0.0
|
|
879
|
+
*/
|
|
880
|
+
async loadController(opt) {
|
|
881
|
+
this.timing.start("Load Controller");
|
|
882
|
+
const controllerBase = path.join(this.options.baseDir, "app/controller");
|
|
883
|
+
opt = {
|
|
884
|
+
caseStyle: CaseStyle.lower,
|
|
885
|
+
directory: controllerBase,
|
|
886
|
+
initializer: (obj, opt$1) => {
|
|
887
|
+
if (isGeneratorFunction(obj)) throw new TypeError(`Support for generators was removed, fullpath: ${opt$1.path}`);
|
|
888
|
+
if (!isClass(obj) && !isAsyncFunction(obj) && typeof obj === "function") {
|
|
889
|
+
obj = obj(this.app);
|
|
890
|
+
debug("[loadController] after init(app) => %o, meta: %j", obj, opt$1);
|
|
891
|
+
if (isGeneratorFunction(obj)) throw new TypeError(`Support for generators was removed, fullpath: ${opt$1.path}`);
|
|
892
|
+
}
|
|
893
|
+
if (isClass(obj)) {
|
|
894
|
+
obj.prototype.pathName = opt$1.pathName;
|
|
895
|
+
obj.prototype.fullPath = opt$1.path;
|
|
896
|
+
return wrapControllerClass(obj, opt$1.path);
|
|
897
|
+
}
|
|
898
|
+
if (isObject(obj)) return wrapObject(obj, opt$1.path);
|
|
899
|
+
if (isAsyncFunction(obj)) return wrapObject({ "module.exports": obj }, opt$1.path)["module.exports"];
|
|
900
|
+
return obj;
|
|
901
|
+
},
|
|
902
|
+
...opt
|
|
903
|
+
};
|
|
904
|
+
await this.loadToApp(controllerBase, "controller", opt);
|
|
905
|
+
debug("[loadController] app.controller => %o", this.app.controller);
|
|
906
|
+
this.options.logger.info("[@eggjs/core/egg_loader] Controller loaded: %s", controllerBase);
|
|
907
|
+
this.timing.end("Load Controller");
|
|
908
|
+
}
|
|
909
|
+
/** end Controller loader */
|
|
910
|
+
/** start Router loader */
|
|
911
|
+
/**
|
|
912
|
+
* Load app/router.js
|
|
913
|
+
* @function EggLoader#loadRouter
|
|
914
|
+
* @since 1.0.0
|
|
915
|
+
*/
|
|
916
|
+
async loadRouter() {
|
|
917
|
+
this.timing.start("Load Router");
|
|
918
|
+
await this.loadFile(path.join(this.options.baseDir, "app/router"));
|
|
919
|
+
this.timing.end("Load Router");
|
|
920
|
+
}
|
|
921
|
+
/** end Router loader */
|
|
922
|
+
/** start CustomLoader loader */
|
|
923
|
+
async loadCustomLoader() {
|
|
924
|
+
assert(this.config, "should loadConfig first");
|
|
925
|
+
const customLoader = this.config.customLoader || {};
|
|
926
|
+
for (const property of Object.keys(customLoader)) {
|
|
927
|
+
const loaderConfig = { ...customLoader[property] };
|
|
928
|
+
assert(loaderConfig.directory, `directory is required for config.customLoader.${property}`);
|
|
929
|
+
let directory;
|
|
930
|
+
if (loaderConfig.loadunit === true) directory = this.getLoadUnits().map((unit) => path.join(unit.path, loaderConfig.directory));
|
|
931
|
+
else directory = path.join(this.appInfo.baseDir, loaderConfig.directory);
|
|
932
|
+
const inject = loaderConfig.inject || "app";
|
|
933
|
+
debug("[loadCustomLoader] loaderConfig: %o, inject: %o, directory: %o", loaderConfig, inject, directory);
|
|
934
|
+
switch (inject) {
|
|
935
|
+
case "ctx": {
|
|
936
|
+
assert(!(property in this.app.context), `customLoader should not override ctx.${property}`);
|
|
937
|
+
const options = {
|
|
938
|
+
caseStyle: CaseStyle.lower,
|
|
939
|
+
fieldClass: `${property}Classes`,
|
|
940
|
+
...loaderConfig,
|
|
941
|
+
directory
|
|
942
|
+
};
|
|
943
|
+
await this.loadToContext(directory, property, options);
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
case "app": {
|
|
947
|
+
assert(!(property in this.app), `customLoader should not override app.${property}`);
|
|
948
|
+
const options = {
|
|
949
|
+
caseStyle: CaseStyle.lower,
|
|
950
|
+
initializer: (Clazz) => {
|
|
951
|
+
return isClass(Clazz) ? new Clazz(this.app) : Clazz;
|
|
952
|
+
},
|
|
953
|
+
...loaderConfig,
|
|
954
|
+
directory
|
|
955
|
+
};
|
|
956
|
+
await this.loadToApp(directory, property, options);
|
|
957
|
+
break;
|
|
958
|
+
}
|
|
959
|
+
default: throw new Error("inject only support app or ctx");
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/** end CustomLoader loader */
|
|
964
|
+
/**
|
|
965
|
+
* Load single file, will invoke when export is function
|
|
966
|
+
*
|
|
967
|
+
* @param {String} filepath - fullpath
|
|
968
|
+
* @param {Array} inject - pass rest arguments into the function when invoke
|
|
969
|
+
* @returns {Object} exports
|
|
970
|
+
* @example
|
|
971
|
+
* ```js
|
|
972
|
+
* app.loader.loadFile(path.join(app.options.baseDir, 'config/router.js'));
|
|
973
|
+
* ```
|
|
974
|
+
* @since 1.0.0
|
|
975
|
+
*/
|
|
976
|
+
async loadFile(filepath, ...inject) {
|
|
977
|
+
const fullpath = filepath && this.resolveModule(filepath);
|
|
978
|
+
if (!fullpath) return null;
|
|
979
|
+
if (inject.length === 0) inject = [this.app];
|
|
980
|
+
let mod = await this.requireFile(fullpath);
|
|
981
|
+
if (typeof mod === "function" && !isClass(mod)) {
|
|
982
|
+
mod = mod(...inject);
|
|
983
|
+
if (isPromise(mod)) mod = await mod;
|
|
984
|
+
}
|
|
985
|
+
return mod;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* @param {String} filepath - fullpath
|
|
989
|
+
* @private
|
|
990
|
+
*/
|
|
991
|
+
async requireFile(filepath) {
|
|
992
|
+
const timingKey = `Require(${this.#requiredCount++}) ${utils_default.getResolvedFilename(filepath, this.options.baseDir)}`;
|
|
993
|
+
this.timing.start(timingKey);
|
|
994
|
+
const mod = await utils_default.loadFile(filepath);
|
|
995
|
+
this.timing.end(timingKey);
|
|
996
|
+
return mod;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Get all loadUnit
|
|
1000
|
+
*
|
|
1001
|
+
* loadUnit is a directory that can be loaded by EggLoader, it has the same structure.
|
|
1002
|
+
* loadUnit has a path and a type(app, framework, plugin).
|
|
1003
|
+
*
|
|
1004
|
+
* The order of the loadUnits:
|
|
1005
|
+
*
|
|
1006
|
+
* 1. plugin
|
|
1007
|
+
* 2. framework
|
|
1008
|
+
* 3. app
|
|
1009
|
+
*
|
|
1010
|
+
* @returns {Array} loadUnits
|
|
1011
|
+
* @since 1.0.0
|
|
1012
|
+
*/
|
|
1013
|
+
getLoadUnits() {
|
|
1014
|
+
if (this.dirs) return this.dirs;
|
|
1015
|
+
this.dirs = [];
|
|
1016
|
+
if (this.orderPlugins) for (const plugin of this.orderPlugins) this.dirs.push({
|
|
1017
|
+
path: plugin.path,
|
|
1018
|
+
type: "plugin"
|
|
1019
|
+
});
|
|
1020
|
+
for (const eggPath of this.eggPaths) this.dirs.push({
|
|
1021
|
+
path: eggPath,
|
|
1022
|
+
type: "framework"
|
|
1023
|
+
});
|
|
1024
|
+
this.dirs.push({
|
|
1025
|
+
path: this.options.baseDir,
|
|
1026
|
+
type: "app"
|
|
1027
|
+
});
|
|
1028
|
+
debug("Loaded dirs %j", this.dirs);
|
|
1029
|
+
return this.dirs;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Load files using {@link FileLoader}, inject to {@link Application}
|
|
1033
|
+
* @param {String|Array} directory - see {@link FileLoader}
|
|
1034
|
+
* @param {String} property - see {@link FileLoader}, e.g.: 'controller', 'middlewares'
|
|
1035
|
+
* @param {Object} options - see {@link FileLoader}
|
|
1036
|
+
* @since 1.0.0
|
|
1037
|
+
*/
|
|
1038
|
+
async loadToApp(directory, property, options) {
|
|
1039
|
+
const target = {};
|
|
1040
|
+
Reflect.set(this.app, property, target);
|
|
1041
|
+
const loadOptions = {
|
|
1042
|
+
...options,
|
|
1043
|
+
directory: options?.directory ?? directory,
|
|
1044
|
+
target,
|
|
1045
|
+
inject: this.app
|
|
1046
|
+
};
|
|
1047
|
+
const timingKey = `Load "${String(property)}" to Application`;
|
|
1048
|
+
this.timing.start(timingKey);
|
|
1049
|
+
await new FileLoader(loadOptions).load();
|
|
1050
|
+
this.timing.end(timingKey);
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Load files using {@link ContextLoader}
|
|
1054
|
+
* @param {String|Array} directory - see {@link ContextLoader}
|
|
1055
|
+
* @param {String} property - see {@link ContextLoader}
|
|
1056
|
+
* @param {Object} options - see {@link ContextLoader}
|
|
1057
|
+
* @since 1.0.0
|
|
1058
|
+
*/
|
|
1059
|
+
async loadToContext(directory, property, options) {
|
|
1060
|
+
const loadOptions = {
|
|
1061
|
+
...options,
|
|
1062
|
+
directory: options?.directory || directory,
|
|
1063
|
+
property,
|
|
1064
|
+
inject: this.app
|
|
1065
|
+
};
|
|
1066
|
+
const timingKey = `Load "${String(property)}" to Context`;
|
|
1067
|
+
this.timing.start(timingKey);
|
|
1068
|
+
await new ContextLoader(loadOptions).load();
|
|
1069
|
+
this.timing.end(timingKey);
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* @member {FileLoader} EggLoader#FileLoader
|
|
1073
|
+
* @since 1.0.0
|
|
1074
|
+
*/
|
|
1075
|
+
get FileLoader() {
|
|
1076
|
+
return FileLoader;
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* @member {ContextLoader} EggLoader#ContextLoader
|
|
1080
|
+
* @since 1.0.0
|
|
1081
|
+
*/
|
|
1082
|
+
get ContextLoader() {
|
|
1083
|
+
return ContextLoader;
|
|
1084
|
+
}
|
|
1085
|
+
getTypeFiles(filename) {
|
|
1086
|
+
const files = [`${filename}.default`];
|
|
1087
|
+
if (this.serverScope) files.push(`${filename}.${this.serverScope}`);
|
|
1088
|
+
if (this.serverEnv === "default") return files;
|
|
1089
|
+
files.push(`${filename}.${this.serverEnv}`);
|
|
1090
|
+
if (this.serverScope) files.push(`${filename}.${this.serverScope}_${this.serverEnv}`);
|
|
1091
|
+
return files;
|
|
1092
|
+
}
|
|
1093
|
+
resolveModule(filepath) {
|
|
1094
|
+
let fullPath;
|
|
1095
|
+
try {
|
|
1096
|
+
fullPath = utils_default.resolvePath(filepath);
|
|
1097
|
+
} catch {
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
return fullPath;
|
|
1101
|
+
}
|
|
25
1102
|
};
|
|
26
|
-
export class EggLoader {
|
|
27
|
-
#requiredCount = 0;
|
|
28
|
-
options;
|
|
29
|
-
timing;
|
|
30
|
-
pkg;
|
|
31
|
-
eggPaths;
|
|
32
|
-
serverEnv;
|
|
33
|
-
serverScope;
|
|
34
|
-
appInfo;
|
|
35
|
-
dirs;
|
|
36
|
-
/**
|
|
37
|
-
* @class
|
|
38
|
-
* @param {Object} options - options
|
|
39
|
-
* @param {String} options.baseDir - the directory of application
|
|
40
|
-
* @param {EggCore} options.app - Application instance
|
|
41
|
-
* @param {Logger} options.logger - logger
|
|
42
|
-
* @param {Object} [options.plugins] - custom plugins
|
|
43
|
-
* @since 1.0.0
|
|
44
|
-
*/
|
|
45
|
-
constructor(options) {
|
|
46
|
-
this.options = options;
|
|
47
|
-
assert(fs.existsSync(this.options.baseDir), `${this.options.baseDir} not exists`);
|
|
48
|
-
assert(this.options.app, 'options.app is required');
|
|
49
|
-
assert(this.options.logger, 'options.logger is required');
|
|
50
|
-
this.timing = this.app.timing || new Timing();
|
|
51
|
-
/**
|
|
52
|
-
* @member {Object} EggLoader#pkg
|
|
53
|
-
* @see {@link AppInfo#pkg}
|
|
54
|
-
* @since 1.0.0
|
|
55
|
-
*/
|
|
56
|
-
this.pkg = readJSONSync(path.join(this.options.baseDir, 'package.json'));
|
|
57
|
-
// auto require('tsconfig-paths/register') on typescript app
|
|
58
|
-
// support env.EGG_TYPESCRIPT = true or { "egg": { "typescript": true } } on package.json
|
|
59
|
-
if (process.env.EGG_TYPESCRIPT === 'true' || (this.pkg.egg && this.pkg.egg.typescript)) {
|
|
60
|
-
// skip require tsconfig-paths if tsconfig.json not exists
|
|
61
|
-
const tsConfigFile = path.join(this.options.baseDir, 'tsconfig.json');
|
|
62
|
-
if (fs.existsSync(tsConfigFile)) {
|
|
63
|
-
// @ts-expect-error only cwd is required
|
|
64
|
-
tsconfigPathsRegister({ cwd: this.options.baseDir });
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
this.logger.info('[@eggjs/core/egg_loader] skip register "tsconfig-paths" because tsconfig.json not exists at %s', tsConfigFile);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
debug('-------------------- type: %s --------------------', this.app.type);
|
|
71
|
-
/**
|
|
72
|
-
* All framework directories.
|
|
73
|
-
*
|
|
74
|
-
* You can extend Application of egg, the entry point is options.app,
|
|
75
|
-
*
|
|
76
|
-
* loader will find all directories from the prototype of Application,
|
|
77
|
-
* you should define `Symbol.for('egg#eggPath')` property.
|
|
78
|
-
*
|
|
79
|
-
* ```ts
|
|
80
|
-
* // src/example.ts
|
|
81
|
-
* import { Application } from 'egg';
|
|
82
|
-
* class ExampleApplication extends Application {
|
|
83
|
-
* get [Symbol.for('egg#eggPath')]() {
|
|
84
|
-
* return baseDir;
|
|
85
|
-
* }
|
|
86
|
-
* }
|
|
87
|
-
* ```
|
|
88
|
-
* @member {Array} EggLoader#eggPaths
|
|
89
|
-
* @see EggLoader#getEggPaths
|
|
90
|
-
* @since 1.0.0
|
|
91
|
-
*/
|
|
92
|
-
this.eggPaths = this.getEggPaths();
|
|
93
|
-
debug('Loaded eggPaths %j', this.eggPaths);
|
|
94
|
-
/**
|
|
95
|
-
* @member {String} EggLoader#serverEnv
|
|
96
|
-
* @see AppInfo#env
|
|
97
|
-
* @since 1.0.0
|
|
98
|
-
*/
|
|
99
|
-
this.serverEnv = this.getServerEnv();
|
|
100
|
-
debug('Loaded serverEnv %j', this.serverEnv);
|
|
101
|
-
/**
|
|
102
|
-
* @member {String} EggLoader#serverScope
|
|
103
|
-
* @see AppInfo#serverScope
|
|
104
|
-
*/
|
|
105
|
-
this.serverScope = options.serverScope ?? this.getServerScope();
|
|
106
|
-
/**
|
|
107
|
-
* @member {AppInfo} EggLoader#appInfo
|
|
108
|
-
* @since 1.0.0
|
|
109
|
-
*/
|
|
110
|
-
this.appInfo = this.getAppInfo();
|
|
111
|
-
}
|
|
112
|
-
get app() {
|
|
113
|
-
return this.options.app;
|
|
114
|
-
}
|
|
115
|
-
get lifecycle() {
|
|
116
|
-
return this.app.lifecycle;
|
|
117
|
-
}
|
|
118
|
-
get logger() {
|
|
119
|
-
return this.options.logger;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Get {@link AppInfo#env}
|
|
123
|
-
* @returns {String} env
|
|
124
|
-
* @see AppInfo#env
|
|
125
|
-
* @private
|
|
126
|
-
* @since 1.0.0
|
|
127
|
-
*/
|
|
128
|
-
getServerEnv() {
|
|
129
|
-
let serverEnv = this.options.env;
|
|
130
|
-
const envPath = path.join(this.options.baseDir, 'config/env');
|
|
131
|
-
if (!serverEnv && fs.existsSync(envPath)) {
|
|
132
|
-
serverEnv = fs.readFileSync(envPath, 'utf8').trim();
|
|
133
|
-
}
|
|
134
|
-
if (!serverEnv && process.env.EGG_SERVER_ENV) {
|
|
135
|
-
serverEnv = process.env.EGG_SERVER_ENV;
|
|
136
|
-
}
|
|
137
|
-
if (serverEnv) {
|
|
138
|
-
serverEnv = serverEnv.trim();
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
// oxlint-disable-next-line eslint/no-lonely-if
|
|
142
|
-
if (process.env.NODE_ENV === 'test') {
|
|
143
|
-
serverEnv = 'unittest';
|
|
144
|
-
}
|
|
145
|
-
else if (process.env.NODE_ENV === 'production') {
|
|
146
|
-
serverEnv = 'prod';
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
serverEnv = 'local';
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return serverEnv;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Get {@link AppInfo#scope}
|
|
156
|
-
* @returns {String} serverScope
|
|
157
|
-
* @private
|
|
158
|
-
*/
|
|
159
|
-
getServerScope() {
|
|
160
|
-
return process.env.EGG_SERVER_SCOPE ?? '';
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Get {@link AppInfo#name}
|
|
164
|
-
* @returns {String} appname
|
|
165
|
-
* @private
|
|
166
|
-
* @since 1.0.0
|
|
167
|
-
*/
|
|
168
|
-
getAppname() {
|
|
169
|
-
if (this.pkg.name) {
|
|
170
|
-
debug('Loaded appname(%s) from package.json', this.pkg.name);
|
|
171
|
-
return this.pkg.name;
|
|
172
|
-
}
|
|
173
|
-
const pkg = path.join(this.options.baseDir, 'package.json');
|
|
174
|
-
throw new Error(`name is required from ${pkg}`);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Get home directory
|
|
178
|
-
* @returns {String} home directory
|
|
179
|
-
* @since 3.4.0
|
|
180
|
-
*/
|
|
181
|
-
getHomedir() {
|
|
182
|
-
// EGG_HOME for test
|
|
183
|
-
return process.env.EGG_HOME || homedir() || '/home/admin';
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Get app info
|
|
187
|
-
* @returns {AppInfo} appInfo
|
|
188
|
-
* @since 1.0.0
|
|
189
|
-
*/
|
|
190
|
-
getAppInfo() {
|
|
191
|
-
const env = this.serverEnv;
|
|
192
|
-
const scope = this.serverScope;
|
|
193
|
-
const home = this.getHomedir();
|
|
194
|
-
const baseDir = this.options.baseDir;
|
|
195
|
-
/**
|
|
196
|
-
* Meta information of the application
|
|
197
|
-
* @class AppInfo
|
|
198
|
-
*/
|
|
199
|
-
return {
|
|
200
|
-
/**
|
|
201
|
-
* The name of the application, retrieve from the name property in `package.json`.
|
|
202
|
-
* @member {String} AppInfo#name
|
|
203
|
-
*/
|
|
204
|
-
name: this.getAppname(),
|
|
205
|
-
/**
|
|
206
|
-
* The current directory, where the application code is.
|
|
207
|
-
* @member {String} AppInfo#baseDir
|
|
208
|
-
*/
|
|
209
|
-
baseDir,
|
|
210
|
-
/**
|
|
211
|
-
* The environment of the application, **it's not NODE_ENV**
|
|
212
|
-
*
|
|
213
|
-
* 1. from `$baseDir/config/env`
|
|
214
|
-
* 2. from EGG_SERVER_ENV
|
|
215
|
-
* 3. from NODE_ENV
|
|
216
|
-
*
|
|
217
|
-
* env | description
|
|
218
|
-
* --- | ---
|
|
219
|
-
* test | system integration testing
|
|
220
|
-
* prod | production
|
|
221
|
-
* local | local on your own computer
|
|
222
|
-
* unittest | unit test
|
|
223
|
-
*
|
|
224
|
-
* @member {String} AppInfo#env
|
|
225
|
-
* @see https://eggjs.org/zh-cn/basics/env.html
|
|
226
|
-
*/
|
|
227
|
-
env,
|
|
228
|
-
/**
|
|
229
|
-
* @member {String} AppInfo#scope
|
|
230
|
-
*/
|
|
231
|
-
scope,
|
|
232
|
-
/**
|
|
233
|
-
* The use directory, same as `process.env.HOME`
|
|
234
|
-
* @member {String} AppInfo#HOME
|
|
235
|
-
*/
|
|
236
|
-
HOME: home,
|
|
237
|
-
/**
|
|
238
|
-
* parsed from `package.json`
|
|
239
|
-
* @member {Object} AppInfo#pkg
|
|
240
|
-
*/
|
|
241
|
-
pkg: this.pkg,
|
|
242
|
-
/**
|
|
243
|
-
* The directory whether is baseDir or HOME depend on env.
|
|
244
|
-
* it's good for test when you want to write some file to HOME,
|
|
245
|
-
* but don't want to write to the real directory,
|
|
246
|
-
* so use root to write file to baseDir instead of HOME when unittest.
|
|
247
|
-
* keep root directory in baseDir when local and unittest
|
|
248
|
-
* @member {String} AppInfo#root
|
|
249
|
-
*/
|
|
250
|
-
root: env === 'local' || env === 'unittest' ? baseDir : home,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Get {@link EggLoader#eggPaths}
|
|
255
|
-
* @returns {Array} framework directories
|
|
256
|
-
* @see {@link EggLoader#eggPaths}
|
|
257
|
-
* @private
|
|
258
|
-
* @since 1.0.0
|
|
259
|
-
*/
|
|
260
|
-
getEggPaths() {
|
|
261
|
-
// avoid require recursively
|
|
262
|
-
const EggCore = this.options.EggCoreClass;
|
|
263
|
-
let eggPaths = [];
|
|
264
|
-
// @ts-expect-error customEggPaths is protected
|
|
265
|
-
if (this.app.customEggPaths) {
|
|
266
|
-
// @ts-expect-error customEggPaths is protected
|
|
267
|
-
eggPaths = this.app.customEggPaths();
|
|
268
|
-
}
|
|
269
|
-
// try to get egg paths from old way
|
|
270
|
-
let proto = this.app;
|
|
271
|
-
// Loop for the prototype chain
|
|
272
|
-
while (proto) {
|
|
273
|
-
proto = Object.getPrototypeOf(proto);
|
|
274
|
-
// stop the loop if
|
|
275
|
-
// - object extends Object
|
|
276
|
-
// - object extends EggCore
|
|
277
|
-
if (proto === Object.prototype || proto === EggCore?.prototype) {
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
let eggPath;
|
|
281
|
-
eggPath = Reflect.get(proto, Symbol.for('egg#eggPath'));
|
|
282
|
-
if (!eggPath) {
|
|
283
|
-
// if (EggCore) {
|
|
284
|
-
// throw new TypeError('Symbol.for(\'egg#eggPath\') is required on Application');
|
|
285
|
-
// }
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
if (this.app.deprecate) {
|
|
289
|
-
this.app.deprecate('Symbol.for(\'egg#eggPath\') is deprecated, please use "override the `customEggPaths()` method" instead');
|
|
290
|
-
}
|
|
291
|
-
assert(typeof eggPath === 'string', "Symbol.for('egg#eggPath') should be string");
|
|
292
|
-
assert(fs.existsSync(eggPath), `${eggPath} not exists`);
|
|
293
|
-
const realpath = fs.realpathSync(eggPath);
|
|
294
|
-
if (!eggPaths.includes(realpath)) {
|
|
295
|
-
eggPaths.unshift(realpath);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
return eggPaths;
|
|
299
|
-
}
|
|
300
|
-
/** start Plugin loader */
|
|
301
|
-
lookupDirs;
|
|
302
|
-
eggPlugins;
|
|
303
|
-
appPlugins;
|
|
304
|
-
customPlugins;
|
|
305
|
-
allPlugins;
|
|
306
|
-
orderPlugins;
|
|
307
|
-
/** enable plugins */
|
|
308
|
-
plugins;
|
|
309
|
-
/**
|
|
310
|
-
* Load config/plugin.js from {EggLoader#loadUnits}
|
|
311
|
-
*
|
|
312
|
-
* plugin.js is written below
|
|
313
|
-
*
|
|
314
|
-
* ```js
|
|
315
|
-
* {
|
|
316
|
-
* 'xxx-client': {
|
|
317
|
-
* enable: true,
|
|
318
|
-
* package: 'xxx-client',
|
|
319
|
-
* dep: [],
|
|
320
|
-
* env: [],
|
|
321
|
-
* },
|
|
322
|
-
* // short hand
|
|
323
|
-
* 'rds': false,
|
|
324
|
-
* 'depd': {
|
|
325
|
-
* enable: true,
|
|
326
|
-
* path: 'path/to/depd'
|
|
327
|
-
* }
|
|
328
|
-
* }
|
|
329
|
-
* ```
|
|
330
|
-
*
|
|
331
|
-
* If the plugin has path, Loader will find the module from it.
|
|
332
|
-
*
|
|
333
|
-
* Otherwise Loader will lookup follow the order by packageName
|
|
334
|
-
*
|
|
335
|
-
* 1. $APP_BASE/node_modules/${package}
|
|
336
|
-
* 2. $EGG_BASE/node_modules/${package}
|
|
337
|
-
*
|
|
338
|
-
* You can call `loader.plugins` that retrieve enabled plugins.
|
|
339
|
-
*
|
|
340
|
-
* ```js
|
|
341
|
-
* loader.plugins['xxx-client'] = {
|
|
342
|
-
* name: 'xxx-client', // the plugin name, it can be used in `dep`
|
|
343
|
-
* package: 'xxx-client', // the package name of plugin
|
|
344
|
-
* enable: true, // whether enabled
|
|
345
|
-
* path: 'path/to/xxx-client', // the directory of the plugin package
|
|
346
|
-
* dep: [], // the dependent plugins, you can use the plugin name
|
|
347
|
-
* env: [ 'local', 'unittest' ], // specify the serverEnv that only enable the plugin in it
|
|
348
|
-
* }
|
|
349
|
-
* ```
|
|
350
|
-
*
|
|
351
|
-
* `loader.allPlugins` can be used when retrieve all plugins.
|
|
352
|
-
* @function EggLoader#loadPlugin
|
|
353
|
-
* @since 1.0.0
|
|
354
|
-
*/
|
|
355
|
-
async loadPlugin() {
|
|
356
|
-
this.timing.start('Load Plugin');
|
|
357
|
-
this.lookupDirs = this.getLookupDirs();
|
|
358
|
-
this.allPlugins = {};
|
|
359
|
-
this.eggPlugins = await this.loadEggPlugins();
|
|
360
|
-
this.appPlugins = await this.loadAppPlugins();
|
|
361
|
-
this.customPlugins = this.loadCustomPlugins();
|
|
362
|
-
this.#extendPlugins(this.allPlugins, this.eggPlugins);
|
|
363
|
-
this.#extendPlugins(this.allPlugins, this.appPlugins);
|
|
364
|
-
this.#extendPlugins(this.allPlugins, this.customPlugins);
|
|
365
|
-
const enabledPluginNames = []; // enabled plugins that configured explicitly
|
|
366
|
-
const plugins = {};
|
|
367
|
-
const env = this.serverEnv;
|
|
368
|
-
for (const name in this.allPlugins) {
|
|
369
|
-
const plugin = this.allPlugins[name];
|
|
370
|
-
// resolve the real plugin.path based on plugin or package
|
|
371
|
-
plugin.path = this.getPluginPath(plugin);
|
|
372
|
-
// read plugin information from ${plugin.path}/package.json
|
|
373
|
-
if (!plugin.skipMerge) {
|
|
374
|
-
await this.#mergePluginConfig(plugin);
|
|
375
|
-
}
|
|
376
|
-
// disable the plugin that not match the serverEnv
|
|
377
|
-
if (env && plugin.env.length > 0 && !plugin.env.includes(env)) {
|
|
378
|
-
this.logger.info('[egg/core] Plugin %o is disabled by env unmatched, require env(%o) but got env is %o', name, plugin.env, env);
|
|
379
|
-
plugin.enable = false;
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
plugins[name] = plugin;
|
|
383
|
-
if (plugin.enable) {
|
|
384
|
-
enabledPluginNames.push(name);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
// retrieve the ordered plugins
|
|
388
|
-
this.orderPlugins = this.getOrderPlugins(plugins, enabledPluginNames, this.appPlugins);
|
|
389
|
-
const enablePlugins = {};
|
|
390
|
-
for (const plugin of this.orderPlugins) {
|
|
391
|
-
enablePlugins[plugin.name] = plugin;
|
|
392
|
-
}
|
|
393
|
-
debug('Loaded enable plugins: %j', Object.keys(enablePlugins));
|
|
394
|
-
/**
|
|
395
|
-
* Retrieve enabled plugins
|
|
396
|
-
* @member {Object} EggLoader#plugins
|
|
397
|
-
* @since 1.0.0
|
|
398
|
-
*/
|
|
399
|
-
this.plugins = enablePlugins;
|
|
400
|
-
this.timing.end('Load Plugin');
|
|
401
|
-
}
|
|
402
|
-
async loadAppPlugins() {
|
|
403
|
-
// loader plugins from application
|
|
404
|
-
const appPlugins = await this.readPluginConfigs(path.join(this.options.baseDir, 'config/plugin.default'));
|
|
405
|
-
debug('Loaded app plugins: %j', Object.keys(appPlugins).map((k) => `${k}:${appPlugins[k].enable}`));
|
|
406
|
-
return appPlugins;
|
|
407
|
-
}
|
|
408
|
-
async loadEggPlugins() {
|
|
409
|
-
// loader plugins from framework
|
|
410
|
-
const eggPluginConfigPaths = this.eggPaths.map((eggPath) => path.join(eggPath, 'config/plugin.default'));
|
|
411
|
-
const eggPlugins = await this.readPluginConfigs(eggPluginConfigPaths);
|
|
412
|
-
debug('Loaded egg plugins: %j', Object.keys(eggPlugins).map((k) => `${k}:${eggPlugins[k].enable}`));
|
|
413
|
-
return eggPlugins;
|
|
414
|
-
}
|
|
415
|
-
loadCustomPlugins() {
|
|
416
|
-
// loader plugins from process.env.EGG_PLUGINS
|
|
417
|
-
let customPlugins = {};
|
|
418
|
-
const configPaths = [];
|
|
419
|
-
if (process.env.EGG_PLUGINS) {
|
|
420
|
-
try {
|
|
421
|
-
customPlugins = JSON.parse(process.env.EGG_PLUGINS);
|
|
422
|
-
configPaths.push('<process.env.EGG_PLUGINS>');
|
|
423
|
-
}
|
|
424
|
-
catch (e) {
|
|
425
|
-
debug('parse EGG_PLUGINS failed, %s', e);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
// loader plugins from options.plugins
|
|
429
|
-
if (this.options.plugins) {
|
|
430
|
-
customPlugins = {
|
|
431
|
-
...customPlugins,
|
|
432
|
-
...this.options.plugins,
|
|
433
|
-
};
|
|
434
|
-
configPaths.push('<options.plugins>');
|
|
435
|
-
}
|
|
436
|
-
if (customPlugins) {
|
|
437
|
-
const configPath = configPaths.join(' or ');
|
|
438
|
-
for (const name in customPlugins) {
|
|
439
|
-
this.#normalizePluginConfig(customPlugins, name, configPath);
|
|
440
|
-
}
|
|
441
|
-
debug('Loaded custom plugins: %o', customPlugins);
|
|
442
|
-
}
|
|
443
|
-
return customPlugins;
|
|
444
|
-
}
|
|
445
|
-
/*
|
|
446
|
-
* Read plugin.js from multiple directory
|
|
447
|
-
*/
|
|
448
|
-
async readPluginConfigs(configPaths) {
|
|
449
|
-
if (!Array.isArray(configPaths)) {
|
|
450
|
-
configPaths = [configPaths];
|
|
451
|
-
}
|
|
452
|
-
// Get all plugin configurations
|
|
453
|
-
// plugin.default.js
|
|
454
|
-
// plugin.${scope}.js
|
|
455
|
-
// plugin.${env}.js
|
|
456
|
-
// plugin.${scope}_${env}.js
|
|
457
|
-
const newConfigPaths = [];
|
|
458
|
-
for (const filename of this.getTypeFiles('plugin')) {
|
|
459
|
-
for (let configPath of configPaths) {
|
|
460
|
-
configPath = path.join(path.dirname(configPath), filename);
|
|
461
|
-
newConfigPaths.push(configPath);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
const plugins = {};
|
|
465
|
-
for (const configPath of newConfigPaths) {
|
|
466
|
-
let filepath = this.resolveModule(configPath);
|
|
467
|
-
// let plugin.js compatible
|
|
468
|
-
if (configPath.endsWith('plugin.default') && !filepath) {
|
|
469
|
-
filepath = this.resolveModule(configPath.replace(/plugin\.default$/, 'plugin'));
|
|
470
|
-
}
|
|
471
|
-
if (!filepath) {
|
|
472
|
-
debug('[readPluginConfigs:ignore] plugin config not found %o', configPath);
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
const config = await utils.loadFile(filepath);
|
|
476
|
-
for (const name in config) {
|
|
477
|
-
this.#normalizePluginConfig(config, name, filepath);
|
|
478
|
-
}
|
|
479
|
-
this.#extendPlugins(plugins, config);
|
|
480
|
-
}
|
|
481
|
-
return plugins;
|
|
482
|
-
}
|
|
483
|
-
#normalizePluginConfig(plugins, name, configPath) {
|
|
484
|
-
const plugin = plugins[name];
|
|
485
|
-
// plugin_name: false
|
|
486
|
-
if (typeof plugin === 'boolean') {
|
|
487
|
-
plugins[name] = {
|
|
488
|
-
name,
|
|
489
|
-
enable: plugin,
|
|
490
|
-
dependencies: [],
|
|
491
|
-
optionalDependencies: [],
|
|
492
|
-
env: [],
|
|
493
|
-
from: configPath,
|
|
494
|
-
};
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
if (!('enable' in plugin)) {
|
|
498
|
-
Reflect.set(plugin, 'enable', true);
|
|
499
|
-
}
|
|
500
|
-
plugin.name = name;
|
|
501
|
-
plugin.dependencies = plugin.dependencies || [];
|
|
502
|
-
plugin.optionalDependencies = plugin.optionalDependencies || [];
|
|
503
|
-
plugin.env = plugin.env || [];
|
|
504
|
-
plugin.from = plugin.from || configPath;
|
|
505
|
-
depCompatible(plugin);
|
|
506
|
-
}
|
|
507
|
-
// Read plugin information from package.json and merge
|
|
508
|
-
// {
|
|
509
|
-
// eggPlugin: {
|
|
510
|
-
// "name": "", plugin name, must be same as name in config/plugin.js
|
|
511
|
-
// "dep": [], dependent plugins
|
|
512
|
-
// "env": "" env
|
|
513
|
-
// "strict": true, whether check plugin name, default to true.
|
|
514
|
-
// }
|
|
515
|
-
// }
|
|
516
|
-
async #mergePluginConfig(plugin) {
|
|
517
|
-
let pkg;
|
|
518
|
-
let eggPluginConfig;
|
|
519
|
-
const pluginPackage = path.join(plugin.path, 'package.json');
|
|
520
|
-
if (await utils.existsPath(pluginPackage)) {
|
|
521
|
-
pkg = await readJSON(pluginPackage);
|
|
522
|
-
eggPluginConfig = pkg.eggPlugin;
|
|
523
|
-
if (pkg.version) {
|
|
524
|
-
plugin.version = pkg.version;
|
|
525
|
-
}
|
|
526
|
-
// support commonjs and esm dist files
|
|
527
|
-
plugin.path = await this.#formatPluginPathFromPackageJSON(plugin.path, pkg);
|
|
528
|
-
}
|
|
529
|
-
const logger = this.options.logger;
|
|
530
|
-
if (!eggPluginConfig) {
|
|
531
|
-
logger.warn('[@eggjs/core/egg_loader] pkg.eggPlugin is missing in %s, plugin: %j', pluginPackage, plugin);
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
if (eggPluginConfig.name && eggPluginConfig.strict !== false && eggPluginConfig.name !== plugin.name) {
|
|
535
|
-
// pluginName is configured in config/plugin.js
|
|
536
|
-
// pluginConfigName is pkg.eggPlugin.name
|
|
537
|
-
logger.warn(`[@eggjs/core/egg_loader] pluginName(${plugin.name}) is different from pluginConfigName(${eggPluginConfig.name})`);
|
|
538
|
-
}
|
|
539
|
-
// dep compatible
|
|
540
|
-
depCompatible(eggPluginConfig);
|
|
541
|
-
for (const key of ['dependencies', 'optionalDependencies', 'env']) {
|
|
542
|
-
const values = eggPluginConfig[key];
|
|
543
|
-
const existsValues = Reflect.get(plugin, key);
|
|
544
|
-
if (Array.isArray(values) && !existsValues?.length) {
|
|
545
|
-
Reflect.set(plugin, key, values);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
getOrderPlugins(allPlugins, enabledPluginNames, appPlugins) {
|
|
550
|
-
// no plugins enabled
|
|
551
|
-
if (enabledPluginNames.length === 0) {
|
|
552
|
-
return [];
|
|
553
|
-
}
|
|
554
|
-
const result = sequencify(allPlugins, enabledPluginNames);
|
|
555
|
-
debug('Got plugins %j after sequencify', result);
|
|
556
|
-
// catch error when result.sequence is empty
|
|
557
|
-
if (result.sequence.length === 0) {
|
|
558
|
-
const err = new Error(`sequencify plugins has problem, missing: [${result.missingTasks}], recursive: [${result.recursiveDependencies}]`);
|
|
559
|
-
// find plugins which is required by the missing plugin
|
|
560
|
-
for (const missName of result.missingTasks) {
|
|
561
|
-
const requires = [];
|
|
562
|
-
for (const name in allPlugins) {
|
|
563
|
-
if (allPlugins[name].dependencies.includes(missName)) {
|
|
564
|
-
requires.push(name);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
err.message += `\n\t>> Plugin [${missName}] is disabled or missed, but is required by [${requires}]`;
|
|
568
|
-
}
|
|
569
|
-
err.name = 'PluginSequencifyError';
|
|
570
|
-
throw err;
|
|
571
|
-
}
|
|
572
|
-
// log the plugins that be enabled implicitly
|
|
573
|
-
const implicitEnabledPlugins = [];
|
|
574
|
-
const requireMap = {};
|
|
575
|
-
for (const name of result.sequence) {
|
|
576
|
-
for (const depName of allPlugins[name].dependencies) {
|
|
577
|
-
if (!requireMap[depName]) {
|
|
578
|
-
requireMap[depName] = [];
|
|
579
|
-
}
|
|
580
|
-
requireMap[depName].push(name);
|
|
581
|
-
}
|
|
582
|
-
if (!allPlugins[name].enable) {
|
|
583
|
-
implicitEnabledPlugins.push(name);
|
|
584
|
-
allPlugins[name].enable = true;
|
|
585
|
-
allPlugins[name].implicitEnable = true;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
for (const [name, dependents] of Object.entries(requireMap)) {
|
|
589
|
-
// note:`dependents` will not includes `optionalDependencies`
|
|
590
|
-
allPlugins[name].dependents = dependents;
|
|
591
|
-
}
|
|
592
|
-
// Following plugins will be enabled implicitly.
|
|
593
|
-
// - configclient required by [rpcClient]
|
|
594
|
-
// - monitor required by [rpcClient]
|
|
595
|
-
// - diamond required by [rpcClient]
|
|
596
|
-
if (implicitEnabledPlugins.length > 0) {
|
|
597
|
-
let message = implicitEnabledPlugins.map((name) => ` - ${name} required by [${requireMap[name]}]`).join('\n');
|
|
598
|
-
this.options.logger.info(`Following plugins will be enabled implicitly.\n${message}`);
|
|
599
|
-
// should warn when the plugin is disabled by app
|
|
600
|
-
const disabledPlugins = implicitEnabledPlugins.filter((name) => appPlugins[name] && appPlugins[name].enable === false);
|
|
601
|
-
if (disabledPlugins.length > 0) {
|
|
602
|
-
message = disabledPlugins.map((name) => ` - ${name} required by [${requireMap[name]}]`).join('\n');
|
|
603
|
-
this.options.logger.warn(`Following plugins will be enabled implicitly that is disabled by application.\n${message}`);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
return result.sequence.map((name) => allPlugins[name]);
|
|
607
|
-
}
|
|
608
|
-
getLookupDirs() {
|
|
609
|
-
const lookupDirs = new Set();
|
|
610
|
-
// try to locate the plugin in the following directories's node_modules
|
|
611
|
-
// -> {APP_PATH} -> {EGG_PATH} -> $CWD
|
|
612
|
-
lookupDirs.add(this.options.baseDir);
|
|
613
|
-
// try to locate the plugin at framework from upper to lower
|
|
614
|
-
for (let i = this.eggPaths.length - 1; i >= 0; i--) {
|
|
615
|
-
const eggPath = this.eggPaths[i];
|
|
616
|
-
lookupDirs.add(eggPath);
|
|
617
|
-
}
|
|
618
|
-
// should find the $cwd when test the plugins under npm3
|
|
619
|
-
lookupDirs.add(process.cwd());
|
|
620
|
-
return lookupDirs;
|
|
621
|
-
}
|
|
622
|
-
// Get the real plugin path
|
|
623
|
-
getPluginPath(plugin) {
|
|
624
|
-
if (plugin.path) {
|
|
625
|
-
return plugin.path;
|
|
626
|
-
}
|
|
627
|
-
if (plugin.package) {
|
|
628
|
-
assert(isValidatePackageName(plugin.package), `plugin ${plugin.name} invalid, use 'path' instead of package: "${plugin.package}"`);
|
|
629
|
-
}
|
|
630
|
-
return this.#resolvePluginPath(plugin);
|
|
631
|
-
}
|
|
632
|
-
#resolvePluginPath(plugin) {
|
|
633
|
-
const name = plugin.package || plugin.name;
|
|
634
|
-
try {
|
|
635
|
-
// should find the plugin directory
|
|
636
|
-
// pnpm will lift the node_modules to the sibling directory
|
|
637
|
-
// 'node_modules/.pnpm/yadan@2.0.0/node_modules/yadan/node_modules',
|
|
638
|
-
// 'node_modules/.pnpm/yadan@2.0.0/node_modules', <- this is the sibling directory
|
|
639
|
-
// 'node_modules/.pnpm/egg@2.33.1/node_modules/egg/node_modules',
|
|
640
|
-
// 'node_modules/.pnpm/egg@2.33.1/node_modules', <- this is the sibling directory
|
|
641
|
-
const pluginPkgFile = utils.resolvePath(`${name}/package.json`, {
|
|
642
|
-
paths: [...this.lookupDirs],
|
|
643
|
-
});
|
|
644
|
-
return path.dirname(pluginPkgFile);
|
|
645
|
-
}
|
|
646
|
-
catch (err) {
|
|
647
|
-
debug('[resolvePluginPath] error: %o, plugin info: %o', err, plugin);
|
|
648
|
-
throw new Error(`Can not find plugin ${name} in "${[...this.lookupDirs].join(', ')}"`, {
|
|
649
|
-
cause: err,
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
async #formatPluginPathFromPackageJSON(pluginPath, pluginPkg) {
|
|
654
|
-
let realPluginPath = pluginPath;
|
|
655
|
-
const exports = pluginPkg.eggPlugin?.exports;
|
|
656
|
-
if (exports) {
|
|
657
|
-
if (isESM) {
|
|
658
|
-
if (exports.import) {
|
|
659
|
-
realPluginPath = path.join(pluginPath, exports.import);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
else if (exports.require) {
|
|
663
|
-
realPluginPath = path.join(pluginPath, exports.require);
|
|
664
|
-
}
|
|
665
|
-
if (exports.typescript && isSupportTypeScript() && !(await exists(realPluginPath))) {
|
|
666
|
-
// if require/import path not exists, use typescript path for development stage
|
|
667
|
-
realPluginPath = path.join(pluginPath, exports.typescript);
|
|
668
|
-
debug('[formatPluginPathFromPackageJSON] use typescript path %o', realPluginPath);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
else if (pluginPkg.exports?.['.'] && pluginPkg.type === 'module') {
|
|
672
|
-
// support esm exports
|
|
673
|
-
let defaultExport = pluginPkg.exports['.'];
|
|
674
|
-
if (typeof defaultExport === 'string') {
|
|
675
|
-
// "exports": {
|
|
676
|
-
// ".": "./src/index.ts",
|
|
677
|
-
// "./app": "./src/app.ts",
|
|
678
|
-
// }
|
|
679
|
-
realPluginPath = path.dirname(path.join(pluginPath, defaultExport));
|
|
680
|
-
}
|
|
681
|
-
else if (defaultExport?.import) {
|
|
682
|
-
if (typeof defaultExport.import === 'string') {
|
|
683
|
-
// {
|
|
684
|
-
// "exports": {
|
|
685
|
-
// ".": {
|
|
686
|
-
// "import": "./src/index.ts",
|
|
687
|
-
// },
|
|
688
|
-
// }
|
|
689
|
-
// }
|
|
690
|
-
realPluginPath = path.dirname(path.join(pluginPath, defaultExport.import));
|
|
691
|
-
}
|
|
692
|
-
else if (defaultExport.import.default) {
|
|
693
|
-
// {
|
|
694
|
-
// "exports": {
|
|
695
|
-
// ".": {
|
|
696
|
-
// "import": {
|
|
697
|
-
// "default": "./src/index.ts",
|
|
698
|
-
// },
|
|
699
|
-
// },
|
|
700
|
-
// }
|
|
701
|
-
// }
|
|
702
|
-
realPluginPath = path.dirname(path.join(pluginPath, defaultExport.import.default));
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
debug('[formatPluginPathFromPackageJSON] resolve plugin path from %o to %o, defaultExport: %o', pluginPath, realPluginPath, defaultExport);
|
|
706
|
-
}
|
|
707
|
-
return realPluginPath;
|
|
708
|
-
}
|
|
709
|
-
#extendPlugins(targets, plugins) {
|
|
710
|
-
if (!plugins) {
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
for (const name in plugins) {
|
|
714
|
-
const plugin = plugins[name];
|
|
715
|
-
let targetPlugin = targets[name];
|
|
716
|
-
if (!targetPlugin) {
|
|
717
|
-
targetPlugin = {};
|
|
718
|
-
targets[name] = targetPlugin;
|
|
719
|
-
}
|
|
720
|
-
if (targetPlugin.package && targetPlugin.package === plugin.package) {
|
|
721
|
-
this.logger.warn('[@eggjs/core] plugin %s has been defined that is %j, but you define again in %s', name, targetPlugin, plugin.from);
|
|
722
|
-
}
|
|
723
|
-
if (plugin.path || plugin.package) {
|
|
724
|
-
delete targetPlugin.path;
|
|
725
|
-
delete targetPlugin.package;
|
|
726
|
-
}
|
|
727
|
-
for (const [prop, value] of Object.entries(plugin)) {
|
|
728
|
-
if (value === undefined) {
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
|
-
if (Reflect.get(targetPlugin, prop) && Array.isArray(value) && value.length === 0) {
|
|
732
|
-
continue;
|
|
733
|
-
}
|
|
734
|
-
Reflect.set(targetPlugin, prop, value);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
/** end Plugin loader */
|
|
739
|
-
/** start Config loader */
|
|
740
|
-
configMeta;
|
|
741
|
-
config;
|
|
742
|
-
/**
|
|
743
|
-
* Load config/config.js
|
|
744
|
-
*
|
|
745
|
-
* Will merge config.default.js 和 config.${env}.js
|
|
746
|
-
*
|
|
747
|
-
* @function EggLoader#loadConfig
|
|
748
|
-
* @since 1.0.0
|
|
749
|
-
*/
|
|
750
|
-
async loadConfig() {
|
|
751
|
-
this.timing.start('Load Config');
|
|
752
|
-
this.configMeta = {};
|
|
753
|
-
const target = {
|
|
754
|
-
middleware: [],
|
|
755
|
-
coreMiddleware: [],
|
|
756
|
-
};
|
|
757
|
-
// Load Application config first
|
|
758
|
-
const appConfig = await this.#preloadAppConfig();
|
|
759
|
-
// plugin config.default
|
|
760
|
-
// framework config.default
|
|
761
|
-
// app config.default
|
|
762
|
-
// plugin config.{env}
|
|
763
|
-
// framework config.{env}
|
|
764
|
-
// app config.{env}
|
|
765
|
-
for (const filename of this.getTypeFiles('config')) {
|
|
766
|
-
for (const unit of this.getLoadUnits()) {
|
|
767
|
-
const isApp = unit.type === 'app';
|
|
768
|
-
const config = await this.#loadConfig(unit.path, filename, isApp ? undefined : appConfig, unit.type);
|
|
769
|
-
if (!config) {
|
|
770
|
-
continue;
|
|
771
|
-
}
|
|
772
|
-
debug('[loadConfig] Loaded config %s/%s, %j', unit.path, filename, config);
|
|
773
|
-
extend(true, target, config);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
// load env from process.env.EGG_APP_CONFIG
|
|
777
|
-
const envConfig = this.#loadConfigFromEnv();
|
|
778
|
-
if (envConfig) {
|
|
779
|
-
debug('[loadConfig] Loaded config from env, %j', envConfig);
|
|
780
|
-
extend(true, target, envConfig);
|
|
781
|
-
}
|
|
782
|
-
// You can manipulate the order of app.config.coreMiddleware and app.config.appMiddleware in app.js
|
|
783
|
-
target.coreMiddleware = target.coreMiddleware || [];
|
|
784
|
-
// alias for coreMiddleware
|
|
785
|
-
target.coreMiddlewares = target.coreMiddleware;
|
|
786
|
-
target.appMiddleware = target.middleware || [];
|
|
787
|
-
// alias for appMiddleware
|
|
788
|
-
target.appMiddlewares = target.appMiddleware;
|
|
789
|
-
this.config = target;
|
|
790
|
-
debug('[loadConfig] type: %s, all config: %o', this.app.type, this.config);
|
|
791
|
-
this.timing.end('Load Config');
|
|
792
|
-
}
|
|
793
|
-
async #preloadAppConfig() {
|
|
794
|
-
const names = ['config.default', `config.${this.serverEnv}`];
|
|
795
|
-
const target = {};
|
|
796
|
-
for (const filename of names) {
|
|
797
|
-
const config = await this.#loadConfig(this.options.baseDir, filename, undefined, 'app');
|
|
798
|
-
if (!config) {
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
extend(true, target, config);
|
|
802
|
-
}
|
|
803
|
-
return target;
|
|
804
|
-
}
|
|
805
|
-
async #loadConfig(dirpath, filename, extraInject, type) {
|
|
806
|
-
const isPlugin = type === 'plugin';
|
|
807
|
-
const isApp = type === 'app';
|
|
808
|
-
let filepath = this.resolveModule(path.join(dirpath, 'config', filename));
|
|
809
|
-
// let config.js compatible
|
|
810
|
-
if (filename === 'config.default' && !filepath) {
|
|
811
|
-
filepath = this.resolveModule(path.join(dirpath, 'config/config'));
|
|
812
|
-
}
|
|
813
|
-
if (!filepath) {
|
|
814
|
-
return;
|
|
815
|
-
}
|
|
816
|
-
const config = await this.loadFile(filepath, this.appInfo, extraInject);
|
|
817
|
-
if (!config)
|
|
818
|
-
return;
|
|
819
|
-
if (isPlugin || isApp) {
|
|
820
|
-
assert(!config.coreMiddleware, 'Can not define coreMiddleware in app or plugin');
|
|
821
|
-
}
|
|
822
|
-
if (!isApp) {
|
|
823
|
-
assert(!config.middleware, 'Can not define middleware in ' + filepath);
|
|
824
|
-
}
|
|
825
|
-
// store config meta, check where is the property of config come from.
|
|
826
|
-
this.#setConfigMeta(config, filepath);
|
|
827
|
-
return config;
|
|
828
|
-
}
|
|
829
|
-
#loadConfigFromEnv() {
|
|
830
|
-
const envConfigStr = process.env.EGG_APP_CONFIG;
|
|
831
|
-
if (!envConfigStr)
|
|
832
|
-
return;
|
|
833
|
-
try {
|
|
834
|
-
const envConfig = JSON.parse(envConfigStr);
|
|
835
|
-
this.#setConfigMeta(envConfig, '<process.env.EGG_APP_CONFIG>');
|
|
836
|
-
return envConfig;
|
|
837
|
-
}
|
|
838
|
-
catch {
|
|
839
|
-
this.options.logger.warn('[egg-loader] process.env.EGG_APP_CONFIG is not invalid JSON: %s', envConfigStr);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
#setConfigMeta(config, filepath) {
|
|
843
|
-
config = extend(true, {}, config);
|
|
844
|
-
this.#setConfig(config, filepath);
|
|
845
|
-
extend(true, this.configMeta, config);
|
|
846
|
-
}
|
|
847
|
-
#setConfig(obj, filepath) {
|
|
848
|
-
for (const key of Object.keys(obj)) {
|
|
849
|
-
const val = obj[key];
|
|
850
|
-
// ignore console
|
|
851
|
-
if (key === 'console' && val && typeof val.Console === 'function' && val.Console === console.Console) {
|
|
852
|
-
obj[key] = filepath;
|
|
853
|
-
continue;
|
|
854
|
-
}
|
|
855
|
-
if (val && Object.getPrototypeOf(val) === Object.prototype && Object.keys(val).length > 0) {
|
|
856
|
-
this.#setConfig(val, filepath);
|
|
857
|
-
continue;
|
|
858
|
-
}
|
|
859
|
-
obj[key] = filepath;
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
/** end Config loader */
|
|
863
|
-
/** start Extend loader */
|
|
864
|
-
/**
|
|
865
|
-
* mixin Agent.prototype
|
|
866
|
-
* @function EggLoader#loadAgentExtend
|
|
867
|
-
* @since 1.0.0
|
|
868
|
-
*/
|
|
869
|
-
async loadAgentExtend() {
|
|
870
|
-
await this.loadExtend('agent', this.app);
|
|
871
|
-
}
|
|
872
|
-
/**
|
|
873
|
-
* mixin Application.prototype
|
|
874
|
-
* @function EggLoader#loadApplicationExtend
|
|
875
|
-
* @since 1.0.0
|
|
876
|
-
*/
|
|
877
|
-
async loadApplicationExtend() {
|
|
878
|
-
await this.loadExtend('application', this.app);
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* mixin Request.prototype
|
|
882
|
-
* @function EggLoader#loadRequestExtend
|
|
883
|
-
* @since 1.0.0
|
|
884
|
-
*/
|
|
885
|
-
async loadRequestExtend() {
|
|
886
|
-
await this.loadExtend('request', this.app.request);
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* mixin Response.prototype
|
|
890
|
-
* @function EggLoader#loadResponseExtend
|
|
891
|
-
* @since 1.0.0
|
|
892
|
-
*/
|
|
893
|
-
async loadResponseExtend() {
|
|
894
|
-
await this.loadExtend('response', this.app.response);
|
|
895
|
-
}
|
|
896
|
-
/**
|
|
897
|
-
* mixin Context.prototype
|
|
898
|
-
* @function EggLoader#loadContextExtend
|
|
899
|
-
* @since 1.0.0
|
|
900
|
-
*/
|
|
901
|
-
async loadContextExtend() {
|
|
902
|
-
await this.loadExtend('context', this.app.context);
|
|
903
|
-
}
|
|
904
|
-
/**
|
|
905
|
-
* mixin app.Helper.prototype
|
|
906
|
-
* @function EggLoader#loadHelperExtend
|
|
907
|
-
* @since 1.0.0
|
|
908
|
-
*/
|
|
909
|
-
async loadHelperExtend() {
|
|
910
|
-
if (this.app.Helper) {
|
|
911
|
-
await this.loadExtend('helper', this.app.Helper.prototype);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
* Find all extend file paths by name
|
|
916
|
-
* can be override in top level framework to support load `app/extends/{name}.js`
|
|
917
|
-
*
|
|
918
|
-
* @param {String} name - filename which may be `app/extend/{name}.js`
|
|
919
|
-
* @returns {Array} filepaths extend file paths
|
|
920
|
-
* @private
|
|
921
|
-
*/
|
|
922
|
-
getExtendFilePaths(name) {
|
|
923
|
-
return this.getLoadUnits().map((unit) => path.join(unit.path, 'app/extend', name));
|
|
924
|
-
}
|
|
925
|
-
/**
|
|
926
|
-
* Loader app/extend/xx.js to `prototype`,
|
|
927
|
-
* @function loadExtend
|
|
928
|
-
* @param {String} name - filename which may be `app/extend/{name}.js`
|
|
929
|
-
* @param {Object} proto - prototype that mixed
|
|
930
|
-
* @since 1.0.0
|
|
931
|
-
*/
|
|
932
|
-
async loadExtend(name, proto) {
|
|
933
|
-
this.timing.start(`Load extend/${name}.js`);
|
|
934
|
-
// All extend files
|
|
935
|
-
const filepaths = this.getExtendFilePaths(name);
|
|
936
|
-
// if use mm.env and serverEnv is not unittest
|
|
937
|
-
const needUnittest = 'EGG_MOCK_SERVER_ENV' in process.env && this.serverEnv !== 'unittest';
|
|
938
|
-
const length = filepaths.length;
|
|
939
|
-
for (let i = 0; i < length; i++) {
|
|
940
|
-
const filepath = filepaths[i];
|
|
941
|
-
filepaths.push(filepath + `.${this.serverEnv}`);
|
|
942
|
-
if (needUnittest) {
|
|
943
|
-
filepaths.push(filepath + '.unittest');
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
debug('loadExtend %s, filepaths: %j', name, filepaths);
|
|
947
|
-
const mergeRecord = new Map();
|
|
948
|
-
for (const rawFilepath of filepaths) {
|
|
949
|
-
const filepath = this.resolveModule(rawFilepath);
|
|
950
|
-
if (!filepath) {
|
|
951
|
-
// debug('loadExtend %o not found', rawFilepath);
|
|
952
|
-
continue;
|
|
953
|
-
}
|
|
954
|
-
if (filepath.endsWith('/index.js')) {
|
|
955
|
-
this.app.deprecate(`app/extend/${name}/index.js is deprecated, use app/extend/${name}.js instead`);
|
|
956
|
-
}
|
|
957
|
-
else if (filepath.endsWith('/index.ts')) {
|
|
958
|
-
this.app.deprecate(`app/extend/${name}/index.ts is deprecated, use app/extend/${name}.ts instead`);
|
|
959
|
-
}
|
|
960
|
-
let ext = await this.requireFile(filepath);
|
|
961
|
-
// if extend object is Class, should use Class.prototype instead
|
|
962
|
-
if (isClass(ext)) {
|
|
963
|
-
ext = ext.prototype;
|
|
964
|
-
}
|
|
965
|
-
const properties = Object.getOwnPropertyNames(ext)
|
|
966
|
-
.concat(Object.getOwnPropertySymbols(ext))
|
|
967
|
-
.filter((name) => name !== 'constructor'); // ignore class constructor for extend
|
|
968
|
-
for (const property of properties) {
|
|
969
|
-
if (mergeRecord.has(property)) {
|
|
970
|
-
debug('Property: "%s" already exists in "%s",it will be redefined by "%s"', property, mergeRecord.get(property), filepath);
|
|
971
|
-
}
|
|
972
|
-
// Copy descriptor
|
|
973
|
-
let descriptor = Object.getOwnPropertyDescriptor(ext, property);
|
|
974
|
-
let originalDescriptor = Object.getOwnPropertyDescriptor(proto, property);
|
|
975
|
-
if (!originalDescriptor) {
|
|
976
|
-
// try to get descriptor from originalPrototypes
|
|
977
|
-
const originalProto = originalPrototypes[name];
|
|
978
|
-
if (originalProto) {
|
|
979
|
-
originalDescriptor = Object.getOwnPropertyDescriptor(originalProto, property);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
if (originalDescriptor) {
|
|
983
|
-
// don't override descriptor
|
|
984
|
-
descriptor = {
|
|
985
|
-
...descriptor,
|
|
986
|
-
};
|
|
987
|
-
if (!descriptor.set && originalDescriptor.set) {
|
|
988
|
-
descriptor.set = originalDescriptor.set;
|
|
989
|
-
}
|
|
990
|
-
if (!descriptor.get && originalDescriptor.get) {
|
|
991
|
-
descriptor.get = originalDescriptor.get;
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
Object.defineProperty(proto, property, descriptor);
|
|
995
|
-
mergeRecord.set(property, filepath);
|
|
996
|
-
}
|
|
997
|
-
debug('merge %j to %s from %s', properties, name, filepath);
|
|
998
|
-
}
|
|
999
|
-
this.timing.end(`Load extend/${name}.js`);
|
|
1000
|
-
}
|
|
1001
|
-
/** end Extend loader */
|
|
1002
|
-
/** start Custom loader */
|
|
1003
|
-
/**
|
|
1004
|
-
* load app.js
|
|
1005
|
-
*
|
|
1006
|
-
* @example
|
|
1007
|
-
* - old:
|
|
1008
|
-
*
|
|
1009
|
-
* ```js
|
|
1010
|
-
* module.exports = function(app) {
|
|
1011
|
-
* doSomething();
|
|
1012
|
-
* }
|
|
1013
|
-
* ```
|
|
1014
|
-
*
|
|
1015
|
-
* - new:
|
|
1016
|
-
*
|
|
1017
|
-
* ```js
|
|
1018
|
-
* module.exports = class Boot {
|
|
1019
|
-
* constructor(app) {
|
|
1020
|
-
* this.app = app;
|
|
1021
|
-
* }
|
|
1022
|
-
* configDidLoad() {
|
|
1023
|
-
* doSomething();
|
|
1024
|
-
* }
|
|
1025
|
-
* }
|
|
1026
|
-
* @since 1.0.0
|
|
1027
|
-
*/
|
|
1028
|
-
async loadCustomApp() {
|
|
1029
|
-
await this.#loadBootHook('app');
|
|
1030
|
-
this.lifecycle.triggerConfigWillLoad();
|
|
1031
|
-
}
|
|
1032
|
-
/**
|
|
1033
|
-
* Load agent.js, same as {@link EggLoader#loadCustomApp}
|
|
1034
|
-
*/
|
|
1035
|
-
async loadCustomAgent() {
|
|
1036
|
-
await this.#loadBootHook('agent');
|
|
1037
|
-
this.lifecycle.triggerConfigWillLoad();
|
|
1038
|
-
}
|
|
1039
|
-
// FIXME: no logger used after egg removed
|
|
1040
|
-
loadBootHook() {
|
|
1041
|
-
// do nothing
|
|
1042
|
-
}
|
|
1043
|
-
async #loadBootHook(fileName) {
|
|
1044
|
-
this.timing.start(`Load ${fileName}.js`);
|
|
1045
|
-
for (const unit of this.getLoadUnits()) {
|
|
1046
|
-
const bootFile = path.join(unit.path, fileName);
|
|
1047
|
-
const bootFilePath = this.resolveModule(bootFile);
|
|
1048
|
-
if (!bootFilePath) {
|
|
1049
|
-
debug('[loadBootHook:ignore] %o not found', bootFile);
|
|
1050
|
-
continue;
|
|
1051
|
-
}
|
|
1052
|
-
debug('[loadBootHook:success] %o => %o', bootFile, bootFilePath);
|
|
1053
|
-
const bootHook = await this.requireFile(bootFilePath);
|
|
1054
|
-
if (isClass(bootHook)) {
|
|
1055
|
-
bootHook.prototype.fullPath = bootFilePath;
|
|
1056
|
-
// if is boot class, add to lifecycle
|
|
1057
|
-
this.lifecycle.addBootHook(bootHook);
|
|
1058
|
-
debug('[loadBootHook] add BootHookClass from %o', bootFilePath);
|
|
1059
|
-
}
|
|
1060
|
-
else if (typeof bootHook === 'function') {
|
|
1061
|
-
// if is boot function, wrap to class
|
|
1062
|
-
// for compatibility
|
|
1063
|
-
this.lifecycle.addFunctionAsBootHook(bootHook, bootFilePath);
|
|
1064
|
-
debug('[loadBootHook] add bootHookFunction from %o', bootFilePath);
|
|
1065
|
-
}
|
|
1066
|
-
else {
|
|
1067
|
-
this.options.logger.warn('[@eggjs/core/egg_loader] %s must exports a boot class', bootFilePath);
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
// init boots
|
|
1071
|
-
this.lifecycle.init();
|
|
1072
|
-
this.timing.end(`Load ${fileName}.js`);
|
|
1073
|
-
}
|
|
1074
|
-
/** end Custom loader */
|
|
1075
|
-
/** start Service loader */
|
|
1076
|
-
/**
|
|
1077
|
-
* Load app/service
|
|
1078
|
-
* @function EggLoader#loadService
|
|
1079
|
-
* @param {Object} options - LoaderOptions
|
|
1080
|
-
* @since 1.0.0
|
|
1081
|
-
*/
|
|
1082
|
-
async loadService(options) {
|
|
1083
|
-
this.timing.start('Load Service');
|
|
1084
|
-
// 载入到 app.serviceClasses
|
|
1085
|
-
const servicePaths = this.getLoadUnits().map((unit) => path.join(unit.path, 'app/service'));
|
|
1086
|
-
options = {
|
|
1087
|
-
call: true,
|
|
1088
|
-
caseStyle: CaseStyle.lower,
|
|
1089
|
-
fieldClass: 'serviceClasses',
|
|
1090
|
-
directory: servicePaths,
|
|
1091
|
-
...options,
|
|
1092
|
-
};
|
|
1093
|
-
debug('[loadService] options: %o', options);
|
|
1094
|
-
await this.loadToContext(servicePaths, 'service', options);
|
|
1095
|
-
this.timing.end('Load Service');
|
|
1096
|
-
}
|
|
1097
|
-
/** end Service loader */
|
|
1098
|
-
/** start Middleware loader */
|
|
1099
|
-
/**
|
|
1100
|
-
* Load app/middleware
|
|
1101
|
-
*
|
|
1102
|
-
* app.config.xx is the options of the middleware xx that has same name as config
|
|
1103
|
-
*
|
|
1104
|
-
* @function EggLoader#loadMiddleware
|
|
1105
|
-
* @param {Object} opt - LoaderOptions
|
|
1106
|
-
* @example
|
|
1107
|
-
* ```js
|
|
1108
|
-
* // app/middleware/status.js
|
|
1109
|
-
* module.exports = function(options, app) {
|
|
1110
|
-
* // options == app.config.status
|
|
1111
|
-
* return async next => {
|
|
1112
|
-
* await next();
|
|
1113
|
-
* }
|
|
1114
|
-
* }
|
|
1115
|
-
* ```
|
|
1116
|
-
* @since 1.0.0
|
|
1117
|
-
*/
|
|
1118
|
-
async loadMiddleware(opt) {
|
|
1119
|
-
this.timing.start('Load Middleware');
|
|
1120
|
-
const app = this.app;
|
|
1121
|
-
// load middleware to app.middleware
|
|
1122
|
-
const middlewarePaths = this.getLoadUnits().map((unit) => path.join(unit.path, 'app/middleware'));
|
|
1123
|
-
opt = {
|
|
1124
|
-
call: false,
|
|
1125
|
-
override: true,
|
|
1126
|
-
caseStyle: CaseStyle.lower,
|
|
1127
|
-
directory: middlewarePaths,
|
|
1128
|
-
...opt,
|
|
1129
|
-
};
|
|
1130
|
-
await this.loadToApp(middlewarePaths, 'middlewares', opt);
|
|
1131
|
-
debug('[loadMiddleware] middlewarePaths: %j', middlewarePaths);
|
|
1132
|
-
for (const name in app.middlewares) {
|
|
1133
|
-
Object.defineProperty(app.middleware, name, {
|
|
1134
|
-
get() {
|
|
1135
|
-
return app.middlewares[name];
|
|
1136
|
-
},
|
|
1137
|
-
enumerable: false,
|
|
1138
|
-
configurable: false,
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1141
|
-
this.options.logger.info('Use coreMiddleware order: %j', this.config.coreMiddleware);
|
|
1142
|
-
this.options.logger.info('Use appMiddleware order: %j', this.config.appMiddleware);
|
|
1143
|
-
// use middleware ordered by app.config.coreMiddleware and app.config.appMiddleware
|
|
1144
|
-
const middlewareNames = this.config.coreMiddleware.concat(this.config.appMiddleware);
|
|
1145
|
-
debug('[loadMiddleware] middlewareNames: %j', middlewareNames);
|
|
1146
|
-
const middlewaresMap = new Map();
|
|
1147
|
-
for (const name of middlewareNames) {
|
|
1148
|
-
const createMiddleware = app.middlewares[name];
|
|
1149
|
-
if (!createMiddleware) {
|
|
1150
|
-
throw new TypeError(`Middleware ${name} not found`);
|
|
1151
|
-
}
|
|
1152
|
-
if (middlewaresMap.has(name)) {
|
|
1153
|
-
throw new TypeError(`Middleware ${name} redefined`);
|
|
1154
|
-
}
|
|
1155
|
-
middlewaresMap.set(name, true);
|
|
1156
|
-
const options = this.config[name] || {};
|
|
1157
|
-
let mw = createMiddleware(options, app);
|
|
1158
|
-
assert(typeof mw === 'function', `Middleware ${name} must be a function, but actual is ${inspect(mw)}`);
|
|
1159
|
-
if (isGeneratorFunction(mw)) {
|
|
1160
|
-
const fullpath = Reflect.get(createMiddleware, FULLPATH);
|
|
1161
|
-
throw new TypeError(`Support for generators was removed, middleware: ${name}, fullpath: ${fullpath}`);
|
|
1162
|
-
}
|
|
1163
|
-
mw._name = name;
|
|
1164
|
-
// middlewares support options.enable, options.ignore and options.match
|
|
1165
|
-
mw = wrapMiddleware(mw, options);
|
|
1166
|
-
if (mw) {
|
|
1167
|
-
if (debug.enabled) {
|
|
1168
|
-
// show mw debug log on every request
|
|
1169
|
-
mw = debugMiddlewareWrapper(mw);
|
|
1170
|
-
}
|
|
1171
|
-
app.use(mw);
|
|
1172
|
-
debug('[loadMiddleware] Use middleware: %s with options: %j', name, options);
|
|
1173
|
-
this.options.logger.info('[@eggjs/core/egg_loader] Use middleware: %s', name);
|
|
1174
|
-
}
|
|
1175
|
-
else {
|
|
1176
|
-
this.options.logger.info('[@eggjs/core/egg_loader] Disable middleware: %s', name);
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
this.options.logger.info('[@eggjs/core/egg_loader] Loaded middleware from %j', middlewarePaths);
|
|
1180
|
-
this.timing.end('Load Middleware');
|
|
1181
|
-
// add router middleware, make sure router is the last middleware
|
|
1182
|
-
const mw = this.app.router.middleware();
|
|
1183
|
-
Reflect.set(mw, '_name', 'routerMiddleware');
|
|
1184
|
-
this.app.use(mw);
|
|
1185
|
-
}
|
|
1186
|
-
/** end Middleware loader */
|
|
1187
|
-
/** start Controller loader */
|
|
1188
|
-
/**
|
|
1189
|
-
* Load app/controller
|
|
1190
|
-
* @param {Object} opt - LoaderOptions
|
|
1191
|
-
* @since 1.0.0
|
|
1192
|
-
*/
|
|
1193
|
-
async loadController(opt) {
|
|
1194
|
-
this.timing.start('Load Controller');
|
|
1195
|
-
const controllerBase = path.join(this.options.baseDir, 'app/controller');
|
|
1196
|
-
opt = {
|
|
1197
|
-
caseStyle: CaseStyle.lower,
|
|
1198
|
-
directory: controllerBase,
|
|
1199
|
-
initializer: (obj, opt) => {
|
|
1200
|
-
// return class if it exports a function
|
|
1201
|
-
// ```js
|
|
1202
|
-
// module.exports = app => {
|
|
1203
|
-
// return class HomeController extends app.Controller {};
|
|
1204
|
-
// }
|
|
1205
|
-
// ```
|
|
1206
|
-
if (isGeneratorFunction(obj)) {
|
|
1207
|
-
throw new TypeError(`Support for generators was removed, fullpath: ${opt.path}`);
|
|
1208
|
-
}
|
|
1209
|
-
if (!isClass(obj) && !isAsyncFunction(obj) && typeof obj === 'function') {
|
|
1210
|
-
obj = obj(this.app);
|
|
1211
|
-
debug('[loadController] after init(app) => %o, meta: %j', obj, opt);
|
|
1212
|
-
if (isGeneratorFunction(obj)) {
|
|
1213
|
-
throw new TypeError(`Support for generators was removed, fullpath: ${opt.path}`);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
if (isClass(obj)) {
|
|
1217
|
-
obj.prototype.pathName = opt.pathName;
|
|
1218
|
-
obj.prototype.fullPath = opt.path;
|
|
1219
|
-
return wrapControllerClass(obj, opt.path);
|
|
1220
|
-
}
|
|
1221
|
-
if (isObject(obj)) {
|
|
1222
|
-
return wrapObject(obj, opt.path);
|
|
1223
|
-
}
|
|
1224
|
-
if (isAsyncFunction(obj)) {
|
|
1225
|
-
return wrapObject({ 'module.exports': obj }, opt.path)['module.exports'];
|
|
1226
|
-
}
|
|
1227
|
-
return obj;
|
|
1228
|
-
},
|
|
1229
|
-
...opt,
|
|
1230
|
-
};
|
|
1231
|
-
await this.loadToApp(controllerBase, 'controller', opt);
|
|
1232
|
-
debug('[loadController] app.controller => %o', this.app.controller);
|
|
1233
|
-
this.options.logger.info('[@eggjs/core/egg_loader] Controller loaded: %s', controllerBase);
|
|
1234
|
-
this.timing.end('Load Controller');
|
|
1235
|
-
}
|
|
1236
|
-
/** end Controller loader */
|
|
1237
|
-
/** start Router loader */
|
|
1238
|
-
/**
|
|
1239
|
-
* Load app/router.js
|
|
1240
|
-
* @function EggLoader#loadRouter
|
|
1241
|
-
* @since 1.0.0
|
|
1242
|
-
*/
|
|
1243
|
-
async loadRouter() {
|
|
1244
|
-
this.timing.start('Load Router');
|
|
1245
|
-
await this.loadFile(path.join(this.options.baseDir, 'app/router'));
|
|
1246
|
-
this.timing.end('Load Router');
|
|
1247
|
-
}
|
|
1248
|
-
/** end Router loader */
|
|
1249
|
-
/** start CustomLoader loader */
|
|
1250
|
-
async loadCustomLoader() {
|
|
1251
|
-
assert(this.config, 'should loadConfig first');
|
|
1252
|
-
const customLoader = this.config.customLoader || {};
|
|
1253
|
-
for (const property of Object.keys(customLoader)) {
|
|
1254
|
-
const loaderConfig = {
|
|
1255
|
-
...customLoader[property],
|
|
1256
|
-
};
|
|
1257
|
-
assert(loaderConfig.directory, `directory is required for config.customLoader.${property}`);
|
|
1258
|
-
let directory;
|
|
1259
|
-
if (loaderConfig.loadunit === true) {
|
|
1260
|
-
directory = this.getLoadUnits().map((unit) => path.join(unit.path, loaderConfig.directory));
|
|
1261
|
-
}
|
|
1262
|
-
else {
|
|
1263
|
-
directory = path.join(this.appInfo.baseDir, loaderConfig.directory);
|
|
1264
|
-
}
|
|
1265
|
-
const inject = loaderConfig.inject || 'app';
|
|
1266
|
-
debug('[loadCustomLoader] loaderConfig: %o, inject: %o, directory: %o', loaderConfig, inject, directory);
|
|
1267
|
-
switch (inject) {
|
|
1268
|
-
case 'ctx': {
|
|
1269
|
-
assert(!(property in this.app.context), `customLoader should not override ctx.${property}`);
|
|
1270
|
-
const options = {
|
|
1271
|
-
caseStyle: CaseStyle.lower,
|
|
1272
|
-
fieldClass: `${property}Classes`,
|
|
1273
|
-
...loaderConfig,
|
|
1274
|
-
directory,
|
|
1275
|
-
};
|
|
1276
|
-
await this.loadToContext(directory, property, options);
|
|
1277
|
-
break;
|
|
1278
|
-
}
|
|
1279
|
-
case 'app': {
|
|
1280
|
-
assert(!(property in this.app), `customLoader should not override app.${property}`);
|
|
1281
|
-
const options = {
|
|
1282
|
-
caseStyle: CaseStyle.lower,
|
|
1283
|
-
initializer: (Clazz) => {
|
|
1284
|
-
return isClass(Clazz) ? new Clazz(this.app) : Clazz;
|
|
1285
|
-
},
|
|
1286
|
-
...loaderConfig,
|
|
1287
|
-
directory,
|
|
1288
|
-
};
|
|
1289
|
-
await this.loadToApp(directory, property, options);
|
|
1290
|
-
break;
|
|
1291
|
-
}
|
|
1292
|
-
default:
|
|
1293
|
-
throw new Error('inject only support app or ctx');
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
/** end CustomLoader loader */
|
|
1298
|
-
// Low Level API
|
|
1299
|
-
/**
|
|
1300
|
-
* Load single file, will invoke when export is function
|
|
1301
|
-
*
|
|
1302
|
-
* @param {String} filepath - fullpath
|
|
1303
|
-
* @param {Array} inject - pass rest arguments into the function when invoke
|
|
1304
|
-
* @returns {Object} exports
|
|
1305
|
-
* @example
|
|
1306
|
-
* ```js
|
|
1307
|
-
* app.loader.loadFile(path.join(app.options.baseDir, 'config/router.js'));
|
|
1308
|
-
* ```
|
|
1309
|
-
* @since 1.0.0
|
|
1310
|
-
*/
|
|
1311
|
-
async loadFile(filepath, ...inject) {
|
|
1312
|
-
const fullpath = filepath && this.resolveModule(filepath);
|
|
1313
|
-
if (!fullpath) {
|
|
1314
|
-
return null;
|
|
1315
|
-
}
|
|
1316
|
-
// function(arg1, args, ...) {}
|
|
1317
|
-
if (inject.length === 0) {
|
|
1318
|
-
inject = [this.app];
|
|
1319
|
-
}
|
|
1320
|
-
let mod = await this.requireFile(fullpath);
|
|
1321
|
-
if (typeof mod === 'function' && !isClass(mod)) {
|
|
1322
|
-
mod = mod(...inject);
|
|
1323
|
-
if (isPromise(mod)) {
|
|
1324
|
-
mod = await mod;
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
return mod;
|
|
1328
|
-
}
|
|
1329
|
-
/**
|
|
1330
|
-
* @param {String} filepath - fullpath
|
|
1331
|
-
* @private
|
|
1332
|
-
*/
|
|
1333
|
-
async requireFile(filepath) {
|
|
1334
|
-
const timingKey = `Require(${this.#requiredCount++}) ${utils.getResolvedFilename(filepath, this.options.baseDir)}`;
|
|
1335
|
-
this.timing.start(timingKey);
|
|
1336
|
-
const mod = await utils.loadFile(filepath);
|
|
1337
|
-
this.timing.end(timingKey);
|
|
1338
|
-
return mod;
|
|
1339
|
-
}
|
|
1340
|
-
/**
|
|
1341
|
-
* Get all loadUnit
|
|
1342
|
-
*
|
|
1343
|
-
* loadUnit is a directory that can be loaded by EggLoader, it has the same structure.
|
|
1344
|
-
* loadUnit has a path and a type(app, framework, plugin).
|
|
1345
|
-
*
|
|
1346
|
-
* The order of the loadUnits:
|
|
1347
|
-
*
|
|
1348
|
-
* 1. plugin
|
|
1349
|
-
* 2. framework
|
|
1350
|
-
* 3. app
|
|
1351
|
-
*
|
|
1352
|
-
* @returns {Array} loadUnits
|
|
1353
|
-
* @since 1.0.0
|
|
1354
|
-
*/
|
|
1355
|
-
getLoadUnits() {
|
|
1356
|
-
if (this.dirs) {
|
|
1357
|
-
return this.dirs;
|
|
1358
|
-
}
|
|
1359
|
-
this.dirs = [];
|
|
1360
|
-
if (this.orderPlugins) {
|
|
1361
|
-
for (const plugin of this.orderPlugins) {
|
|
1362
|
-
this.dirs.push({
|
|
1363
|
-
path: plugin.path,
|
|
1364
|
-
type: 'plugin',
|
|
1365
|
-
});
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
// framework or egg path
|
|
1369
|
-
for (const eggPath of this.eggPaths) {
|
|
1370
|
-
this.dirs.push({
|
|
1371
|
-
path: eggPath,
|
|
1372
|
-
type: 'framework',
|
|
1373
|
-
});
|
|
1374
|
-
}
|
|
1375
|
-
// application
|
|
1376
|
-
this.dirs.push({
|
|
1377
|
-
path: this.options.baseDir,
|
|
1378
|
-
type: 'app',
|
|
1379
|
-
});
|
|
1380
|
-
debug('Loaded dirs %j', this.dirs);
|
|
1381
|
-
return this.dirs;
|
|
1382
|
-
}
|
|
1383
|
-
/**
|
|
1384
|
-
* Load files using {@link FileLoader}, inject to {@link Application}
|
|
1385
|
-
* @param {String|Array} directory - see {@link FileLoader}
|
|
1386
|
-
* @param {String} property - see {@link FileLoader}, e.g.: 'controller', 'middlewares'
|
|
1387
|
-
* @param {Object} options - see {@link FileLoader}
|
|
1388
|
-
* @since 1.0.0
|
|
1389
|
-
*/
|
|
1390
|
-
async loadToApp(directory, property, options) {
|
|
1391
|
-
const target = {};
|
|
1392
|
-
Reflect.set(this.app, property, target);
|
|
1393
|
-
const loadOptions = {
|
|
1394
|
-
...options,
|
|
1395
|
-
directory: options?.directory ?? directory,
|
|
1396
|
-
target,
|
|
1397
|
-
inject: this.app,
|
|
1398
|
-
};
|
|
1399
|
-
const timingKey = `Load "${String(property)}" to Application`;
|
|
1400
|
-
this.timing.start(timingKey);
|
|
1401
|
-
await new FileLoader(loadOptions).load();
|
|
1402
|
-
this.timing.end(timingKey);
|
|
1403
|
-
}
|
|
1404
|
-
/**
|
|
1405
|
-
* Load files using {@link ContextLoader}
|
|
1406
|
-
* @param {String|Array} directory - see {@link ContextLoader}
|
|
1407
|
-
* @param {String} property - see {@link ContextLoader}
|
|
1408
|
-
* @param {Object} options - see {@link ContextLoader}
|
|
1409
|
-
* @since 1.0.0
|
|
1410
|
-
*/
|
|
1411
|
-
async loadToContext(directory, property, options) {
|
|
1412
|
-
const loadOptions = {
|
|
1413
|
-
...options,
|
|
1414
|
-
directory: options?.directory || directory,
|
|
1415
|
-
property,
|
|
1416
|
-
inject: this.app,
|
|
1417
|
-
};
|
|
1418
|
-
const timingKey = `Load "${String(property)}" to Context`;
|
|
1419
|
-
this.timing.start(timingKey);
|
|
1420
|
-
await new ContextLoader(loadOptions).load();
|
|
1421
|
-
this.timing.end(timingKey);
|
|
1422
|
-
}
|
|
1423
|
-
/**
|
|
1424
|
-
* @member {FileLoader} EggLoader#FileLoader
|
|
1425
|
-
* @since 1.0.0
|
|
1426
|
-
*/
|
|
1427
|
-
get FileLoader() {
|
|
1428
|
-
return FileLoader;
|
|
1429
|
-
}
|
|
1430
|
-
/**
|
|
1431
|
-
* @member {ContextLoader} EggLoader#ContextLoader
|
|
1432
|
-
* @since 1.0.0
|
|
1433
|
-
*/
|
|
1434
|
-
get ContextLoader() {
|
|
1435
|
-
return ContextLoader;
|
|
1436
|
-
}
|
|
1437
|
-
getTypeFiles(filename) {
|
|
1438
|
-
const files = [`${filename}.default`];
|
|
1439
|
-
if (this.serverScope)
|
|
1440
|
-
files.push(`${filename}.${this.serverScope}`);
|
|
1441
|
-
if (this.serverEnv === 'default')
|
|
1442
|
-
return files;
|
|
1443
|
-
files.push(`${filename}.${this.serverEnv}`);
|
|
1444
|
-
if (this.serverScope) {
|
|
1445
|
-
files.push(`${filename}.${this.serverScope}_${this.serverEnv}`);
|
|
1446
|
-
}
|
|
1447
|
-
return files;
|
|
1448
|
-
}
|
|
1449
|
-
resolveModule(filepath) {
|
|
1450
|
-
let fullPath;
|
|
1451
|
-
try {
|
|
1452
|
-
fullPath = utils.resolvePath(filepath);
|
|
1453
|
-
}
|
|
1454
|
-
catch {
|
|
1455
|
-
// debug('[resolveModule] Module %o resolve error: %s', filepath, err.stack);
|
|
1456
|
-
return undefined;
|
|
1457
|
-
}
|
|
1458
|
-
// if (process.env.EGG_TYPESCRIPT !== 'true' && fullPath.endsWith('.ts')) {
|
|
1459
|
-
// return undefined;
|
|
1460
|
-
// }
|
|
1461
|
-
return fullPath;
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
// convert dep to dependencies for compatibility
|
|
1465
1103
|
function depCompatible(plugin) {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1104
|
+
if (plugin.dep && !(Array.isArray(plugin.dependencies) && plugin.dependencies.length > 0)) {
|
|
1105
|
+
plugin.dependencies = plugin.dep;
|
|
1106
|
+
delete plugin.dep;
|
|
1107
|
+
}
|
|
1470
1108
|
}
|
|
1471
1109
|
function isValidatePackageName(name) {
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
return false;
|
|
1477
|
-
if (name.includes(':'))
|
|
1478
|
-
return false;
|
|
1479
|
-
return true;
|
|
1110
|
+
if (name.startsWith(".")) return false;
|
|
1111
|
+
if (name.startsWith("/")) return false;
|
|
1112
|
+
if (name.includes(":")) return false;
|
|
1113
|
+
return true;
|
|
1480
1114
|
}
|
|
1481
|
-
// support pathMatching on middleware
|
|
1482
1115
|
function wrapMiddleware(mw, options) {
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
const fn = (ctx, next) => {
|
|
1493
|
-
if (!match(ctx))
|
|
1494
|
-
return next();
|
|
1495
|
-
return mw(ctx, next);
|
|
1496
|
-
};
|
|
1497
|
-
fn._name = `${mw._name}middlewareWrapper`;
|
|
1498
|
-
return fn;
|
|
1116
|
+
if (options.enable === false) return null;
|
|
1117
|
+
if (!options.match && !options.ignore) return mw;
|
|
1118
|
+
const match = pathMatching(options);
|
|
1119
|
+
const fn = (ctx, next) => {
|
|
1120
|
+
if (!match(ctx)) return next();
|
|
1121
|
+
return mw(ctx, next);
|
|
1122
|
+
};
|
|
1123
|
+
fn._name = `${mw._name}middlewareWrapper`;
|
|
1124
|
+
return fn;
|
|
1499
1125
|
}
|
|
1500
1126
|
function debugMiddlewareWrapper(mw) {
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1127
|
+
const fn = async (ctx, next) => {
|
|
1128
|
+
const startTime = now();
|
|
1129
|
+
debug("[debugMiddlewareWrapper] [%s %s] enter middleware: %s", ctx.method, ctx.url, mw._name);
|
|
1130
|
+
await mw(ctx, next);
|
|
1131
|
+
const rt = diff(startTime);
|
|
1132
|
+
debug("[debugMiddlewareWrapper] [%s %s] after middleware: %s [%sms]", ctx.method, ctx.url, mw._name, rt);
|
|
1133
|
+
};
|
|
1134
|
+
fn._name = `${mw._name}DebugWrapper`;
|
|
1135
|
+
return fn;
|
|
1510
1136
|
}
|
|
1511
|
-
// wrap the controller class, yield a object with middlewares
|
|
1512
1137
|
function wrapControllerClass(Controller, fullPath) {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
throw new TypeError(`Support for generators was removed, controller \`${controllerMethodName}\`, fullpath: ${fullPath}`);
|
|
1531
|
-
}
|
|
1532
|
-
ret[key] = controllerMethodToMiddleware(Controller, key);
|
|
1533
|
-
ret[key][FULLPATH] = `${fullPath}#${controllerMethodName}()`;
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
proto = Object.getPrototypeOf(proto);
|
|
1537
|
-
}
|
|
1538
|
-
return ret;
|
|
1138
|
+
let proto = Controller.prototype;
|
|
1139
|
+
const ret = {};
|
|
1140
|
+
while (proto !== Object.prototype) {
|
|
1141
|
+
const keys = Object.getOwnPropertyNames(proto);
|
|
1142
|
+
for (const key of keys) {
|
|
1143
|
+
if (key === "constructor") continue;
|
|
1144
|
+
const d = Object.getOwnPropertyDescriptor(proto, key);
|
|
1145
|
+
if (typeof d?.value === "function" && !Object.hasOwn(ret, key)) {
|
|
1146
|
+
const controllerMethodName = `${Controller.name}.${key}`;
|
|
1147
|
+
if (isGeneratorFunction(d.value)) throw new TypeError(`Support for generators was removed, controller \`${controllerMethodName}\`, fullpath: ${fullPath}`);
|
|
1148
|
+
ret[key] = controllerMethodToMiddleware(Controller, key);
|
|
1149
|
+
ret[key][FULLPATH] = `${fullPath}#${controllerMethodName}()`;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
proto = Object.getPrototypeOf(proto);
|
|
1153
|
+
}
|
|
1154
|
+
return ret;
|
|
1539
1155
|
}
|
|
1540
1156
|
function controllerMethodToMiddleware(Controller, key) {
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
// @ts-expect-error key exists
|
|
1547
|
-
return controller[key](...args);
|
|
1548
|
-
};
|
|
1157
|
+
return function classControllerMiddleware(...args) {
|
|
1158
|
+
const controller = new Controller(this);
|
|
1159
|
+
if (!this.app.config.controller?.supportParams) args = [this];
|
|
1160
|
+
return controller[key](...args);
|
|
1161
|
+
};
|
|
1549
1162
|
}
|
|
1550
|
-
// wrap the method of the object, method can receive ctx as it's first argument
|
|
1551
1163
|
function wrapObject(obj, fullPath, prefix) {
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
ret[key][FULLPATH] = `${fullPath}#${controllerMethodName}()`;
|
|
1568
|
-
}
|
|
1569
|
-
else if (isObject(item)) {
|
|
1570
|
-
ret[key] = wrapObject(item, fullPath, `${controllerMethodName}.`);
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
debug('[wrapObject] fullPath: %s, prefix: %s => %o', fullPath, prefix, ret);
|
|
1574
|
-
return ret;
|
|
1164
|
+
const keys = Object.keys(obj);
|
|
1165
|
+
const ret = {};
|
|
1166
|
+
prefix = prefix ?? "";
|
|
1167
|
+
for (const key of keys) {
|
|
1168
|
+
const controllerMethodName = `${prefix}${key}`;
|
|
1169
|
+
const item = obj[key];
|
|
1170
|
+
if (isGeneratorFunction(item)) throw new TypeError(`Support for generators was removed, controller \`${controllerMethodName}\`, fullpath: ${fullPath}`);
|
|
1171
|
+
if (typeof item === "function") {
|
|
1172
|
+
if (getParamNames(item)[0] === "next") throw new Error(`controller \`${controllerMethodName}\` should not use next as argument from file ${fullPath}`);
|
|
1173
|
+
ret[key] = objectFunctionToMiddleware(item);
|
|
1174
|
+
ret[key][FULLPATH] = `${fullPath}#${controllerMethodName}()`;
|
|
1175
|
+
} else if (isObject(item)) ret[key] = wrapObject(item, fullPath, `${controllerMethodName}.`);
|
|
1176
|
+
}
|
|
1177
|
+
debug("[wrapObject] fullPath: %s, prefix: %s => %o", fullPath, prefix, ret);
|
|
1178
|
+
return ret;
|
|
1575
1179
|
}
|
|
1576
1180
|
function objectFunctionToMiddleware(func) {
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
for (const key in func) {
|
|
1584
|
-
Reflect.set(objectControllerMiddleware, key, Reflect.get(func, key));
|
|
1585
|
-
}
|
|
1586
|
-
return objectControllerMiddleware;
|
|
1181
|
+
async function objectControllerMiddleware(...args) {
|
|
1182
|
+
if (!this.app.config.controller?.supportParams) args = [this];
|
|
1183
|
+
return await func.apply(this, args);
|
|
1184
|
+
}
|
|
1185
|
+
for (const key in func) Reflect.set(objectControllerMiddleware, key, Reflect.get(func, key));
|
|
1186
|
+
return objectControllerMiddleware;
|
|
1587
1187
|
}
|
|
1588
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWdnX2xvYWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sb2FkZXIvZWdnX2xvYWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLE1BQU0sTUFBTSxhQUFhLENBQUM7QUFDakMsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3pCLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUM3QixPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUU5QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEMsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLE9BQU8sSUFBSSxVQUFVLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDbkYsT0FBTyxFQUFFLFlBQVksRUFBNEIsTUFBTSxzQkFBc0IsQ0FBQztBQUM5RSxPQUFPLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBRTFELE9BQU8sRUFBRSxlQUFlLEVBQUUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDaEcsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN2QyxPQUFPLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzNDLE9BQU8sRUFBRSxRQUFRLElBQUkscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNuRSxPQUFPLEVBQUUsYUFBYSxFQUFFLFlBQVksRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBTXhFLE9BQU8sS0FBSyxFQUFFLEVBQVksTUFBTSxtQkFBbUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDcEQsT0FBTyxFQUFFLE1BQU0sRUFBRSxN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
|
|
1188
|
+
|
|
1189
|
+
//#endregion
|
|
1190
|
+
export { EggLoader };
|