@eggjs/core 7.0.2-beta.2 → 7.0.2-beta.22
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/dist/egg.d.ts +25 -0
- package/dist/egg.js +21 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.js +6 -1
- package/dist/lifecycle.d.ts +62 -1
- package/dist/lifecycle.js +131 -8
- package/dist/loader/egg_loader.d.ts +15 -0
- package/dist/loader/egg_loader.js +195 -17
- package/dist/loader/file_loader.d.ts +10 -3
- package/dist/loader/file_loader.js +18 -10
- package/dist/loader/loader_fs.d.ts +17 -0
- package/dist/loader/loader_fs.js +271 -0
- package/dist/loader/manifest.d.ts +98 -0
- package/dist/loader/manifest.js +341 -0
- package/package.json +23 -16
|
@@ -3,6 +3,7 @@ import { sequencify } from "../utils/sequencify.js";
|
|
|
3
3
|
import { Timing } from "../utils/timing.js";
|
|
4
4
|
import { CaseStyle, FULLPATH, FileLoader } from "./file_loader.js";
|
|
5
5
|
import { ContextLoader } from "./context_loader.js";
|
|
6
|
+
import { ManifestStore } from "./manifest.js";
|
|
6
7
|
import fs from "node:fs";
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
import { debuglog, inspect } from "node:util";
|
|
@@ -11,14 +12,35 @@ import assert from "node:assert";
|
|
|
11
12
|
import { Application, Context, Request, Response } from "@eggjs/koa";
|
|
12
13
|
import { isAsyncFunction, isClass, isGeneratorFunction, isObject, isPromise } from "is-type-of";
|
|
13
14
|
import { extend } from "@eggjs/extend2";
|
|
15
|
+
import { RealLoaderFS } from "@eggjs/loader-fs";
|
|
14
16
|
import { pathMatching } from "@eggjs/path-matching";
|
|
17
|
+
import globby from "globby";
|
|
15
18
|
import { homedir } from "node-homedir";
|
|
16
19
|
import { diff, now } from "performance-ms";
|
|
17
20
|
import { register } from "tsconfig-paths";
|
|
18
|
-
import {
|
|
21
|
+
import { getParamNames } from "utility";
|
|
19
22
|
|
|
20
23
|
//#region src/loader/egg_loader.ts
|
|
21
24
|
const debug = debuglog("egg/core/loader/egg_loader");
|
|
25
|
+
const CONVENTIONAL_MANIFEST_LOADS = [
|
|
26
|
+
{
|
|
27
|
+
type: "resolve",
|
|
28
|
+
path: ["agent"]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: "resolve",
|
|
32
|
+
path: ["app"]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: "discover",
|
|
36
|
+
path: ["app", "extend"],
|
|
37
|
+
extensionlessResolve: true
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: "discover",
|
|
41
|
+
path: ["app", "middleware"]
|
|
42
|
+
}
|
|
43
|
+
];
|
|
22
44
|
const originalPrototypes = {
|
|
23
45
|
request: Request.prototype,
|
|
24
46
|
response: Response.prototype,
|
|
@@ -34,7 +56,11 @@ var EggLoader = class {
|
|
|
34
56
|
serverEnv;
|
|
35
57
|
serverScope;
|
|
36
58
|
appInfo;
|
|
59
|
+
outDir;
|
|
37
60
|
dirs;
|
|
61
|
+
/** Startup manifest — loaded from cache or collecting for generation */
|
|
62
|
+
manifest;
|
|
63
|
+
loaderFS;
|
|
38
64
|
/**
|
|
39
65
|
* @class
|
|
40
66
|
* @param {Object} options - options
|
|
@@ -46,6 +72,7 @@ var EggLoader = class {
|
|
|
46
72
|
*/
|
|
47
73
|
constructor(options) {
|
|
48
74
|
this.options = options;
|
|
75
|
+
this.loaderFS = this.options.loaderFS ?? new RealLoaderFS();
|
|
49
76
|
assert(fs.existsSync(this.options.baseDir), `${this.options.baseDir} not exists`);
|
|
50
77
|
assert(this.options.app, "options.app is required");
|
|
51
78
|
assert(this.options.logger, "options.logger is required");
|
|
@@ -55,7 +82,8 @@ var EggLoader = class {
|
|
|
55
82
|
* @see {@link AppInfo#pkg}
|
|
56
83
|
* @since 1.0.0
|
|
57
84
|
*/
|
|
58
|
-
this.pkg =
|
|
85
|
+
this.pkg = this.loaderFS.readJSON(path.join(this.options.baseDir, "package.json"));
|
|
86
|
+
this.outDir = this.#resolveOutDir();
|
|
59
87
|
if (process.env.EGG_TYPESCRIPT === "true" || this.pkg.egg && this.pkg.egg.typescript) {
|
|
60
88
|
const tsConfigFile = path.join(this.options.baseDir, "tsconfig.json");
|
|
61
89
|
if (fs.existsSync(tsConfigFile)) register({ cwd: this.options.baseDir });
|
|
@@ -102,6 +130,7 @@ var EggLoader = class {
|
|
|
102
130
|
* @since 1.0.0
|
|
103
131
|
*/
|
|
104
132
|
this.appInfo = this.getAppInfo();
|
|
133
|
+
this.manifest = ManifestStore.load(this.options.baseDir, this.serverEnv, this.serverScope) ?? ManifestStore.createCollector(this.options.baseDir);
|
|
105
134
|
}
|
|
106
135
|
get app() {
|
|
107
136
|
return this.options.app;
|
|
@@ -382,17 +411,14 @@ var EggLoader = class {
|
|
|
382
411
|
let pkg;
|
|
383
412
|
let eggPluginConfig;
|
|
384
413
|
const pluginPackage = path.join(plugin.path, "package.json");
|
|
385
|
-
if (
|
|
386
|
-
pkg = await
|
|
414
|
+
if (this.loaderFS.exists(pluginPackage)) {
|
|
415
|
+
pkg = await this.loaderFS.loadFile(pluginPackage);
|
|
387
416
|
eggPluginConfig = pkg.eggPlugin;
|
|
388
417
|
if (pkg.version) plugin.version = pkg.version;
|
|
389
418
|
plugin.path = await this.#formatPluginPathFromPackageJSON(plugin.path, pkg);
|
|
390
419
|
}
|
|
420
|
+
if (!eggPluginConfig) return;
|
|
391
421
|
const logger = this.options.logger;
|
|
392
|
-
if (!eggPluginConfig) {
|
|
393
|
-
logger.warn("[@eggjs/core/egg_loader] pkg.eggPlugin is missing in %s, plugin: %j", pluginPackage, plugin);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
422
|
if (eggPluginConfig.name && eggPluginConfig.strict !== false && eggPluginConfig.name !== plugin.name) logger.warn(`[@eggjs/core/egg_loader] pluginName(${plugin.name}) is different from pluginConfigName(${eggPluginConfig.name})`);
|
|
397
423
|
depCompatible(eggPluginConfig);
|
|
398
424
|
for (const key of [
|
|
@@ -455,10 +481,60 @@ var EggLoader = class {
|
|
|
455
481
|
return lookupDirs;
|
|
456
482
|
}
|
|
457
483
|
getPluginPath(plugin) {
|
|
458
|
-
if (plugin.path) return plugin.path;
|
|
484
|
+
if (plugin.path && !this.#isBundlePluginPathArtifact(plugin.path)) return plugin.path;
|
|
459
485
|
if (plugin.package) assert(isValidatePackageName(plugin.package), `plugin ${plugin.name} invalid, use 'path' instead of package: "${plugin.package}"`);
|
|
486
|
+
if (this.#isBundleModeForThisApp() && (!plugin.path || this.#isBundlePluginPathArtifact(plugin.path))) return this.#resolveBundlePluginPath(plugin);
|
|
460
487
|
return this.#resolvePluginPath(plugin);
|
|
461
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* In bundle mode a plugin declared via `definePluginFactory({ path: import.meta.dirname })`
|
|
491
|
+
* carries a `path` rewritten by the bundler to the bundle output directory (= baseDir),
|
|
492
|
+
* which does not contain the plugin's own files. Detect that case so the plugin is
|
|
493
|
+
* re-resolved by package name instead.
|
|
494
|
+
*/
|
|
495
|
+
/**
|
|
496
|
+
* Whether an active bundle store belongs to THIS app. The store is shared via globalThis
|
|
497
|
+
* across @eggjs/core copies, so one registered for a different app must not reinterpret this
|
|
498
|
+
* app's plugin paths — mirror `ManifestStore.load()`'s `bundleStore.baseDir === baseDir` gate.
|
|
499
|
+
*/
|
|
500
|
+
#isBundleModeForThisApp() {
|
|
501
|
+
const bundleStore = ManifestStore.getBundleStore();
|
|
502
|
+
return !!bundleStore && path.resolve(bundleStore.baseDir) === path.resolve(this.options.baseDir);
|
|
503
|
+
}
|
|
504
|
+
#isBundlePluginPathArtifact(pluginPath) {
|
|
505
|
+
if (!this.#isBundleModeForThisApp()) return false;
|
|
506
|
+
return path.resolve(pluginPath) === path.resolve(this.options.baseDir);
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Re-resolve a bundle-artifact plugin path to the directory of the plugin package's entry
|
|
510
|
+
* module — the same directory `definePluginFactory` captured via `import.meta.dirname` at
|
|
511
|
+
* build time. Built-in framework plugins only carry a `name` (no `package`), so fall back to
|
|
512
|
+
* the conventional `@eggjs/<name>` package name in addition to the bare name.
|
|
513
|
+
*/
|
|
514
|
+
#resolveBundlePluginPath(plugin) {
|
|
515
|
+
const candidates = plugin.package ? [plugin.package] : plugin.name.includes("/") ? [plugin.name] : [plugin.name, `@eggjs/${plugin.name}`];
|
|
516
|
+
let lastErr;
|
|
517
|
+
for (const name$1 of candidates) try {
|
|
518
|
+
let realDir;
|
|
519
|
+
try {
|
|
520
|
+
realDir = path.dirname(utils_default.resolvePath(name$1, { paths: [...this.lookupDirs] }));
|
|
521
|
+
} catch {
|
|
522
|
+
realDir = path.dirname(utils_default.resolvePath(`${name$1}/package.json`, { paths: [...this.lookupDirs] }));
|
|
523
|
+
}
|
|
524
|
+
const segments = realDir.split(/[/\\]/);
|
|
525
|
+
const nmIdx = segments.lastIndexOf("node_modules");
|
|
526
|
+
if (nmIdx !== -1) {
|
|
527
|
+
const rebased = path.join(this.options.baseDir, ...segments.slice(nmIdx));
|
|
528
|
+
if (fs.existsSync(rebased)) return rebased;
|
|
529
|
+
}
|
|
530
|
+
return realDir;
|
|
531
|
+
} catch (err) {
|
|
532
|
+
lastErr = err;
|
|
533
|
+
}
|
|
534
|
+
const name = plugin.package || plugin.name;
|
|
535
|
+
debug("[resolveBundlePluginPath] error: %o, plugin info: %o", lastErr, plugin);
|
|
536
|
+
throw new Error(`Can not find plugin ${name} in "${[...this.lookupDirs].join(", ")}"`, { cause: lastErr });
|
|
537
|
+
}
|
|
462
538
|
#resolvePluginPath(plugin) {
|
|
463
539
|
const name = plugin.package || plugin.name;
|
|
464
540
|
try {
|
|
@@ -476,7 +552,7 @@ var EggLoader = class {
|
|
|
476
552
|
if (isESM) {
|
|
477
553
|
if (exports.import) realPluginPath = path.join(pluginPath, exports.import);
|
|
478
554
|
} else if (exports.require) realPluginPath = path.join(pluginPath, exports.require);
|
|
479
|
-
if (exports.typescript && isSupportTypeScript() && !
|
|
555
|
+
if (exports.typescript && isSupportTypeScript() && !this.loaderFS.exists(realPluginPath)) {
|
|
480
556
|
realPluginPath = path.join(pluginPath, exports.typescript);
|
|
481
557
|
debug("[formatPluginPathFromPackageJSON] use typescript path %o", realPluginPath);
|
|
482
558
|
}
|
|
@@ -742,14 +818,16 @@ var EggLoader = class {
|
|
|
742
818
|
*/
|
|
743
819
|
async loadCustomApp() {
|
|
744
820
|
await this.#loadBootHook("app");
|
|
745
|
-
this.lifecycle.
|
|
821
|
+
if (this.options.metadataOnly) await this.lifecycle.triggerLoadMetadata();
|
|
822
|
+
else this.lifecycle.triggerConfigWillLoad();
|
|
746
823
|
}
|
|
747
824
|
/**
|
|
748
825
|
* Load agent.js, same as {@link EggLoader#loadCustomApp}
|
|
749
826
|
*/
|
|
750
827
|
async loadCustomAgent() {
|
|
751
828
|
await this.#loadBootHook("agent");
|
|
752
|
-
this.lifecycle.
|
|
829
|
+
if (this.options.metadataOnly) await this.lifecycle.triggerLoadMetadata();
|
|
830
|
+
else this.lifecycle.triggerConfigWillLoad();
|
|
753
831
|
}
|
|
754
832
|
loadBootHook() {}
|
|
755
833
|
async #loadBootHook(fileName) {
|
|
@@ -1042,7 +1120,9 @@ var EggLoader = class {
|
|
|
1042
1120
|
...options,
|
|
1043
1121
|
directory: options?.directory ?? directory,
|
|
1044
1122
|
target,
|
|
1045
|
-
inject: this.app
|
|
1123
|
+
inject: this.app,
|
|
1124
|
+
manifest: this.manifest,
|
|
1125
|
+
loaderFS: options?.loaderFS ?? this.loaderFS
|
|
1046
1126
|
};
|
|
1047
1127
|
const timingKey = `Load "${String(property)}" to Application`;
|
|
1048
1128
|
this.timing.start(timingKey);
|
|
@@ -1061,7 +1141,9 @@ var EggLoader = class {
|
|
|
1061
1141
|
...options,
|
|
1062
1142
|
directory: options?.directory || directory,
|
|
1063
1143
|
property,
|
|
1064
|
-
inject: this.app
|
|
1144
|
+
inject: this.app,
|
|
1145
|
+
manifest: this.manifest,
|
|
1146
|
+
loaderFS: options?.loaderFS ?? this.loaderFS
|
|
1065
1147
|
};
|
|
1066
1148
|
const timingKey = `Load "${String(property)}" to Context`;
|
|
1067
1149
|
this.timing.start(timingKey);
|
|
@@ -1091,14 +1173,110 @@ var EggLoader = class {
|
|
|
1091
1173
|
return files;
|
|
1092
1174
|
}
|
|
1093
1175
|
resolveModule(filepath) {
|
|
1176
|
+
return this.manifest.resolveModule(filepath, () => this.#doResolveModule(filepath));
|
|
1177
|
+
}
|
|
1178
|
+
#doResolveModule(filepath) {
|
|
1094
1179
|
let fullPath;
|
|
1095
1180
|
try {
|
|
1096
1181
|
fullPath = utils_default.resolvePath(filepath);
|
|
1097
|
-
} catch {
|
|
1098
|
-
|
|
1099
|
-
}
|
|
1182
|
+
} catch {}
|
|
1183
|
+
if (!fullPath) fullPath = this.#resolveFromOutDir(filepath);
|
|
1100
1184
|
return fullPath;
|
|
1101
1185
|
}
|
|
1186
|
+
#resolveOutDir() {
|
|
1187
|
+
if (this.pkg.egg?.outDir) {
|
|
1188
|
+
debug("[resolveOutDir] use pkg.egg.outDir: %o", this.pkg.egg.outDir);
|
|
1189
|
+
return this.pkg.egg.outDir;
|
|
1190
|
+
}
|
|
1191
|
+
const tsConfigFile = path.join(this.options.baseDir, "tsconfig.json");
|
|
1192
|
+
if (fs.existsSync(tsConfigFile)) try {
|
|
1193
|
+
const tsConfig = JSON.parse(fs.readFileSync(tsConfigFile, "utf-8"));
|
|
1194
|
+
if (tsConfig.compilerOptions?.outDir) {
|
|
1195
|
+
debug("[resolveOutDir] use tsconfig.json compilerOptions.outDir: %o", tsConfig.compilerOptions.outDir);
|
|
1196
|
+
return tsConfig.compilerOptions.outDir;
|
|
1197
|
+
}
|
|
1198
|
+
} catch {}
|
|
1199
|
+
}
|
|
1200
|
+
#resolveFromOutDir(filepath) {
|
|
1201
|
+
if (!this.outDir) return;
|
|
1202
|
+
const baseDir = this.options.baseDir;
|
|
1203
|
+
if (!filepath.startsWith(baseDir + path.sep)) return;
|
|
1204
|
+
const relativePath = path.relative(baseDir, filepath);
|
|
1205
|
+
for (const ext of [".js", ".mjs"]) {
|
|
1206
|
+
const outDirPath = path.join(baseDir, this.outDir, relativePath + ext);
|
|
1207
|
+
if (fs.existsSync(outDirPath)) {
|
|
1208
|
+
debug("[resolveModule:outDir] %o => %o", filepath, outDirPath);
|
|
1209
|
+
return outDirPath;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Generate startup manifest from collected data.
|
|
1215
|
+
* Should be called after all loading phases complete.
|
|
1216
|
+
*/
|
|
1217
|
+
generateManifest() {
|
|
1218
|
+
const manifest = this.manifest.generateManifest({
|
|
1219
|
+
serverEnv: this.serverEnv,
|
|
1220
|
+
serverScope: this.serverScope,
|
|
1221
|
+
typescriptEnabled: isSupportTypeScript()
|
|
1222
|
+
});
|
|
1223
|
+
this.#collectConventionalDynamicFiles(manifest);
|
|
1224
|
+
return manifest;
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* metadataOnly startup intentionally skips the agent process, but bundled
|
|
1228
|
+
* single-mode workers still load agent boot hooks and agent extends later.
|
|
1229
|
+
* Record convention-based dynamic entry points so the bundle can satisfy
|
|
1230
|
+
* those runtime lookups without running agent lifecycle hooks at manifest
|
|
1231
|
+
* generation time.
|
|
1232
|
+
*/
|
|
1233
|
+
#collectConventionalDynamicFiles(manifest) {
|
|
1234
|
+
const resolveCacheTargets = new Set(Object.values(manifest.resolveCache).filter((target) => typeof target === "string"));
|
|
1235
|
+
for (const unit of this.getLoadUnits()) {
|
|
1236
|
+
this.#collectConventionFile(manifest, path.join(unit.path, "package.json"), resolveCacheTargets);
|
|
1237
|
+
for (const load of CONVENTIONAL_MANIFEST_LOADS) {
|
|
1238
|
+
const target = path.join(unit.path, ...load.path);
|
|
1239
|
+
if (load.type === "resolve") this.#collectConventionResolve(manifest, target, resolveCacheTargets);
|
|
1240
|
+
else if ("extensionlessResolve" in load && load.extensionlessResolve) this.#collectConventionFileResolves(manifest, target, resolveCacheTargets);
|
|
1241
|
+
else this.#collectConventionFileDiscovery(manifest, target);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
#collectConventionResolve(manifest, request, resolveCacheTargets) {
|
|
1246
|
+
const requestKey = this.#toManifestRel(request);
|
|
1247
|
+
if (Object.hasOwn(manifest.resolveCache, requestKey)) return;
|
|
1248
|
+
const resolved = this.#doResolveModule(request);
|
|
1249
|
+
const resolvedKey = resolved ? this.#toManifestRel(resolved) : null;
|
|
1250
|
+
manifest.resolveCache[requestKey] = resolvedKey;
|
|
1251
|
+
if (resolvedKey !== null) resolveCacheTargets.add(resolvedKey);
|
|
1252
|
+
}
|
|
1253
|
+
#collectConventionFileResolves(manifest, directory, resolveCacheTargets) {
|
|
1254
|
+
const files = this.#collectConventionFileDiscovery(manifest, directory);
|
|
1255
|
+
for (const file of files) {
|
|
1256
|
+
const ext = path.extname(file);
|
|
1257
|
+
if (!ext) continue;
|
|
1258
|
+
const request = path.join(directory, file.slice(0, -ext.length));
|
|
1259
|
+
this.#collectConventionResolve(manifest, request, resolveCacheTargets);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
#collectConventionFileDiscovery(manifest, directory) {
|
|
1263
|
+
const dirKey = this.#toManifestRel(directory);
|
|
1264
|
+
if (Object.hasOwn(manifest.fileDiscovery, dirKey)) return manifest.fileDiscovery[dirKey];
|
|
1265
|
+
manifest.fileDiscovery[dirKey] = fs.existsSync(directory) && fs.statSync(directory).isDirectory() ? globby.sync(FileLoader.getDefaultMatch(), { cwd: directory }).sort() : [];
|
|
1266
|
+
return manifest.fileDiscovery[dirKey];
|
|
1267
|
+
}
|
|
1268
|
+
#collectConventionFile(manifest, filepath, resolveCacheTargets) {
|
|
1269
|
+
const fileKey = this.#toManifestRel(filepath);
|
|
1270
|
+
if (resolveCacheTargets.has(fileKey)) return;
|
|
1271
|
+
if (!fs.existsSync(filepath) || !fs.statSync(filepath).isFile()) return;
|
|
1272
|
+
const dirKey = this.#toManifestRel(path.dirname(filepath));
|
|
1273
|
+
const basename = path.basename(filepath);
|
|
1274
|
+
const files = manifest.fileDiscovery[dirKey] ?? [];
|
|
1275
|
+
if (!files.includes(basename)) manifest.fileDiscovery[dirKey] = [...files, basename].sort();
|
|
1276
|
+
}
|
|
1277
|
+
#toManifestRel(filepath) {
|
|
1278
|
+
return (path.isAbsolute(filepath) ? path.relative(this.options.baseDir, filepath) : filepath).replaceAll(path.sep, "/");
|
|
1279
|
+
}
|
|
1102
1280
|
};
|
|
1103
1281
|
function depCompatible(plugin) {
|
|
1104
1282
|
if (plugin.dep && !(Array.isArray(plugin.dependencies) && plugin.dependencies.length > 0)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ManifestStore } from "./manifest.js";
|
|
2
|
+
import { LoaderFS } from "@eggjs/loader-fs";
|
|
2
3
|
|
|
3
4
|
//#region src/loader/file_loader.d.ts
|
|
4
5
|
declare const FULLPATH: unique symbol;
|
|
@@ -37,12 +38,17 @@ interface FileLoaderOptions {
|
|
|
37
38
|
/** set property's case when converting a filepath to property list. */
|
|
38
39
|
caseStyle?: CaseStyle | CaseStyleFunction;
|
|
39
40
|
lowercaseFirst?: boolean;
|
|
41
|
+
/** Startup manifest for caching globby scans and collecting results */
|
|
42
|
+
manifest?: ManifestStore;
|
|
43
|
+
/** Loader-facing filesystem abstraction */
|
|
44
|
+
loaderFS?: LoaderFS;
|
|
40
45
|
}
|
|
41
46
|
interface FileLoaderParseItem {
|
|
42
47
|
fullpath: string;
|
|
43
48
|
properties: string[];
|
|
44
|
-
exports:
|
|
49
|
+
exports: unknown;
|
|
45
50
|
}
|
|
51
|
+
type NormalizedFileLoaderOptions = FileLoaderOptions & Required<Pick<FileLoaderOptions, "caseStyle" | "loaderFS">>;
|
|
46
52
|
/**
|
|
47
53
|
* Load files from directory to target object.
|
|
48
54
|
* @since 1.0.0
|
|
@@ -50,7 +56,8 @@ interface FileLoaderParseItem {
|
|
|
50
56
|
declare class FileLoader {
|
|
51
57
|
static get FULLPATH(): typeof FULLPATH;
|
|
52
58
|
static get EXPORTS(): typeof EXPORTS;
|
|
53
|
-
|
|
59
|
+
static getDefaultMatch(): string[];
|
|
60
|
+
readonly options: NormalizedFileLoaderOptions;
|
|
54
61
|
/**
|
|
55
62
|
* @class
|
|
56
63
|
* @param {Object} options - options
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import utils_default from "../utils/index.js";
|
|
2
|
-
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { debuglog } from "node:util";
|
|
5
4
|
import { isSupportTypeScript } from "@eggjs/utils";
|
|
6
5
|
import assert from "node:assert";
|
|
7
6
|
import { isAsyncFunction, isClass, isGeneratorFunction, isPrimitive } from "is-type-of";
|
|
8
|
-
import
|
|
7
|
+
import { RealLoaderFS } from "@eggjs/loader-fs";
|
|
9
8
|
|
|
10
9
|
//#region src/loader/file_loader.ts
|
|
11
10
|
const debug = debuglog("egg/core/file_loader");
|
|
@@ -16,17 +15,23 @@ const CaseStyle = {
|
|
|
16
15
|
lower: "lower",
|
|
17
16
|
upper: "upper"
|
|
18
17
|
};
|
|
18
|
+
function getDefaultFileLoaderMatch() {
|
|
19
|
+
return isSupportTypeScript() ? ["**/*.(js|ts|mjs|cjs)", "!**/*.d.ts"] : ["**/*.{js,mjs,cjs}"];
|
|
20
|
+
}
|
|
19
21
|
/**
|
|
20
22
|
* Load files from directory to target object.
|
|
21
23
|
* @since 1.0.0
|
|
22
24
|
*/
|
|
23
|
-
var FileLoader = class {
|
|
25
|
+
var FileLoader = class FileLoader {
|
|
24
26
|
static get FULLPATH() {
|
|
25
27
|
return FULLPATH;
|
|
26
28
|
}
|
|
27
29
|
static get EXPORTS() {
|
|
28
30
|
return EXPORTS;
|
|
29
31
|
}
|
|
32
|
+
static getDefaultMatch() {
|
|
33
|
+
return getDefaultFileLoaderMatch();
|
|
34
|
+
}
|
|
30
35
|
options;
|
|
31
36
|
/**
|
|
32
37
|
* @class
|
|
@@ -45,10 +50,12 @@ var FileLoader = class {
|
|
|
45
50
|
constructor(options) {
|
|
46
51
|
assert(options.directory, "options.directory is required");
|
|
47
52
|
assert(options.target, "options.target is required");
|
|
53
|
+
if (!options.manifest && options.inject) options.manifest = options.inject.loader?.manifest;
|
|
48
54
|
this.options = {
|
|
49
55
|
caseStyle: CaseStyle.camel,
|
|
50
56
|
call: true,
|
|
51
57
|
override: false,
|
|
58
|
+
loaderFS: new RealLoaderFS(),
|
|
52
59
|
...options
|
|
53
60
|
};
|
|
54
61
|
if (this.options.lowercaseFirst === true) {
|
|
@@ -114,7 +121,7 @@ var FileLoader = class {
|
|
|
114
121
|
async parse() {
|
|
115
122
|
let files = this.options.match;
|
|
116
123
|
if (files) files = Array.isArray(files) ? files : [files];
|
|
117
|
-
else files =
|
|
124
|
+
else files = FileLoader.getDefaultMatch();
|
|
118
125
|
let ignore = this.options.ignore;
|
|
119
126
|
if (ignore) {
|
|
120
127
|
ignore = Array.isArray(ignore) ? ignore : [ignore];
|
|
@@ -127,13 +134,14 @@ var FileLoader = class {
|
|
|
127
134
|
const items = [];
|
|
128
135
|
debug("[parse] parsing directories: %j", directories);
|
|
129
136
|
for (const directory of directories) {
|
|
130
|
-
const
|
|
131
|
-
|
|
137
|
+
const manifest = this.options.manifest;
|
|
138
|
+
const filepaths = manifest ? manifest.globFiles(directory, () => this.options.loaderFS.glob(files, { cwd: directory })) : this.options.loaderFS.glob(files, { cwd: directory });
|
|
139
|
+
debug("[parse] files: %o, cwd: %o => %o", files, directory, filepaths);
|
|
132
140
|
for (const filepath of filepaths) {
|
|
133
141
|
const fullpath = path.join(directory, filepath);
|
|
134
|
-
if (!
|
|
135
|
-
if (filepath.endsWith(".js")) {
|
|
136
|
-
const filepathTs = filepath.replace(/\.js$/, ".ts");
|
|
142
|
+
if (!this.options.loaderFS.stat(fullpath).isFile()) continue;
|
|
143
|
+
if (filepath.endsWith(".js") || filepath.endsWith(".mjs") || filepath.endsWith(".cjs")) {
|
|
144
|
+
const filepathTs = filepath.replace(/\.(?:js|mjs|cjs)$/, ".ts");
|
|
137
145
|
if (filepaths.includes(filepathTs)) {
|
|
138
146
|
debug("[parse] ignore %s, because %s exists", fullpath, filepathTs);
|
|
139
147
|
continue;
|
|
@@ -167,7 +175,7 @@ function getProperties(filepath, caseStyle) {
|
|
|
167
175
|
return defaultCamelize(filepath, caseStyle);
|
|
168
176
|
}
|
|
169
177
|
async function getExports(fullpath, options, pathName) {
|
|
170
|
-
let exports = await
|
|
178
|
+
let exports = await options.loaderFS.loadFile(fullpath);
|
|
171
179
|
if (options.initializer) {
|
|
172
180
|
exports = options.initializer(exports, {
|
|
173
181
|
path: fullpath,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ManifestStore } from "./manifest.js";
|
|
2
|
+
import { Stats } from "node:fs";
|
|
3
|
+
import { LoaderFS as LoaderFS$1, LoaderFSGlobOptions } from "@eggjs/loader-fs";
|
|
4
|
+
|
|
5
|
+
//#region src/loader/loader_fs.d.ts
|
|
6
|
+
declare class ManifestLoaderFS implements LoaderFS$1 {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(manifest: ManifestStore, fallback?: LoaderFS$1);
|
|
9
|
+
exists(filepath: string): boolean;
|
|
10
|
+
stat(filepath: string): Stats;
|
|
11
|
+
realpath(filepath: string): string;
|
|
12
|
+
readJSON<T = unknown>(filepath: string): T;
|
|
13
|
+
glob(patterns: string | string[], options?: LoaderFSGlobOptions): string[];
|
|
14
|
+
loadFile(filepath: string): Promise<unknown>;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { type LoaderFS$1 as LoaderFS, ManifestLoaderFS };
|