@eggjs/development 4.0.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/dist/commonjs/agent.d.ts +7 -0
  4. package/dist/commonjs/agent.js +98 -0
  5. package/dist/commonjs/app/middleware/egg_loader_trace.d.ts +3 -0
  6. package/dist/commonjs/app/middleware/egg_loader_trace.js +44 -0
  7. package/dist/commonjs/app.d.ts +6 -0
  8. package/dist/commonjs/app.js +17 -0
  9. package/dist/commonjs/config/config.default.d.ts +15 -0
  10. package/dist/commonjs/config/config.default.js +24 -0
  11. package/dist/commonjs/config/loader_trace.html +49 -0
  12. package/dist/commonjs/index.d.ts +1 -0
  13. package/dist/commonjs/index.js +4 -0
  14. package/dist/commonjs/package.json +3 -0
  15. package/dist/commonjs/types.d.ts +35 -0
  16. package/dist/commonjs/types.js +3 -0
  17. package/dist/commonjs/utils.d.ts +3 -0
  18. package/dist/commonjs/utils.js +25 -0
  19. package/dist/esm/agent.d.ts +7 -0
  20. package/dist/esm/agent.js +92 -0
  21. package/dist/esm/app/middleware/egg_loader_trace.d.ts +3 -0
  22. package/dist/esm/app/middleware/egg_loader_trace.js +39 -0
  23. package/dist/esm/app.d.ts +6 -0
  24. package/dist/esm/app.js +14 -0
  25. package/dist/esm/config/config.default.d.ts +15 -0
  26. package/dist/esm/config/config.default.js +22 -0
  27. package/dist/esm/config/loader_trace.html +49 -0
  28. package/dist/esm/index.d.ts +1 -0
  29. package/dist/esm/index.js +2 -0
  30. package/dist/esm/package.json +3 -0
  31. package/dist/esm/types.d.ts +35 -0
  32. package/dist/esm/types.js +2 -0
  33. package/dist/esm/utils.d.ts +3 -0
  34. package/dist/esm/utils.js +17 -0
  35. package/dist/package.json +4 -0
  36. package/package.json +98 -0
  37. package/src/agent.ts +106 -0
  38. package/src/app/middleware/egg_loader_trace.ts +39 -0
  39. package/src/app.ts +17 -0
  40. package/src/config/config.default.ts +23 -0
  41. package/src/config/loader_trace.html +49 -0
  42. package/src/index.ts +1 -0
  43. package/src/types.ts +37 -0
  44. package/src/typings/index.d.ts +4 -0
  45. package/src/utils.ts +19 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Alibaba Group Holding Limited and other contributors.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @eggjs/development
2
+
3
+ [![NPM version][npm-image]][npm-url]
4
+ [![Node.js CI](https://github.com/eggjs/egg-development/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/egg-development/actions/workflows/nodejs.yml)
5
+ [![Test coverage][codecov-image]][codecov-url]
6
+ [![Known Vulnerabilities][snyk-image]][snyk-url]
7
+ [![npm download][download-image]][download-url]
8
+ [![Node.js Version](https://img.shields.io/node/v/egg-development.svg?style=flat)](https://nodejs.org/en/download/)
9
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
10
+
11
+ [npm-image]: https://img.shields.io/npm/v/egg-development.svg?style=flat-square
12
+ [npm-url]: https://npmjs.org/package/egg-development
13
+ [codecov-image]: https://img.shields.io/codecov/c/github/eggjs/egg-development.svg?style=flat-square
14
+ [codecov-url]: https://codecov.io/github/eggjs/egg-development?branch=master
15
+ [snyk-image]: https://snyk.io/test/npm/egg-development/badge.svg?style=flat-square
16
+ [snyk-url]: https://snyk.io/test/npm/egg-development
17
+ [download-image]: https://img.shields.io/npm/dm/egg-development.svg?style=flat-square
18
+ [download-url]: https://npmjs.org/package/egg-development
19
+
20
+ This is an egg plugin for local development, under development environment enabled by default, and closed under other environment.
21
+
22
+ `@eggjs/development` has been built-in for egg. It is enabled by default.
23
+
24
+ ## Requirements
25
+
26
+ - egg >= 4.x
27
+
28
+ ## Configuration
29
+
30
+ see [config/config.default.ts](https://github.com/eggjs/egg-development/blob/master/src/config/config.default.ts) for more detail.
31
+
32
+ ## Features
33
+
34
+ - Under development environment, Output request log in STDOUT, statistic and output all key parts time-consuming;
35
+ - Watch file changes, and reload application;
36
+
37
+ ### About Reload
38
+
39
+ Under the following directory (including subdirectories) will watch file changes under development environment by default, trigger an Egg development environment server reload:
40
+
41
+ - ${app_root}/app
42
+ - ${app_root}/config
43
+ - ${app_root}/mocks
44
+ - ${app_root}/mocks_proxy
45
+ - ${app_root}/app.js
46
+
47
+ > set `config.development.overrideDefault` to `true` to skip defaults merge.
48
+
49
+ Under the following directory (including subdirectories) will ignore file changes under development environment by default:
50
+
51
+ - ${app_root}/app/view
52
+ - ${app_root}/app/assets
53
+ - ${app_root}/app/public
54
+ - ${app_root}/app/web
55
+
56
+ > set `config.development.overrideIgnore` to `true` to skip defaults merge.
57
+
58
+ Developer can use `config.reloadPattern`([multimatch](https://github.com/sindresorhus/multimatch)) to control whether to reload.
59
+
60
+ ```ts
61
+ // config/config.default.ts
62
+ export default = {
63
+ development: {
64
+ // don't reload when css fileChanged
65
+ // https://github.com/sindresorhus/multimatch
66
+ reloadPattern: ['**', '!**/*.css'],
67
+ },
68
+ };
69
+ ```
70
+
71
+ ### Loader Trace
72
+
73
+ You can view loader trace for performance issue from `http://127.0.0.1:7001/__loader_trace__`
74
+
75
+ ## Questions & Suggestions
76
+
77
+ Please open an issue [here](https://github.com/eggjs/egg/issues).
78
+
79
+ ## License
80
+
81
+ [MIT](LICENSE)
82
+
83
+ ## Contributors
84
+
85
+ [![Contributors](https://contrib.rocks/image?repo=eggjs/development)](https://github.com/eggjs/development/graphs/contributors)
86
+
87
+ Made with [contributors-img](https://contrib.rocks).
@@ -0,0 +1,7 @@
1
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
2
+ export default class AgentBoot implements ILifecycleBoot {
3
+ #private;
4
+ constructor(agent: EggCore);
5
+ didLoad(): Promise<void>;
6
+ serverDidReady(): Promise<void>;
7
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_path_1 = __importDefault(require("node:path"));
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const debounce_1 = __importDefault(require("debounce"));
9
+ const multimatch_1 = __importDefault(require("multimatch"));
10
+ const utility_1 = require("utility");
11
+ const utils_js_1 = require("./utils.js");
12
+ class AgentBoot {
13
+ #agent;
14
+ constructor(agent) {
15
+ this.#agent = agent;
16
+ }
17
+ async didLoad() {
18
+ // clean all timing json
19
+ const rundir = this.#agent.config.rundir;
20
+ const stat = await (0, utility_1.exists)(rundir);
21
+ if (!stat)
22
+ return;
23
+ const files = await promises_1.default.readdir(rundir);
24
+ for (const file of files) {
25
+ if (!(0, utils_js_1.isTimingFile)(file))
26
+ continue;
27
+ await promises_1.default.rm(node_path_1.default.join(rundir, file), { force: true, recursive: true });
28
+ }
29
+ }
30
+ async serverDidReady() {
31
+ const agent = this.#agent;
32
+ // single process mode don't watch and reload
33
+ if (agent.options && Reflect.get(agent.options, 'mode') === 'single') {
34
+ return;
35
+ }
36
+ const logger = agent.logger;
37
+ const baseDir = agent.config.baseDir;
38
+ const config = agent.config.development;
39
+ let watchDirs = config.overrideDefault ? [] : [
40
+ 'app',
41
+ 'config',
42
+ 'mocks',
43
+ 'mocks_proxy',
44
+ 'app.js',
45
+ ];
46
+ watchDirs = watchDirs.concat(config.watchDirs).map(dir => node_path_1.default.resolve(baseDir, dir));
47
+ let ignoreReloadFileDirs = config.overrideIgnore ? [] : [
48
+ 'app/views',
49
+ 'app/view',
50
+ 'app/assets',
51
+ 'app/public',
52
+ 'app/web',
53
+ ];
54
+ ignoreReloadFileDirs = ignoreReloadFileDirs.concat(config.ignoreDirs).map(dir => node_path_1.default.resolve(baseDir, dir));
55
+ const reloadFile = (0, debounce_1.default)(function (info) {
56
+ logger.warn(`[agent:development] reload worker because ${info.path} ${info.event}`);
57
+ process.send({
58
+ to: 'master',
59
+ action: 'reload-worker',
60
+ });
61
+ }, 200);
62
+ // watch dirs to reload worker, will debounce 200ms
63
+ /**
64
+ * reload app worker:
65
+ * [AgentWorker] - on file change
66
+ * |-> emit reload-worker
67
+ * [Master] - receive reload-worker event
68
+ * |-> TODO: Mark worker will die
69
+ * |-> Fork new worker
70
+ * |-> kill old worker
71
+ *
72
+ * @param {Object} info - changed fileInfo
73
+ */
74
+ agent.watcher.watch(watchDirs, info => {
75
+ if (!config.reloadOnDebug) {
76
+ return;
77
+ }
78
+ if (isAssetsDir(info.path) || info.isDirectory) {
79
+ return;
80
+ }
81
+ // don't reload if don't match
82
+ if (config.reloadPattern && (0, multimatch_1.default)(info.path, config.reloadPattern).length === 0) {
83
+ return;
84
+ }
85
+ reloadFile(info);
86
+ });
87
+ function isAssetsDir(filepath) {
88
+ for (const ignorePath of ignoreReloadFileDirs) {
89
+ if (filepath.startsWith(ignorePath)) {
90
+ return true;
91
+ }
92
+ }
93
+ return false;
94
+ }
95
+ }
96
+ }
97
+ exports.default = AgentBoot;
98
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSwwREFBNkI7QUFDN0IsZ0VBQWtDO0FBQ2xDLHdEQUFnQztBQUNoQyw0REFBb0M7QUFDcEMscUNBQWlDO0FBRWpDLHlDQUEwQztBQUUxQyxNQUFxQixTQUFTO0lBQzVCLE1BQU0sQ0FBVTtJQUVoQixZQUFZLEtBQWM7UUFDeEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7SUFDdEIsQ0FBQztJQUVELEtBQUssQ0FBQyxPQUFPO1FBQ1gsd0JBQXdCO1FBQ3hCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUN6QyxNQUFNLElBQUksR0FBRyxNQUFNLElBQUEsZ0JBQU0sRUFBQyxNQUFNLENBQUMsQ0FBQztRQUNsQyxJQUFJLENBQUMsSUFBSTtZQUFFLE9BQU87UUFDbEIsTUFBTSxLQUFLLEdBQUcsTUFBTSxrQkFBRSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN2QyxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQyxJQUFBLHVCQUFZLEVBQUMsSUFBSSxDQUFDO2dCQUFFLFNBQVM7WUFDbEMsTUFBTSxrQkFBRSxDQUFDLEVBQUUsQ0FBQyxtQkFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3pFLENBQUM7SUFDSCxDQUFDO0lBRUQsS0FBSyxDQUFDLGNBQWM7UUFDbEIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUMxQiw2Q0FBNkM7UUFDN0MsSUFBSSxLQUFLLENBQUMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FB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
@@ -0,0 +1,3 @@
1
+ import type { EggCore, MiddlewareFunc } from '@eggjs/core';
2
+ declare const _default: (_: unknown, app: EggCore) => MiddlewareFunc;
3
+ export default _default;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_path_1 = __importDefault(require("node:path"));
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const utility_1 = require("utility");
9
+ const utils_js_1 = require("../../utils.js");
10
+ exports.default = (_, app) => {
11
+ return async (ctx, next) => {
12
+ if (ctx.path !== '/__loader_trace__') {
13
+ return await next();
14
+ }
15
+ const template = await promises_1.default.readFile((0, utils_js_1.getSourceFile)('config/loader_trace.html'), 'utf8');
16
+ const data = await loadTimingData(app);
17
+ ctx.body = template.replace('{{placeholder}}', JSON.stringify(data));
18
+ };
19
+ };
20
+ async function loadTimingData(app) {
21
+ const rundir = app.config.rundir;
22
+ const files = await promises_1.default.readdir(rundir);
23
+ const data = [];
24
+ for (const file of files) {
25
+ if (!(0, utils_js_1.isTimingFile)(file))
26
+ continue;
27
+ const json = await (0, utility_1.readJSON)(node_path_1.default.join(rundir, file));
28
+ const isAgent = /^agent/.test(file);
29
+ for (const item of json) {
30
+ if (isAgent) {
31
+ item.type = 'agent';
32
+ }
33
+ else {
34
+ item.type = `app_${item.pid}`;
35
+ }
36
+ item.pid = String(item.pid);
37
+ item.range = [item.start, item.end];
38
+ item.title = `${item.type}(${item.index})`;
39
+ data.push(item);
40
+ }
41
+ }
42
+ return data;
43
+ }
44
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWdnX2xvYWRlcl90cmFjZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9hcHAvbWlkZGxld2FyZS9lZ2dfbG9hZGVyX3RyYWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsMERBQTZCO0FBQzdCLGdFQUFrQztBQUNsQyxxQ0FBbUM7QUFFbkMsNkNBQTZEO0FBRTdELGtCQUFlLENBQUMsQ0FBVSxFQUFFLEdBQVksRUFBa0IsRUFBRTtJQUMxRCxPQUFPLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7UUFDekIsSUFBSSxHQUFHLENBQUMsSUFBSSxLQUFLLG1CQUFtQixFQUFFLENBQUM7WUFDckMsT0FBTyxNQUFNLElBQUksRUFBRSxDQUFDO1FBQ3RCLENBQUM7UUFDRCxNQUFNLFFBQVEsR0FBRyxNQUFNLGtCQUFFLENBQUMsUUFBUSxDQUFDLElBQUEsd0JBQWEsRUFBQywwQkFBMEIsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3RGLE1BQU0sSUFBSSxHQUFHLE1BQU0sY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZDLEdBQUcsQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDdkUsQ0FBQyxDQUFDO0FBQ0osQ0FBQyxDQUFDO0FBRUYsS0FBSyxVQUFVLGNBQWMsQ0FBQyxHQUFZO0lBQ3hDLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ2pDLE1BQU0sS0FBSyxHQUFHLE1BQU0sa0JBQUUsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdkMsTUFBTSxJQUFJLEdBQVUsRUFBRSxDQUFDO0lBQ3ZCLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFLENBQUM7UUFDekIsSUFBSSxDQUFDLElBQUEsdUJBQVksRUFBQyxJQUFJLENBQUM7WUFBRSxTQUFTO1FBQ2xDLE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBQSxrQkFBUSxFQUFDLG1CQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3JELE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDcEMsS0FBSyxNQUFNLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQztZQUN4QixJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUNaLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDO1lBQ3RCLENBQUM7aUJBQU0sQ0FBQztnQkFDTixJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ2hDLENBQUM7WUFDRCxJQUFJLENBQUMsR0FBRyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDNUIsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxLQUFLLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxLQUFLLEdBQUcsQ0FBQztZQUMzQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2xCLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDIn0=
@@ -0,0 +1,6 @@
1
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
2
+ export default class AppBoot implements ILifecycleBoot {
3
+ #private;
4
+ constructor(app: EggCore);
5
+ configWillLoad(): Promise<void>;
6
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class AppBoot {
4
+ #app;
5
+ constructor(app) {
6
+ this.#app = app;
7
+ // if true, then don't need to wait at local development mode
8
+ if (app.config.development.fastReady) {
9
+ process.nextTick(() => this.#app.ready(true));
10
+ }
11
+ }
12
+ async configWillLoad() {
13
+ this.#app.config.coreMiddleware.push('eggLoaderTrace');
14
+ }
15
+ }
16
+ exports.default = AppBoot;
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLE1BQXFCLE9BQU87SUFDMUIsSUFBSSxDQUFVO0lBRWQsWUFBWSxHQUFZO1FBQ3RCLElBQUksQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDO1FBQ2hCLDZEQUE2RDtRQUM3RCxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ3JDLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNoRCxDQUFDO0lBQ0gsQ0FBQztJQUVELEtBQUssQ0FBQyxjQUFjO1FBQ2xCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUN6RCxDQUFDO0NBQ0Y7QUFkRCwwQkFjQyJ9
@@ -0,0 +1,15 @@
1
+ import type { DevelopmentConfig } from '../types.js';
2
+ /**
3
+ * @member Config#development
4
+ * @property {Array} watchDirs - dirs needed watch, when files under these change, application will reload, use relative path
5
+ * @property {Array} ignoreDirs - dirs don't need watch, including subdirectories, use relative path
6
+ * @property {Boolean} fastReady - don't wait all plugins ready, default is false.
7
+ * @property {Boolean} reloadOnDebug - whether reload on debug, default is true.
8
+ * @property {Boolean} overrideDefault - whether override default watchDirs, default is false.
9
+ * @property {Boolean} overrideIgnore - whether override default ignoreDirs, default is false.
10
+ * @property {Array|String} reloadPattern - whether to reload, use https://github.com/sindresorhus/multimatch
11
+ */
12
+ declare const _default: {
13
+ development: DevelopmentConfig;
14
+ };
15
+ export default _default;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * @member Config#development
5
+ * @property {Array} watchDirs - dirs needed watch, when files under these change, application will reload, use relative path
6
+ * @property {Array} ignoreDirs - dirs don't need watch, including subdirectories, use relative path
7
+ * @property {Boolean} fastReady - don't wait all plugins ready, default is false.
8
+ * @property {Boolean} reloadOnDebug - whether reload on debug, default is true.
9
+ * @property {Boolean} overrideDefault - whether override default watchDirs, default is false.
10
+ * @property {Boolean} overrideIgnore - whether override default ignoreDirs, default is false.
11
+ * @property {Array|String} reloadPattern - whether to reload, use https://github.com/sindresorhus/multimatch
12
+ */
13
+ exports.default = {
14
+ development: {
15
+ watchDirs: [],
16
+ ignoreDirs: [],
17
+ fastReady: false,
18
+ reloadOnDebug: true,
19
+ overrideDefault: false,
20
+ overrideIgnore: false,
21
+ reloadPattern: undefined,
22
+ },
23
+ };
24
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmRlZmF1bHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29uZmlnL2NvbmZpZy5kZWZhdWx0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBRUE7Ozs7Ozs7OztHQVNHO0FBQ0gsa0JBQWU7SUFDYixXQUFXLEVBQUU7UUFDWCxTQUFTLEVBQUUsRUFBRTtRQUNiLFVBQVUsRUFBRSxFQUFFO1FBQ2QsU0FBUyxFQUFFLEtBQUs7UUFDaEIsYUFBYSxFQUFFLElBQUk7UUFDbkIsZUFBZSxFQUFFLEtBQUs7UUFDdEIsY0FBYyxFQUFFLEtBQUs7UUFDckIsYUFBYSxFQUFFLFNBQVM7S0FDSjtDQUN2QixDQUFDIn0=
@@ -0,0 +1,49 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title></title>
5
+ </head>
6
+ <body>
7
+ <div id="mountNode"></div>
8
+ <script src="https://gw.alipayobjects.com/os/antv/assets/g2/3.0.9/g2.min.js"></script>
9
+ <script>
10
+ var data = {{placeholder}};
11
+
12
+ const chart = new G2.Chart({
13
+ container: 'mountNode', // 指定图表容器 ID
14
+ height: data.length * 22, // 指定图表高度
15
+ forceFit: true,
16
+ padding: 'auto',
17
+ });
18
+
19
+ // Step 2: 载入数据源
20
+ chart.source(data, {
21
+ range: {
22
+ type: 'time',
23
+ mask: 'HH:mm:ss',
24
+ nice: true,
25
+ },
26
+ });
27
+ chart
28
+ .coord()
29
+ .transpose()
30
+ .scale(1, -1);
31
+
32
+ chart.legend({ position: 'top' });
33
+
34
+ chart.tooltip({
35
+ showTitle: false,
36
+ itemTpl: '<li>{duration}ms: {name} </li>'
37
+ });
38
+
39
+ chart
40
+ .interval()
41
+ .position('title*range')
42
+ .color('type')
43
+ .tooltip('name*duration', (name, duration) => ({ name, duration }))
44
+ .size(5);
45
+
46
+ chart.render();
47
+ </script>
48
+ </body>
49
+ </html>
@@ -0,0 +1 @@
1
+ import './types.js';
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("./types.js");
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxzQkFBb0IifQ==
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,35 @@
1
+ export interface DevelopmentConfig {
2
+ /**
3
+ * dirs needed watch, when files under these change, application will reload, use relative path
4
+ */
5
+ watchDirs: string[];
6
+ /**
7
+ * dirs don't need watch, including subdirectories, use relative path
8
+ */
9
+ ignoreDirs: string[];
10
+ /**
11
+ * don't wait all plugins ready, default is false.
12
+ */
13
+ fastReady: boolean;
14
+ /**
15
+ * whether reload on debug, default is true.
16
+ */
17
+ reloadOnDebug: boolean;
18
+ /**
19
+ * whether override default watchDirs, default is false.
20
+ */
21
+ overrideDefault: boolean;
22
+ /**
23
+ * whether override default ignoreDirs, default is false.
24
+ */
25
+ overrideIgnore: boolean;
26
+ /**
27
+ * whether to reload, use https://github.com/sindresorhus/multimatch
28
+ */
29
+ reloadPattern?: string[] | string;
30
+ }
31
+ declare module '@eggjs/core' {
32
+ interface EggAppConfig {
33
+ development: DevelopmentConfig;
34
+ }
35
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
@@ -0,0 +1,3 @@
1
+ export declare function getSourceDirname(): string;
2
+ export declare function getSourceFile(filename: string): string;
3
+ export declare function isTimingFile(file: string): boolean;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getSourceDirname = getSourceDirname;
7
+ exports.getSourceFile = getSourceFile;
8
+ exports.isTimingFile = isTimingFile;
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const node_url_1 = require("node:url");
11
+ function getSourceDirname() {
12
+ if (typeof __dirname !== 'undefined') {
13
+ return node_path_1.default.dirname(__dirname);
14
+ }
15
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
16
+ // @ts-ignore
17
+ return node_path_1.default.dirname((0, node_url_1.fileURLToPath)('import_meta_url_placeholder_by_tshy_after'));
18
+ }
19
+ function getSourceFile(filename) {
20
+ return node_path_1.default.join(getSourceDirname(), filename);
21
+ }
22
+ function isTimingFile(file) {
23
+ return /^(agent|application)_timing/.test(file);
24
+ }
25
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFHQSw0Q0FPQztBQUVELHNDQUVDO0FBRUQsb0NBRUM7QUFsQkQsMERBQTZCO0FBQzdCLHVDQUF5QztBQUV6QyxTQUFnQixnQkFBZ0I7SUFDOUIsSUFBSSxPQUFPLFNBQVMsS0FBSyxXQUFXLEVBQUUsQ0FBQztRQUNyQyxPQUFPLG1CQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2pDLENBQUM7SUFDRCw2REFBNkQ7SUFDN0QsYUFBYTtJQUNiLE9BQU8sbUJBQUksQ0FBQyxPQUFPLENBQUMsSUFBQSx3QkFBYSxFQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUN0RCxDQUFDO0FBRUQsU0FBZ0IsYUFBYSxDQUFDLFFBQWdCO0lBQzVDLE9BQU8sbUJBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FBQztBQUNqRCxDQUFDO0FBRUQsU0FBZ0IsWUFBWSxDQUFDLElBQVk7SUFDdkMsT0FBTyw2QkFBNkIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDbEQsQ0FBQyJ9
@@ -0,0 +1,7 @@
1
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
2
+ export default class AgentBoot implements ILifecycleBoot {
3
+ #private;
4
+ constructor(agent: EggCore);
5
+ didLoad(): Promise<void>;
6
+ serverDidReady(): Promise<void>;
7
+ }
@@ -0,0 +1,92 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ import debounce from 'debounce';
4
+ import multimatch from 'multimatch';
5
+ import { exists } from 'utility';
6
+ import { isTimingFile } from './utils.js';
7
+ export default class AgentBoot {
8
+ #agent;
9
+ constructor(agent) {
10
+ this.#agent = agent;
11
+ }
12
+ async didLoad() {
13
+ // clean all timing json
14
+ const rundir = this.#agent.config.rundir;
15
+ const stat = await exists(rundir);
16
+ if (!stat)
17
+ return;
18
+ const files = await fs.readdir(rundir);
19
+ for (const file of files) {
20
+ if (!isTimingFile(file))
21
+ continue;
22
+ await fs.rm(path.join(rundir, file), { force: true, recursive: true });
23
+ }
24
+ }
25
+ async serverDidReady() {
26
+ const agent = this.#agent;
27
+ // single process mode don't watch and reload
28
+ if (agent.options && Reflect.get(agent.options, 'mode') === 'single') {
29
+ return;
30
+ }
31
+ const logger = agent.logger;
32
+ const baseDir = agent.config.baseDir;
33
+ const config = agent.config.development;
34
+ let watchDirs = config.overrideDefault ? [] : [
35
+ 'app',
36
+ 'config',
37
+ 'mocks',
38
+ 'mocks_proxy',
39
+ 'app.js',
40
+ ];
41
+ watchDirs = watchDirs.concat(config.watchDirs).map(dir => path.resolve(baseDir, dir));
42
+ let ignoreReloadFileDirs = config.overrideIgnore ? [] : [
43
+ 'app/views',
44
+ 'app/view',
45
+ 'app/assets',
46
+ 'app/public',
47
+ 'app/web',
48
+ ];
49
+ ignoreReloadFileDirs = ignoreReloadFileDirs.concat(config.ignoreDirs).map(dir => path.resolve(baseDir, dir));
50
+ const reloadFile = debounce(function (info) {
51
+ logger.warn(`[agent:development] reload worker because ${info.path} ${info.event}`);
52
+ process.send({
53
+ to: 'master',
54
+ action: 'reload-worker',
55
+ });
56
+ }, 200);
57
+ // watch dirs to reload worker, will debounce 200ms
58
+ /**
59
+ * reload app worker:
60
+ * [AgentWorker] - on file change
61
+ * |-> emit reload-worker
62
+ * [Master] - receive reload-worker event
63
+ * |-> TODO: Mark worker will die
64
+ * |-> Fork new worker
65
+ * |-> kill old worker
66
+ *
67
+ * @param {Object} info - changed fileInfo
68
+ */
69
+ agent.watcher.watch(watchDirs, info => {
70
+ if (!config.reloadOnDebug) {
71
+ return;
72
+ }
73
+ if (isAssetsDir(info.path) || info.isDirectory) {
74
+ return;
75
+ }
76
+ // don't reload if don't match
77
+ if (config.reloadPattern && multimatch(info.path, config.reloadPattern).length === 0) {
78
+ return;
79
+ }
80
+ reloadFile(info);
81
+ });
82
+ function isAssetsDir(filepath) {
83
+ for (const ignorePath of ignoreReloadFileDirs) {
84
+ if (filepath.startsWith(ignorePath)) {
85
+ return true;
86
+ }
87
+ }
88
+ return false;
89
+ }
90
+ }
91
+ }
92
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQzdCLE9BQU8sRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ2xDLE9BQU8sUUFBUSxNQUFNLFVBQVUsQ0FBQztBQUNoQyxPQUFPLFVBQVUsTUFBTSxZQUFZLENBQUM7QUFDcEMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUVqQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTFDLE1BQU0sQ0FBQyxPQUFPLE9BQU8sU0FBUztJQUM1QixNQUFNLENBQVU7SUFFaEIsWUFBWSxLQUFjO1FBQ3hCLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO0lBQ3RCLENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTztRQUNYLHdCQUF3QjtRQUN4QixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUM7UUFDekMsTUFBTSxJQUFJLEdBQUcsTUFBTSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDbEMsSUFBSSxDQUFDLElBQUk7WUFBRSxPQUFPO1FBQ2xCLE1BQU0sS0FBSyxHQUFHLE1BQU0sRUFBRSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN2QyxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDO2dCQUFFLFNBQVM7WUFDbEMsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUN6RSxDQUFDO0lBQ0gsQ0FBQztJQUVE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
@@ -0,0 +1,3 @@
1
+ import type { EggCore, MiddlewareFunc } from '@eggjs/core';
2
+ declare const _default: (_: unknown, app: EggCore) => MiddlewareFunc;
3
+ export default _default;
@@ -0,0 +1,39 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ import { readJSON } from 'utility';
4
+ import { getSourceFile, isTimingFile } from '../../utils.js';
5
+ export default (_, app) => {
6
+ return async (ctx, next) => {
7
+ if (ctx.path !== '/__loader_trace__') {
8
+ return await next();
9
+ }
10
+ const template = await fs.readFile(getSourceFile('config/loader_trace.html'), 'utf8');
11
+ const data = await loadTimingData(app);
12
+ ctx.body = template.replace('{{placeholder}}', JSON.stringify(data));
13
+ };
14
+ };
15
+ async function loadTimingData(app) {
16
+ const rundir = app.config.rundir;
17
+ const files = await fs.readdir(rundir);
18
+ const data = [];
19
+ for (const file of files) {
20
+ if (!isTimingFile(file))
21
+ continue;
22
+ const json = await readJSON(path.join(rundir, file));
23
+ const isAgent = /^agent/.test(file);
24
+ for (const item of json) {
25
+ if (isAgent) {
26
+ item.type = 'agent';
27
+ }
28
+ else {
29
+ item.type = `app_${item.pid}`;
30
+ }
31
+ item.pid = String(item.pid);
32
+ item.range = [item.start, item.end];
33
+ item.title = `${item.type}(${item.index})`;
34
+ data.push(item);
35
+ }
36
+ }
37
+ return data;
38
+ }
39
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWdnX2xvYWRlcl90cmFjZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9hcHAvbWlkZGxld2FyZS9lZ2dfbG9hZGVyX3RyYWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUM3QixPQUFPLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBRW5DLE9BQU8sRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFN0QsZUFBZSxDQUFDLENBQVUsRUFBRSxHQUFZLEVBQWtCLEVBQUU7SUFDMUQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFO1FBQ3pCLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxtQkFBbUIsRUFBRSxDQUFDO1lBQ3JDLE9BQU8sTUFBTSxJQUFJLEVBQUUsQ0FBQztRQUN0QixDQUFDO1FBQ0QsTUFBTSxRQUFRLEdBQUcsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQywwQkFBMEIsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3RGLE1BQU0sSUFBSSxHQUFHLE1BQU0sY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZDLEdBQUcsQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDdkUsQ0FBQyxDQUFDO0FBQ0osQ0FBQyxDQUFDO0FBRUYsS0FBSyxVQUFVLGNBQWMsQ0FBQyxHQUFZO0lBQ3hDLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ2pDLE1BQU0sS0FBSyxHQUFHLE1BQU0sRUFBRSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN2QyxNQUFNLElBQUksR0FBVSxFQUFFLENBQUM7SUFDdkIsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUN6QixJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQztZQUFFLFNBQVM7UUFDbEMsTUFBTSxJQUFJLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNyRCxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BDLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUM7WUFDeEIsSUFBSSxPQUFPLEVBQUUsQ0FBQztnQkFDWixJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQztZQUN0QixDQUFDO2lCQUFNLENBQUM7Z0JBQ04sSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNoQyxDQUFDO1lBQ0QsSUFBSSxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQzVCLElBQUksQ0FBQyxLQUFLLEdBQUcsQ0FBRSxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUUsQ0FBQztZQUN0QyxJQUFJLENBQUMsS0FBSyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsS0FBSyxHQUFHLENBQUM7WUFDM0MsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNsQixDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQyJ9
@@ -0,0 +1,6 @@
1
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
2
+ export default class AppBoot implements ILifecycleBoot {
3
+ #private;
4
+ constructor(app: EggCore);
5
+ configWillLoad(): Promise<void>;
6
+ }
@@ -0,0 +1,14 @@
1
+ export default class AppBoot {
2
+ #app;
3
+ constructor(app) {
4
+ this.#app = app;
5
+ // if true, then don't need to wait at local development mode
6
+ if (app.config.development.fastReady) {
7
+ process.nextTick(() => this.#app.ready(true));
8
+ }
9
+ }
10
+ async configWillLoad() {
11
+ this.#app.config.coreMiddleware.push('eggLoaderTrace');
12
+ }
13
+ }
14
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxNQUFNLENBQUMsT0FBTyxPQUFPLE9BQU87SUFDMUIsSUFBSSxDQUFVO0lBRWQsWUFBWSxHQUFZO1FBQ3RCLElBQUksQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDO1FBQ2hCLDZEQUE2RDtRQUM3RCxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ3JDLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNoRCxDQUFDO0lBQ0gsQ0FBQztJQUVELEtBQUssQ0FBQyxjQUFjO1FBQ2xCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUN6RCxDQUFDO0NBQ0YifQ==
@@ -0,0 +1,15 @@
1
+ import type { DevelopmentConfig } from '../types.js';
2
+ /**
3
+ * @member Config#development
4
+ * @property {Array} watchDirs - dirs needed watch, when files under these change, application will reload, use relative path
5
+ * @property {Array} ignoreDirs - dirs don't need watch, including subdirectories, use relative path
6
+ * @property {Boolean} fastReady - don't wait all plugins ready, default is false.
7
+ * @property {Boolean} reloadOnDebug - whether reload on debug, default is true.
8
+ * @property {Boolean} overrideDefault - whether override default watchDirs, default is false.
9
+ * @property {Boolean} overrideIgnore - whether override default ignoreDirs, default is false.
10
+ * @property {Array|String} reloadPattern - whether to reload, use https://github.com/sindresorhus/multimatch
11
+ */
12
+ declare const _default: {
13
+ development: DevelopmentConfig;
14
+ };
15
+ export default _default;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @member Config#development
3
+ * @property {Array} watchDirs - dirs needed watch, when files under these change, application will reload, use relative path
4
+ * @property {Array} ignoreDirs - dirs don't need watch, including subdirectories, use relative path
5
+ * @property {Boolean} fastReady - don't wait all plugins ready, default is false.
6
+ * @property {Boolean} reloadOnDebug - whether reload on debug, default is true.
7
+ * @property {Boolean} overrideDefault - whether override default watchDirs, default is false.
8
+ * @property {Boolean} overrideIgnore - whether override default ignoreDirs, default is false.
9
+ * @property {Array|String} reloadPattern - whether to reload, use https://github.com/sindresorhus/multimatch
10
+ */
11
+ export default {
12
+ development: {
13
+ watchDirs: [],
14
+ ignoreDirs: [],
15
+ fastReady: false,
16
+ reloadOnDebug: true,
17
+ overrideDefault: false,
18
+ overrideIgnore: false,
19
+ reloadPattern: undefined,
20
+ },
21
+ };
22
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmRlZmF1bHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29uZmlnL2NvbmZpZy5kZWZhdWx0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBOzs7Ozs7Ozs7R0FTRztBQUNILGVBQWU7SUFDYixXQUFXLEVBQUU7UUFDWCxTQUFTLEVBQUUsRUFBRTtRQUNiLFVBQVUsRUFBRSxFQUFFO1FBQ2QsU0FBUyxFQUFFLEtBQUs7UUFDaEIsYUFBYSxFQUFFLElBQUk7UUFDbkIsZUFBZSxFQUFFLEtBQUs7UUFDdEIsY0FBYyxFQUFFLEtBQUs7UUFDckIsYUFBYSxFQUFFLFNBQVM7S0FDSjtDQUN2QixDQUFDIn0=
@@ -0,0 +1,49 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title></title>
5
+ </head>
6
+ <body>
7
+ <div id="mountNode"></div>
8
+ <script src="https://gw.alipayobjects.com/os/antv/assets/g2/3.0.9/g2.min.js"></script>
9
+ <script>
10
+ var data = {{placeholder}};
11
+
12
+ const chart = new G2.Chart({
13
+ container: 'mountNode', // 指定图表容器 ID
14
+ height: data.length * 22, // 指定图表高度
15
+ forceFit: true,
16
+ padding: 'auto',
17
+ });
18
+
19
+ // Step 2: 载入数据源
20
+ chart.source(data, {
21
+ range: {
22
+ type: 'time',
23
+ mask: 'HH:mm:ss',
24
+ nice: true,
25
+ },
26
+ });
27
+ chart
28
+ .coord()
29
+ .transpose()
30
+ .scale(1, -1);
31
+
32
+ chart.legend({ position: 'top' });
33
+
34
+ chart.tooltip({
35
+ showTitle: false,
36
+ itemTpl: '<li>{duration}ms: {name} </li>'
37
+ });
38
+
39
+ chart
40
+ .interval()
41
+ .position('title*range')
42
+ .color('type')
43
+ .tooltip('name*duration', (name, duration) => ({ name, duration }))
44
+ .size(5);
45
+
46
+ chart.render();
47
+ </script>
48
+ </body>
49
+ </html>
@@ -0,0 +1 @@
1
+ import './types.js';
@@ -0,0 +1,2 @@
1
+ import './types.js';
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxZQUFZLENBQUMifQ==
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,35 @@
1
+ export interface DevelopmentConfig {
2
+ /**
3
+ * dirs needed watch, when files under these change, application will reload, use relative path
4
+ */
5
+ watchDirs: string[];
6
+ /**
7
+ * dirs don't need watch, including subdirectories, use relative path
8
+ */
9
+ ignoreDirs: string[];
10
+ /**
11
+ * don't wait all plugins ready, default is false.
12
+ */
13
+ fastReady: boolean;
14
+ /**
15
+ * whether reload on debug, default is true.
16
+ */
17
+ reloadOnDebug: boolean;
18
+ /**
19
+ * whether override default watchDirs, default is false.
20
+ */
21
+ overrideDefault: boolean;
22
+ /**
23
+ * whether override default ignoreDirs, default is false.
24
+ */
25
+ overrideIgnore: boolean;
26
+ /**
27
+ * whether to reload, use https://github.com/sindresorhus/multimatch
28
+ */
29
+ reloadPattern?: string[] | string;
30
+ }
31
+ declare module '@eggjs/core' {
32
+ interface EggAppConfig {
33
+ development: DevelopmentConfig;
34
+ }
35
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
@@ -0,0 +1,3 @@
1
+ export declare function getSourceDirname(): string;
2
+ export declare function getSourceFile(filename: string): string;
3
+ export declare function isTimingFile(file: string): boolean;
@@ -0,0 +1,17 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ export function getSourceDirname() {
4
+ if (typeof __dirname !== 'undefined') {
5
+ return path.dirname(__dirname);
6
+ }
7
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
8
+ // @ts-ignore
9
+ return path.dirname(fileURLToPath(import.meta.url));
10
+ }
11
+ export function getSourceFile(filename) {
12
+ return path.join(getSourceDirname(), filename);
13
+ }
14
+ export function isTimingFile(file) {
15
+ return /^(agent|application)_timing/.test(file);
16
+ }
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQzdCLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFFekMsTUFBTSxVQUFVLGdCQUFnQjtJQUM5QixJQUFJLE9BQU8sU0FBUyxLQUFLLFdBQVcsRUFBRSxDQUFDO1FBQ3JDLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNqQyxDQUFDO0lBQ0QsNkRBQTZEO0lBQzdELGFBQWE7SUFDYixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUN0RCxDQUFDO0FBRUQsTUFBTSxVQUFVLGFBQWEsQ0FBQyxRQUFnQjtJQUM1QyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FBQztBQUNqRCxDQUFDO0FBRUQsTUFBTSxVQUFVLFlBQVksQ0FBQyxJQUFZO0lBQ3ZDLE9BQU8sNkJBQTZCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ2xELENBQUMifQ==
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "@eggjs/development",
3
+ "version": "4.0.0"
4
+ }
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@eggjs/development",
3
+ "version": "4.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "development tool for egg",
8
+ "eggPlugin": {
9
+ "name": "development",
10
+ "env": [
11
+ "local"
12
+ ],
13
+ "dependencies": [
14
+ "watcher"
15
+ ],
16
+ "exports": {
17
+ "import": "./dist/esm",
18
+ "require": "./dist/commonjs",
19
+ "typescript": "./src"
20
+ }
21
+ },
22
+ "keywords": [
23
+ "egg",
24
+ "plugin",
25
+ "egg-plugin",
26
+ "eggPlugin"
27
+ ],
28
+ "dependencies": {
29
+ "@eggjs/core": "^6.2.11",
30
+ "debounce": "^2.2.0",
31
+ "multimatch": "^5.0.0",
32
+ "utility": "^2.4.0"
33
+ },
34
+ "devDependencies": {
35
+ "@arethetypeswrong/cli": "^0.17.2",
36
+ "@eggjs/bin": "7",
37
+ "@eggjs/mock": "6",
38
+ "@eggjs/supertest": "8",
39
+ "@eggjs/tsconfig": "1",
40
+ "@types/mocha": "10",
41
+ "@types/node": "22",
42
+ "egg": "beta",
43
+ "eslint": "8",
44
+ "eslint-config-egg": "14",
45
+ "rimraf": "^6.0.1",
46
+ "tshy": "3",
47
+ "tshy-after": "1",
48
+ "typescript": "5"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.19.0"
52
+ },
53
+ "scripts": {
54
+ "lint": "eslint --cache src test --ext .ts",
55
+ "pretest": "npm run clean && npm run lint -- --fix",
56
+ "test": "egg-bin test",
57
+ "preci": "npm run clean && npm run lint",
58
+ "ci": "egg-bin cov",
59
+ "postci": "npm run prepublishOnly && npm run clean",
60
+ "clean": "rimraf dist",
61
+ "prepublishOnly": "tshy && tshy-after && attw --pack"
62
+ },
63
+ "repository": {
64
+ "type": "git",
65
+ "url": "git+https://github.com/eggjs/development.git"
66
+ },
67
+ "bugs": "https://github.com/eggjs/egg/issues",
68
+ "homepage": "https://github.com/eggjs/development#readme",
69
+ "author": "jtyjty99999",
70
+ "license": "MIT",
71
+ "type": "module",
72
+ "tshy": {
73
+ "exports": {
74
+ ".": "./src/index.ts",
75
+ "./package.json": "./package.json"
76
+ }
77
+ },
78
+ "exports": {
79
+ ".": {
80
+ "import": {
81
+ "types": "./dist/esm/index.d.ts",
82
+ "default": "./dist/esm/index.js"
83
+ },
84
+ "require": {
85
+ "types": "./dist/commonjs/index.d.ts",
86
+ "default": "./dist/commonjs/index.js"
87
+ }
88
+ },
89
+ "./package.json": "./package.json"
90
+ },
91
+ "files": [
92
+ "dist",
93
+ "src"
94
+ ],
95
+ "types": "./dist/commonjs/index.d.ts",
96
+ "main": "./dist/commonjs/index.js",
97
+ "module": "./dist/esm/index.js"
98
+ }
package/src/agent.ts ADDED
@@ -0,0 +1,106 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ import debounce from 'debounce';
4
+ import multimatch from 'multimatch';
5
+ import { exists } from 'utility';
6
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
7
+ import { isTimingFile } from './utils.js';
8
+
9
+ export default class AgentBoot implements ILifecycleBoot {
10
+ #agent: EggCore;
11
+
12
+ constructor(agent: EggCore) {
13
+ this.#agent = agent;
14
+ }
15
+
16
+ async didLoad() {
17
+ // clean all timing json
18
+ const rundir = this.#agent.config.rundir;
19
+ const stat = await exists(rundir);
20
+ if (!stat) return;
21
+ const files = await fs.readdir(rundir);
22
+ for (const file of files) {
23
+ if (!isTimingFile(file)) continue;
24
+ await fs.rm(path.join(rundir, file), { force: true, recursive: true });
25
+ }
26
+ }
27
+
28
+ async serverDidReady() {
29
+ const agent = this.#agent;
30
+ // single process mode don't watch and reload
31
+ if (agent.options && Reflect.get(agent.options, 'mode') === 'single') {
32
+ return;
33
+ }
34
+
35
+ const logger = agent.logger;
36
+ const baseDir = agent.config.baseDir;
37
+ const config = agent.config.development;
38
+
39
+ let watchDirs = config.overrideDefault ? [] : [
40
+ 'app',
41
+ 'config',
42
+ 'mocks',
43
+ 'mocks_proxy',
44
+ 'app.js',
45
+ ];
46
+
47
+ watchDirs = watchDirs.concat(config.watchDirs).map(dir => path.resolve(baseDir, dir));
48
+
49
+ let ignoreReloadFileDirs = config.overrideIgnore ? [] : [
50
+ 'app/views',
51
+ 'app/view',
52
+ 'app/assets',
53
+ 'app/public',
54
+ 'app/web',
55
+ ];
56
+
57
+ ignoreReloadFileDirs = ignoreReloadFileDirs.concat(config.ignoreDirs).map(dir => path.resolve(baseDir, dir));
58
+
59
+ const reloadFile = debounce(function(info) {
60
+ logger.warn(`[agent:development] reload worker because ${info.path} ${info.event}`);
61
+
62
+ process.send!({
63
+ to: 'master',
64
+ action: 'reload-worker',
65
+ });
66
+ }, 200);
67
+
68
+ // watch dirs to reload worker, will debounce 200ms
69
+ /**
70
+ * reload app worker:
71
+ * [AgentWorker] - on file change
72
+ * |-> emit reload-worker
73
+ * [Master] - receive reload-worker event
74
+ * |-> TODO: Mark worker will die
75
+ * |-> Fork new worker
76
+ * |-> kill old worker
77
+ *
78
+ * @param {Object} info - changed fileInfo
79
+ */
80
+ agent.watcher.watch(watchDirs, info => {
81
+ if (!config.reloadOnDebug) {
82
+ return;
83
+ }
84
+
85
+ if (isAssetsDir(info.path) || info.isDirectory) {
86
+ return;
87
+ }
88
+
89
+ // don't reload if don't match
90
+ if (config.reloadPattern && multimatch(info.path, config.reloadPattern).length === 0) {
91
+ return;
92
+ }
93
+
94
+ reloadFile(info);
95
+ });
96
+
97
+ function isAssetsDir(filepath: string) {
98
+ for (const ignorePath of ignoreReloadFileDirs) {
99
+ if (filepath.startsWith(ignorePath)) {
100
+ return true;
101
+ }
102
+ }
103
+ return false;
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,39 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ import { readJSON } from 'utility';
4
+ import type { EggCore, MiddlewareFunc } from '@eggjs/core';
5
+ import { getSourceFile, isTimingFile } from '../../utils.js';
6
+
7
+ export default (_: unknown, app: EggCore): MiddlewareFunc => {
8
+ return async (ctx, next) => {
9
+ if (ctx.path !== '/__loader_trace__') {
10
+ return await next();
11
+ }
12
+ const template = await fs.readFile(getSourceFile('config/loader_trace.html'), 'utf8');
13
+ const data = await loadTimingData(app);
14
+ ctx.body = template.replace('{{placeholder}}', JSON.stringify(data));
15
+ };
16
+ };
17
+
18
+ async function loadTimingData(app: EggCore) {
19
+ const rundir = app.config.rundir;
20
+ const files = await fs.readdir(rundir);
21
+ const data: any[] = [];
22
+ for (const file of files) {
23
+ if (!isTimingFile(file)) continue;
24
+ const json = await readJSON(path.join(rundir, file));
25
+ const isAgent = /^agent/.test(file);
26
+ for (const item of json) {
27
+ if (isAgent) {
28
+ item.type = 'agent';
29
+ } else {
30
+ item.type = `app_${item.pid}`;
31
+ }
32
+ item.pid = String(item.pid);
33
+ item.range = [ item.start, item.end ];
34
+ item.title = `${item.type}(${item.index})`;
35
+ data.push(item);
36
+ }
37
+ }
38
+ return data;
39
+ }
package/src/app.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { ILifecycleBoot, EggCore } from '@eggjs/core';
2
+
3
+ export default class AppBoot implements ILifecycleBoot {
4
+ #app: EggCore;
5
+
6
+ constructor(app: EggCore) {
7
+ this.#app = app;
8
+ // if true, then don't need to wait at local development mode
9
+ if (app.config.development.fastReady) {
10
+ process.nextTick(() => this.#app.ready(true));
11
+ }
12
+ }
13
+
14
+ async configWillLoad() {
15
+ this.#app.config.coreMiddleware.push('eggLoaderTrace');
16
+ }
17
+ }
@@ -0,0 +1,23 @@
1
+ import type { DevelopmentConfig } from '../types.js';
2
+
3
+ /**
4
+ * @member Config#development
5
+ * @property {Array} watchDirs - dirs needed watch, when files under these change, application will reload, use relative path
6
+ * @property {Array} ignoreDirs - dirs don't need watch, including subdirectories, use relative path
7
+ * @property {Boolean} fastReady - don't wait all plugins ready, default is false.
8
+ * @property {Boolean} reloadOnDebug - whether reload on debug, default is true.
9
+ * @property {Boolean} overrideDefault - whether override default watchDirs, default is false.
10
+ * @property {Boolean} overrideIgnore - whether override default ignoreDirs, default is false.
11
+ * @property {Array|String} reloadPattern - whether to reload, use https://github.com/sindresorhus/multimatch
12
+ */
13
+ export default {
14
+ development: {
15
+ watchDirs: [],
16
+ ignoreDirs: [],
17
+ fastReady: false,
18
+ reloadOnDebug: true,
19
+ overrideDefault: false,
20
+ overrideIgnore: false,
21
+ reloadPattern: undefined,
22
+ } as DevelopmentConfig,
23
+ };
@@ -0,0 +1,49 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title></title>
5
+ </head>
6
+ <body>
7
+ <div id="mountNode"></div>
8
+ <script src="https://gw.alipayobjects.com/os/antv/assets/g2/3.0.9/g2.min.js"></script>
9
+ <script>
10
+ var data = {{placeholder}};
11
+
12
+ const chart = new G2.Chart({
13
+ container: 'mountNode', // 指定图表容器 ID
14
+ height: data.length * 22, // 指定图表高度
15
+ forceFit: true,
16
+ padding: 'auto',
17
+ });
18
+
19
+ // Step 2: 载入数据源
20
+ chart.source(data, {
21
+ range: {
22
+ type: 'time',
23
+ mask: 'HH:mm:ss',
24
+ nice: true,
25
+ },
26
+ });
27
+ chart
28
+ .coord()
29
+ .transpose()
30
+ .scale(1, -1);
31
+
32
+ chart.legend({ position: 'top' });
33
+
34
+ chart.tooltip({
35
+ showTitle: false,
36
+ itemTpl: '<li>{duration}ms: {name} </li>'
37
+ });
38
+
39
+ chart
40
+ .interval()
41
+ .position('title*range')
42
+ .color('type')
43
+ .tooltip('name*duration', (name, duration) => ({ name, duration }))
44
+ .size(5);
45
+
46
+ chart.render();
47
+ </script>
48
+ </body>
49
+ </html>
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ import './types.js';
package/src/types.ts ADDED
@@ -0,0 +1,37 @@
1
+ export interface DevelopmentConfig {
2
+ /**
3
+ * dirs needed watch, when files under these change, application will reload, use relative path
4
+ */
5
+ watchDirs: string[];
6
+ /**
7
+ * dirs don't need watch, including subdirectories, use relative path
8
+ */
9
+ ignoreDirs: string[];
10
+ /**
11
+ * don't wait all plugins ready, default is false.
12
+ */
13
+ fastReady: boolean;
14
+ /**
15
+ * whether reload on debug, default is true.
16
+ */
17
+ reloadOnDebug: boolean;
18
+ /**
19
+ * whether override default watchDirs, default is false.
20
+ */
21
+ overrideDefault: boolean;
22
+ /**
23
+ * whether override default ignoreDirs, default is false.
24
+ */
25
+ overrideIgnore: boolean;
26
+ /**
27
+ * whether to reload, use https://github.com/sindresorhus/multimatch
28
+ */
29
+ reloadPattern?: string[] | string;
30
+ }
31
+
32
+ declare module '@eggjs/core' {
33
+ // add EggAppConfig overrides types
34
+ interface EggAppConfig {
35
+ development: DevelopmentConfig;
36
+ }
37
+ }
@@ -0,0 +1,4 @@
1
+ // make sure to import egg typings and let typescript know about it
2
+ // @see https://github.com/whxaxes/blog/issues/11
3
+ // and https://www.typescriptlang.org/docs/handbook/declaration-merging.html
4
+ import 'egg';
package/src/utils.ts ADDED
@@ -0,0 +1,19 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ export function getSourceDirname() {
5
+ if (typeof __dirname !== 'undefined') {
6
+ return path.dirname(__dirname);
7
+ }
8
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
9
+ // @ts-ignore
10
+ return path.dirname(fileURLToPath(import.meta.url));
11
+ }
12
+
13
+ export function getSourceFile(filename: string) {
14
+ return path.join(getSourceDirname(), filename);
15
+ }
16
+
17
+ export function isTimingFile(file: string) {
18
+ return /^(agent|application)_timing/.test(file);
19
+ }