@eggjs/core 6.4.0 → 6.5.0
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/README.md +22 -22
- package/dist/commonjs/egg.d.ts +17 -18
- package/dist/commonjs/egg.js +11 -11
- package/dist/commonjs/lifecycle.d.ts +2 -2
- package/dist/commonjs/lifecycle.js +16 -13
- package/dist/commonjs/loader/context_loader.js +2 -2
- package/dist/commonjs/loader/egg_loader.d.ts +13 -13
- package/dist/commonjs/loader/egg_loader.js +77 -62
- package/dist/commonjs/loader/file_loader.d.ts +3 -3
- package/dist/commonjs/loader/file_loader.js +22 -24
- package/dist/commonjs/singleton.d.ts +2 -2
- package/dist/commonjs/singleton.js +6 -7
- package/dist/commonjs/utils/index.d.ts +2 -2
- package/dist/commonjs/utils/index.js +4 -4
- package/dist/commonjs/utils/sequencify.d.ts +1 -1
- package/dist/commonjs/utils/sequencify.js +18 -13
- package/dist/commonjs/utils/timing.js +14 -8
- package/dist/esm/egg.d.ts +17 -18
- package/dist/esm/egg.js +13 -13
- package/dist/esm/lifecycle.d.ts +2 -2
- package/dist/esm/lifecycle.js +16 -13
- package/dist/esm/loader/context_loader.js +2 -2
- package/dist/esm/loader/egg_loader.d.ts +13 -13
- package/dist/esm/loader/egg_loader.js +80 -65
- package/dist/esm/loader/file_loader.d.ts +3 -3
- package/dist/esm/loader/file_loader.js +23 -25
- package/dist/esm/singleton.d.ts +2 -2
- package/dist/esm/singleton.js +6 -7
- package/dist/esm/utils/index.d.ts +2 -2
- package/dist/esm/utils/index.js +4 -4
- package/dist/esm/utils/sequencify.d.ts +1 -1
- package/dist/esm/utils/sequencify.js +18 -13
- package/dist/esm/utils/timing.js +14 -8
- package/dist/package.json +1 -1
- package/package.json +16 -6
- package/src/egg.ts +161 -61
- package/src/lifecycle.ts +72 -33
- package/src/loader/context_loader.ts +9 -7
- package/src/loader/egg_loader.ts +445 -183
- package/src/loader/file_loader.ts +78 -37
- package/src/singleton.ts +64 -26
- package/src/utils/index.ts +20 -13
- package/src/utils/sequencify.ts +50 -15
- package/src/utils/timing.ts +27 -13
package/src/loader/egg_loader.ts
CHANGED
|
@@ -2,39 +2,52 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import assert from 'node:assert';
|
|
4
4
|
import { debuglog, inspect } from 'node:util';
|
|
5
|
+
|
|
5
6
|
import { homedir } from 'node-homedir';
|
|
6
|
-
import { isAsyncFunction, isClass, isGeneratorFunction, isObject, isPromise } from 'is-type-of';
|
|
7
|
-
import type { Logger } from 'egg-logger';
|
|
8
7
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
isAsyncFunction,
|
|
9
|
+
isClass,
|
|
10
|
+
isGeneratorFunction,
|
|
11
|
+
isObject,
|
|
12
|
+
isPromise,
|
|
13
|
+
} from 'is-type-of';
|
|
14
|
+
import type { Logger } from 'egg-logger';
|
|
15
|
+
import { getParamNames, readJSONSync, readJSON, exists } from 'utility';
|
|
11
16
|
import { extend } from 'extend2';
|
|
12
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
Request,
|
|
19
|
+
Response,
|
|
20
|
+
Application,
|
|
21
|
+
Context as KoaContext,
|
|
22
|
+
} from '@eggjs/koa';
|
|
13
23
|
import { register as tsconfigPathsRegister } from 'tsconfig-paths';
|
|
14
24
|
import { isESM, isSupportTypeScript } from '@eggjs/utils';
|
|
15
25
|
import { pathMatching, type PathMatchingOptions } from 'egg-path-matching';
|
|
16
26
|
import { now, diff } from 'performance-ms';
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
type FileLoaderOptions,
|
|
30
|
+
CaseStyle,
|
|
31
|
+
FULLPATH,
|
|
32
|
+
FileLoader,
|
|
33
|
+
} from './file_loader.js';
|
|
34
|
+
import { type ContextLoaderOptions, ContextLoader } from './context_loader.js';
|
|
35
|
+
import utils, { type Fun } from '../utils/index.js';
|
|
36
|
+
import { sequencify } from '../utils/sequencify.js';
|
|
21
37
|
import { Timing } from '../utils/timing.js';
|
|
22
|
-
import type {
|
|
23
|
-
Context, EggCore, MiddlewareFunc,
|
|
24
|
-
} from '../egg.js';
|
|
38
|
+
import type { Context, EggCore, MiddlewareFunc } from '../egg.js';
|
|
25
39
|
import type { BaseContextClass } from '../base_context_class.js';
|
|
26
40
|
import type { EggAppConfig, EggAppInfo, EggPluginInfo } from '../types.js';
|
|
27
41
|
|
|
28
42
|
const debug = debuglog('@eggjs/core/loader/egg_loader');
|
|
29
43
|
|
|
30
|
-
const originalPrototypes: Record<string,
|
|
44
|
+
const originalPrototypes: Record<string, unknown> = {
|
|
31
45
|
request: Request.prototype,
|
|
32
46
|
response: Response.prototype,
|
|
33
47
|
context: KoaContext.prototype,
|
|
34
48
|
application: Application.prototype,
|
|
35
49
|
};
|
|
36
50
|
|
|
37
|
-
|
|
38
51
|
export interface EggLoaderOptions {
|
|
39
52
|
/** server env */
|
|
40
53
|
env: string;
|
|
@@ -80,7 +93,10 @@ export class EggLoader {
|
|
|
80
93
|
*/
|
|
81
94
|
constructor(options: EggLoaderOptions) {
|
|
82
95
|
this.options = options;
|
|
83
|
-
assert(
|
|
96
|
+
assert(
|
|
97
|
+
fs.existsSync(this.options.baseDir),
|
|
98
|
+
`${this.options.baseDir} not exists`
|
|
99
|
+
);
|
|
84
100
|
assert(this.options.app, 'options.app is required');
|
|
85
101
|
assert(this.options.logger, 'options.logger is required');
|
|
86
102
|
|
|
@@ -95,15 +111,20 @@ export class EggLoader {
|
|
|
95
111
|
|
|
96
112
|
// auto require('tsconfig-paths/register') on typescript app
|
|
97
113
|
// support env.EGG_TYPESCRIPT = true or { "egg": { "typescript": true } } on package.json
|
|
98
|
-
if (
|
|
114
|
+
if (
|
|
115
|
+
process.env.EGG_TYPESCRIPT === 'true' ||
|
|
116
|
+
(this.pkg.egg && this.pkg.egg.typescript)
|
|
117
|
+
) {
|
|
99
118
|
// skip require tsconfig-paths if tsconfig.json not exists
|
|
100
119
|
const tsConfigFile = path.join(this.options.baseDir, 'tsconfig.json');
|
|
101
120
|
if (fs.existsSync(tsConfigFile)) {
|
|
102
|
-
|
|
121
|
+
// @ts-expect-error only cwd is required
|
|
122
|
+
tsconfigPathsRegister({ cwd: this.options.baseDir });
|
|
103
123
|
} else {
|
|
104
124
|
this.logger.info(
|
|
105
125
|
'[@eggjs/core/egg_loader] skip register "tsconfig-paths" because tsconfig.json not exists at %s',
|
|
106
|
-
tsConfigFile
|
|
126
|
+
tsConfigFile
|
|
127
|
+
);
|
|
107
128
|
}
|
|
108
129
|
}
|
|
109
130
|
|
|
@@ -143,9 +164,7 @@ export class EggLoader {
|
|
|
143
164
|
* @member {String} EggLoader#serverScope
|
|
144
165
|
* @see AppInfo#serverScope
|
|
145
166
|
*/
|
|
146
|
-
this.serverScope = options.serverScope
|
|
147
|
-
? options.serverScope
|
|
148
|
-
: this.getServerScope();
|
|
167
|
+
this.serverScope = options.serverScope ?? this.getServerScope();
|
|
149
168
|
|
|
150
169
|
/**
|
|
151
170
|
* @member {AppInfo} EggLoader#appInfo
|
|
@@ -168,7 +187,7 @@ export class EggLoader {
|
|
|
168
187
|
|
|
169
188
|
/**
|
|
170
189
|
* Get {@link AppInfo#env}
|
|
171
|
-
* @
|
|
190
|
+
* @returns {String} env
|
|
172
191
|
* @see AppInfo#env
|
|
173
192
|
* @private
|
|
174
193
|
* @since 1.0.0
|
|
@@ -185,7 +204,10 @@ export class EggLoader {
|
|
|
185
204
|
serverEnv = process.env.EGG_SERVER_ENV;
|
|
186
205
|
}
|
|
187
206
|
|
|
188
|
-
if (
|
|
207
|
+
if (serverEnv) {
|
|
208
|
+
serverEnv = serverEnv.trim();
|
|
209
|
+
} else {
|
|
210
|
+
// oxlint-disable-next-line eslint/no-lonely-if
|
|
189
211
|
if (process.env.NODE_ENV === 'test') {
|
|
190
212
|
serverEnv = 'unittest';
|
|
191
213
|
} else if (process.env.NODE_ENV === 'production') {
|
|
@@ -193,8 +215,6 @@ export class EggLoader {
|
|
|
193
215
|
} else {
|
|
194
216
|
serverEnv = 'local';
|
|
195
217
|
}
|
|
196
|
-
} else {
|
|
197
|
-
serverEnv = serverEnv.trim();
|
|
198
218
|
}
|
|
199
219
|
|
|
200
220
|
return serverEnv;
|
|
@@ -202,16 +222,16 @@ export class EggLoader {
|
|
|
202
222
|
|
|
203
223
|
/**
|
|
204
224
|
* Get {@link AppInfo#scope}
|
|
205
|
-
* @
|
|
225
|
+
* @returns {String} serverScope
|
|
206
226
|
* @private
|
|
207
227
|
*/
|
|
208
228
|
protected getServerScope(): string {
|
|
209
|
-
return process.env.EGG_SERVER_SCOPE
|
|
229
|
+
return process.env.EGG_SERVER_SCOPE ?? '';
|
|
210
230
|
}
|
|
211
231
|
|
|
212
232
|
/**
|
|
213
233
|
* Get {@link AppInfo#name}
|
|
214
|
-
* @
|
|
234
|
+
* @returns {String} appname
|
|
215
235
|
* @private
|
|
216
236
|
* @since 1.0.0
|
|
217
237
|
*/
|
|
@@ -226,7 +246,7 @@ export class EggLoader {
|
|
|
226
246
|
|
|
227
247
|
/**
|
|
228
248
|
* Get home directory
|
|
229
|
-
* @
|
|
249
|
+
* @returns {String} home directory
|
|
230
250
|
* @since 3.4.0
|
|
231
251
|
*/
|
|
232
252
|
getHomedir(): string {
|
|
@@ -236,7 +256,7 @@ export class EggLoader {
|
|
|
236
256
|
|
|
237
257
|
/**
|
|
238
258
|
* Get app info
|
|
239
|
-
* @
|
|
259
|
+
* @returns {AppInfo} appInfo
|
|
240
260
|
* @since 1.0.0
|
|
241
261
|
*/
|
|
242
262
|
protected getAppInfo(): EggAppInfo {
|
|
@@ -312,7 +332,7 @@ export class EggLoader {
|
|
|
312
332
|
|
|
313
333
|
/**
|
|
314
334
|
* Get {@link EggLoader#eggPaths}
|
|
315
|
-
* @
|
|
335
|
+
* @returns {Array} framework directories
|
|
316
336
|
* @see {@link EggLoader#eggPaths}
|
|
317
337
|
* @private
|
|
318
338
|
* @since 1.0.0
|
|
@@ -340,7 +360,10 @@ export class EggLoader {
|
|
|
340
360
|
// }
|
|
341
361
|
continue;
|
|
342
362
|
}
|
|
343
|
-
assert(
|
|
363
|
+
assert(
|
|
364
|
+
typeof eggPath === 'string',
|
|
365
|
+
"Symbol.for('egg#eggPath') should be string"
|
|
366
|
+
);
|
|
344
367
|
assert(fs.existsSync(eggPath), `${eggPath} not exists`);
|
|
345
368
|
const realpath = fs.realpathSync(eggPath);
|
|
346
369
|
if (!eggPaths.includes(realpath)) {
|
|
@@ -433,8 +456,12 @@ export class EggLoader {
|
|
|
433
456
|
|
|
434
457
|
// disable the plugin that not match the serverEnv
|
|
435
458
|
if (env && plugin.env.length > 0 && !plugin.env.includes(env)) {
|
|
436
|
-
this.logger.info(
|
|
437
|
-
|
|
459
|
+
this.logger.info(
|
|
460
|
+
'[@eggjs/core] Plugin %o is disabled by env unmatched, require env(%o) but got env is %o',
|
|
461
|
+
name,
|
|
462
|
+
plugin.env,
|
|
463
|
+
env
|
|
464
|
+
);
|
|
438
465
|
plugin.enable = false;
|
|
439
466
|
continue;
|
|
440
467
|
}
|
|
@@ -446,7 +473,11 @@ export class EggLoader {
|
|
|
446
473
|
}
|
|
447
474
|
|
|
448
475
|
// retrieve the ordered plugins
|
|
449
|
-
this.orderPlugins = this.getOrderPlugins(
|
|
476
|
+
this.orderPlugins = this.getOrderPlugins(
|
|
477
|
+
plugins,
|
|
478
|
+
enabledPluginNames,
|
|
479
|
+
this.appPlugins
|
|
480
|
+
);
|
|
450
481
|
|
|
451
482
|
const enablePlugins: Record<string, EggPluginInfo> = {};
|
|
452
483
|
for (const plugin of this.orderPlugins) {
|
|
@@ -465,16 +496,26 @@ export class EggLoader {
|
|
|
465
496
|
|
|
466
497
|
protected async loadAppPlugins() {
|
|
467
498
|
// loader plugins from application
|
|
468
|
-
const appPlugins = await this.readPluginConfigs(
|
|
469
|
-
|
|
499
|
+
const appPlugins = await this.readPluginConfigs(
|
|
500
|
+
path.join(this.options.baseDir, 'config/plugin.default')
|
|
501
|
+
);
|
|
502
|
+
debug(
|
|
503
|
+
'Loaded app plugins: %j',
|
|
504
|
+
Object.keys(appPlugins).map(k => `${k}:${appPlugins[k].enable}`)
|
|
505
|
+
);
|
|
470
506
|
return appPlugins;
|
|
471
507
|
}
|
|
472
508
|
|
|
473
509
|
protected async loadEggPlugins() {
|
|
474
510
|
// loader plugins from framework
|
|
475
|
-
const eggPluginConfigPaths = this.eggPaths.map(eggPath =>
|
|
511
|
+
const eggPluginConfigPaths = this.eggPaths.map(eggPath =>
|
|
512
|
+
path.join(eggPath, 'config/plugin.default')
|
|
513
|
+
);
|
|
476
514
|
const eggPlugins = await this.readPluginConfigs(eggPluginConfigPaths);
|
|
477
|
-
debug(
|
|
515
|
+
debug(
|
|
516
|
+
'Loaded egg plugins: %j',
|
|
517
|
+
Object.keys(eggPlugins).map(k => `${k}:${eggPlugins[k].enable}`)
|
|
518
|
+
);
|
|
478
519
|
return eggPlugins;
|
|
479
520
|
}
|
|
480
521
|
|
|
@@ -515,7 +556,7 @@ export class EggLoader {
|
|
|
515
556
|
*/
|
|
516
557
|
protected async readPluginConfigs(configPaths: string[] | string) {
|
|
517
558
|
if (!Array.isArray(configPaths)) {
|
|
518
|
-
configPaths = [
|
|
559
|
+
configPaths = [configPaths];
|
|
519
560
|
}
|
|
520
561
|
|
|
521
562
|
// Get all plugin configurations
|
|
@@ -537,14 +578,19 @@ export class EggLoader {
|
|
|
537
578
|
|
|
538
579
|
// let plugin.js compatible
|
|
539
580
|
if (configPath.endsWith('plugin.default') && !filepath) {
|
|
540
|
-
filepath = this.resolveModule(
|
|
581
|
+
filepath = this.resolveModule(
|
|
582
|
+
configPath.replace(/plugin\.default$/, 'plugin')
|
|
583
|
+
);
|
|
541
584
|
}
|
|
542
585
|
|
|
543
586
|
if (!filepath) {
|
|
544
587
|
continue;
|
|
545
588
|
}
|
|
546
589
|
|
|
547
|
-
const config = await utils.loadFile(filepath) as Record<
|
|
590
|
+
const config = (await utils.loadFile(filepath)) as Record<
|
|
591
|
+
string,
|
|
592
|
+
EggPluginInfo
|
|
593
|
+
>;
|
|
548
594
|
for (const name in config) {
|
|
549
595
|
this.#normalizePluginConfig(config, name, filepath);
|
|
550
596
|
}
|
|
@@ -554,7 +600,11 @@ export class EggLoader {
|
|
|
554
600
|
return plugins;
|
|
555
601
|
}
|
|
556
602
|
|
|
557
|
-
#normalizePluginConfig(
|
|
603
|
+
#normalizePluginConfig(
|
|
604
|
+
plugins: Record<string, EggPluginInfo | boolean>,
|
|
605
|
+
name: string,
|
|
606
|
+
configPath: string
|
|
607
|
+
) {
|
|
558
608
|
const plugin = plugins[name];
|
|
559
609
|
|
|
560
610
|
// plugin_name: false
|
|
@@ -593,7 +643,7 @@ export class EggLoader {
|
|
|
593
643
|
async #mergePluginConfig(plugin: EggPluginInfo) {
|
|
594
644
|
let pkg;
|
|
595
645
|
let config;
|
|
596
|
-
const pluginPackage = path.join(plugin.path
|
|
646
|
+
const pluginPackage = path.join(plugin.path as string, 'package.json');
|
|
597
647
|
if (await utils.existsPath(pluginPackage)) {
|
|
598
648
|
pkg = await readJSON(pluginPackage);
|
|
599
649
|
config = pkg.eggPlugin;
|
|
@@ -601,25 +651,32 @@ export class EggLoader {
|
|
|
601
651
|
plugin.version = pkg.version;
|
|
602
652
|
}
|
|
603
653
|
// support commonjs and esm dist files
|
|
604
|
-
plugin.path = await this.#formatPluginPathFromPackageJSON(
|
|
654
|
+
plugin.path = await this.#formatPluginPathFromPackageJSON(
|
|
655
|
+
plugin.path as string,
|
|
656
|
+
pkg
|
|
657
|
+
);
|
|
605
658
|
}
|
|
606
659
|
|
|
607
660
|
const logger = this.options.logger;
|
|
608
661
|
if (!config) {
|
|
609
|
-
logger.warn(
|
|
662
|
+
logger.warn(
|
|
663
|
+
`[@eggjs/core/egg_loader] pkg.eggPlugin is missing in ${pluginPackage}`
|
|
664
|
+
);
|
|
610
665
|
return;
|
|
611
666
|
}
|
|
612
667
|
|
|
613
668
|
if (config.name && config.strict !== false && config.name !== plugin.name) {
|
|
614
669
|
// pluginName is configured in config/plugin.js
|
|
615
670
|
// pluginConfigName is pkg.eggPlugin.name
|
|
616
|
-
logger.warn(
|
|
671
|
+
logger.warn(
|
|
672
|
+
`[@eggjs/core/egg_loader] pluginName(${plugin.name}) is different from pluginConfigName(${config.name})`
|
|
673
|
+
);
|
|
617
674
|
}
|
|
618
675
|
|
|
619
676
|
// dep compatible
|
|
620
677
|
depCompatible(config);
|
|
621
678
|
|
|
622
|
-
for (const key of [
|
|
679
|
+
for (const key of ['dependencies', 'optionalDependencies', 'env']) {
|
|
623
680
|
const values = config[key];
|
|
624
681
|
const existsValues = Reflect.get(plugin, key);
|
|
625
682
|
if (Array.isArray(values) && !existsValues?.length) {
|
|
@@ -628,10 +685,13 @@ export class EggLoader {
|
|
|
628
685
|
}
|
|
629
686
|
}
|
|
630
687
|
|
|
631
|
-
protected getOrderPlugins(
|
|
632
|
-
|
|
688
|
+
protected getOrderPlugins(
|
|
689
|
+
allPlugins: Record<string, EggPluginInfo>,
|
|
690
|
+
enabledPluginNames: string[],
|
|
691
|
+
appPlugins: Record<string, EggPluginInfo>
|
|
692
|
+
) {
|
|
633
693
|
// no plugins enabled
|
|
634
|
-
if (
|
|
694
|
+
if (enabledPluginNames.length === 0) {
|
|
635
695
|
return [];
|
|
636
696
|
}
|
|
637
697
|
|
|
@@ -639,9 +699,10 @@ export class EggLoader {
|
|
|
639
699
|
debug('Got plugins %j after sequencify', result);
|
|
640
700
|
|
|
641
701
|
// catch error when result.sequence is empty
|
|
642
|
-
if (
|
|
702
|
+
if (result.sequence.length === 0) {
|
|
643
703
|
const err = new Error(
|
|
644
|
-
`sequencify plugins has problem, missing: [${result.missingTasks}], recursive: [${result.recursiveDependencies}]`
|
|
704
|
+
`sequencify plugins has problem, missing: [${result.missingTasks}], recursive: [${result.recursiveDependencies}]`
|
|
705
|
+
);
|
|
645
706
|
// find plugins which is required by the missing plugin
|
|
646
707
|
for (const missName of result.missingTasks) {
|
|
647
708
|
const requires = [];
|
|
@@ -660,7 +721,7 @@ export class EggLoader {
|
|
|
660
721
|
// log the plugins that be enabled implicitly
|
|
661
722
|
const implicitEnabledPlugins: string[] = [];
|
|
662
723
|
const requireMap: Record<string, string[]> = {};
|
|
663
|
-
result.sequence
|
|
724
|
+
for (const name of result.sequence) {
|
|
664
725
|
for (const depName of allPlugins[name].dependencies) {
|
|
665
726
|
if (!requireMap[depName]) {
|
|
666
727
|
requireMap[depName] = [];
|
|
@@ -673,9 +734,9 @@ export class EggLoader {
|
|
|
673
734
|
allPlugins[name].enable = true;
|
|
674
735
|
allPlugins[name].implicitEnable = true;
|
|
675
736
|
}
|
|
676
|
-
}
|
|
737
|
+
}
|
|
677
738
|
|
|
678
|
-
for (const [
|
|
739
|
+
for (const [name, dependents] of Object.entries(requireMap)) {
|
|
679
740
|
// note:`dependents` will not includes `optionalDependencies`
|
|
680
741
|
allPlugins[name].dependents = dependents;
|
|
681
742
|
}
|
|
@@ -684,21 +745,25 @@ export class EggLoader {
|
|
|
684
745
|
// - configclient required by [rpcClient]
|
|
685
746
|
// - monitor required by [rpcClient]
|
|
686
747
|
// - diamond required by [rpcClient]
|
|
687
|
-
if (implicitEnabledPlugins.length) {
|
|
748
|
+
if (implicitEnabledPlugins.length > 0) {
|
|
688
749
|
let message = implicitEnabledPlugins
|
|
689
750
|
.map(name => ` - ${name} required by [${requireMap[name]}]`)
|
|
690
751
|
.join('\n');
|
|
691
|
-
this.options.logger.info(
|
|
752
|
+
this.options.logger.info(
|
|
753
|
+
`Following plugins will be enabled implicitly.\n${message}`
|
|
754
|
+
);
|
|
692
755
|
|
|
693
756
|
// should warn when the plugin is disabled by app
|
|
694
757
|
const disabledPlugins = implicitEnabledPlugins.filter(
|
|
695
|
-
name => appPlugins[name] && appPlugins[name].enable === false
|
|
696
|
-
|
|
758
|
+
name => appPlugins[name] && appPlugins[name].enable === false
|
|
759
|
+
);
|
|
760
|
+
if (disabledPlugins.length > 0) {
|
|
697
761
|
message = disabledPlugins
|
|
698
762
|
.map(name => ` - ${name} required by [${requireMap[name]}]`)
|
|
699
763
|
.join('\n');
|
|
700
764
|
this.options.logger.warn(
|
|
701
|
-
`Following plugins will be enabled implicitly that is disabled by application.\n${message}`
|
|
765
|
+
`Following plugins will be enabled implicitly that is disabled by application.\n${message}`
|
|
766
|
+
);
|
|
702
767
|
}
|
|
703
768
|
}
|
|
704
769
|
|
|
@@ -730,8 +795,10 @@ export class EggLoader {
|
|
|
730
795
|
}
|
|
731
796
|
|
|
732
797
|
if (plugin.package) {
|
|
733
|
-
assert(
|
|
734
|
-
|
|
798
|
+
assert(
|
|
799
|
+
isValidatePackageName(plugin.package),
|
|
800
|
+
`plugin ${plugin.name} invalid, use 'path' instead of package: "${plugin.package}"`
|
|
801
|
+
);
|
|
735
802
|
}
|
|
736
803
|
return this.#resolvePluginPath(plugin);
|
|
737
804
|
}
|
|
@@ -745,25 +812,33 @@ export class EggLoader {
|
|
|
745
812
|
// 'node_modules/.pnpm/yadan@2.0.0/node_modules', <- this is the sibling directory
|
|
746
813
|
// 'node_modules/.pnpm/egg@2.33.1/node_modules/egg/node_modules',
|
|
747
814
|
// 'node_modules/.pnpm/egg@2.33.1/node_modules', <- this is the sibling directory
|
|
748
|
-
const pluginPkgFile = utils.resolvePath(`${name}/package.json`, {
|
|
815
|
+
const pluginPkgFile = utils.resolvePath(`${name}/package.json`, {
|
|
816
|
+
paths: [...this.lookupDirs],
|
|
817
|
+
});
|
|
749
818
|
return path.dirname(pluginPkgFile);
|
|
750
819
|
} catch (err) {
|
|
751
820
|
debug('[resolvePluginPath] error: %o, plugin info: %o', err, plugin);
|
|
752
|
-
throw new Error(
|
|
753
|
-
|
|
754
|
-
|
|
821
|
+
throw new Error(
|
|
822
|
+
`Can not find plugin ${name} in "${[...this.lookupDirs].join(', ')}"`,
|
|
823
|
+
{
|
|
824
|
+
cause: err,
|
|
825
|
+
}
|
|
826
|
+
);
|
|
755
827
|
}
|
|
756
828
|
}
|
|
757
829
|
|
|
758
|
-
async #formatPluginPathFromPackageJSON(
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
830
|
+
async #formatPluginPathFromPackageJSON(
|
|
831
|
+
pluginPath: string,
|
|
832
|
+
pluginPkg: {
|
|
833
|
+
eggPlugin?: {
|
|
834
|
+
exports?: {
|
|
835
|
+
import?: string;
|
|
836
|
+
require?: string;
|
|
837
|
+
typescript?: string;
|
|
838
|
+
};
|
|
764
839
|
};
|
|
765
|
-
}
|
|
766
|
-
|
|
840
|
+
}
|
|
841
|
+
): Promise<string> {
|
|
767
842
|
let realPluginPath = pluginPath;
|
|
768
843
|
const exports = pluginPkg.eggPlugin?.exports;
|
|
769
844
|
if (exports) {
|
|
@@ -771,23 +846,29 @@ export class EggLoader {
|
|
|
771
846
|
if (exports.import) {
|
|
772
847
|
realPluginPath = path.join(pluginPath, exports.import);
|
|
773
848
|
}
|
|
774
|
-
} else {
|
|
775
|
-
|
|
776
|
-
realPluginPath = path.join(pluginPath, exports.require);
|
|
777
|
-
}
|
|
849
|
+
} else if (exports.require) {
|
|
850
|
+
realPluginPath = path.join(pluginPath, exports.require);
|
|
778
851
|
}
|
|
779
|
-
if (
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
852
|
+
if (
|
|
853
|
+
exports.typescript &&
|
|
854
|
+
isSupportTypeScript() &&
|
|
855
|
+
!(await exists(realPluginPath))
|
|
856
|
+
) {
|
|
857
|
+
// if require/import path not exists, use typescript path for development stage
|
|
858
|
+
realPluginPath = path.join(pluginPath, exports.typescript);
|
|
859
|
+
debug(
|
|
860
|
+
'[formatPluginPathFromPackageJSON] use typescript path %o',
|
|
861
|
+
realPluginPath
|
|
862
|
+
);
|
|
785
863
|
}
|
|
786
864
|
}
|
|
787
865
|
return realPluginPath;
|
|
788
866
|
}
|
|
789
867
|
|
|
790
|
-
#extendPlugins(
|
|
868
|
+
#extendPlugins(
|
|
869
|
+
targets: Record<string, EggPluginInfo>,
|
|
870
|
+
plugins: Record<string, EggPluginInfo>
|
|
871
|
+
) {
|
|
791
872
|
if (!plugins) {
|
|
792
873
|
return;
|
|
793
874
|
}
|
|
@@ -795,21 +876,30 @@ export class EggLoader {
|
|
|
795
876
|
const plugin = plugins[name];
|
|
796
877
|
let targetPlugin = targets[name];
|
|
797
878
|
if (!targetPlugin) {
|
|
798
|
-
targetPlugin =
|
|
879
|
+
targetPlugin = {} as EggPluginInfo;
|
|
880
|
+
targets[name] = targetPlugin;
|
|
799
881
|
}
|
|
800
882
|
if (targetPlugin.package && targetPlugin.package === plugin.package) {
|
|
801
|
-
this.logger.warn(
|
|
802
|
-
|
|
883
|
+
this.logger.warn(
|
|
884
|
+
'[@eggjs/core] plugin %s has been defined that is %j, but you define again in %s',
|
|
885
|
+
name,
|
|
886
|
+
targetPlugin,
|
|
887
|
+
plugin.from
|
|
888
|
+
);
|
|
803
889
|
}
|
|
804
890
|
if (plugin.path || plugin.package) {
|
|
805
891
|
delete targetPlugin.path;
|
|
806
892
|
delete targetPlugin.package;
|
|
807
893
|
}
|
|
808
|
-
for (const [
|
|
894
|
+
for (const [prop, value] of Object.entries(plugin)) {
|
|
809
895
|
if (value === undefined) {
|
|
810
896
|
continue;
|
|
811
897
|
}
|
|
812
|
-
if (
|
|
898
|
+
if (
|
|
899
|
+
Reflect.get(targetPlugin, prop) &&
|
|
900
|
+
Array.isArray(value) &&
|
|
901
|
+
value.length === 0
|
|
902
|
+
) {
|
|
813
903
|
continue;
|
|
814
904
|
}
|
|
815
905
|
Reflect.set(targetPlugin, prop, value);
|
|
@@ -852,11 +942,20 @@ export class EggLoader {
|
|
|
852
942
|
for (const unit of this.getLoadUnits()) {
|
|
853
943
|
const isApp = unit.type === 'app';
|
|
854
944
|
const config = await this.#loadConfig(
|
|
855
|
-
unit.path,
|
|
945
|
+
unit.path,
|
|
946
|
+
filename,
|
|
947
|
+
isApp ? undefined : appConfig,
|
|
948
|
+
unit.type
|
|
949
|
+
);
|
|
856
950
|
if (!config) {
|
|
857
951
|
continue;
|
|
858
952
|
}
|
|
859
|
-
debug(
|
|
953
|
+
debug(
|
|
954
|
+
'[loadConfig] Loaded config %s/%s, %j',
|
|
955
|
+
unit.path,
|
|
956
|
+
filename,
|
|
957
|
+
config
|
|
958
|
+
);
|
|
860
959
|
extend(true, target, config);
|
|
861
960
|
}
|
|
862
961
|
}
|
|
@@ -867,8 +966,13 @@ export class EggLoader {
|
|
|
867
966
|
extend(true, target, envConfig);
|
|
868
967
|
|
|
869
968
|
// You can manipulate the order of app.config.coreMiddleware and app.config.appMiddleware in app.js
|
|
870
|
-
target.coreMiddleware = target.
|
|
871
|
-
|
|
969
|
+
target.coreMiddleware = target.coreMiddleware || [];
|
|
970
|
+
// alias for coreMiddleware
|
|
971
|
+
target.coreMiddlewares = target.coreMiddleware;
|
|
972
|
+
|
|
973
|
+
target.appMiddleware = target.middleware || [];
|
|
974
|
+
// alias for appMiddleware
|
|
975
|
+
target.appMiddlewares = target.appMiddleware;
|
|
872
976
|
|
|
873
977
|
this.config = target;
|
|
874
978
|
debug('[loadConfig] all config: %o', this.config);
|
|
@@ -876,13 +980,15 @@ export class EggLoader {
|
|
|
876
980
|
}
|
|
877
981
|
|
|
878
982
|
async #preloadAppConfig() {
|
|
879
|
-
const names = [
|
|
880
|
-
'config.default',
|
|
881
|
-
`config.${this.serverEnv}`,
|
|
882
|
-
];
|
|
983
|
+
const names = ['config.default', `config.${this.serverEnv}`];
|
|
883
984
|
const target: Record<string, any> = {};
|
|
884
985
|
for (const filename of names) {
|
|
885
|
-
const config = await this.#loadConfig(
|
|
986
|
+
const config = await this.#loadConfig(
|
|
987
|
+
this.options.baseDir,
|
|
988
|
+
filename,
|
|
989
|
+
undefined,
|
|
990
|
+
'app'
|
|
991
|
+
);
|
|
886
992
|
if (!config) {
|
|
887
993
|
continue;
|
|
888
994
|
}
|
|
@@ -891,7 +997,12 @@ export class EggLoader {
|
|
|
891
997
|
return target;
|
|
892
998
|
}
|
|
893
999
|
|
|
894
|
-
async #loadConfig(
|
|
1000
|
+
async #loadConfig(
|
|
1001
|
+
dirpath: string,
|
|
1002
|
+
filename: string,
|
|
1003
|
+
extraInject: object | undefined,
|
|
1004
|
+
type: EggDirInfoType
|
|
1005
|
+
) {
|
|
895
1006
|
const isPlugin = type === 'plugin';
|
|
896
1007
|
const isApp = type === 'app';
|
|
897
1008
|
|
|
@@ -900,16 +1011,26 @@ export class EggLoader {
|
|
|
900
1011
|
if (filename === 'config.default' && !filepath) {
|
|
901
1012
|
filepath = this.resolveModule(path.join(dirpath, 'config/config'));
|
|
902
1013
|
}
|
|
903
|
-
|
|
1014
|
+
if (!filepath) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
const config: Record<string, any> = await this.loadFile(
|
|
1018
|
+
filepath,
|
|
1019
|
+
this.appInfo,
|
|
1020
|
+
extraInject
|
|
1021
|
+
);
|
|
904
1022
|
if (!config) return;
|
|
905
1023
|
if (isPlugin || isApp) {
|
|
906
|
-
assert(
|
|
1024
|
+
assert(
|
|
1025
|
+
!config.coreMiddleware,
|
|
1026
|
+
'Can not define coreMiddleware in app or plugin'
|
|
1027
|
+
);
|
|
907
1028
|
}
|
|
908
1029
|
if (!isApp) {
|
|
909
1030
|
assert(!config.middleware, 'Can not define middleware in ' + filepath);
|
|
910
1031
|
}
|
|
911
1032
|
// store config meta, check where is the property of config come from.
|
|
912
|
-
this.#setConfigMeta(config, filepath
|
|
1033
|
+
this.#setConfigMeta(config, filepath);
|
|
913
1034
|
return config;
|
|
914
1035
|
}
|
|
915
1036
|
|
|
@@ -917,15 +1038,18 @@ export class EggLoader {
|
|
|
917
1038
|
const envConfigStr = process.env.EGG_APP_CONFIG;
|
|
918
1039
|
if (!envConfigStr) return;
|
|
919
1040
|
try {
|
|
920
|
-
const envConfig: Record<string,
|
|
1041
|
+
const envConfig: Record<string, unknown> = JSON.parse(envConfigStr);
|
|
921
1042
|
this.#setConfigMeta(envConfig, '<process.env.EGG_APP_CONFIG>');
|
|
922
1043
|
return envConfig;
|
|
923
|
-
} catch
|
|
924
|
-
this.options.logger.warn(
|
|
1044
|
+
} catch {
|
|
1045
|
+
this.options.logger.warn(
|
|
1046
|
+
'[egg-loader] process.env.EGG_APP_CONFIG is not invalid JSON: %s',
|
|
1047
|
+
envConfigStr
|
|
1048
|
+
);
|
|
925
1049
|
}
|
|
926
1050
|
}
|
|
927
1051
|
|
|
928
|
-
#setConfigMeta(config: Record<string,
|
|
1052
|
+
#setConfigMeta(config: Record<string, unknown>, filepath: string) {
|
|
929
1053
|
config = extend(true, {}, config);
|
|
930
1054
|
this.#setConfig(config, filepath);
|
|
931
1055
|
extend(true, this.configMeta, config);
|
|
@@ -935,11 +1059,20 @@ export class EggLoader {
|
|
|
935
1059
|
for (const key of Object.keys(obj)) {
|
|
936
1060
|
const val = obj[key];
|
|
937
1061
|
// ignore console
|
|
938
|
-
if (
|
|
1062
|
+
if (
|
|
1063
|
+
key === 'console' &&
|
|
1064
|
+
val &&
|
|
1065
|
+
typeof val.Console === 'function' &&
|
|
1066
|
+
val.Console === console.Console
|
|
1067
|
+
) {
|
|
939
1068
|
obj[key] = filepath;
|
|
940
1069
|
continue;
|
|
941
1070
|
}
|
|
942
|
-
if (
|
|
1071
|
+
if (
|
|
1072
|
+
val &&
|
|
1073
|
+
Object.getPrototypeOf(val) === Object.prototype &&
|
|
1074
|
+
Object.keys(val).length > 0
|
|
1075
|
+
) {
|
|
943
1076
|
this.#setConfig(val, filepath);
|
|
944
1077
|
continue;
|
|
945
1078
|
}
|
|
@@ -1010,11 +1143,13 @@ export class EggLoader {
|
|
|
1010
1143
|
* can be override in top level framework to support load `app/extends/{name}.js`
|
|
1011
1144
|
*
|
|
1012
1145
|
* @param {String} name - filename which may be `app/extend/{name}.js`
|
|
1013
|
-
* @
|
|
1146
|
+
* @returns {Array} filepaths extend file paths
|
|
1014
1147
|
* @private
|
|
1015
1148
|
*/
|
|
1016
1149
|
protected getExtendFilePaths(name: string): string[] {
|
|
1017
|
-
return this.getLoadUnits().map(unit =>
|
|
1150
|
+
return this.getLoadUnits().map(unit =>
|
|
1151
|
+
path.join(unit.path, 'app/extend', name)
|
|
1152
|
+
);
|
|
1018
1153
|
}
|
|
1019
1154
|
|
|
1020
1155
|
/**
|
|
@@ -1029,7 +1164,8 @@ export class EggLoader {
|
|
|
1029
1164
|
// All extend files
|
|
1030
1165
|
const filepaths = this.getExtendFilePaths(name);
|
|
1031
1166
|
// if use mm.env and serverEnv is not unittest
|
|
1032
|
-
const needUnittest =
|
|
1167
|
+
const needUnittest =
|
|
1168
|
+
'EGG_MOCK_SERVER_ENV' in process.env && this.serverEnv !== 'unittest';
|
|
1033
1169
|
const length = filepaths.length;
|
|
1034
1170
|
for (let i = 0; i < length; i++) {
|
|
1035
1171
|
const filepath = filepaths[i];
|
|
@@ -1042,15 +1178,19 @@ export class EggLoader {
|
|
|
1042
1178
|
|
|
1043
1179
|
const mergeRecord = new Map();
|
|
1044
1180
|
for (const rawFilepath of filepaths) {
|
|
1045
|
-
const filepath = this.resolveModule(rawFilepath)
|
|
1181
|
+
const filepath = this.resolveModule(rawFilepath);
|
|
1046
1182
|
if (!filepath) {
|
|
1047
1183
|
// debug('loadExtend %o not found', rawFilepath);
|
|
1048
1184
|
continue;
|
|
1049
1185
|
}
|
|
1050
1186
|
if (filepath.endsWith('/index.js')) {
|
|
1051
|
-
this.app.deprecate(
|
|
1187
|
+
this.app.deprecate(
|
|
1188
|
+
`app/extend/${name}/index.js is deprecated, use app/extend/${name}.js instead`
|
|
1189
|
+
);
|
|
1052
1190
|
} else if (filepath.endsWith('/index.ts')) {
|
|
1053
|
-
this.app.deprecate(
|
|
1191
|
+
this.app.deprecate(
|
|
1192
|
+
`app/extend/${name}/index.ts is deprecated, use app/extend/${name}.ts instead`
|
|
1193
|
+
);
|
|
1054
1194
|
}
|
|
1055
1195
|
|
|
1056
1196
|
let ext = await this.requireFile(filepath);
|
|
@@ -1059,23 +1199,36 @@ export class EggLoader {
|
|
|
1059
1199
|
ext = ext.prototype;
|
|
1060
1200
|
}
|
|
1061
1201
|
const properties = Object.getOwnPropertyNames(ext)
|
|
1062
|
-
.concat(Object.getOwnPropertySymbols(ext) as
|
|
1202
|
+
.concat(Object.getOwnPropertySymbols(ext) as unknown as string[])
|
|
1063
1203
|
.filter(name => name !== 'constructor'); // ignore class constructor for extend
|
|
1064
1204
|
|
|
1065
1205
|
for (const property of properties) {
|
|
1066
1206
|
if (mergeRecord.has(property)) {
|
|
1067
|
-
debug(
|
|
1068
|
-
|
|
1207
|
+
debug(
|
|
1208
|
+
'Property: "%s" already exists in "%s",it will be redefined by "%s"',
|
|
1209
|
+
property,
|
|
1210
|
+
mergeRecord.get(property),
|
|
1211
|
+
filepath
|
|
1212
|
+
);
|
|
1069
1213
|
}
|
|
1070
1214
|
|
|
1071
1215
|
// Copy descriptor
|
|
1072
|
-
let descriptor = Object.getOwnPropertyDescriptor(
|
|
1073
|
-
|
|
1216
|
+
let descriptor = Object.getOwnPropertyDescriptor(
|
|
1217
|
+
ext,
|
|
1218
|
+
property
|
|
1219
|
+
) as PropertyDescriptor;
|
|
1220
|
+
let originalDescriptor = Object.getOwnPropertyDescriptor(
|
|
1221
|
+
proto,
|
|
1222
|
+
property
|
|
1223
|
+
);
|
|
1074
1224
|
if (!originalDescriptor) {
|
|
1075
1225
|
// try to get descriptor from originalPrototypes
|
|
1076
1226
|
const originalProto = originalPrototypes[name];
|
|
1077
1227
|
if (originalProto) {
|
|
1078
|
-
originalDescriptor = Object.getOwnPropertyDescriptor(
|
|
1228
|
+
originalDescriptor = Object.getOwnPropertyDescriptor(
|
|
1229
|
+
originalProto,
|
|
1230
|
+
property
|
|
1231
|
+
);
|
|
1079
1232
|
}
|
|
1080
1233
|
}
|
|
1081
1234
|
if (originalDescriptor) {
|
|
@@ -1090,7 +1243,7 @@ export class EggLoader {
|
|
|
1090
1243
|
descriptor.get = originalDescriptor.get;
|
|
1091
1244
|
}
|
|
1092
1245
|
}
|
|
1093
|
-
Object.defineProperty(proto, property, descriptor
|
|
1246
|
+
Object.defineProperty(proto, property, descriptor);
|
|
1094
1247
|
mergeRecord.set(property, filepath);
|
|
1095
1248
|
}
|
|
1096
1249
|
debug('merge %j to %s from %s', properties, name, filepath);
|
|
@@ -1164,7 +1317,10 @@ export class EggLoader {
|
|
|
1164
1317
|
this.lifecycle.addFunctionAsBootHook(bootHook, bootFilePath);
|
|
1165
1318
|
debug('[loadBootHook] add bootHookFunction from %o', bootFilePath);
|
|
1166
1319
|
} else {
|
|
1167
|
-
this.options.logger.warn(
|
|
1320
|
+
this.options.logger.warn(
|
|
1321
|
+
'[@eggjs/core/egg_loader] %s must exports a boot class',
|
|
1322
|
+
bootFilePath
|
|
1323
|
+
);
|
|
1168
1324
|
}
|
|
1169
1325
|
}
|
|
1170
1326
|
// init boots
|
|
@@ -1183,7 +1339,9 @@ export class EggLoader {
|
|
|
1183
1339
|
async loadService(options?: Partial<ContextLoaderOptions>) {
|
|
1184
1340
|
this.timing.start('Load Service');
|
|
1185
1341
|
// 载入到 app.serviceClasses
|
|
1186
|
-
const servicePaths = this.getLoadUnits().map(unit =>
|
|
1342
|
+
const servicePaths = this.getLoadUnits().map(unit =>
|
|
1343
|
+
path.join(unit.path, 'app/service')
|
|
1344
|
+
);
|
|
1187
1345
|
options = {
|
|
1188
1346
|
call: true,
|
|
1189
1347
|
caseStyle: CaseStyle.lower,
|
|
@@ -1192,7 +1350,11 @@ export class EggLoader {
|
|
|
1192
1350
|
...options,
|
|
1193
1351
|
};
|
|
1194
1352
|
debug('[loadService] options: %o', options);
|
|
1195
|
-
await this.loadToContext(
|
|
1353
|
+
await this.loadToContext(
|
|
1354
|
+
servicePaths,
|
|
1355
|
+
'service',
|
|
1356
|
+
options as ContextLoaderOptions
|
|
1357
|
+
);
|
|
1196
1358
|
this.timing.end('Load Service');
|
|
1197
1359
|
}
|
|
1198
1360
|
/** end Service loader */
|
|
@@ -1222,7 +1384,9 @@ export class EggLoader {
|
|
|
1222
1384
|
const app = this.app;
|
|
1223
1385
|
|
|
1224
1386
|
// load middleware to app.middleware
|
|
1225
|
-
const middlewarePaths = this.getLoadUnits().map(unit =>
|
|
1387
|
+
const middlewarePaths = this.getLoadUnits().map(unit =>
|
|
1388
|
+
path.join(unit.path, 'app/middleware')
|
|
1389
|
+
);
|
|
1226
1390
|
opt = {
|
|
1227
1391
|
call: false,
|
|
1228
1392
|
override: true,
|
|
@@ -1230,7 +1394,11 @@ export class EggLoader {
|
|
|
1230
1394
|
directory: middlewarePaths,
|
|
1231
1395
|
...opt,
|
|
1232
1396
|
};
|
|
1233
|
-
await this.loadToApp(
|
|
1397
|
+
await this.loadToApp(
|
|
1398
|
+
middlewarePaths,
|
|
1399
|
+
'middlewares',
|
|
1400
|
+
opt as FileLoaderOptions
|
|
1401
|
+
);
|
|
1234
1402
|
|
|
1235
1403
|
for (const name in app.middlewares) {
|
|
1236
1404
|
Object.defineProperty(app.middleware, name, {
|
|
@@ -1242,11 +1410,19 @@ export class EggLoader {
|
|
|
1242
1410
|
});
|
|
1243
1411
|
}
|
|
1244
1412
|
|
|
1245
|
-
this.options.logger.info(
|
|
1246
|
-
|
|
1413
|
+
this.options.logger.info(
|
|
1414
|
+
'Use coreMiddleware order: %j',
|
|
1415
|
+
this.config.coreMiddleware
|
|
1416
|
+
);
|
|
1417
|
+
this.options.logger.info(
|
|
1418
|
+
'Use appMiddleware order: %j',
|
|
1419
|
+
this.config.appMiddleware
|
|
1420
|
+
);
|
|
1247
1421
|
|
|
1248
1422
|
// use middleware ordered by app.config.coreMiddleware and app.config.appMiddleware
|
|
1249
|
-
const middlewareNames = this.config.coreMiddleware.concat(
|
|
1423
|
+
const middlewareNames = this.config.coreMiddleware.concat(
|
|
1424
|
+
this.config.appMiddleware
|
|
1425
|
+
);
|
|
1250
1426
|
debug('[loadMiddleware] middlewareNames: %j', middlewareNames);
|
|
1251
1427
|
const middlewaresMap = new Map<string, boolean>();
|
|
1252
1428
|
for (const name of middlewareNames) {
|
|
@@ -1260,10 +1436,15 @@ export class EggLoader {
|
|
|
1260
1436
|
middlewaresMap.set(name, true);
|
|
1261
1437
|
const options = this.config[name] || {};
|
|
1262
1438
|
let mw: MiddlewareFunc | null = createMiddleware(options, app);
|
|
1263
|
-
assert(
|
|
1439
|
+
assert(
|
|
1440
|
+
typeof mw === 'function',
|
|
1441
|
+
`Middleware ${name} must be a function, but actual is ${inspect(mw)}`
|
|
1442
|
+
);
|
|
1264
1443
|
if (isGeneratorFunction(mw)) {
|
|
1265
1444
|
const fullpath = Reflect.get(createMiddleware, FULLPATH);
|
|
1266
|
-
throw new TypeError(
|
|
1445
|
+
throw new TypeError(
|
|
1446
|
+
`Support for generators was removed, middleware: ${name}, fullpath: ${fullpath}`
|
|
1447
|
+
);
|
|
1267
1448
|
}
|
|
1268
1449
|
mw._name = name;
|
|
1269
1450
|
// middlewares support options.enable, options.ignore and options.match
|
|
@@ -1274,14 +1455,27 @@ export class EggLoader {
|
|
|
1274
1455
|
mw = debugMiddlewareWrapper(mw);
|
|
1275
1456
|
}
|
|
1276
1457
|
app.use(mw);
|
|
1277
|
-
debug(
|
|
1278
|
-
|
|
1458
|
+
debug(
|
|
1459
|
+
'[loadMiddleware] Use middleware: %s with options: %j',
|
|
1460
|
+
name,
|
|
1461
|
+
options
|
|
1462
|
+
);
|
|
1463
|
+
this.options.logger.info(
|
|
1464
|
+
'[@eggjs/core/egg_loader] Use middleware: %s',
|
|
1465
|
+
name
|
|
1466
|
+
);
|
|
1279
1467
|
} else {
|
|
1280
|
-
this.options.logger.info(
|
|
1468
|
+
this.options.logger.info(
|
|
1469
|
+
'[@eggjs/core/egg_loader] Disable middleware: %s',
|
|
1470
|
+
name
|
|
1471
|
+
);
|
|
1281
1472
|
}
|
|
1282
1473
|
}
|
|
1283
1474
|
|
|
1284
|
-
this.options.logger.info(
|
|
1475
|
+
this.options.logger.info(
|
|
1476
|
+
'[@eggjs/core/egg_loader] Loaded middleware from %j',
|
|
1477
|
+
middlewarePaths
|
|
1478
|
+
);
|
|
1285
1479
|
this.timing.end('Load Middleware');
|
|
1286
1480
|
|
|
1287
1481
|
// add router middleware, make sure router is the last middleware
|
|
@@ -1311,15 +1505,21 @@ export class EggLoader {
|
|
|
1311
1505
|
// }
|
|
1312
1506
|
// ```
|
|
1313
1507
|
if (isGeneratorFunction(obj)) {
|
|
1314
|
-
throw new TypeError(
|
|
1508
|
+
throw new TypeError(
|
|
1509
|
+
`Support for generators was removed, fullpath: ${opt.path}`
|
|
1510
|
+
);
|
|
1315
1511
|
}
|
|
1316
|
-
if (
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1512
|
+
if (
|
|
1513
|
+
!isClass(obj) &&
|
|
1514
|
+
!isAsyncFunction(obj) &&
|
|
1515
|
+
typeof obj === 'function'
|
|
1516
|
+
) {
|
|
1517
|
+
obj = obj(this.app);
|
|
1518
|
+
debug('[loadController] after init(app) => %o, meta: %j', obj, opt);
|
|
1519
|
+
if (isGeneratorFunction(obj)) {
|
|
1520
|
+
throw new TypeError(
|
|
1521
|
+
`Support for generators was removed, fullpath: ${opt.path}`
|
|
1522
|
+
);
|
|
1323
1523
|
}
|
|
1324
1524
|
}
|
|
1325
1525
|
if (isClass(obj)) {
|
|
@@ -1331,15 +1531,24 @@ export class EggLoader {
|
|
|
1331
1531
|
return wrapObject(obj, opt.path);
|
|
1332
1532
|
}
|
|
1333
1533
|
if (isAsyncFunction(obj)) {
|
|
1334
|
-
return wrapObject({ 'module.exports': obj }, opt.path)[
|
|
1534
|
+
return wrapObject({ 'module.exports': obj }, opt.path)[
|
|
1535
|
+
'module.exports'
|
|
1536
|
+
];
|
|
1335
1537
|
}
|
|
1336
1538
|
return obj;
|
|
1337
1539
|
},
|
|
1338
1540
|
...opt,
|
|
1339
1541
|
};
|
|
1340
|
-
await this.loadToApp(
|
|
1542
|
+
await this.loadToApp(
|
|
1543
|
+
controllerBase,
|
|
1544
|
+
'controller',
|
|
1545
|
+
opt as FileLoaderOptions
|
|
1546
|
+
);
|
|
1341
1547
|
debug('[loadController] app.controller => %o', this.app.controller);
|
|
1342
|
-
this.options.logger.info(
|
|
1548
|
+
this.options.logger.info(
|
|
1549
|
+
'[@eggjs/core/egg_loader] Controller loaded: %s',
|
|
1550
|
+
controllerBase
|
|
1551
|
+
);
|
|
1343
1552
|
this.timing.end('Load Controller');
|
|
1344
1553
|
}
|
|
1345
1554
|
/** end Controller loader */
|
|
@@ -1366,20 +1575,32 @@ export class EggLoader {
|
|
|
1366
1575
|
const loaderConfig = {
|
|
1367
1576
|
...customLoader[property],
|
|
1368
1577
|
};
|
|
1369
|
-
assert(
|
|
1578
|
+
assert(
|
|
1579
|
+
loaderConfig.directory,
|
|
1580
|
+
`directory is required for config.customLoader.${property}`
|
|
1581
|
+
);
|
|
1370
1582
|
let directory: string | string[];
|
|
1371
1583
|
if (loaderConfig.loadunit === true) {
|
|
1372
|
-
directory = this.getLoadUnits().map(unit =>
|
|
1584
|
+
directory = this.getLoadUnits().map(unit =>
|
|
1585
|
+
path.join(unit.path, loaderConfig.directory)
|
|
1586
|
+
);
|
|
1373
1587
|
} else {
|
|
1374
1588
|
directory = path.join(this.appInfo.baseDir, loaderConfig.directory);
|
|
1375
1589
|
}
|
|
1376
1590
|
const inject = loaderConfig.inject || 'app';
|
|
1377
|
-
debug(
|
|
1378
|
-
loaderConfig, inject, directory
|
|
1591
|
+
debug(
|
|
1592
|
+
'[loadCustomLoader] loaderConfig: %o, inject: %o, directory: %o',
|
|
1593
|
+
loaderConfig,
|
|
1594
|
+
inject,
|
|
1595
|
+
directory
|
|
1596
|
+
);
|
|
1379
1597
|
|
|
1380
1598
|
switch (inject) {
|
|
1381
1599
|
case 'ctx': {
|
|
1382
|
-
assert(
|
|
1600
|
+
assert(
|
|
1601
|
+
!(property in this.app.context),
|
|
1602
|
+
`customLoader should not override ctx.${property}`
|
|
1603
|
+
);
|
|
1383
1604
|
const options = {
|
|
1384
1605
|
caseStyle: CaseStyle.lower,
|
|
1385
1606
|
fieldClass: `${property}Classes`,
|
|
@@ -1390,7 +1611,10 @@ export class EggLoader {
|
|
|
1390
1611
|
break;
|
|
1391
1612
|
}
|
|
1392
1613
|
case 'app': {
|
|
1393
|
-
assert(
|
|
1614
|
+
assert(
|
|
1615
|
+
!(property in this.app),
|
|
1616
|
+
`customLoader should not override app.${property}`
|
|
1617
|
+
);
|
|
1394
1618
|
const options = {
|
|
1395
1619
|
caseStyle: CaseStyle.lower,
|
|
1396
1620
|
initializer: (Clazz: unknown) => {
|
|
@@ -1416,14 +1640,14 @@ export class EggLoader {
|
|
|
1416
1640
|
*
|
|
1417
1641
|
* @param {String} filepath - fullpath
|
|
1418
1642
|
* @param {Array} inject - pass rest arguments into the function when invoke
|
|
1419
|
-
* @
|
|
1643
|
+
* @returns {Object} exports
|
|
1420
1644
|
* @example
|
|
1421
1645
|
* ```js
|
|
1422
1646
|
* app.loader.loadFile(path.join(app.options.baseDir, 'config/router.js'));
|
|
1423
1647
|
* ```
|
|
1424
1648
|
* @since 1.0.0
|
|
1425
1649
|
*/
|
|
1426
|
-
async loadFile(filepath: string, ...inject:
|
|
1650
|
+
async loadFile(filepath: string, ...inject: unknown[]) {
|
|
1427
1651
|
const fullpath = filepath && this.resolveModule(filepath);
|
|
1428
1652
|
if (!fullpath) {
|
|
1429
1653
|
return null;
|
|
@@ -1431,7 +1655,7 @@ export class EggLoader {
|
|
|
1431
1655
|
|
|
1432
1656
|
// function(arg1, args, ...) {}
|
|
1433
1657
|
if (inject.length === 0) {
|
|
1434
|
-
inject = [
|
|
1658
|
+
inject = [this.app];
|
|
1435
1659
|
}
|
|
1436
1660
|
let mod = await this.requireFile(fullpath);
|
|
1437
1661
|
if (typeof mod === 'function' && !isClass(mod)) {
|
|
@@ -1445,7 +1669,7 @@ export class EggLoader {
|
|
|
1445
1669
|
|
|
1446
1670
|
/**
|
|
1447
1671
|
* @param {String} filepath - fullpath
|
|
1448
|
-
* @
|
|
1672
|
+
* @returns {Object} exports
|
|
1449
1673
|
* @private
|
|
1450
1674
|
*/
|
|
1451
1675
|
async requireFile(filepath: string) {
|
|
@@ -1468,7 +1692,7 @@ export class EggLoader {
|
|
|
1468
1692
|
* 2. framework
|
|
1469
1693
|
* 3. app
|
|
1470
1694
|
*
|
|
1471
|
-
* @
|
|
1695
|
+
* @returns {Array} loadUnits
|
|
1472
1696
|
* @since 1.0.0
|
|
1473
1697
|
*/
|
|
1474
1698
|
getLoadUnits(): EggDirInfo[] {
|
|
@@ -1480,7 +1704,7 @@ export class EggLoader {
|
|
|
1480
1704
|
if (this.orderPlugins) {
|
|
1481
1705
|
for (const plugin of this.orderPlugins) {
|
|
1482
1706
|
this.dirs.push({
|
|
1483
|
-
path: plugin.path
|
|
1707
|
+
path: plugin.path as string,
|
|
1484
1708
|
type: 'plugin',
|
|
1485
1709
|
});
|
|
1486
1710
|
}
|
|
@@ -1511,8 +1735,11 @@ export class EggLoader {
|
|
|
1511
1735
|
* @param {Object} options - see {@link FileLoader}
|
|
1512
1736
|
* @since 1.0.0
|
|
1513
1737
|
*/
|
|
1514
|
-
async loadToApp(
|
|
1515
|
-
|
|
1738
|
+
async loadToApp(
|
|
1739
|
+
directory: string | string[],
|
|
1740
|
+
property: string | symbol,
|
|
1741
|
+
options?: Omit<FileLoaderOptions, 'inject' | 'target'>
|
|
1742
|
+
) {
|
|
1516
1743
|
const target = {};
|
|
1517
1744
|
Reflect.set(this.app, property, target);
|
|
1518
1745
|
const loadOptions: FileLoaderOptions = {
|
|
@@ -1535,8 +1762,11 @@ export class EggLoader {
|
|
|
1535
1762
|
* @param {Object} options - see {@link ContextLoader}
|
|
1536
1763
|
* @since 1.0.0
|
|
1537
1764
|
*/
|
|
1538
|
-
async loadToContext(
|
|
1539
|
-
|
|
1765
|
+
async loadToContext(
|
|
1766
|
+
directory: string | string[],
|
|
1767
|
+
property: string | symbol,
|
|
1768
|
+
options?: Omit<ContextLoaderOptions, 'inject' | 'property'>
|
|
1769
|
+
) {
|
|
1540
1770
|
const loadOptions: ContextLoaderOptions = {
|
|
1541
1771
|
...options,
|
|
1542
1772
|
directory: options?.directory || directory,
|
|
@@ -1567,7 +1797,7 @@ export class EggLoader {
|
|
|
1567
1797
|
}
|
|
1568
1798
|
|
|
1569
1799
|
getTypeFiles(filename: string) {
|
|
1570
|
-
const files = [
|
|
1800
|
+
const files = [`${filename}.default`];
|
|
1571
1801
|
if (this.serverScope) files.push(`${filename}.${this.serverScope}`);
|
|
1572
1802
|
if (this.serverEnv === 'default') return files;
|
|
1573
1803
|
files.push(`${filename}.${this.serverEnv}`);
|
|
@@ -1581,7 +1811,7 @@ export class EggLoader {
|
|
|
1581
1811
|
let fullPath;
|
|
1582
1812
|
try {
|
|
1583
1813
|
fullPath = utils.resolvePath(filepath);
|
|
1584
|
-
} catch
|
|
1814
|
+
} catch {
|
|
1585
1815
|
// debug('[resolveModule] Module %o resolve error: %s', filepath, err.stack);
|
|
1586
1816
|
return undefined;
|
|
1587
1817
|
}
|
|
@@ -1593,7 +1823,10 @@ export class EggLoader {
|
|
|
1593
1823
|
}
|
|
1594
1824
|
|
|
1595
1825
|
function depCompatible(plugin: EggPluginInfo & { dep?: string[] }) {
|
|
1596
|
-
if (
|
|
1826
|
+
if (
|
|
1827
|
+
plugin.dep &&
|
|
1828
|
+
!(Array.isArray(plugin.dependencies) && plugin.dependencies.length > 0)
|
|
1829
|
+
) {
|
|
1597
1830
|
plugin.dependencies = plugin.dep;
|
|
1598
1831
|
delete plugin.dep;
|
|
1599
1832
|
}
|
|
@@ -1608,8 +1841,10 @@ function isValidatePackageName(name: string) {
|
|
|
1608
1841
|
}
|
|
1609
1842
|
|
|
1610
1843
|
// support pathMatching on middleware
|
|
1611
|
-
function wrapMiddleware(
|
|
1612
|
-
|
|
1844
|
+
function wrapMiddleware(
|
|
1845
|
+
mw: MiddlewareFunc,
|
|
1846
|
+
options: PathMatchingOptions & { enable?: boolean }
|
|
1847
|
+
): MiddlewareFunc | null {
|
|
1613
1848
|
// support options.enable
|
|
1614
1849
|
if (options.enable === false) {
|
|
1615
1850
|
return null;
|
|
@@ -1632,17 +1867,31 @@ function wrapMiddleware(mw: MiddlewareFunc,
|
|
|
1632
1867
|
function debugMiddlewareWrapper(mw: MiddlewareFunc): MiddlewareFunc {
|
|
1633
1868
|
const fn: MiddlewareFunc = async (ctx, next) => {
|
|
1634
1869
|
const startTime = now();
|
|
1635
|
-
debug(
|
|
1870
|
+
debug(
|
|
1871
|
+
'[debugMiddlewareWrapper] [%s %s] enter middleware: %s',
|
|
1872
|
+
ctx.method,
|
|
1873
|
+
ctx.url,
|
|
1874
|
+
mw._name
|
|
1875
|
+
);
|
|
1636
1876
|
await mw(ctx, next);
|
|
1637
1877
|
const rt = diff(startTime);
|
|
1638
|
-
debug(
|
|
1878
|
+
debug(
|
|
1879
|
+
'[debugMiddlewareWrapper] [%s %s] after middleware: %s [%sms]',
|
|
1880
|
+
ctx.method,
|
|
1881
|
+
ctx.url,
|
|
1882
|
+
mw._name,
|
|
1883
|
+
rt
|
|
1884
|
+
);
|
|
1639
1885
|
};
|
|
1640
1886
|
fn._name = `${mw._name}DebugWrapper`;
|
|
1641
1887
|
return fn;
|
|
1642
1888
|
}
|
|
1643
1889
|
|
|
1644
1890
|
// wrap the controller class, yield a object with middlewares
|
|
1645
|
-
function wrapControllerClass(
|
|
1891
|
+
function wrapControllerClass(
|
|
1892
|
+
Controller: typeof BaseContextClass,
|
|
1893
|
+
fullPath: string
|
|
1894
|
+
) {
|
|
1646
1895
|
let proto = Controller.prototype;
|
|
1647
1896
|
const ret: Record<string, any> = {};
|
|
1648
1897
|
// tracing the prototype chain
|
|
@@ -1657,11 +1906,12 @@ function wrapControllerClass(Controller: typeof BaseContextClass, fullPath: stri
|
|
|
1657
1906
|
// skip getter, setter & non-function properties
|
|
1658
1907
|
const d = Object.getOwnPropertyDescriptor(proto, key);
|
|
1659
1908
|
// prevent to override sub method
|
|
1660
|
-
if (typeof d?.value === 'function' && !
|
|
1909
|
+
if (typeof d?.value === 'function' && !Object.hasOwn(ret, key)) {
|
|
1661
1910
|
const controllerMethodName = `${Controller.name}.${key}`;
|
|
1662
1911
|
if (isGeneratorFunction(d.value)) {
|
|
1663
1912
|
throw new TypeError(
|
|
1664
|
-
`Support for generators was removed, controller \`${controllerMethodName}\`, fullpath: ${fullPath}`
|
|
1913
|
+
`Support for generators was removed, controller \`${controllerMethodName}\`, fullpath: ${fullPath}`
|
|
1914
|
+
);
|
|
1665
1915
|
}
|
|
1666
1916
|
ret[key] = controllerMethodToMiddleware(Controller, key);
|
|
1667
1917
|
ret[key][FULLPATH] = `${fullPath}#${controllerMethodName}()`;
|
|
@@ -1672,18 +1922,26 @@ function wrapControllerClass(Controller: typeof BaseContextClass, fullPath: stri
|
|
|
1672
1922
|
return ret;
|
|
1673
1923
|
}
|
|
1674
1924
|
|
|
1675
|
-
function controllerMethodToMiddleware(
|
|
1676
|
-
|
|
1677
|
-
|
|
1925
|
+
function controllerMethodToMiddleware(
|
|
1926
|
+
Controller: typeof BaseContextClass,
|
|
1927
|
+
key: string
|
|
1928
|
+
) {
|
|
1929
|
+
return function classControllerMiddleware(this: Context, ...args: unknown[]) {
|
|
1930
|
+
const controller = new Controller(this);
|
|
1678
1931
|
if (!this.app.config.controller?.supportParams) {
|
|
1679
|
-
args = [
|
|
1932
|
+
args = [this];
|
|
1680
1933
|
}
|
|
1934
|
+
// @ts-expect-error key exists
|
|
1681
1935
|
return controller[key](...args);
|
|
1682
1936
|
};
|
|
1683
1937
|
}
|
|
1684
1938
|
|
|
1685
1939
|
// wrap the method of the object, method can receive ctx as it's first argument
|
|
1686
|
-
function wrapObject(
|
|
1940
|
+
function wrapObject(
|
|
1941
|
+
obj: Record<string, any>,
|
|
1942
|
+
fullPath: string,
|
|
1943
|
+
prefix?: string
|
|
1944
|
+
) {
|
|
1687
1945
|
const keys = Object.keys(obj);
|
|
1688
1946
|
const ret: Record<string, any> = {};
|
|
1689
1947
|
prefix = prefix ?? '';
|
|
@@ -1691,12 +1949,16 @@ function wrapObject(obj: Record<string, any>, fullPath: string, prefix?: string)
|
|
|
1691
1949
|
const controllerMethodName = `${prefix}${key}`;
|
|
1692
1950
|
const item = obj[key];
|
|
1693
1951
|
if (isGeneratorFunction(item)) {
|
|
1694
|
-
throw new TypeError(
|
|
1952
|
+
throw new TypeError(
|
|
1953
|
+
`Support for generators was removed, controller \`${controllerMethodName}\`, fullpath: ${fullPath}`
|
|
1954
|
+
);
|
|
1695
1955
|
}
|
|
1696
1956
|
if (typeof item === 'function') {
|
|
1697
1957
|
const names = getParamNames(item);
|
|
1698
1958
|
if (names[0] === 'next') {
|
|
1699
|
-
throw new Error(
|
|
1959
|
+
throw new Error(
|
|
1960
|
+
`controller \`${controllerMethodName}\` should not use next as argument from file ${fullPath}`
|
|
1961
|
+
);
|
|
1700
1962
|
}
|
|
1701
1963
|
ret[key] = objectFunctionToMiddleware(item);
|
|
1702
1964
|
ret[key][FULLPATH] = `${fullPath}#${controllerMethodName}()`;
|
|
@@ -1709,9 +1971,9 @@ function wrapObject(obj: Record<string, any>, fullPath: string, prefix?: string)
|
|
|
1709
1971
|
}
|
|
1710
1972
|
|
|
1711
1973
|
function objectFunctionToMiddleware(func: Fun) {
|
|
1712
|
-
async function objectControllerMiddleware(this: Context, ...args:
|
|
1974
|
+
async function objectControllerMiddleware(this: Context, ...args: unknown[]) {
|
|
1713
1975
|
if (!this.app.config.controller?.supportParams) {
|
|
1714
|
-
args = [
|
|
1976
|
+
args = [this];
|
|
1715
1977
|
}
|
|
1716
1978
|
return await func.apply(this, args);
|
|
1717
1979
|
}
|