Skip to content

Commit f1cd095

Browse files
authored
feat: modern server static should always use system fs (#6924)
1 parent 0d47cb8 commit f1cd095

File tree

3 files changed

+75
-29
lines changed

3 files changed

+75
-29
lines changed

.changeset/metal-walls-matter.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@modern-js/runtime-utils': patch
3+
'@modern-js/server-core': patch
4+
---
5+
6+
feat: Modern.js server static middleware should always use system fs
7+
feat: Modern.js 的静态中间件应该始终使用系统 fs

packages/server/core/src/adapters/node/plugins/static.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ export interface ServerStaticOptions {
9494
routes?: ServerRoute[];
9595
}
9696

97+
/**
98+
* This middleware is used to serve static assets
99+
* TODO: In next major version, only serve static assets in the `static` and `upload` directory.
100+
*
101+
* 1. In dev mode, the static assets generated by bundler will be served by the rsbuildDevMiddleware, and other file in `static` directory will be served by this middleware.
102+
* 2. In prod mode, all the static assets in `static` and `upload` directory will be served by this middleware.
103+
* 3. So some file not in `static` can be access in dev mode, but not in prod mode. Cause we can not serve all files in prod mode, as we should not expose server code in prod mode.
104+
* 4. Through Modern.js not serve this file in prod mode, you can upload the files to a CDN.
105+
*/
97106
export function createStaticMiddleware(
98107
options: ServerStaticOptions,
99108
): Middleware {
@@ -107,7 +116,9 @@ export function createStaticMiddleware(
107116
const favicons = prepareFavicons(favicon, faviconByEntries);
108117
const staticFiles = [cssPath, jsPath, mediaPath].filter(v => Boolean(v));
109118

119+
// TODO: If possible, we should not use `...staticFiles` here, file should only be read in static and upload dir.
110120
const staticReg = ['static/', 'upload/', ...staticFiles];
121+
// TODO: Also remove iconReg
111122
const iconReg = ['favicon.ico', 'icon.png', ...favicons];
112123
const regPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
113124
const staticPathRegExp = new RegExp(
@@ -147,7 +158,8 @@ export function createStaticMiddleware(
147158
}
148159
const stat = await fs.lstat(filepath);
149160
const { size } = stat;
150-
const chunk = await fileReader.readFile(filepath, 'buffer');
161+
// serve static middleware always read file from real filesystem.
162+
const chunk = await fileReader.readFileFromSystem(filepath, 'buffer');
151163

152164
// TODO: handle http range
153165
c.header('Content-Length', String(size));

packages/toolkit/runtime-utils/src/node/fileReader.ts

+55-28
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,66 @@ export class FileReader {
2020
encoding: 'utf-8' | 'buffer' = 'utf-8',
2121
): Promise<string | Buffer | null> {
2222
const { fs } = this;
23-
const cache = await this.storage.get(path);
24-
if (cache === null) {
25-
return null;
26-
}
27-
if (cache) {
28-
return this.encodingContent(cache, encoding);
29-
}
23+
const _readfile = this._readFileFactory(fs);
24+
return _readfile(path, encoding);
25+
}
26+
27+
async readFileFromSystem(
28+
path: string,
29+
encoding?: 'utf-8',
30+
): Promise<string | null>;
31+
async readFileFromSystem(
32+
path: string,
33+
encoding?: 'buffer',
34+
): Promise<Buffer | null>;
35+
async readFileFromSystem(
36+
path: string,
37+
encoding: 'utf-8' | 'buffer' = 'utf-8',
38+
): Promise<string | Buffer | null> {
39+
console.log(path, '!!');
40+
const _readfile = this._readFileFactory(Fs);
41+
return _readfile(path, encoding);
42+
}
43+
44+
_readFileFactory(fs: typeof Fs) {
45+
return async (
46+
path: string,
47+
encoding: 'utf-8' | 'buffer' = 'utf-8',
48+
): Promise<string | Buffer | null> => {
49+
const cache = await this.storage.get(path);
50+
if (cache === null) {
51+
return null;
52+
}
53+
if (cache) {
54+
return this.encodingContent(cache, encoding);
55+
}
3056

31-
const isExistFile = await new Promise(resolve => {
32-
fs.stat(path, (err, stats) => {
33-
if (err) {
34-
resolve(false);
35-
return;
36-
}
37-
if (stats.isFile()) {
38-
resolve(true);
39-
} else {
40-
resolve(false);
41-
}
57+
const isExistFile = await new Promise(resolve => {
58+
fs.stat(path, (err, stats) => {
59+
if (err) {
60+
resolve(false);
61+
return;
62+
}
63+
if (stats.isFile()) {
64+
resolve(true);
65+
} else {
66+
resolve(false);
67+
}
68+
});
4269
});
43-
});
4470

45-
if (isExistFile) {
46-
const content = await fs.promises.readFile(path);
71+
if (isExistFile) {
72+
const content = await fs.promises.readFile(path);
4773

48-
this.storage.set(path, content);
74+
this.storage.set(path, content);
4975

50-
return this.encodingContent(content, encoding);
51-
} else {
52-
// if is not exist, return null value.
53-
this.storage.set(path, null);
54-
return null;
55-
}
76+
return this.encodingContent(content, encoding);
77+
} else {
78+
// if is not exist, return null value.
79+
this.storage.set(path, null);
80+
return null;
81+
}
82+
};
5683
}
5784

5885
/**

0 commit comments

Comments
 (0)