@eggjs/core 6.0.0-beta.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +296 -0
  3. package/dist/commonjs/base_context_class.d.ts +16 -0
  4. package/dist/commonjs/base_context_class.js +41 -0
  5. package/dist/commonjs/egg.d.ts +204 -0
  6. package/dist/commonjs/egg.js +346 -0
  7. package/dist/commonjs/index.d.ts +5 -0
  8. package/dist/commonjs/index.js +26 -0
  9. package/dist/commonjs/lifecycle.d.ts +75 -0
  10. package/dist/commonjs/lifecycle.js +306 -0
  11. package/dist/commonjs/loader/context_loader.d.ts +24 -0
  12. package/dist/commonjs/loader/context_loader.js +109 -0
  13. package/dist/commonjs/loader/egg_loader.d.ts +405 -0
  14. package/dist/commonjs/loader/egg_loader.js +1497 -0
  15. package/dist/commonjs/loader/file_loader.d.ts +96 -0
  16. package/dist/commonjs/loader/file_loader.js +248 -0
  17. package/dist/commonjs/package.json +3 -0
  18. package/dist/commonjs/types.d.ts +1 -0
  19. package/dist/commonjs/types.js +403 -0
  20. package/dist/commonjs/utils/index.d.ts +14 -0
  21. package/dist/commonjs/utils/index.js +146 -0
  22. package/dist/commonjs/utils/sequencify.d.ts +13 -0
  23. package/dist/commonjs/utils/sequencify.js +59 -0
  24. package/dist/commonjs/utils/timing.d.ts +22 -0
  25. package/dist/commonjs/utils/timing.js +100 -0
  26. package/dist/esm/base_context_class.d.ts +16 -0
  27. package/dist/esm/base_context_class.js +37 -0
  28. package/dist/esm/egg.d.ts +204 -0
  29. package/dist/esm/egg.js +339 -0
  30. package/dist/esm/index.d.ts +5 -0
  31. package/dist/esm/index.js +6 -0
  32. package/dist/esm/lifecycle.d.ts +75 -0
  33. package/dist/esm/lifecycle.js +276 -0
  34. package/dist/esm/loader/context_loader.d.ts +24 -0
  35. package/dist/esm/loader/context_loader.js +102 -0
  36. package/dist/esm/loader/egg_loader.d.ts +405 -0
  37. package/dist/esm/loader/egg_loader.js +1490 -0
  38. package/dist/esm/loader/file_loader.d.ts +96 -0
  39. package/dist/esm/loader/file_loader.js +241 -0
  40. package/dist/esm/package.json +3 -0
  41. package/dist/esm/types.d.ts +1 -0
  42. package/dist/esm/types.js +402 -0
  43. package/dist/esm/utils/index.d.ts +14 -0
  44. package/dist/esm/utils/index.js +141 -0
  45. package/dist/esm/utils/sequencify.d.ts +13 -0
  46. package/dist/esm/utils/sequencify.js +56 -0
  47. package/dist/esm/utils/timing.d.ts +22 -0
  48. package/dist/esm/utils/timing.js +93 -0
  49. package/package.json +103 -0
  50. package/src/base_context_class.ts +39 -0
  51. package/src/egg.ts +430 -0
  52. package/src/index.ts +6 -0
  53. package/src/lifecycle.ts +363 -0
  54. package/src/loader/context_loader.ts +121 -0
  55. package/src/loader/egg_loader.ts +1703 -0
  56. package/src/loader/file_loader.ts +295 -0
  57. package/src/types.ts +447 -0
  58. package/src/utils/index.ts +154 -0
  59. package/src/utils/sequencify.ts +70 -0
  60. package/src/utils/timing.ts +114 -0
package/src/types.ts ADDED
@@ -0,0 +1,447 @@
1
+ // import type KoaApplication from '@eggjs/koa';
2
+ // // import type depd = require('depd');
3
+ // import type { Logger } from 'egg-logger';
4
+
5
+ // export type EggType = 'application' | 'agent';
6
+
7
+ // interface PlainObject<T = any> {
8
+ // [key: string]: T;
9
+ // }
10
+
11
+ // export interface EggCoreOptions {
12
+ // /** egg type, application or agent */
13
+ // type?: EggType;
14
+ // /** the directory of application */
15
+ // baseDir?: EggAppInfo['baseDir'];
16
+ // /** server scope */
17
+ // serverScope?: string;
18
+ // /** custom plugins */
19
+ // plugins?: Plugins;
20
+ // }
21
+
22
+ // export interface EggLoaderOptions {
23
+ // /** Application instance */
24
+ // app: EggCore;
25
+ // /** the directory of application */
26
+ // baseDir: EggAppInfo['baseDir'];
27
+ // /** egg logger */
28
+ // logger: Logger;
29
+ // /** server scope */
30
+ // serverScope?: string;
31
+ // /** custom plugins */
32
+ // plugins?: Plugins;
33
+ // }
34
+
35
+ // export interface PluginInfo {
36
+ // /** the plugin name, it can be used in `dep` */
37
+ // name: string;
38
+ // /** the package name of plugin */
39
+ // package: string;
40
+ // /** whether enabled */
41
+ // enable: boolean;
42
+ // /** the directory of the plugin package */
43
+ // path: string;
44
+ // /** the dependent plugins, you can use the plugin name */
45
+ // dependencies: string[];
46
+ // /** the optional dependent plugins. */
47
+ // optionalDependencies: string[];
48
+ // /** specify the serverEnv that only enable the plugin in it */
49
+ // env: string[];
50
+ // /** the file plugin config in. */
51
+ // from: string;
52
+ // }
53
+
54
+ // export interface Plugins extends PlainObject<PluginInfo> { }
55
+
56
+ // export interface EggCoreBase<Config> extends KoaApplication {
57
+ // /**
58
+ // * Whether `application` or `agent`
59
+ // * @member {String}
60
+ // * @since 1.0.0
61
+ // */
62
+ // type: EggType;
63
+
64
+ // /**
65
+ // * The current directory of application
66
+ // * @member {String}
67
+ // * @see {@link EggAppInfo#baseDir}
68
+ // * @since 1.0.0
69
+ // */
70
+ // baseDir: EggAppInfo['baseDir'];
71
+
72
+ // /**
73
+ // * The name of application
74
+ // * @member {String}
75
+ // * @see {@link EggAppInfo#name}
76
+ // * @since 1.0.0
77
+ // */
78
+ // name: EggAppInfo['name'];
79
+
80
+ // /**
81
+ // * Convert a generator function to a promisable one.
82
+ // *
83
+ // * Notice: for other kinds of functions, it directly returns you what it is.
84
+ // *
85
+ // * @param {Function} fn The inputted function.
86
+ // * @return {AsyncFunction} An async promise-based function.
87
+ // * @example
88
+ // * ```javascript
89
+ // * const fn = function* (arg) {
90
+ // return arg;
91
+ // };
92
+ // const wrapped = app.toAsyncFunction(fn);
93
+ // wrapped(true).then((value) => console.log(value));
94
+ // * ```
95
+ // */
96
+ // toAsyncFunction<T = any>(fn: (...args: any[]) => IterableIterator<T>): (...args: any[]) => Promise<T>;
97
+
98
+ // /**
99
+ // * Convert an object with generator functions to a Promisable one.
100
+ // * @param {Mixed} obj The inputted object.
101
+ // * @return {Promise} A Promisable result.
102
+ // * @example
103
+ // * ```javascript
104
+ // * const fn = function* (arg) {
105
+ // return arg;
106
+ // };
107
+ // const arr = [ fn(1), fn(2) ];
108
+ // const promise = app.toPromise(arr);
109
+ // promise.then(res => console.log(res));
110
+ // * ```
111
+ // */
112
+ // toPromise<T = any>(obj: any): Promise<T>;
113
+
114
+ // /**
115
+ // * register an callback function that will be invoked when application is ready.
116
+ // * @see https://github.com/node-modules/ready
117
+ // * @since 1.0.0
118
+ // * @param {boolean|Error|Function} flagOrFunction -
119
+ // * @return {Promise|null} return promise when argument is undefined
120
+ // * @example
121
+ // * const app = new Application(...);
122
+ // * app.ready(err => {
123
+ // * if (err) throw err;
124
+ // * console.log('done');
125
+ // * });
126
+ // */
127
+ // ready(fn?: (err?: Error) => void): any;
128
+
129
+ // /**
130
+ // * Close all, it wil close
131
+ // * - callbacks registered by beforeClose
132
+ // * - emit `close` event
133
+ // * - remove add listeners
134
+ // *
135
+ // * If error is thrown when it's closing, the promise will reject.
136
+ // * It will also reject after following call.
137
+ // * @return {Promise} promise
138
+ // * @since 1.0.0
139
+ // */
140
+ // close(): Promise<any>;
141
+
142
+ // /**
143
+ // * If a client starts asynchronously, you can register `readyCallback`,
144
+ // * then the application will wait for the callback to ready
145
+ // *
146
+ // * It will log when the callback is not invoked after 10s
147
+ // *
148
+ // * Recommend to use {@link EggCore#beforeStart}
149
+ // * @since 1.0.0
150
+ // *
151
+ // * @param {String} name - readyCallback task name
152
+ // * @param {object} opts -
153
+ // * - {Number} [timeout=10000] - emit `ready_timeout` when it doesn't finish but reach the timeout
154
+ // * - {Boolean} [isWeakDep=false] - whether it's a weak dependency
155
+ // * @return {Function} - a callback
156
+ // * @example
157
+ // * const done = app.readyCallback('mysql');
158
+ // * mysql.ready(done);
159
+ // */
160
+ // readyCallback(name: string, opts?: { timeout?: number; isWeakDep?: boolean }): () => void;
161
+
162
+ // /**
163
+ // * The loader instance, the default class is {@link EggLoader}.
164
+ // * If you want define
165
+ // * @member {EggLoader} EggCore#loader
166
+ // * @since 1.0.0
167
+ // */
168
+ // loader: EggLoader<this, Config>;
169
+
170
+ // /**
171
+ // * The configuration of application
172
+ // * @member {Config}
173
+ // * @since 1.0.0
174
+ // */
175
+ // config: Config;
176
+
177
+ // /**
178
+ // * Retrieve enabled plugins
179
+ // * @member {Object}
180
+ // * @since 1.0.0
181
+ // */
182
+ // plugins: Plugins;
183
+
184
+ // /**
185
+ // * Register a function that will be called when app close
186
+ // */
187
+ // beforeClose(fn: () => void): void;
188
+
189
+ // /**
190
+ // * Execute scope after loaded and before app start
191
+ // */
192
+ // beforeStart(scope: () => void): void;
193
+
194
+ // /**
195
+ // * Alias to {@link https://npmjs.com/package/depd}
196
+ // * @member {Function}
197
+ // * @since 1.0.0
198
+ // */
199
+ // deprecate: depd.Deprecate;
200
+ // }
201
+
202
+ // export interface EggCore<Config = PlainObject> extends EggCoreBase<Config> {
203
+ // Controller: typeof BaseContextClass;
204
+ // Service: typeof BaseContextClass;
205
+ // }
206
+
207
+ // export class EggCore {
208
+ // /**
209
+ // * @class
210
+ // * @param {Object} options - options
211
+ // * @param {String} [options.baseDir=process.cwd()] - the directory of application
212
+ // * @param {String} [options.type=application|agent] - whether it's running in app worker or agent worker
213
+ // * @param {Object} [options.plugins] - custom plugins
214
+ // * @since 1.0.0
215
+ // */
216
+ // constructor(options?: EggCoreOptions);
217
+ // }
218
+
219
+ // /**
220
+ // * egg app info
221
+ // * @example
222
+ // * ```js
223
+ // * // config/config.default.ts
224
+ // * import { EggAppInfo } from 'egg';
225
+ // *
226
+ // * export default (appInfo: EggAppInfo) => {
227
+ // * return {
228
+ // * keys: appInfo.name + '123456',
229
+ // * };
230
+ // * }
231
+ // * ```
232
+ // */
233
+ // export interface EggAppInfo {
234
+ // /** package.json */
235
+ // pkg: PlainObject;
236
+ // /** the application name from package.json */
237
+ // name: string;
238
+ // /** current directory of application */
239
+ // baseDir: string;
240
+ // /** equals to serverEnv */
241
+ // env: string;
242
+ // /** home directory of the OS */
243
+ // HOME: string;
244
+ // /** baseDir when local and unittest, HOME when other environment */
245
+ // root: string;
246
+ // }
247
+
248
+ // /**
249
+ // * BaseContextClass is a base class that can be extended,
250
+ // * it's instantiated in context level,
251
+ // * {@link Helper}, {@link Service} is extending it.
252
+ // */
253
+ // export class BaseContextClass<
254
+ // Context = any,
255
+ // Application = any,
256
+ // EggAppConfig = any,
257
+ // Service = any
258
+ // > {
259
+ // constructor(ctx: Context);
260
+
261
+ // /** request context */
262
+ // protected ctx: Context;
263
+
264
+ // /** Application */
265
+ // protected app: Application;
266
+
267
+ // /** Application config object */
268
+ // protected config: EggAppConfig;
269
+
270
+ // /** service */
271
+ // protected service: Service;
272
+ // }
273
+
274
+ // declare interface FileLoaderBase {
275
+ // /**
276
+ // * attach items to target object. Mapping the directory to properties.
277
+ // * `app/controller/group/repository.js` => `target.group.repository`
278
+ // * @return {Object} target
279
+ // * @since 1.0.0
280
+ // */
281
+ // load(): object;
282
+
283
+ // /**
284
+ // * Parse files from given directories, then return an items list, each item contains properties and exports.
285
+ // *
286
+ // * For example, parse `app/controller/group/repository.js`
287
+ // *
288
+ // * ```js
289
+ // * module.exports = app => {
290
+ // * return class RepositoryController extends app.Controller {};
291
+ // * }
292
+ // * ```
293
+ // *
294
+ // * It returns a item
295
+ // *
296
+ // * ```js
297
+ // * {
298
+ // * properties: [ 'group', 'repository' ],
299
+ // * exports: app => { ... },
300
+ // * }
301
+ // * ```
302
+ // *
303
+ // * `Properties` is an array that contains the directory of a filepath.
304
+ // *
305
+ // * `Exports` depends on type, if exports is a function, it will be called. if initializer is specified, it will be called with exports for customizing.
306
+ // * @return {Array} items
307
+ // * @since 1.0.0
308
+ // */
309
+ // parse(): Array<{ fullpath: string; properties: string[]; exports: any; }>;
310
+ // }
311
+
312
+ // declare interface ContextLoaderBase extends FileLoaderBase {}
313
+
314
+ // export interface FileLoader {
315
+ // /**
316
+ // * Load files from directory to target object.
317
+ // * @since 1.0.0
318
+ // */
319
+ // new (options: FileLoaderOption): FileLoaderBase;
320
+ // }
321
+
322
+ // export interface ContextLoader {
323
+ // /**
324
+ // * Same as {@link FileLoader}, but it will attach file to `inject[fieldClass]`. The exports will be lazy loaded, such as `ctx.group.repository`.
325
+ // * @augments FileLoader
326
+ // * @since 1.0.0
327
+ // */
328
+ // new (options: ContextLoaderOption): ContextLoaderBase;
329
+ // }
330
+
331
+ // export class EggLoader<
332
+ // T = EggCore,
333
+ // Config = any,
334
+ // Options extends EggLoaderOptions = EggLoaderOptions
335
+ // > {
336
+ // app: T;
337
+ // eggPaths: string[];
338
+ // pkg: PlainObject;
339
+ // appInfo: EggAppInfo;
340
+ // serverScope: string;
341
+ // plugins: Plugins;
342
+ // config: Config;
343
+ // options: Options;
344
+
345
+ // /**
346
+ // * @class
347
+ // * @param {Object} options - options
348
+ // * @param {String} options.baseDir - the directory of application
349
+ // * @param {EggCore} options.app - Application instance
350
+ // * @param {Logger} options.logger - logger
351
+ // * @param {Object} [options.plugins] - custom plugins
352
+ // * @since 1.0.0
353
+ // */
354
+ // constructor(options: EggLoaderOptions);
355
+
356
+ // /**
357
+ // * Get home directory
358
+ // * @return {String} home directory
359
+ // * @since 3.4.0
360
+ // */
361
+ // getHomedir(): EggAppInfo['HOME'];
362
+
363
+ // /**
364
+ // * Get app info
365
+ // * @return {EggAppInfo} appInfo
366
+ // * @since 1.0.0
367
+ // */
368
+ // getAppInfo(): EggAppInfo;
369
+
370
+ // // Low Level API
371
+
372
+ // /**
373
+ // * Load single file, will invoke when export is function
374
+ // *
375
+ // * @param {String} filepath - fullpath
376
+ // * @param {Array} arguments - pass rest arguments into the function when invoke
377
+ // * @return {Object} exports
378
+ // * @example
379
+ // * ```js
380
+ // * app.loader.loadFile(path.join(app.options.baseDir, 'config/router.js'));
381
+ // * ```
382
+ // * @since 1.0.0
383
+ // */
384
+ // loadFile<T = any>(filepath: string, ...inject: any[]): T;
385
+
386
+ // /**
387
+ // * Get all loadUnit
388
+ // *
389
+ // * loadUnit is a directory that can be loaded by EggLoader, it has the same structure.
390
+ // * loadUnit has a path and a type(app, framework, plugin).
391
+ // *
392
+ // * The order of the loadUnits:
393
+ // *
394
+ // * 1. plugin
395
+ // * 2. framework
396
+ // * 3. app
397
+ // *
398
+ // * @return {Array} loadUnits
399
+ // * @since 1.0.0
400
+ // */
401
+ // getLoadUnits(): Array<{ path: string; type: string; }>;
402
+
403
+ // getEggPaths(): string[];
404
+
405
+ // getServerEnv(): string;
406
+
407
+ // /**
408
+ // * Load files using {@link FileLoader}, inject to {@link Application}
409
+ // * @param {String|Array} directory - see {@link FileLoader}
410
+ // * @param {String} property - see {@link FileLoader}
411
+ // * @param {Object} opt - see {@link FileLoader}
412
+ // * @since 1.0.0
413
+ // */
414
+ // loadToApp(directory: string | string[], property: string, opt?: Partial<FileLoaderOption>): void;
415
+
416
+ // /**
417
+ // * Load files using {@link ContextLoader}
418
+ // * @param {String|Array} directory - see {@link ContextLoader}
419
+ // * @param {String} property - see {@link ContextLoader}
420
+ // * @param {Object} opt - see {@link ContextLoader}
421
+ // * @since 1.0.0
422
+ // */
423
+ // loadToContext(directory: string | string[], property: string, opt?: Partial<ContextLoaderOption>): void;
424
+
425
+ // getTypeFiles(filename: string): string[];
426
+ // resolveModule(filepath: string): string | undefined;
427
+
428
+ // FileLoader: FileLoader;
429
+ // ContextLoader: ContextLoader;
430
+
431
+ // // load methods
432
+ // protected loadConfig(): void;
433
+ // protected loadController(opt?: Partial<FileLoaderOption>): void;
434
+ // protected loadCustomLoader(): void;
435
+ // protected loadCustomApp(): void;
436
+ // protected loadCustomAgent(): void;
437
+ // protected loadAgentExtend(): void;
438
+ // protected loadApplicationExtend(): void;
439
+ // protected loadRequestExtend(): void;
440
+ // protected loadResponseExtend(): void;
441
+ // protected loadContextExtend(): void;
442
+ // protected loadHelperExtend(): void;
443
+ // protected loadMiddleware(opt?: Partial<FileLoaderOption>): void;
444
+ // protected loadPlugin(): void;
445
+ // protected loadRouter(): void;
446
+ // protected loadService(opt?: Partial<ContextLoaderOption>): void;
447
+ // }
@@ -0,0 +1,154 @@
1
+ import { debuglog } from 'node:util';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import { pathToFileURL } from 'node:url';
5
+ import BuiltinModule from 'node:module';
6
+ import { createRequire } from 'node:module';
7
+
8
+ const debug = debuglog('@eggjs/core:utils');
9
+
10
+ export type Fun = (...args: any[]) => any;
11
+
12
+ // Guard against poorly mocked module constructors.
13
+ const Module = typeof module !== 'undefined' && module.constructor.length > 1
14
+ ? module.constructor
15
+ /* istanbul ignore next */
16
+ : BuiltinModule;
17
+
18
+ const extensions = (Module as any)._extensions;
19
+ const extensionNames = Object.keys(extensions).concat([ '.cjs', '.mjs' ]);
20
+ debug('Module extensions: %j', extensionNames);
21
+
22
+ let _customRequire: NodeRequire;
23
+ function getCustomRequire() {
24
+ if (!_customRequire && typeof require === 'undefined') {
25
+ _customRequire = createRequire(process.cwd());
26
+ // _customRequire = createRequire(import.meta.url);
27
+ }
28
+ return _customRequire;
29
+ }
30
+
31
+ export default {
32
+ deprecated(message: string) {
33
+ console.warn('[@eggjs/core:deprecated] %s', message);
34
+ },
35
+
36
+ extensions,
37
+
38
+ // async _importOrRequire(filepath: string) {
39
+ // // try import first
40
+ // let obj: any;
41
+ // try {
42
+ // obj = await import(filepath);
43
+ // } catch (err: any) {
44
+ // debug('await import error, use require instead, %s', err);
45
+ // // use custom require
46
+ // obj = getCustomRequire()(filepath);
47
+ // }
48
+ // return obj;
49
+ // },
50
+
51
+ async loadFile(filepath: string) {
52
+ try {
53
+ // if not js module, just return content buffer
54
+ const extname = path.extname(filepath);
55
+ if (extname && !extensionNames.includes(extname)) {
56
+ return fs.readFileSync(filepath);
57
+ }
58
+ let obj: any;
59
+ let isESM = false;
60
+ if (typeof require === 'function') {
61
+ // commonjs
62
+ obj = require(filepath);
63
+ debug('[loadFile] require %s => %o', filepath, obj);
64
+ if (obj && obj.__esModule) {
65
+ isESM = true;
66
+ }
67
+ } else {
68
+ // esm
69
+ debug('[loadFile] await import start: %s', filepath);
70
+ const fileUrl = pathToFileURL(filepath).toString();
71
+ obj = await import(fileUrl);
72
+ debug('[loadFile] await import end: %s => %o', filepath, obj);
73
+ isESM = true;
74
+ if (obj && typeof obj === 'object' && 'default' in obj) {
75
+ // default: { default: [Function (anonymous)] }
76
+ obj = obj.default;
77
+ }
78
+ }
79
+ if (!obj) return obj;
80
+ // it's es module, use default export
81
+ if (isESM && typeof obj === 'object') {
82
+ obj = 'default' in obj ? obj.default : obj;
83
+ }
84
+ debug('[loadFile] return %s => %o', filepath, obj);
85
+ return obj;
86
+ } catch (e: any) {
87
+ const err = new Error(`[@eggjs/core] load file: ${filepath}, error: ${e.message}`);
88
+ err.cause = e;
89
+ debug('[loadFile] handle %s error: %s', filepath, e);
90
+ throw err;
91
+ }
92
+ },
93
+
94
+ resolvePath(filepath: string, options?: { paths?: string[] }) {
95
+ if (typeof require !== 'undefined') {
96
+ return require.resolve(filepath, options);
97
+ }
98
+ return getCustomRequire().resolve(filepath, options);
99
+ },
100
+
101
+ methods: [ 'head', 'options', 'get', 'put', 'patch', 'post', 'delete' ],
102
+
103
+ async callFn(fn: Fun, args?: any[], ctx?: any) {
104
+ args = args || [];
105
+ if (typeof fn !== 'function') return;
106
+ return ctx ? fn.call(ctx, ...args) : fn(...args);
107
+ },
108
+
109
+ getCalleeFromStack(withLine?: boolean, stackIndex?: number) {
110
+ stackIndex = stackIndex === undefined ? 2 : stackIndex;
111
+ const limit = Error.stackTraceLimit;
112
+ const prep = Error.prepareStackTrace;
113
+
114
+ Error.prepareStackTrace = prepareObjectStackTrace;
115
+ Error.stackTraceLimit = 5;
116
+
117
+ // capture the stack
118
+ const obj: any = {};
119
+ Error.captureStackTrace(obj);
120
+ let callSite = obj.stack[stackIndex];
121
+ let fileName = '';
122
+ if (callSite) {
123
+ // egg-mock will create a proxy
124
+ // https://github.com/eggjs/egg-mock/blob/master/lib/app.js#L174
125
+ fileName = callSite.getFileName();
126
+ /* istanbul ignore if */
127
+ if (fileName && fileName.endsWith('egg-mock/lib/app.js')) {
128
+ // TODO: add test
129
+ callSite = obj.stack[stackIndex + 1];
130
+ fileName = callSite.getFileName();
131
+ }
132
+ }
133
+
134
+ Error.prepareStackTrace = prep;
135
+ Error.stackTraceLimit = limit;
136
+
137
+ if (!callSite || !fileName) return '<anonymous>';
138
+ if (!withLine) return fileName;
139
+ return `${fileName}:${callSite.getLineNumber()}:${callSite.getColumnNumber()}`;
140
+ },
141
+
142
+ getResolvedFilename(filepath: string, baseDir: string) {
143
+ const reg = /[/\\]/g;
144
+ return filepath.replace(baseDir + path.sep, '').replace(reg, '/');
145
+ },
146
+ };
147
+
148
+ /**
149
+ * Capture call site stack from v8.
150
+ * https://github.com/v8/v8/wiki/Stack-Trace-API
151
+ */
152
+ function prepareObjectStackTrace(_obj: any, stack: any) {
153
+ return stack;
154
+ }
@@ -0,0 +1,70 @@
1
+ import { debuglog } from 'node:util';
2
+
3
+ const debug = debuglog('@eggjs/core:utils:sequencify');
4
+
5
+ export interface SequencifyResult {
6
+ sequence: string[];
7
+ requires: Record<string, true>;
8
+ }
9
+
10
+ export interface SequencifyTask {
11
+ dependencies: string[];
12
+ optionalDependencies: string[];
13
+ }
14
+
15
+ function sequence(tasks: Record<string, SequencifyTask>, names: string[], result: SequencifyResult,
16
+ missing: string[], recursive: string[],
17
+ nest: string[], optional: boolean, parent: string) {
18
+ names.forEach(function(name) {
19
+ if (result.requires[name]) return;
20
+
21
+ const node = tasks[name];
22
+ if (!node) {
23
+ if (optional === true) return;
24
+ missing.push(name);
25
+ } else if (nest.includes(name)) {
26
+ nest.push(name);
27
+ recursive.push(...nest.slice(0));
28
+ nest.pop();
29
+ } else if (node.dependencies.length || node.optionalDependencies.length) {
30
+ nest.push(name);
31
+ if (node.dependencies.length) {
32
+ sequence(tasks, node.dependencies, result, missing, recursive, nest, optional, name);
33
+ }
34
+ if (node.optionalDependencies.length) {
35
+ sequence(tasks, node.optionalDependencies, result, missing, recursive, nest, true, name);
36
+ }
37
+ nest.pop();
38
+ }
39
+ if (!optional) {
40
+ result.requires[name] = true;
41
+ debug('task: %s is enabled by %s', name, parent);
42
+ }
43
+ if (!result.sequence.includes(name)) {
44
+ result.sequence.push(name);
45
+ }
46
+ });
47
+ }
48
+
49
+ // tasks: object with keys as task names
50
+ // names: array of task names
51
+ export default function sequencify(tasks: Record<string, SequencifyTask>, names: string[]) {
52
+ const result: SequencifyResult = {
53
+ sequence: [],
54
+ requires: {},
55
+ }; // the final sequence
56
+ const missing: string[] = []; // missing tasks
57
+ const recursive: string[] = []; // recursive task dependencies
58
+
59
+ sequence(tasks, names, result, missing, recursive, [], false, 'app');
60
+
61
+ if (missing.length || recursive.length) {
62
+ result.sequence = []; // results are incomplete at best, completely wrong at worst, remove them to avoid confusion
63
+ }
64
+
65
+ return {
66
+ sequence: result.sequence.filter(item => result.requires[item]),
67
+ missingTasks: missing,
68
+ recursiveDependencies: recursive,
69
+ };
70
+ }