@eggjs/core 6.3.0-beta.0 → 6.3.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/dist/commonjs/base_context_class.d.ts +3 -3
- package/dist/commonjs/base_context_class.js +1 -1
- package/dist/commonjs/egg.d.ts +17 -10
- package/dist/commonjs/egg.js +30 -4
- package/dist/commonjs/index.d.ts +2 -0
- package/dist/commonjs/index.js +2 -1
- package/dist/commonjs/lifecycle.d.ts +5 -2
- package/dist/commonjs/lifecycle.js +35 -15
- package/dist/commonjs/loader/context_loader.d.ts +3 -3
- package/dist/commonjs/loader/context_loader.js +1 -1
- package/dist/commonjs/loader/egg_loader.d.ts +4 -40
- package/dist/commonjs/loader/egg_loader.js +56 -39
- package/dist/commonjs/loader/file_loader.d.ts +5 -1
- package/dist/commonjs/loader/file_loader.js +18 -5
- package/dist/commonjs/singleton.d.ts +29 -0
- package/dist/commonjs/singleton.js +125 -0
- package/dist/commonjs/types.d.ts +53 -0
- package/dist/commonjs/types.js +3 -0
- package/dist/commonjs/utils/index.js +10 -5
- package/dist/commonjs/utils/sequencify.js +1 -1
- package/dist/commonjs/utils/timing.js +1 -1
- package/dist/esm/base_context_class.d.ts +3 -3
- package/dist/esm/base_context_class.js +1 -1
- package/dist/esm/egg.d.ts +17 -10
- package/dist/esm/egg.js +30 -4
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/lifecycle.d.ts +5 -2
- package/dist/esm/lifecycle.js +35 -15
- package/dist/esm/loader/context_loader.d.ts +3 -3
- package/dist/esm/loader/context_loader.js +1 -1
- package/dist/esm/loader/egg_loader.d.ts +4 -40
- package/dist/esm/loader/egg_loader.js +60 -43
- package/dist/esm/loader/file_loader.d.ts +5 -1
- package/dist/esm/loader/file_loader.js +17 -4
- package/dist/esm/singleton.d.ts +29 -0
- package/dist/esm/singleton.js +118 -0
- package/dist/esm/types.d.ts +53 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/utils/index.js +10 -5
- package/dist/esm/utils/sequencify.js +1 -1
- package/dist/esm/utils/timing.js +1 -1
- package/dist/package.json +1 -1
- package/package.json +16 -15
- package/src/base_context_class.ts +3 -3
- package/src/egg.ts +40 -12
- package/src/index.ts +2 -0
- package/src/lifecycle.ts +40 -15
- package/src/loader/context_loader.ts +4 -4
- package/src/loader/egg_loader.ts +76 -90
- package/src/loader/file_loader.ts +16 -4
- package/src/singleton.ts +149 -0
- package/src/types.ts +56 -0
- package/src/utils/index.ts +9 -4
- package/src/utils/sequencify.ts +1 -1
- package/src/utils/timing.ts +1 -1
package/src/lifecycle.ts
CHANGED
|
@@ -62,13 +62,15 @@ export interface LifecycleOptions {
|
|
|
62
62
|
logger: EggConsoleLogger;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export type FunWithFullPath = Fun & { fullPath?: string };
|
|
66
|
+
|
|
65
67
|
export class Lifecycle extends EventEmitter {
|
|
66
68
|
#init: boolean;
|
|
67
69
|
#readyObject: ReadyObject;
|
|
68
70
|
#bootHooks: (BootImplClass | ILifecycleBoot)[];
|
|
69
71
|
#boots: ILifecycleBoot[];
|
|
70
72
|
#isClosed: boolean;
|
|
71
|
-
#closeFunctionSet: Set<
|
|
73
|
+
#closeFunctionSet: Set<FunWithFullPath>;
|
|
72
74
|
loadReady: Ready;
|
|
73
75
|
bootReady: Ready;
|
|
74
76
|
options: LifecycleOptions;
|
|
@@ -96,10 +98,10 @@ export class Lifecycle extends EventEmitter {
|
|
|
96
98
|
this.#initReady();
|
|
97
99
|
this
|
|
98
100
|
.on('ready_stat', data => {
|
|
99
|
-
this.logger.info('[
|
|
101
|
+
this.logger.info('[@eggjs/core/lifecycle:ready_stat] end ready task %s, remain %j', data.id, data.remain);
|
|
100
102
|
})
|
|
101
103
|
.on('ready_timeout', id => {
|
|
102
|
-
this.logger.warn('[
|
|
104
|
+
this.logger.warn('[@eggjs/core/lifecycle:ready_timeout] %s seconds later %s was still unable to finish.', this.readyTimeout / 1000, id);
|
|
103
105
|
});
|
|
104
106
|
|
|
105
107
|
this.ready(err => {
|
|
@@ -149,11 +151,12 @@ export class Lifecycle extends EventEmitter {
|
|
|
149
151
|
this.#bootHooks.push(bootHootOrBootClass);
|
|
150
152
|
}
|
|
151
153
|
|
|
152
|
-
addFunctionAsBootHook<T = EggCore>(hook: (app: T) => void) {
|
|
154
|
+
addFunctionAsBootHook<T = EggCore>(hook: (app: T) => void, fullPath?: string) {
|
|
153
155
|
assert(this.#init === false, 'do not add hook when lifecycle has been initialized');
|
|
154
156
|
// app.js is exported as a function
|
|
155
157
|
// call this function in configDidLoad
|
|
156
|
-
|
|
158
|
+
class Boot implements ILifecycleBoot {
|
|
159
|
+
static fullPath?: string;
|
|
157
160
|
app: T;
|
|
158
161
|
constructor(app: T) {
|
|
159
162
|
this.app = app;
|
|
@@ -161,25 +164,34 @@ export class Lifecycle extends EventEmitter {
|
|
|
161
164
|
configDidLoad() {
|
|
162
165
|
hook(this.app);
|
|
163
166
|
}
|
|
164
|
-
}
|
|
167
|
+
}
|
|
168
|
+
Boot.fullPath = fullPath;
|
|
169
|
+
this.#bootHooks.push(Boot);
|
|
165
170
|
}
|
|
166
171
|
|
|
167
172
|
/**
|
|
168
173
|
* init boots and trigger config did config
|
|
169
174
|
*/
|
|
170
175
|
init() {
|
|
176
|
+
debug('%s init lifecycle', this.app.type);
|
|
171
177
|
assert(this.#init === false, 'lifecycle have been init');
|
|
172
178
|
this.#init = true;
|
|
173
179
|
this.#boots = this.#bootHooks.map(BootHootOrBootClass => {
|
|
180
|
+
let instance = BootHootOrBootClass as ILifecycleBoot;
|
|
174
181
|
if (isClass(BootHootOrBootClass)) {
|
|
175
|
-
|
|
182
|
+
instance = new BootHootOrBootClass(this.app);
|
|
183
|
+
if (!instance.fullPath && 'fullPath' in BootHootOrBootClass) {
|
|
184
|
+
instance.fullPath = BootHootOrBootClass.fullPath as string;
|
|
185
|
+
}
|
|
176
186
|
}
|
|
177
|
-
|
|
187
|
+
debug('[init] add boot instance: %o', instance.fullPath);
|
|
188
|
+
return instance;
|
|
178
189
|
});
|
|
179
190
|
}
|
|
180
191
|
|
|
181
192
|
registerBeforeStart(scope: Fun, name: string) {
|
|
182
|
-
debug('add registerBeforeStart, name: %o',
|
|
193
|
+
debug('%s add registerBeforeStart, name: %o',
|
|
194
|
+
this.options.app.type, name);
|
|
183
195
|
this.#registerReadyCallback({
|
|
184
196
|
scope,
|
|
185
197
|
ready: this.loadReady,
|
|
@@ -188,17 +200,24 @@ export class Lifecycle extends EventEmitter {
|
|
|
188
200
|
});
|
|
189
201
|
}
|
|
190
202
|
|
|
191
|
-
registerBeforeClose(fn:
|
|
203
|
+
registerBeforeClose(fn: FunWithFullPath, fullPath?: string) {
|
|
192
204
|
assert(typeof fn === 'function', 'argument should be function');
|
|
193
205
|
assert(this.#isClosed === false, 'app has been closed');
|
|
206
|
+
if (fullPath) {
|
|
207
|
+
fn.fullPath = fullPath;
|
|
208
|
+
}
|
|
194
209
|
this.#closeFunctionSet.add(fn);
|
|
210
|
+
debug('%s register beforeClose at %o, count: %d',
|
|
211
|
+
this.app.type, fullPath, this.#closeFunctionSet.size);
|
|
195
212
|
}
|
|
196
213
|
|
|
197
214
|
async close() {
|
|
198
215
|
// close in reverse order: first created, last closed
|
|
199
216
|
const closeFns = Array.from(this.#closeFunctionSet);
|
|
200
|
-
debug('
|
|
217
|
+
debug('%s start trigger %d beforeClose functions',
|
|
218
|
+
this.app.type, closeFns.length);
|
|
201
219
|
for (const fn of closeFns.reverse()) {
|
|
220
|
+
debug('%s trigger beforeClose at %o', this.app.type, fn.fullPath);
|
|
202
221
|
await utils.callFn(fn);
|
|
203
222
|
this.#closeFunctionSet.delete(fn);
|
|
204
223
|
}
|
|
@@ -207,13 +226,14 @@ export class Lifecycle extends EventEmitter {
|
|
|
207
226
|
this.removeAllListeners();
|
|
208
227
|
this.app.removeAllListeners();
|
|
209
228
|
this.#isClosed = true;
|
|
210
|
-
debug('%s closed', this.
|
|
229
|
+
debug('%s closed', this.app.type);
|
|
211
230
|
}
|
|
212
231
|
|
|
213
232
|
triggerConfigWillLoad() {
|
|
214
233
|
debug('trigger configWillLoad start');
|
|
215
234
|
for (const boot of this.#boots) {
|
|
216
235
|
if (typeof boot.configWillLoad === 'function') {
|
|
236
|
+
debug('trigger configWillLoad at %o', boot.fullPath);
|
|
217
237
|
boot.configWillLoad();
|
|
218
238
|
}
|
|
219
239
|
}
|
|
@@ -225,12 +245,13 @@ export class Lifecycle extends EventEmitter {
|
|
|
225
245
|
debug('trigger configDidLoad start');
|
|
226
246
|
for (const boot of this.#boots) {
|
|
227
247
|
if (typeof boot.configDidLoad === 'function') {
|
|
248
|
+
debug('trigger configDidLoad at %o', boot.fullPath);
|
|
228
249
|
boot.configDidLoad();
|
|
229
250
|
}
|
|
230
251
|
// function boot hook register after configDidLoad trigger
|
|
231
252
|
if (typeof boot.beforeClose === 'function') {
|
|
232
253
|
const beforeClose = boot.beforeClose.bind(boot);
|
|
233
|
-
this.registerBeforeClose(beforeClose);
|
|
254
|
+
this.registerBeforeClose(beforeClose, boot.fullPath);
|
|
234
255
|
}
|
|
235
256
|
}
|
|
236
257
|
debug('trigger configDidLoad end');
|
|
@@ -276,10 +297,12 @@ export class Lifecycle extends EventEmitter {
|
|
|
276
297
|
return (async () => {
|
|
277
298
|
for (const boot of this.#boots) {
|
|
278
299
|
if (typeof boot.didReady === 'function') {
|
|
300
|
+
debug('trigger didReady at %o', boot.fullPath);
|
|
279
301
|
try {
|
|
280
302
|
await boot.didReady(err);
|
|
281
|
-
} catch (
|
|
282
|
-
|
|
303
|
+
} catch (err) {
|
|
304
|
+
debug('trigger didReady error at %o, error: %s', boot.fullPath, err);
|
|
305
|
+
this.emit('error', err);
|
|
283
306
|
}
|
|
284
307
|
}
|
|
285
308
|
}
|
|
@@ -294,9 +317,11 @@ export class Lifecycle extends EventEmitter {
|
|
|
294
317
|
if (typeof boot.serverDidReady !== 'function') {
|
|
295
318
|
continue;
|
|
296
319
|
}
|
|
320
|
+
debug('trigger serverDidReady at %o', boot.fullPath);
|
|
297
321
|
try {
|
|
298
322
|
await boot.serverDidReady();
|
|
299
323
|
} catch (err) {
|
|
324
|
+
debug('trigger serverDidReady error at %o, error: %s', boot.fullPath, err);
|
|
300
325
|
this.emit('error', err);
|
|
301
326
|
}
|
|
302
327
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
import { isClass, isPrimitive } from 'is-type-of';
|
|
3
3
|
import { FileLoader, EXPORTS, type FileLoaderOptions } from './file_loader.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { Context } from '../egg.js';
|
|
5
5
|
|
|
6
6
|
const CLASS_LOADER = Symbol('classLoader');
|
|
7
7
|
|
|
8
8
|
export interface ClassLoaderOptions {
|
|
9
|
-
ctx:
|
|
9
|
+
ctx: Context;
|
|
10
10
|
properties: any;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export class ClassLoader {
|
|
14
14
|
readonly _cache = new Map();
|
|
15
|
-
_ctx:
|
|
15
|
+
_ctx: Context;
|
|
16
16
|
|
|
17
17
|
constructor(options: ClassLoaderOptions) {
|
|
18
18
|
assert(options.ctx, 'options.ctx is required');
|
|
@@ -98,7 +98,7 @@ export class ContextLoader extends FileLoader {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function getInstance(values: any, ctx:
|
|
101
|
+
function getInstance(values: any, ctx: Context) {
|
|
102
102
|
// it's a directory when it has no exports
|
|
103
103
|
// then use ClassLoader
|
|
104
104
|
const Class = values[EXPORTS] ? values : null;
|
package/src/loader/egg_loader.ts
CHANGED
|
@@ -5,66 +5,35 @@ import { debuglog, inspect } from 'node:util';
|
|
|
5
5
|
import { homedir } from 'node-homedir';
|
|
6
6
|
import { isAsyncFunction, isClass, isGeneratorFunction, isObject, isPromise } from 'is-type-of';
|
|
7
7
|
import type { Logger } from 'egg-logger';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getParamNames, readJSONSync, readJSON, exists,
|
|
10
|
+
} from 'utility';
|
|
9
11
|
import { extend } from 'extend2';
|
|
10
|
-
import { Request, Response,
|
|
12
|
+
import { Request, Response, Application, Context as KoaContext } from '@eggjs/koa';
|
|
13
|
+
import { register as tsconfigPathsRegister } from 'tsconfig-paths';
|
|
14
|
+
import { isESM, isSupportTypeScript } from '@eggjs/utils';
|
|
11
15
|
import { pathMatching, type PathMatchingOptions } from 'egg-path-matching';
|
|
12
16
|
import { now, diff } from 'performance-ms';
|
|
13
|
-
import { FULLPATH, FileLoader, FileLoaderOptions } from './file_loader.js';
|
|
17
|
+
import { CaseStyle, FULLPATH, FileLoader, FileLoaderOptions } from './file_loader.js';
|
|
14
18
|
import { ContextLoader, ContextLoaderOptions } from './context_loader.js';
|
|
15
19
|
import utils, { Fun } from '../utils/index.js';
|
|
16
20
|
import sequencify from '../utils/sequencify.js';
|
|
17
21
|
import { Timing } from '../utils/timing.js';
|
|
18
|
-
import type {
|
|
19
|
-
|
|
22
|
+
import type {
|
|
23
|
+
Context, EggCore, MiddlewareFunc,
|
|
24
|
+
} from '../egg.js';
|
|
25
|
+
import type { BaseContextClass } from '../base_context_class.js';
|
|
26
|
+
import type { EggAppConfig, EggAppInfo, EggPluginInfo } from '../types.js';
|
|
20
27
|
|
|
21
28
|
const debug = debuglog('@eggjs/core/loader/egg_loader');
|
|
22
29
|
|
|
23
30
|
const originalPrototypes: Record<string, any> = {
|
|
24
31
|
request: Request.prototype,
|
|
25
32
|
response: Response.prototype,
|
|
26
|
-
context:
|
|
33
|
+
context: KoaContext.prototype,
|
|
27
34
|
application: Application.prototype,
|
|
28
35
|
};
|
|
29
36
|
|
|
30
|
-
export interface EggAppInfo {
|
|
31
|
-
/** package.json */
|
|
32
|
-
pkg: Record<string, any>;
|
|
33
|
-
/** the application name from package.json */
|
|
34
|
-
name: string;
|
|
35
|
-
/** current directory of application */
|
|
36
|
-
baseDir: string;
|
|
37
|
-
/** equals to serverEnv */
|
|
38
|
-
env: string;
|
|
39
|
-
/** equals to serverScope */
|
|
40
|
-
scope: string;
|
|
41
|
-
/** home directory of the OS */
|
|
42
|
-
HOME: string;
|
|
43
|
-
/** baseDir when local and unittest, HOME when other environment */
|
|
44
|
-
root: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface EggPluginInfo {
|
|
48
|
-
/** the plugin name, it can be used in `dep` */
|
|
49
|
-
name: string;
|
|
50
|
-
/** the package name of plugin */
|
|
51
|
-
package?: string;
|
|
52
|
-
version?: string;
|
|
53
|
-
/** whether enabled */
|
|
54
|
-
enable: boolean;
|
|
55
|
-
implicitEnable?: boolean;
|
|
56
|
-
/** the directory of the plugin package */
|
|
57
|
-
path?: string;
|
|
58
|
-
/** the dependent plugins, you can use the plugin name */
|
|
59
|
-
dependencies: string[];
|
|
60
|
-
/** the optional dependent plugins. */
|
|
61
|
-
optionalDependencies: string[];
|
|
62
|
-
dependents?: string[];
|
|
63
|
-
/** specify the serverEnv that only enable the plugin in it */
|
|
64
|
-
env: string[];
|
|
65
|
-
/** the file plugin config in. */
|
|
66
|
-
from: string;
|
|
67
|
-
}
|
|
68
37
|
|
|
69
38
|
export interface EggLoaderOptions {
|
|
70
39
|
/** server env */
|
|
@@ -100,7 +69,6 @@ export class EggLoader {
|
|
|
100
69
|
readonly appInfo: EggAppInfo;
|
|
101
70
|
dirs?: EggDirInfo[];
|
|
102
71
|
|
|
103
|
-
|
|
104
72
|
/**
|
|
105
73
|
* @class
|
|
106
74
|
* @param {Object} options - options
|
|
@@ -130,12 +98,11 @@ export class EggLoader {
|
|
|
130
98
|
if (process.env.EGG_TYPESCRIPT === 'true' || (this.pkg.egg && this.pkg.egg.typescript)) {
|
|
131
99
|
// skip require tsconfig-paths if tsconfig.json not exists
|
|
132
100
|
const tsConfigFile = path.join(this.options.baseDir, 'tsconfig.json');
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
136
|
-
require('tsconfig-paths').register({ cwd: this.options.baseDir });
|
|
101
|
+
if (fs.existsSync(tsConfigFile)) {
|
|
102
|
+
tsconfigPathsRegister({ cwd: this.options.baseDir } as any);
|
|
137
103
|
} else {
|
|
138
|
-
this.logger.info(
|
|
104
|
+
this.logger.info(
|
|
105
|
+
'[@eggjs/core/egg_loader] skip register "tsconfig-paths" because tsconfig.json not exists at %s',
|
|
139
106
|
tsConfigFile);
|
|
140
107
|
}
|
|
141
108
|
}
|
|
@@ -634,19 +601,19 @@ export class EggLoader {
|
|
|
634
601
|
plugin.version = pkg.version;
|
|
635
602
|
}
|
|
636
603
|
// support commonjs and esm dist files
|
|
637
|
-
plugin.path = this.#formatPluginPathFromPackageJSON(plugin.path!, pkg);
|
|
604
|
+
plugin.path = await this.#formatPluginPathFromPackageJSON(plugin.path!, pkg);
|
|
638
605
|
}
|
|
639
606
|
|
|
640
607
|
const logger = this.options.logger;
|
|
641
608
|
if (!config) {
|
|
642
|
-
logger.warn(`[@eggjs/core
|
|
609
|
+
logger.warn(`[@eggjs/core/egg_loader] pkg.eggPlugin is missing in ${pluginPackage}`);
|
|
643
610
|
return;
|
|
644
611
|
}
|
|
645
612
|
|
|
646
613
|
if (config.name && config.strict !== false && config.name !== plugin.name) {
|
|
647
614
|
// pluginName is configured in config/plugin.js
|
|
648
615
|
// pluginConfigName is pkg.eggPlugin.name
|
|
649
|
-
logger.warn(`[@eggjs/core
|
|
616
|
+
logger.warn(`[@eggjs/core/egg_loader] pluginName(${plugin.name}) is different from pluginConfigName(${config.name})`);
|
|
650
617
|
}
|
|
651
618
|
|
|
652
619
|
// dep compatible
|
|
@@ -781,33 +748,43 @@ export class EggLoader {
|
|
|
781
748
|
const pluginPkgFile = utils.resolvePath(`${name}/package.json`, { paths: [ ...this.lookupDirs ] });
|
|
782
749
|
return path.dirname(pluginPkgFile);
|
|
783
750
|
} catch (err) {
|
|
784
|
-
debug('[resolvePluginPath] error: %o', err);
|
|
751
|
+
debug('[resolvePluginPath] error: %o, plugin info: %o', err, plugin);
|
|
785
752
|
throw new Error(`Can not find plugin ${name} in "${[ ...this.lookupDirs ].join(', ')}"`, {
|
|
786
753
|
cause: err,
|
|
787
754
|
});
|
|
788
755
|
}
|
|
789
756
|
}
|
|
790
757
|
|
|
791
|
-
#formatPluginPathFromPackageJSON(pluginPath: string, pluginPkg: {
|
|
758
|
+
async #formatPluginPathFromPackageJSON(pluginPath: string, pluginPkg: {
|
|
792
759
|
eggPlugin?: {
|
|
793
760
|
exports?: {
|
|
794
761
|
import?: string;
|
|
795
762
|
require?: string;
|
|
763
|
+
typescript?: string;
|
|
796
764
|
};
|
|
797
765
|
};
|
|
798
|
-
}) {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
766
|
+
}): Promise<string> {
|
|
767
|
+
let realPluginPath = pluginPath;
|
|
768
|
+
const exports = pluginPkg.eggPlugin?.exports;
|
|
769
|
+
if (exports) {
|
|
770
|
+
if (isESM) {
|
|
771
|
+
if (exports.import) {
|
|
772
|
+
realPluginPath = path.join(pluginPath, exports.import);
|
|
803
773
|
}
|
|
804
774
|
} else {
|
|
805
|
-
if (
|
|
806
|
-
|
|
775
|
+
if (exports.require) {
|
|
776
|
+
realPluginPath = path.join(pluginPath, exports.require);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (exports.typescript && isSupportTypeScript()) {
|
|
780
|
+
if (!(await exists(realPluginPath))) {
|
|
781
|
+
// if require/import path not exists, use typescript path for development stage
|
|
782
|
+
realPluginPath = path.join(pluginPath, exports.typescript);
|
|
783
|
+
debug('[formatPluginPathFromPackageJSON] use typescript path %o', realPluginPath);
|
|
807
784
|
}
|
|
808
785
|
}
|
|
809
786
|
}
|
|
810
|
-
return
|
|
787
|
+
return realPluginPath;
|
|
811
788
|
}
|
|
812
789
|
|
|
813
790
|
#extendPlugins(targets: Record<string, EggPluginInfo>, plugins: Record<string, EggPluginInfo>) {
|
|
@@ -843,7 +820,7 @@ export class EggLoader {
|
|
|
843
820
|
|
|
844
821
|
/** start Config loader */
|
|
845
822
|
configMeta: Record<string, any>;
|
|
846
|
-
config:
|
|
823
|
+
config: EggAppConfig;
|
|
847
824
|
|
|
848
825
|
/**
|
|
849
826
|
* Load config/config.js
|
|
@@ -857,7 +834,10 @@ export class EggLoader {
|
|
|
857
834
|
this.timing.start('Load Config');
|
|
858
835
|
this.configMeta = {};
|
|
859
836
|
|
|
860
|
-
const target:
|
|
837
|
+
const target: EggAppConfig = {
|
|
838
|
+
middleware: [],
|
|
839
|
+
coreMiddleware: [],
|
|
840
|
+
};
|
|
861
841
|
|
|
862
842
|
// Load Application config first
|
|
863
843
|
const appConfig = await this.#preloadAppConfig();
|
|
@@ -1166,8 +1146,10 @@ export class EggLoader {
|
|
|
1166
1146
|
async #loadBootHook(fileName: string) {
|
|
1167
1147
|
this.timing.start(`Load ${fileName}.js`);
|
|
1168
1148
|
for (const unit of this.getLoadUnits()) {
|
|
1169
|
-
const
|
|
1149
|
+
const bootFile = path.join(unit.path, fileName);
|
|
1150
|
+
const bootFilePath = this.resolveModule(bootFile);
|
|
1170
1151
|
if (!bootFilePath) {
|
|
1152
|
+
// debug('[loadBootHook] %o not found', bootFile);
|
|
1171
1153
|
continue;
|
|
1172
1154
|
}
|
|
1173
1155
|
const bootHook = await this.requireFile(bootFilePath);
|
|
@@ -1175,12 +1157,14 @@ export class EggLoader {
|
|
|
1175
1157
|
bootHook.prototype.fullPath = bootFilePath;
|
|
1176
1158
|
// if is boot class, add to lifecycle
|
|
1177
1159
|
this.lifecycle.addBootHook(bootHook);
|
|
1160
|
+
debug('[loadBootHook] add BootHookClass from %o', bootFilePath);
|
|
1178
1161
|
} else if (typeof bootHook === 'function') {
|
|
1179
1162
|
// if is boot function, wrap to class
|
|
1180
1163
|
// for compatibility
|
|
1181
|
-
this.lifecycle.addFunctionAsBootHook(bootHook);
|
|
1164
|
+
this.lifecycle.addFunctionAsBootHook(bootHook, bootFilePath);
|
|
1165
|
+
debug('[loadBootHook] add bootHookFunction from %o', bootFilePath);
|
|
1182
1166
|
} else {
|
|
1183
|
-
this.options.logger.warn('[@eggjs/core
|
|
1167
|
+
this.options.logger.warn('[@eggjs/core/egg_loader] %s must exports a boot class', bootFilePath);
|
|
1184
1168
|
}
|
|
1185
1169
|
}
|
|
1186
1170
|
// init boots
|
|
@@ -1202,7 +1186,7 @@ export class EggLoader {
|
|
|
1202
1186
|
const servicePaths = this.getLoadUnits().map(unit => path.join(unit.path, 'app/service'));
|
|
1203
1187
|
options = {
|
|
1204
1188
|
call: true,
|
|
1205
|
-
caseStyle:
|
|
1189
|
+
caseStyle: CaseStyle.lower,
|
|
1206
1190
|
fieldClass: 'serviceClasses',
|
|
1207
1191
|
directory: servicePaths,
|
|
1208
1192
|
...options,
|
|
@@ -1242,7 +1226,7 @@ export class EggLoader {
|
|
|
1242
1226
|
opt = {
|
|
1243
1227
|
call: false,
|
|
1244
1228
|
override: true,
|
|
1245
|
-
caseStyle:
|
|
1229
|
+
caseStyle: CaseStyle.lower,
|
|
1246
1230
|
directory: middlewarePaths,
|
|
1247
1231
|
...opt,
|
|
1248
1232
|
};
|
|
@@ -1291,13 +1275,13 @@ export class EggLoader {
|
|
|
1291
1275
|
}
|
|
1292
1276
|
app.use(mw);
|
|
1293
1277
|
debug('[loadMiddleware] Use middleware: %s with options: %j', name, options);
|
|
1294
|
-
this.options.logger.info('[@eggjs/core
|
|
1278
|
+
this.options.logger.info('[@eggjs/core/egg_loader] Use middleware: %s', name);
|
|
1295
1279
|
} else {
|
|
1296
|
-
this.options.logger.info('[@eggjs/core
|
|
1280
|
+
this.options.logger.info('[@eggjs/core/egg_loader] Disable middleware: %s', name);
|
|
1297
1281
|
}
|
|
1298
1282
|
}
|
|
1299
1283
|
|
|
1300
|
-
this.options.logger.info('[@eggjs/core
|
|
1284
|
+
this.options.logger.info('[@eggjs/core/egg_loader] Loaded middleware from %j', middlewarePaths);
|
|
1301
1285
|
this.timing.end('Load Middleware');
|
|
1302
1286
|
|
|
1303
1287
|
// add router middleware, make sure router is the last middleware
|
|
@@ -1317,7 +1301,7 @@ export class EggLoader {
|
|
|
1317
1301
|
this.timing.start('Load Controller');
|
|
1318
1302
|
const controllerBase = path.join(this.options.baseDir, 'app/controller');
|
|
1319
1303
|
opt = {
|
|
1320
|
-
caseStyle:
|
|
1304
|
+
caseStyle: CaseStyle.lower,
|
|
1321
1305
|
directory: controllerBase,
|
|
1322
1306
|
initializer: (obj, opt) => {
|
|
1323
1307
|
// return class if it exports a function
|
|
@@ -1355,7 +1339,7 @@ export class EggLoader {
|
|
|
1355
1339
|
};
|
|
1356
1340
|
await this.loadToApp(controllerBase, 'controller', opt as FileLoaderOptions);
|
|
1357
1341
|
debug('[loadController] app.controller => %o', this.app.controller);
|
|
1358
|
-
this.options.logger.info('[@eggjs/core
|
|
1342
|
+
this.options.logger.info('[@eggjs/core/egg_loader] Controller loaded: %s', controllerBase);
|
|
1359
1343
|
this.timing.end('Load Controller');
|
|
1360
1344
|
}
|
|
1361
1345
|
/** end Controller loader */
|
|
@@ -1397,7 +1381,7 @@ export class EggLoader {
|
|
|
1397
1381
|
case 'ctx': {
|
|
1398
1382
|
assert(!(property in this.app.context), `customLoader should not override ctx.${property}`);
|
|
1399
1383
|
const options = {
|
|
1400
|
-
caseStyle:
|
|
1384
|
+
caseStyle: CaseStyle.lower,
|
|
1401
1385
|
fieldClass: `${property}Classes`,
|
|
1402
1386
|
...loaderConfig,
|
|
1403
1387
|
directory,
|
|
@@ -1408,7 +1392,7 @@ export class EggLoader {
|
|
|
1408
1392
|
case 'app': {
|
|
1409
1393
|
assert(!(property in this.app), `customLoader should not override app.${property}`);
|
|
1410
1394
|
const options = {
|
|
1411
|
-
caseStyle:
|
|
1395
|
+
caseStyle: CaseStyle.lower,
|
|
1412
1396
|
initializer: (Clazz: unknown) => {
|
|
1413
1397
|
return isClass(Clazz) ? new Clazz(this.app) : Clazz;
|
|
1414
1398
|
},
|
|
@@ -1527,10 +1511,11 @@ export class EggLoader {
|
|
|
1527
1511
|
* @param {Object} options - see {@link FileLoader}
|
|
1528
1512
|
* @since 1.0.0
|
|
1529
1513
|
*/
|
|
1530
|
-
async loadToApp(directory: string | string[], property: string | symbol,
|
|
1514
|
+
async loadToApp(directory: string | string[], property: string | symbol,
|
|
1515
|
+
options?: Omit<FileLoaderOptions, 'inject' | 'target'>) {
|
|
1531
1516
|
const target = {};
|
|
1532
1517
|
Reflect.set(this.app, property, target);
|
|
1533
|
-
|
|
1518
|
+
const loadOptions: FileLoaderOptions = {
|
|
1534
1519
|
...options,
|
|
1535
1520
|
directory: options?.directory ?? directory,
|
|
1536
1521
|
target,
|
|
@@ -1539,7 +1524,7 @@ export class EggLoader {
|
|
|
1539
1524
|
|
|
1540
1525
|
const timingKey = `Load "${String(property)}" to Application`;
|
|
1541
1526
|
this.timing.start(timingKey);
|
|
1542
|
-
await new FileLoader(
|
|
1527
|
+
await new FileLoader(loadOptions).load();
|
|
1543
1528
|
this.timing.end(timingKey);
|
|
1544
1529
|
}
|
|
1545
1530
|
|
|
@@ -1550,8 +1535,9 @@ export class EggLoader {
|
|
|
1550
1535
|
* @param {Object} options - see {@link ContextLoader}
|
|
1551
1536
|
* @since 1.0.0
|
|
1552
1537
|
*/
|
|
1553
|
-
async loadToContext(directory: string | string[], property: string | symbol,
|
|
1554
|
-
options
|
|
1538
|
+
async loadToContext(directory: string | string[], property: string | symbol,
|
|
1539
|
+
options?: Omit<ContextLoaderOptions, 'inject' | 'property'>) {
|
|
1540
|
+
const loadOptions: ContextLoaderOptions = {
|
|
1555
1541
|
...options,
|
|
1556
1542
|
directory: options?.directory || directory,
|
|
1557
1543
|
property,
|
|
@@ -1560,7 +1546,7 @@ export class EggLoader {
|
|
|
1560
1546
|
|
|
1561
1547
|
const timingKey = `Load "${String(property)}" to Context`;
|
|
1562
1548
|
this.timing.start(timingKey);
|
|
1563
|
-
await new ContextLoader(
|
|
1549
|
+
await new ContextLoader(loadOptions).load();
|
|
1564
1550
|
this.timing.end(timingKey);
|
|
1565
1551
|
}
|
|
1566
1552
|
|
|
@@ -1595,13 +1581,13 @@ export class EggLoader {
|
|
|
1595
1581
|
let fullPath;
|
|
1596
1582
|
try {
|
|
1597
1583
|
fullPath = utils.resolvePath(filepath);
|
|
1598
|
-
} catch (
|
|
1599
|
-
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
if (process.env.EGG_TYPESCRIPT !== 'true' && fullPath.endsWith('.ts')) {
|
|
1584
|
+
} catch (err: any) {
|
|
1585
|
+
// debug('[resolveModule] Module %o resolve error: %s', filepath, err.stack);
|
|
1603
1586
|
return undefined;
|
|
1604
1587
|
}
|
|
1588
|
+
// if (process.env.EGG_TYPESCRIPT !== 'true' && fullPath.endsWith('.ts')) {
|
|
1589
|
+
// return undefined;
|
|
1590
|
+
// }
|
|
1605
1591
|
return fullPath;
|
|
1606
1592
|
}
|
|
1607
1593
|
}
|
|
@@ -1687,7 +1673,7 @@ function wrapControllerClass(Controller: typeof BaseContextClass, fullPath: stri
|
|
|
1687
1673
|
}
|
|
1688
1674
|
|
|
1689
1675
|
function controllerMethodToMiddleware(Controller: typeof BaseContextClass, key: string) {
|
|
1690
|
-
return function classControllerMiddleware(this:
|
|
1676
|
+
return function classControllerMiddleware(this: Context, ...args: any[]) {
|
|
1691
1677
|
const controller: any = new Controller(this);
|
|
1692
1678
|
if (!this.app.config.controller?.supportParams) {
|
|
1693
1679
|
args = [ this ];
|
|
@@ -1723,7 +1709,7 @@ function wrapObject(obj: Record<string, any>, fullPath: string, prefix?: string)
|
|
|
1723
1709
|
}
|
|
1724
1710
|
|
|
1725
1711
|
function objectFunctionToMiddleware(func: Fun) {
|
|
1726
|
-
async function objectControllerMiddleware(this:
|
|
1712
|
+
async function objectControllerMiddleware(this: Context, ...args: any[]) {
|
|
1727
1713
|
if (!this.app.config.controller?.supportParams) {
|
|
1728
1714
|
args = [ this ];
|
|
1729
1715
|
}
|
|
@@ -6,12 +6,17 @@ import globby from 'globby';
|
|
|
6
6
|
import { isClass, isGeneratorFunction, isAsyncFunction, isPrimitive } from 'is-type-of';
|
|
7
7
|
import utils, { Fun } from '../utils/index.js';
|
|
8
8
|
|
|
9
|
-
const debug = debuglog('@eggjs/core
|
|
9
|
+
const debug = debuglog('@eggjs/core/file_loader');
|
|
10
10
|
|
|
11
11
|
export const FULLPATH = Symbol('EGG_LOADER_ITEM_FULLPATH');
|
|
12
12
|
export const EXPORTS = Symbol('EGG_LOADER_ITEM_EXPORTS');
|
|
13
13
|
|
|
14
|
-
export
|
|
14
|
+
export enum CaseStyle {
|
|
15
|
+
camel = 'camel',
|
|
16
|
+
lower = 'lower',
|
|
17
|
+
upper = 'upper',
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
export type CaseStyleFunction = (filepath: string) => string[];
|
|
16
21
|
export type FileLoaderInitializer = (exports: unknown, options: { path: string; pathName: string }) => unknown;
|
|
17
22
|
export type FileLoaderFilter = (exports: unknown) => boolean;
|
|
@@ -79,7 +84,7 @@ export class FileLoader {
|
|
|
79
84
|
assert(options.directory, 'options.directory is required');
|
|
80
85
|
assert(options.target, 'options.target is required');
|
|
81
86
|
this.options = {
|
|
82
|
-
caseStyle:
|
|
87
|
+
caseStyle: CaseStyle.camel,
|
|
83
88
|
call: true,
|
|
84
89
|
override: false,
|
|
85
90
|
...options,
|
|
@@ -88,7 +93,7 @@ export class FileLoader {
|
|
|
88
93
|
// compatible old options _lowercaseFirst_
|
|
89
94
|
if (this.options.lowercaseFirst === true) {
|
|
90
95
|
utils.deprecated('lowercaseFirst is deprecated, use caseStyle instead');
|
|
91
|
-
this.options.caseStyle =
|
|
96
|
+
this.options.caseStyle = CaseStyle.lower;
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
|
|
@@ -185,6 +190,13 @@ export class FileLoader {
|
|
|
185
190
|
for (const filepath of filepaths) {
|
|
186
191
|
const fullpath = path.join(directory, filepath);
|
|
187
192
|
if (!fs.statSync(fullpath).isFile()) continue;
|
|
193
|
+
if (filepath.endsWith('.js')) {
|
|
194
|
+
const filepathTs = filepath.replace(/\.js$/, '.ts');
|
|
195
|
+
if (filepaths.includes(filepathTs)) {
|
|
196
|
+
debug('[parse] ignore %s, because %s exists', fullpath, filepathTs);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
188
200
|
// get properties
|
|
189
201
|
// app/service/foo/bar.js => [ 'foo', 'bar' ]
|
|
190
202
|
const properties = getProperties(filepath, this.options.caseStyle);
|