Skip to content

Commit cb198b7

Browse files
committed
feat: extract loader fs package
1 parent 0dec2c9 commit cb198b7

22 files changed

Lines changed: 249 additions & 65 deletions

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"dependencies": {
4242
"@eggjs/extend2": "workspace:*",
4343
"@eggjs/koa": "workspace:*",
44+
"@eggjs/loader-fs": "workspace:*",
4445
"@eggjs/path-matching": "workspace:*",
4546
"@eggjs/router": "workspace:*",
4647
"@eggjs/typings": "workspace:*",

packages/core/src/loader/egg_loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { debuglog, inspect } from 'node:util';
55

66
import { extend } from '@eggjs/extend2';
77
import { Request, Response, Application, Context as KoaContext } from '@eggjs/koa';
8+
import { RealLoaderFS, type LoaderFS } from '@eggjs/loader-fs';
89
import { pathMatching, type PathMatchingOptions } from '@eggjs/path-matching';
910
import { isESM, isSupportTypeScript } from '@eggjs/utils';
1011
import type { Logger } from 'egg-logger';
@@ -24,7 +25,6 @@ import { sequencify } from '../utils/sequencify.ts';
2425
import { Timing } from '../utils/timing.ts';
2526
import { type ContextLoaderOptions, ContextLoader } from './context_loader.ts';
2627
import { type FileLoaderOptions, CaseStyle, FULLPATH, FileLoader } from './file_loader.ts';
27-
import { RealLoaderFS, type LoaderFS } from './loader_fs.ts';
2828
import { ManifestStore, type StartupManifest } from './manifest.ts';
2929

3030
const debug = debuglog('egg/core/loader/egg_loader');

packages/core/src/loader/file_loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import assert from 'node:assert';
22
import path from 'node:path';
33
import { debuglog } from 'node:util';
44

5+
import { RealLoaderFS, type LoaderFS } from '@eggjs/loader-fs';
56
import { isSupportTypeScript } from '@eggjs/utils';
67
import { isClass, isGeneratorFunction, isAsyncFunction, isPrimitive } from 'is-type-of';
78

89
import utils from '../utils/index.ts';
9-
import { RealLoaderFS, type LoaderFS } from './loader_fs.ts';
1010
import type { ManifestStore } from './manifest.ts';
1111

1212
const debug = debuglog('egg/core/file_loader');
Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,2 @@
1-
import fs, { type Stats } from 'node:fs';
2-
3-
import globby from 'globby';
4-
import { readJSONSync } from 'utility';
5-
6-
import utils from '../utils/index.ts';
7-
8-
export type LoaderFSGlobOptions = globby.GlobbyOptions;
9-
10-
export interface LoaderFS {
11-
exists(filepath: string): boolean;
12-
stat(filepath: string): Stats;
13-
realpath(filepath: string): string;
14-
readJSON<T = unknown>(filepath: string): T;
15-
glob(patterns: string | string[], options?: LoaderFSGlobOptions): string[];
16-
loadFile(filepath: string): Promise<unknown>;
17-
}
18-
19-
export class RealLoaderFS implements LoaderFS {
20-
exists(filepath: string): boolean {
21-
return fs.existsSync(filepath);
22-
}
23-
24-
stat(filepath: string): Stats {
25-
return fs.statSync(filepath);
26-
}
27-
28-
realpath(filepath: string): string {
29-
return fs.realpathSync(filepath);
30-
}
31-
32-
readJSON<T = unknown>(filepath: string): T {
33-
return readJSONSync(filepath) as T;
34-
}
35-
36-
glob(patterns: string | string[], options?: LoaderFSGlobOptions): string[] {
37-
return globby.sync(patterns, options);
38-
}
39-
40-
async loadFile(filepath: string): Promise<unknown> {
41-
return utils.loadFile(filepath);
42-
}
43-
}
1+
export { RealLoaderFS } from '@eggjs/loader-fs';
2+
export type { LoaderFS, LoaderFSGlobOptions } from '@eggjs/loader-fs';

packages/core/test/loader/egg_loader.test.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,12 @@ import assert from 'node:assert/strict';
22
import os from 'node:os';
33
import path from 'node:path';
44

5+
import { RealLoaderFS, type LoaderFS } from '@eggjs/loader-fs';
56
import { getPlugins } from '@eggjs/utils';
67
import { mm } from 'mm';
78
import { describe, it, beforeAll, afterAll, afterEach } from 'vitest';
89

9-
import {
10-
ContextLoader,
11-
EggLoader,
12-
FileLoader,
13-
RealLoaderFS,
14-
type EggLoaderOptions,
15-
type LoaderFS,
16-
} from '../../src/index.js';
10+
import { ContextLoader, EggLoader, FileLoader, type EggLoaderOptions } from '../../src/index.js';
1711
import { createApp, getFilepath, type Application } from '../helper.js';
1812

1913
describe('test/loader/egg_loader.test.ts', () => {

packages/core/test/loader/file_loader.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import assert from 'node:assert/strict';
22
import path from 'node:path';
33

4+
import { RealLoaderFS, type LoaderFSGlobOptions } from '@eggjs/loader-fs';
45
import { isClass } from 'is-type-of';
56
import yaml from 'js-yaml';
67
import { describe, it, expect } from 'vitest';
78

89
import { FileLoader, CaseStyle } from '../../src/loader/file_loader.ts';
9-
import { RealLoaderFS, type LoaderFSGlobOptions } from '../../src/loader/loader_fs.ts';
1010
import { ManifestStore } from '../../src/loader/manifest.ts';
1111
import { getFilepath } from '../helper.ts';
1212

packages/core/test/loader/loader_fs.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import assert from 'node:assert/strict';
22
import fs from 'node:fs';
33
import path from 'node:path';
44

5+
import { RealLoaderFS } from '@eggjs/loader-fs';
56
import globby from 'globby';
67
import { describe, it } from 'vitest';
78

8-
import { RealLoaderFS } from '../../src/loader/loader_fs.ts';
9-
import utils from '../../src/utils/index.ts';
9+
import { RealLoaderFS as CoreRealLoaderFS } from '../../src/loader/loader_fs.ts';
1010
import { getFilepath } from '../helper.ts';
1111

1212
describe('test/loader/loader_fs.test.ts', () => {
@@ -30,7 +30,7 @@ describe('test/loader/loader_fs.test.ts', () => {
3030
assert.deepEqual(loaderFS.glob(patterns, { cwd: baseDir }).sort(), globby.sync(patterns, { cwd: baseDir }).sort());
3131
assert.deepEqual(
3232
await loaderFS.loadFile(path.join(baseDir, 'object.js')),
33-
await utils.loadFile(path.join(baseDir, 'object.js')),
33+
await new CoreRealLoaderFS().loadFile(path.join(baseDir, 'object.js')),
3434
);
3535
});
3636
});

packages/loader-fs/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# @eggjs/loader-fs
2+
3+
Minimal loader-facing filesystem abstraction for Egg loaders.
4+
5+
`LoaderFS` intentionally covers only the file operations the loader boundary needs:
6+
`exists`, `stat`, `realpath`, `readJSON`, `glob`, and `loadFile`.
7+
8+
`RealLoaderFS` is the default implementation backed by Node.js filesystem APIs,
9+
`globby`, `utility.readJSONSync`, and `@eggjs/utils` module loading behavior.

packages/loader-fs/package.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"name": "@eggjs/loader-fs",
3+
"version": "1.0.2-beta.9",
4+
"description": "Minimal loader-facing filesystem abstraction for Egg loaders",
5+
"keywords": [
6+
"egg",
7+
"filesystem",
8+
"loader"
9+
],
10+
"homepage": "https://github.com/eggjs/egg/tree/next/packages/loader-fs",
11+
"bugs": {
12+
"url": "https://github.com/eggjs/egg/issues"
13+
},
14+
"license": "MIT",
15+
"author": "fengmk2 <fengmk2@gmail.com>",
16+
"repository": {
17+
"type": "git",
18+
"url": "git+https://github.com/eggjs/egg.git",
19+
"directory": "packages/loader-fs"
20+
},
21+
"files": [
22+
"dist"
23+
],
24+
"type": "module",
25+
"main": "./dist/index.js",
26+
"module": "./dist/index.js",
27+
"types": "./dist/index.d.ts",
28+
"exports": {
29+
".": "./src/index.ts",
30+
"./package.json": "./package.json"
31+
},
32+
"publishConfig": {
33+
"access": "public",
34+
"exports": {
35+
".": "./dist/index.js",
36+
"./package.json": "./package.json"
37+
}
38+
},
39+
"scripts": {
40+
"typecheck": "tsgo --noEmit"
41+
},
42+
"dependencies": {
43+
"@eggjs/utils": "workspace:*",
44+
"globby": "catalog:",
45+
"utility": "catalog:"
46+
},
47+
"devDependencies": {
48+
"@eggjs/tsconfig": "workspace:*",
49+
"@types/node": "catalog:",
50+
"typescript": "catalog:"
51+
},
52+
"engines": {
53+
"node": ">=22.18.0"
54+
}
55+
}

packages/loader-fs/src/index.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import fs, { type Stats } from 'node:fs';
2+
import path from 'node:path';
3+
import { debuglog } from 'node:util';
4+
5+
import { getExtensions, importModule } from '@eggjs/utils';
6+
import globby from 'globby';
7+
import { readJSONSync } from 'utility';
8+
9+
const debug = debuglog('egg/loader-fs');
10+
const extensionNames = Object.keys(getExtensions()).concat(['.cjs', '.mjs']);
11+
12+
export type LoaderFSGlobOptions = globby.GlobbyOptions;
13+
14+
export interface LoaderFS {
15+
exists(filepath: string): boolean;
16+
stat(filepath: string): Stats;
17+
realpath(filepath: string): string;
18+
readJSON<T = unknown>(filepath: string): T;
19+
glob(patterns: string | string[], options?: LoaderFSGlobOptions): string[];
20+
loadFile(filepath: string): Promise<unknown>;
21+
}
22+
23+
export class RealLoaderFS implements LoaderFS {
24+
exists(filepath: string): boolean {
25+
return fs.existsSync(filepath);
26+
}
27+
28+
stat(filepath: string): Stats {
29+
return fs.statSync(filepath);
30+
}
31+
32+
realpath(filepath: string): string {
33+
return fs.realpathSync(filepath);
34+
}
35+
36+
readJSON<T = unknown>(filepath: string): T {
37+
return readJSONSync(filepath) as T;
38+
}
39+
40+
glob(patterns: string | string[], options?: LoaderFSGlobOptions): string[] {
41+
return globby.sync(patterns, options);
42+
}
43+
44+
async loadFile(filepath: string): Promise<unknown> {
45+
debug('[loadFile:start] filepath: %s', filepath);
46+
try {
47+
const extname = path.extname(filepath);
48+
if (extname && !extensionNames.includes(extname) && extname !== '.ts') {
49+
return fs.readFileSync(filepath);
50+
}
51+
return await importModule(filepath, { importDefaultOnly: true });
52+
} catch (err) {
53+
if (!(err instanceof Error)) {
54+
console.trace(err);
55+
throw err;
56+
}
57+
const error = new Error(`[egg/loader-fs] load file: ${filepath}, error: ${err.message}`);
58+
error.cause = err;
59+
debug('[loadFile] handle %s error: %s', filepath, err);
60+
throw error;
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)