|
1 | 1 | /** |
2 | | - * Module loader for bundled Egg apps. Called with the `importModule()` filepath |
3 | | - * after POSIX path normalization. Return `undefined` to fall through to the |
4 | | - * standard import path. |
| 2 | + * Module loader for bundled Egg apps, registered on `globalThis` as |
| 3 | + * `__EGG_BUNDLE_MODULE_LOADER__` (use `setBundleModuleLoader()` from |
| 4 | + * `@eggjs/utils`). This is the highest-priority hook: it runs before on-disk |
| 5 | + * resolution in both `importModule()` / `importResolve()` (`@eggjs/utils`) and |
| 6 | + * the loaders in `@eggjs/core` and the tegg loader. |
| 7 | + * |
| 8 | + * It is called with the `importModule()` filepath (or a virtual specifier) |
| 9 | + * after POSIX path normalization, and is meant to return a module already |
| 10 | + * inlined into the bundle β typically a lookup into a static bundle map emitted |
| 11 | + * by `egg-bundler`. Return `undefined` to fall through to the next hook (the |
| 12 | + * snapshot loader registered via `setSnapshotModuleLoader()`, then |
| 13 | + * `__EGG_MODULE_IMPORTER__`) or the standard `import()` / `require()` path. |
| 14 | + * |
| 15 | + * The non-undefined return value follows the same default-export unwrapping |
| 16 | + * rules as a native import (double-default `__esModule` compatibility plus the |
| 17 | + * caller's `importDefaultOnly` option). |
5 | 18 | */ |
6 | 19 | export type BundleModuleLoader = (filepath: string) => unknown; |
7 | 20 |
|
8 | 21 | /** |
9 | | - * Async module importer override for the tegg loader's file loading. |
| 22 | + * Async module importer override, registered on `globalThis` as |
| 23 | + * `__EGG_MODULE_IMPORTER__`. When set (and neither the bundle loader nor a |
| 24 | + * snapshot loader registered via `setSnapshotModuleLoader()` already resolved |
| 25 | + * the path), `importModule()` / the loaders delegate module loading to this |
| 26 | + * importer instead of the built-in `await import(filePath)`. The return value |
| 27 | + * is awaited and mirrors `await import()` (default unwrapping and |
| 28 | + * `importDefaultOnly` apply); because it is awaited, a synchronous return value |
| 29 | + * such as the result of `require()` is also valid. |
| 30 | + * |
| 31 | + * The path passed in depends on the caller: `@eggjs/utils` `importModule()` |
| 32 | + * passes the resolved module path from `importResolve()` (OS-native separators, |
| 33 | + * not normalized), while the tegg loader passes the original loader filepath |
| 34 | + * with separators normalized to POSIX. Importers that care about separators |
| 35 | + * should normalize defensively. |
| 36 | + * |
| 37 | + * Two main uses: |
10 | 38 | * |
11 | | - * When set, the loader delegates module loading to this importer instead of the |
12 | | - * built-in `await import(filePath)`. Its main use is testing with a bundler-based |
13 | | - * test runner (e.g. Vitest): when an app's egg modules are loaded by the loader |
14 | | - * via the native `import()` while the test file imports the same source through |
15 | | - * the runner's module graph, the two resolve to *different* module instances β |
16 | | - * so a class decorated as an egg proto by the loader is not the same class the |
17 | | - * test references, and `ctx.getEggObject(ClassRef)` fails with "can not get proto". |
| 39 | + * 1. Testing with a bundler-based test runner (e.g. Vitest): when an app's egg |
| 40 | + * modules are loaded by the loader via the native `import()` while the test |
| 41 | + * file imports the same source through the runner's module graph, the two |
| 42 | + * resolve to *different* module instances β so a class decorated as an egg |
| 43 | + * proto by the loader is not the same class the test references, and |
| 44 | + * `ctx.getEggObject(ClassRef)` fails with "can not get proto". A test runner |
| 45 | + * injects an importer that routes loading through its own module graph |
| 46 | + * (e.g. `filePath => import(filePath)` evaluated inside the runner context), |
| 47 | + * keeping a single module instance. |
18 | 48 | * |
19 | | - * A test runner can inject an importer that routes loading through its own module |
20 | | - * graph (e.g. `filePath => import(filePath)` evaluated inside the runner context), |
21 | | - * keeping a single module instance. Return value mirrors `await import()`. |
| 49 | + * 2. V8 startup-snapshot restore: the deserialized main function runs without a |
| 50 | + * host dynamic-import callback, so native `import()` throws. The snapshot |
| 51 | + * entry generated by `egg-bundler` installs a synchronous `require()`-based |
| 52 | + * importer (`createRequire()` over the bundle output dir); `require()` can |
| 53 | + * load ESM on Node >= 22, so modules resolve without dynamic import. |
22 | 54 | */ |
23 | 55 | export type ModuleImporter = (filePath: string) => Promise<unknown>; |
0 commit comments