@eggjs/utils 5.0.2-beta.0 → 5.0.2-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,6 +42,22 @@ npm i @eggjs/utils
42
42
  - {String} baseDir - the current directory of application
43
43
  - {String} framework - the directory of framework
44
44
 
45
+ ### `setBundleModuleLoader(loader)`
46
+
47
+ Register a module loader hook for bundled Egg apps. The hook runs before the
48
+ normal `importModule()` resolution path.
49
+
50
+ - {Function | undefined} loader - a synchronous function that receives the
51
+ original `filepath` argument passed to `importModule()` after POSIX separator
52
+ normalization, or a virtual specifier. It does not receive the resolved
53
+ absolute file path from `importResolve()`. Return `undefined` to fall back to
54
+ the normal import path.
55
+
56
+ The bundle loader is stored on `globalThis`, so bundled and external copies of
57
+ `@eggjs/utils` share the same loader. Non-`undefined` results follow the same
58
+ default export unwrapping rules as `importModule()`, including
59
+ `importDefaultOnly`.
60
+
45
61
  ## License
46
62
 
47
63
  [MIT](LICENSE)
package/dist/import.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { BundleModuleLoader, BundleModuleLoader as BundleModuleLoader$1 } from "@eggjs/typings";
2
+
1
3
  //#region src/import.d.ts
2
4
  interface ImportResolveOptions {
3
5
  paths?: string[];
@@ -10,6 +12,39 @@ declare function getRequire(): NodeRequire;
10
12
  declare function getExtensions(): NodeJS.RequireExtensions;
11
13
  declare function isSupportTypeScript(): boolean;
12
14
  declare function importResolve(filepath: string, options?: ImportResolveOptions): string;
15
+ /**
16
+ * Module loader function type for V8 snapshot support.
17
+ * Called with the resolved absolute file path, returns the module exports.
18
+ */
19
+ type SnapshotModuleLoader = (resolvedPath: string) => any;
20
+ /**
21
+ * Register a snapshot module loader that intercepts `importModule()` calls.
22
+ *
23
+ * When set, `importModule()` delegates to this loader instead of calling
24
+ * `import()` or `require()`. This is used by the V8 snapshot entry generator
25
+ * to provide pre-bundled modules — the bundler generates a static module map
26
+ * from the egg manifest and registers it via this API.
27
+ *
28
+ * Also sets `isESM = false` because the snapshot bundle is CJS and
29
+ * esbuild's `import.meta` polyfill causes incorrect ESM detection.
30
+ *
31
+ * Pass `undefined` to clear the loader and restore the auto-detected `isESM`
32
+ * value. Always clear it once snapshot mode is no longer needed (e.g. in test
33
+ * teardown) so the module-level state does not leak into other files when
34
+ * vitest runs with `isolate: false`.
35
+ */
36
+ declare function setSnapshotModuleLoader(loader: SnapshotModuleLoader | undefined): void;
37
+ /**
38
+ * Register a bundle module loader. Uses globalThis so that bundled and
39
+ * external copies of @eggjs/utils share the same loader.
40
+ *
41
+ * The loader receives a POSIX-normalized filepath or virtual specifier before
42
+ * normal resolution runs. Return `undefined` to fall through to the default
43
+ * import path. Non-undefined hits use the same default unwrapping semantics as
44
+ * normal imports, including `importDefaultOnly` and double-default `__esModule`
45
+ * compatibility.
46
+ */
47
+ declare function setBundleModuleLoader(loader: BundleModuleLoader | undefined): void;
13
48
  declare function importModule(filepath: string, options?: ImportModuleOptions): Promise<any>;
14
49
  //#endregion
15
- export { ImportModuleOptions, ImportResolveOptions, getExtensions, getRequire, importModule, importResolve, isESM, isSupportTypeScript };
50
+ export { type BundleModuleLoader$1 as BundleModuleLoader, ImportModuleOptions, ImportResolveOptions, SnapshotModuleLoader, getExtensions, getRequire, importModule, importResolve, isESM, isSupportTypeScript, setBundleModuleLoader, setSnapshotModuleLoader };
package/dist/import.js CHANGED
@@ -8,17 +8,30 @@ import { debuglog } from "node:util";
8
8
 
9
9
  //#region src/import.ts
10
10
  const debug = debuglog("egg/utils/import");
11
+ let nativeDynamicImport;
12
+ /* v8 ignore next -- covered by the spawned Node fixture; Vitest cannot instrument this opaque import. */
13
+ function getNativeDynamicImport() {
14
+ if (!nativeDynamicImport) try {
15
+ nativeDynamicImport = new Function("specifier", "return import(specifier);");
16
+ } catch (err) {
17
+ const error = /* @__PURE__ */ new Error("Native dynamic import fallback for bundled module loader misses requires code generation from strings.");
18
+ error.cause = err;
19
+ throw error;
20
+ }
21
+ return nativeDynamicImport;
22
+ }
11
23
  let isESM = true;
12
24
  try {
13
25
  if (typeof import.meta !== "undefined") isESM = true;
14
26
  } catch {
15
27
  isESM = false;
16
28
  }
29
+ const detectedIsESM = isESM;
17
30
  const nodeMajorVersion = parseInt(process.versions.node.split(".", 1)[0], 10);
18
31
  const supportImportMetaResolve = nodeMajorVersion >= 18;
19
32
  let _customRequire;
20
33
  function getRequire() {
21
- if (!_customRequire) if (typeof __require !== "undefined") _customRequire = __require;
34
+ if (!_customRequire) if (typeof __require !== "undefined" && __require.extensions) _customRequire = __require;
22
35
  else _customRequire = createRequire(process.cwd());
23
36
  return _customRequire;
24
37
  }
@@ -193,6 +206,11 @@ function importResolve(filepath, options) {
193
206
  }
194
207
  }
195
208
  }
209
+ const bundleModuleLoader = globalThis.__EGG_BUNDLE_MODULE_LOADER__;
210
+ if (bundleModuleLoader && bundleModuleLoader(normalizeBundleModulePath(filepath)) !== void 0) {
211
+ debug("[importResolve:bundle] %o => %o", filepath, filepath);
212
+ return filepath;
213
+ }
196
214
  const extname = path.extname(filepath);
197
215
  if (!isAbsolute && extname === ".json" || !isESM) moduleFilePath = getRequire().resolve(filepath, { paths });
198
216
  else if (supportImportMetaResolve) {
@@ -209,15 +227,72 @@ function importResolve(filepath, options) {
209
227
  debug("[importResolve:success] %o, options: %o => %o, isESM: %s", filepath, options, moduleFilePath, isESM);
210
228
  return moduleFilePath;
211
229
  }
230
+ let _snapshotModuleLoader;
231
+ /**
232
+ * Register a snapshot module loader that intercepts `importModule()` calls.
233
+ *
234
+ * When set, `importModule()` delegates to this loader instead of calling
235
+ * `import()` or `require()`. This is used by the V8 snapshot entry generator
236
+ * to provide pre-bundled modules — the bundler generates a static module map
237
+ * from the egg manifest and registers it via this API.
238
+ *
239
+ * Also sets `isESM = false` because the snapshot bundle is CJS and
240
+ * esbuild's `import.meta` polyfill causes incorrect ESM detection.
241
+ *
242
+ * Pass `undefined` to clear the loader and restore the auto-detected `isESM`
243
+ * value. Always clear it once snapshot mode is no longer needed (e.g. in test
244
+ * teardown) so the module-level state does not leak into other files when
245
+ * vitest runs with `isolate: false`.
246
+ */
247
+ function setSnapshotModuleLoader(loader) {
248
+ _snapshotModuleLoader = loader;
249
+ isESM = loader ? false : detectedIsESM;
250
+ }
251
+ function normalizeBundleModulePath(filepath) {
252
+ return filepath.split(path.win32.sep).join(path.posix.sep);
253
+ }
254
+ /**
255
+ * Register a bundle module loader. Uses globalThis so that bundled and
256
+ * external copies of @eggjs/utils share the same loader.
257
+ *
258
+ * The loader receives a POSIX-normalized filepath or virtual specifier before
259
+ * normal resolution runs. Return `undefined` to fall through to the default
260
+ * import path. Non-undefined hits use the same default unwrapping semantics as
261
+ * normal imports, including `importDefaultOnly` and double-default `__esModule`
262
+ * compatibility.
263
+ */
264
+ function setBundleModuleLoader(loader) {
265
+ globalThis.__EGG_BUNDLE_MODULE_LOADER__ = loader;
266
+ }
212
267
  async function importModule(filepath, options) {
268
+ const _bundleModuleLoader = globalThis.__EGG_BUNDLE_MODULE_LOADER__;
269
+ if (_bundleModuleLoader) {
270
+ const hit = _bundleModuleLoader(normalizeBundleModulePath(filepath));
271
+ if (hit !== void 0) {
272
+ let obj$1 = hit;
273
+ if (obj$1?.default?.__esModule === true && "default" in obj$1.default) obj$1 = obj$1.default;
274
+ if (options?.importDefaultOnly && obj$1 && typeof obj$1 === "object" && "default" in obj$1) obj$1 = obj$1.default;
275
+ return obj$1;
276
+ }
277
+ }
213
278
  const moduleFilePath = importResolve(filepath, options);
279
+ if (_snapshotModuleLoader) {
280
+ let obj$1 = _snapshotModuleLoader(moduleFilePath);
281
+ if (obj$1 && typeof obj$1 === "object" && obj$1.default?.__esModule === true && obj$1.default && "default" in obj$1.default) obj$1 = obj$1.default;
282
+ if (options?.importDefaultOnly) {
283
+ if (obj$1 && typeof obj$1 === "object" && "default" in obj$1) obj$1 = obj$1.default;
284
+ }
285
+ return obj$1;
286
+ }
214
287
  let obj;
215
288
  if (isESM) {
216
289
  const fileUrl = pathToFileURL(moduleFilePath).toString();
217
290
  debug("[importModule:start] await import fileUrl: %s, isESM: %s", fileUrl, isESM);
218
- obj = await import(fileUrl);
291
+ /* v8 ignore if -- covered by the spawned Node fixture; Vitest cannot instrument this opaque import. */
292
+ if (_bundleModuleLoader) obj = await getNativeDynamicImport()(fileUrl);
293
+ else obj = await import(fileUrl);
219
294
  debug("[importModule:success] await import %o", fileUrl);
220
- if (obj?.default?.__esModule === true && "default" in obj?.default) obj = obj.default;
295
+ if (obj?.default?.__esModule === true && obj.default && "default" in obj.default) obj = obj.default;
221
296
  if (options?.importDefaultOnly) {
222
297
  if ("default" in obj) obj = obj.default;
223
298
  }
@@ -231,4 +306,4 @@ async function importModule(filepath, options) {
231
306
  }
232
307
 
233
308
  //#endregion
234
- export { getExtensions, getRequire, importModule, importResolve, isESM, isSupportTypeScript };
309
+ export { getExtensions, getRequire, importModule, importResolve, isESM, isSupportTypeScript, setBundleModuleLoader, setSnapshotModuleLoader };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getFrameworkOrEggPath } from "./deprecated.js";
2
2
  import { getFrameworkPath } from "./framework.js";
3
3
  import { findEggCore, getConfig, getLoadUnits, getLoader, getPlugins } from "./plugin.js";
4
- import { ImportModuleOptions, ImportResolveOptions, getExtensions, getRequire, importModule, importResolve, isESM, isSupportTypeScript } from "./import.js";
4
+ import { BundleModuleLoader, ImportModuleOptions, ImportResolveOptions, SnapshotModuleLoader, getExtensions, getRequire, importModule, importResolve, isESM, isSupportTypeScript, setBundleModuleLoader, setSnapshotModuleLoader } from "./import.js";
5
5
  import { ImportResolveError } from "./error/ImportResolveError.js";
6
6
 
7
7
  //#region src/index.d.ts
@@ -25,4 +25,4 @@ type EggType = (typeof EggType)[keyof typeof EggType];
25
25
  */
26
26
  declare function detectType(baseDir: string): Promise<keyof typeof EggType>;
27
27
  //#endregion
28
- export { EggType, ImportModuleOptions, ImportResolveError, ImportResolveOptions, _default as default, detectType, findEggCore, getConfig, getExtensions, getFrameworkOrEggPath, getFrameworkPath, getLoadUnits, getLoader, getPlugins, getRequire, importModule, importResolve, isESM, isSupportTypeScript };
28
+ export { BundleModuleLoader, EggType, ImportModuleOptions, ImportResolveError, ImportResolveOptions, SnapshotModuleLoader, _default as default, detectType, findEggCore, getConfig, getExtensions, getFrameworkOrEggPath, getFrameworkPath, getLoadUnits, getLoader, getPlugins, getRequire, importModule, importResolve, isESM, isSupportTypeScript, setBundleModuleLoader, setSnapshotModuleLoader };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getFrameworkOrEggPath } from "./deprecated.js";
2
2
  import { ImportResolveError } from "./error/ImportResolveError.js";
3
- import { getExtensions, getRequire, importModule, importResolve, isESM, isSupportTypeScript } from "./import.js";
3
+ import { getExtensions, getRequire, importModule, importResolve, isESM, isSupportTypeScript, setBundleModuleLoader, setSnapshotModuleLoader } from "./import.js";
4
4
  import { getFrameworkPath } from "./framework.js";
5
5
  import { findEggCore, getConfig, getLoadUnits, getLoader, getPlugins } from "./plugin.js";
6
6
  import fs from "node:fs/promises";
@@ -42,4 +42,4 @@ async function detectType(baseDir) {
42
42
  }
43
43
 
44
44
  //#endregion
45
- export { EggType, ImportResolveError, src_default as default, detectType, findEggCore, getConfig, getExtensions, getFrameworkOrEggPath, getFrameworkPath, getLoadUnits, getLoader, getPlugins, getRequire, importModule, importResolve, isESM, isSupportTypeScript };
45
+ export { EggType, ImportResolveError, src_default as default, detectType, findEggCore, getConfig, getExtensions, getFrameworkOrEggPath, getFrameworkPath, getLoadUnits, getLoader, getPlugins, getRequire, importModule, importResolve, isESM, isSupportTypeScript, setBundleModuleLoader, setSnapshotModuleLoader };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eggjs/utils",
3
- "version": "5.0.2-beta.0",
3
+ "version": "5.0.2-beta.10",
4
4
  "description": "Utils for all egg projects",
5
5
  "keywords": [
6
6
  "egg",
@@ -28,7 +28,9 @@
28
28
  "publishConfig": {
29
29
  "access": "public"
30
30
  },
31
- "dependencies": {},
31
+ "dependencies": {
32
+ "@eggjs/typings": "4.1.2-beta.10"
33
+ },
32
34
  "devDependencies": {
33
35
  "coffee": "5",
34
36
  "mm": "^4.0.2",