@eggjs/core 6.2.11 → 6.2.13

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.
@@ -5,12 +5,16 @@ 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 { getParamNames, readJSONSync, readJSON } from 'utility';
8
+ import {
9
+ getParamNames, readJSONSync, readJSON, exists,
10
+ } from 'utility';
9
11
  import { extend } from 'extend2';
10
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';
@@ -19,6 +23,7 @@ import type {
19
23
  Context, EggCore, MiddlewareFunc,
20
24
  } from '../egg.js';
21
25
  import type { BaseContextClass } from '../base_context_class.js';
26
+ import type { EggAppConfig, EggAppInfo, EggPluginInfo } from '../types.js';
22
27
 
23
28
  const debug = debuglog('@eggjs/core/loader/egg_loader');
24
29
 
@@ -29,44 +34,6 @@ const originalPrototypes: Record<string, any> = {
29
34
  application: Application.prototype,
30
35
  };
31
36
 
32
- export interface EggAppInfo {
33
- /** package.json */
34
- pkg: Record<string, any>;
35
- /** the application name from package.json */
36
- name: string;
37
- /** current directory of application */
38
- baseDir: string;
39
- /** equals to serverEnv */
40
- env: string;
41
- /** equals to serverScope */
42
- scope: string;
43
- /** home directory of the OS */
44
- HOME: string;
45
- /** baseDir when local and unittest, HOME when other environment */
46
- root: string;
47
- }
48
-
49
- export interface EggPluginInfo {
50
- /** the plugin name, it can be used in `dep` */
51
- name: string;
52
- /** the package name of plugin */
53
- package?: string;
54
- version?: string;
55
- /** whether enabled */
56
- enable: boolean;
57
- implicitEnable?: boolean;
58
- /** the directory of the plugin package */
59
- path?: string;
60
- /** the dependent plugins, you can use the plugin name */
61
- dependencies: string[];
62
- /** the optional dependent plugins. */
63
- optionalDependencies: string[];
64
- dependents?: string[];
65
- /** specify the serverEnv that only enable the plugin in it */
66
- env: string[];
67
- /** the file plugin config in. */
68
- from: string;
69
- }
70
37
 
71
38
  export interface EggLoaderOptions {
72
39
  /** server env */
@@ -102,7 +69,6 @@ export class EggLoader {
102
69
  readonly appInfo: EggAppInfo;
103
70
  dirs?: EggDirInfo[];
104
71
 
105
-
106
72
  /**
107
73
  * @class
108
74
  * @param {Object} options - options
@@ -132,12 +98,11 @@ export class EggLoader {
132
98
  if (process.env.EGG_TYPESCRIPT === 'true' || (this.pkg.egg && this.pkg.egg.typescript)) {
133
99
  // skip require tsconfig-paths if tsconfig.json not exists
134
100
  const tsConfigFile = path.join(this.options.baseDir, 'tsconfig.json');
135
- // FIXME: support esm
136
- if (fs.existsSync(tsConfigFile) && typeof require === 'function') {
137
- // eslint-disable-next-line @typescript-eslint/no-var-requires
138
- require('tsconfig-paths').register({ cwd: this.options.baseDir });
101
+ if (fs.existsSync(tsConfigFile)) {
102
+ tsconfigPathsRegister({ cwd: this.options.baseDir } as any);
139
103
  } else {
140
- this.logger.info('[@eggjs/core/egg_loader] skip register "tsconfig-paths" because tsconfig.json not exists at %s',
104
+ this.logger.info(
105
+ '[@eggjs/core/egg_loader] skip register "tsconfig-paths" because tsconfig.json not exists at %s',
141
106
  tsConfigFile);
142
107
  }
143
108
  }
@@ -636,7 +601,7 @@ export class EggLoader {
636
601
  plugin.version = pkg.version;
637
602
  }
638
603
  // support commonjs and esm dist files
639
- plugin.path = this.#formatPluginPathFromPackageJSON(plugin.path!, pkg);
604
+ plugin.path = await this.#formatPluginPathFromPackageJSON(plugin.path!, pkg);
640
605
  }
641
606
 
642
607
  const logger = this.options.logger;
@@ -790,26 +755,36 @@ export class EggLoader {
790
755
  }
791
756
  }
792
757
 
793
- #formatPluginPathFromPackageJSON(pluginPath: string, pluginPkg: {
758
+ async #formatPluginPathFromPackageJSON(pluginPath: string, pluginPkg: {
794
759
  eggPlugin?: {
795
760
  exports?: {
796
761
  import?: string;
797
762
  require?: string;
763
+ typescript?: string;
798
764
  };
799
765
  };
800
- }) {
801
- if (pluginPkg.eggPlugin?.exports) {
802
- if (typeof require === 'function') {
803
- if (pluginPkg.eggPlugin.exports.require) {
804
- pluginPath = path.join(pluginPath, pluginPkg.eggPlugin.exports.require);
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);
805
773
  }
806
774
  } else {
807
- if (pluginPkg.eggPlugin.exports.import) {
808
- pluginPath = path.join(pluginPath, pluginPkg.eggPlugin.exports.import);
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);
809
784
  }
810
785
  }
811
786
  }
812
- return pluginPath;
787
+ return realPluginPath;
813
788
  }
814
789
 
815
790
  #extendPlugins(targets: Record<string, EggPluginInfo>, plugins: Record<string, EggPluginInfo>) {
@@ -845,7 +820,7 @@ export class EggLoader {
845
820
 
846
821
  /** start Config loader */
847
822
  configMeta: Record<string, any>;
848
- config: Record<string, any>;
823
+ config: EggAppConfig;
849
824
 
850
825
  /**
851
826
  * Load config/config.js
@@ -859,7 +834,10 @@ export class EggLoader {
859
834
  this.timing.start('Load Config');
860
835
  this.configMeta = {};
861
836
 
862
- const target: Record<string, any> = {};
837
+ const target: EggAppConfig = {
838
+ middleware: [],
839
+ coreMiddleware: [],
840
+ };
863
841
 
864
842
  // Load Application config first
865
843
  const appConfig = await this.#preloadAppConfig();
@@ -1208,7 +1186,7 @@ export class EggLoader {
1208
1186
  const servicePaths = this.getLoadUnits().map(unit => path.join(unit.path, 'app/service'));
1209
1187
  options = {
1210
1188
  call: true,
1211
- caseStyle: 'lower',
1189
+ caseStyle: CaseStyle.lower,
1212
1190
  fieldClass: 'serviceClasses',
1213
1191
  directory: servicePaths,
1214
1192
  ...options,
@@ -1248,7 +1226,7 @@ export class EggLoader {
1248
1226
  opt = {
1249
1227
  call: false,
1250
1228
  override: true,
1251
- caseStyle: 'lower',
1229
+ caseStyle: CaseStyle.lower,
1252
1230
  directory: middlewarePaths,
1253
1231
  ...opt,
1254
1232
  };
@@ -1323,7 +1301,7 @@ export class EggLoader {
1323
1301
  this.timing.start('Load Controller');
1324
1302
  const controllerBase = path.join(this.options.baseDir, 'app/controller');
1325
1303
  opt = {
1326
- caseStyle: 'lower',
1304
+ caseStyle: CaseStyle.lower,
1327
1305
  directory: controllerBase,
1328
1306
  initializer: (obj, opt) => {
1329
1307
  // return class if it exports a function
@@ -1403,7 +1381,7 @@ export class EggLoader {
1403
1381
  case 'ctx': {
1404
1382
  assert(!(property in this.app.context), `customLoader should not override ctx.${property}`);
1405
1383
  const options = {
1406
- caseStyle: 'lower',
1384
+ caseStyle: CaseStyle.lower,
1407
1385
  fieldClass: `${property}Classes`,
1408
1386
  ...loaderConfig,
1409
1387
  directory,
@@ -1414,7 +1392,7 @@ export class EggLoader {
1414
1392
  case 'app': {
1415
1393
  assert(!(property in this.app), `customLoader should not override app.${property}`);
1416
1394
  const options = {
1417
- caseStyle: 'lower',
1395
+ caseStyle: CaseStyle.lower,
1418
1396
  initializer: (Clazz: unknown) => {
1419
1397
  return isClass(Clazz) ? new Clazz(this.app) : Clazz;
1420
1398
  },
@@ -1533,10 +1511,11 @@ export class EggLoader {
1533
1511
  * @param {Object} options - see {@link FileLoader}
1534
1512
  * @since 1.0.0
1535
1513
  */
1536
- async loadToApp(directory: string | string[], property: string | symbol, options?: FileLoaderOptions) {
1514
+ async loadToApp(directory: string | string[], property: string | symbol,
1515
+ options?: Omit<FileLoaderOptions, 'inject' | 'target'>) {
1537
1516
  const target = {};
1538
1517
  Reflect.set(this.app, property, target);
1539
- options = {
1518
+ const loadOptions: FileLoaderOptions = {
1540
1519
  ...options,
1541
1520
  directory: options?.directory ?? directory,
1542
1521
  target,
@@ -1545,7 +1524,7 @@ export class EggLoader {
1545
1524
 
1546
1525
  const timingKey = `Load "${String(property)}" to Application`;
1547
1526
  this.timing.start(timingKey);
1548
- await new FileLoader(options).load();
1527
+ await new FileLoader(loadOptions).load();
1549
1528
  this.timing.end(timingKey);
1550
1529
  }
1551
1530
 
@@ -1556,8 +1535,9 @@ export class EggLoader {
1556
1535
  * @param {Object} options - see {@link ContextLoader}
1557
1536
  * @since 1.0.0
1558
1537
  */
1559
- async loadToContext(directory: string | string[], property: string | symbol, options?: ContextLoaderOptions) {
1560
- options = {
1538
+ async loadToContext(directory: string | string[], property: string | symbol,
1539
+ options?: Omit<ContextLoaderOptions, 'inject' | 'property'>) {
1540
+ const loadOptions: ContextLoaderOptions = {
1561
1541
  ...options,
1562
1542
  directory: options?.directory || directory,
1563
1543
  property,
@@ -1566,7 +1546,7 @@ export class EggLoader {
1566
1546
 
1567
1547
  const timingKey = `Load "${String(property)}" to Context`;
1568
1548
  this.timing.start(timingKey);
1569
- await new ContextLoader(options).load();
1549
+ await new ContextLoader(loadOptions).load();
1570
1550
  this.timing.end(timingKey);
1571
1551
  }
1572
1552
 
@@ -11,7 +11,12 @@ const debug = debuglog('@eggjs/core/file_loader');
11
11
  export const FULLPATH = Symbol('EGG_LOADER_ITEM_FULLPATH');
12
12
  export const EXPORTS = Symbol('EGG_LOADER_ITEM_EXPORTS');
13
13
 
14
- export type CaseStyle = 'camel' | 'lower' | 'upper';
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: 'camel',
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 = 'lower';
96
+ this.options.caseStyle = CaseStyle.lower;
92
97
  }
93
98
  }
94
99
 
package/src/types.ts ADDED
@@ -0,0 +1,56 @@
1
+ export interface EggAppInfo {
2
+ /** package.json */
3
+ pkg: Record<string, any>;
4
+ /** the application name from package.json */
5
+ name: string;
6
+ /** current directory of application */
7
+ baseDir: string;
8
+ /** equals to serverEnv */
9
+ env: string;
10
+ /** equals to serverScope */
11
+ scope: string;
12
+ /** home directory of the OS */
13
+ HOME: string;
14
+ /** baseDir when local and unittest, HOME when other environment */
15
+ root: string;
16
+ }
17
+
18
+ export interface EggPluginInfo {
19
+ /** the plugin name, it can be used in `dep` */
20
+ name: string;
21
+ /** the package name of plugin */
22
+ package?: string;
23
+ version?: string;
24
+ /** whether enabled */
25
+ enable: boolean;
26
+ implicitEnable?: boolean;
27
+ /** the directory of the plugin package */
28
+ path?: string;
29
+ /** the dependent plugins, you can use the plugin name */
30
+ dependencies: string[];
31
+ /** the optional dependent plugins. */
32
+ optionalDependencies: string[];
33
+ dependents?: string[];
34
+ /** specify the serverEnv that only enable the plugin in it */
35
+ env: string[];
36
+ /** the file plugin config in. */
37
+ from: string;
38
+ }
39
+
40
+ export interface CustomLoaderConfigItem {
41
+ /** the directory of the custom loader */
42
+ directory: string;
43
+ /** the inject object, it can be app or ctx */
44
+ inject: string;
45
+ /** whether load unit files */
46
+ loadunit?: boolean;
47
+ }
48
+
49
+ export interface EggAppConfig extends Record<string, any> {
50
+ coreMiddleware: string[];
51
+ middleware: string[];
52
+ customLoader?: Record<string, CustomLoaderConfigItem>;
53
+ controller?: {
54
+ supportParams?: boolean;
55
+ };
56
+ }