Skip to content

Commit 6712067

Browse files
committed
fix(fresh/plugin-vite): fix staticFiles path encode ([#3657](#3657)),
1 parent ab14d10 commit 6712067

File tree

9 files changed

+94
-12
lines changed

9 files changed

+94
-12
lines changed

deno.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/fresh/src/dev/dev_build_cache.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,23 @@ ${serializedFsRoutes}
528528
`.replaceAll(/\n[\n]+/g, "\n\n");
529529
}
530530

531+
export function systemPathToUrlEncoded(systemPath: string) {
532+
const normalizedPath = systemPath.replaceAll(WINDOWS_SEPARATOR, "/");
533+
534+
const pathComponents = normalizedPath.split("/").filter((comp) => comp);
535+
536+
const encodedComponents = pathComponents.map((comp) =>
537+
encodeURIComponent(comp)
538+
);
539+
540+
const encodePath = "/" + encodedComponents.join("/");
541+
542+
return new URL(
543+
encodePath,
544+
"http://localhost",
545+
).pathname;
546+
}
547+
531548
export async function prepareStaticFile(
532549
item: PendingStaticFile,
533550
outDir: string,
@@ -536,10 +553,11 @@ export async function prepareStaticFile(
536553
> {
537554
const file = await Deno.open(item.filePath);
538555
const hash = item.hash ? item.hash : await hashContent(file.readable);
539-
const url = new URL(item.pathname, "http://localhost");
556+
// fix issues[#3657]: system path convert to uri path, only new URL will convert `test %20encodeUri` to `test%20%20encodeUri`
557+
const urlEncodePath = systemPathToUrlEncoded(item.pathname);
540558

541559
return {
542-
name: url.pathname,
560+
name: urlEncodePath,
543561
hash,
544562
filePath: path.isAbsolute(item.filePath)
545563
? path.relative(outDir, item.filePath)

packages/fresh/src/middlewares/static_files.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export function staticFiles<T>(): Middleware<T> {
1818
const buildCache = getBuildCache(ctx);
1919
if (buildCache === null) return await ctx.next();
2020

21-
let pathname = decodeURIComponent(url.pathname);
21+
// fix issues[#3657]: because build staticFiles use Url encode the path
22+
let pathname = url.pathname;
2223
if (config.basePath) {
2324
pathname = pathname !== config.basePath
2425
? pathname.slice(config.basePath.length)

packages/fresh/src/middlewares/static_files_test.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import { ASSET_CACHE_BUST_KEY } from "../constants.ts";
66
import { BUILD_ID } from "@fresh/build-id";
77
import type { Command } from "../commands.ts";
88
import type { ServerIslandRegistry } from "../context.ts";
9-
import { getContentType } from "../dev/dev_build_cache.ts";
9+
import {
10+
getContentType,
11+
systemPathToUrlEncoded,
12+
} from "../dev/dev_build_cache.ts";
1013

1114
class MockBuildCache implements BuildCache {
1215
root = "";
@@ -224,11 +227,25 @@ Deno.test("static files - enables caching in production", async () => {
224227
});
225228

226229
Deno.test("static files - decoded pathname", async () => {
227-
const buildCache = new MockBuildCache({
228-
"C#.svg": { content: "body {}", hash: null },
229-
"西安市.png": { content: "body {}", hash: null },
230-
"인천.avif": { content: "body {}", hash: null },
231-
});
230+
const fileKeys = [
231+
"C#.svg",
232+
"西安市.png",
233+
"인천.avif",
234+
"/开头分隔符",
235+
"\\windows\\EndSplit\\",
236+
];
237+
238+
// Simulate build
239+
const entries = await Promise.all(
240+
fileKeys.map((key) => {
241+
const encodedKey = systemPathToUrlEncoded(key);
242+
return [encodedKey, { content: "body {}", hash: null }];
243+
}),
244+
);
245+
246+
const buildCache = new MockBuildCache(
247+
Object.fromEntries(entries),
248+
);
232249
const server = serveMiddleware(
233250
staticFiles(),
234251
{ buildCache },
@@ -239,6 +256,8 @@ Deno.test("static files - decoded pathname", async () => {
239256
"C%23.svg",
240257
"%E8%A5%BF%E5%AE%89%E5%B8%82.png",
241258
"%EC%9D%B8%EC%B2%9C.avif",
259+
"%E5%BC%80%E5%A4%B4%E5%88%86%E9%9A%94%E7%AC%A6",
260+
"windows/EndSplit",
242261
]
243262
) {
244263
const res = await server.get("/" + path);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
space it works
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Document</title>
7+
</head>
8+
<body>
9+
<h1>ok</h1>
10+
</body>
11+
</html>

packages/plugin-vite/src/plugins/dev_server.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,15 @@ export function devServer(): Plugin[] {
4343
return next();
4444
}
4545

46+
const decodePathName: string = decodeURIComponent(
47+
url.pathname.slice(1),
48+
);
49+
4650
// Check if it's a static file first
47-
const staticFilePath = path.join(publicDir, url.pathname.slice(1));
51+
const staticFilePath = path.join(
52+
publicDir,
53+
decodePathName,
54+
);
4855
try {
4956
const stat = await Deno.stat(staticFilePath);
5057
if (stat.isFile) {
@@ -57,7 +64,7 @@ export function devServer(): Plugin[] {
5764
// Check if it's a static/index.html file
5865
const staticFilePathIndex = path.join(
5966
publicDir,
60-
url.pathname.slice(1),
67+
decodePathName,
6168
"index.html",
6269
);
6370
try {

packages/plugin-vite/tests/build_test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ Deno.test({
5353
const res = await fetch(`${address}/test_static/foo.txt`);
5454
const text = await res.text();
5555
expect(text).toEqual("it works");
56+
57+
// test space
58+
const resWithSpace = await fetch(
59+
`${address}/test%20%2520encodeUri/foo%20%2520encodeUri.txt`,
60+
);
61+
const textWithSpace = await resWithSpace.text();
62+
expect(textWithSpace).toEqual("space it works");
5663
},
5764
);
5865
},
@@ -482,6 +489,11 @@ Deno.test({
482489
const res = await fetch(`${address}/test_static/foo`);
483490
const text = await res.text();
484491
expect(text).toContain("<h1>ok</h1>");
492+
493+
//test encodeUri
494+
const resWithSpace = await fetch(`${address}/test%20%2520encodeUri`);
495+
const textWithSpace = await resWithSpace.text();
496+
expect(textWithSpace).toContain("<h1>ok</h1>");
485497
},
486498
);
487499
},

packages/plugin-vite/tests/dev_server_test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ Deno.test({
3737
const res = await fetch(`${demoServer.address()}/test_static/foo.txt`);
3838
const text = await res.text();
3939
expect(text).toContain("it works");
40+
41+
// test space
42+
const resWithSpace = await fetch(
43+
`${demoServer.address()}/test%20%2520encodeUri/foo%20%2520encodeUri.txt`,
44+
);
45+
const textWithSpace = await resWithSpace.text();
46+
expect(textWithSpace).toContain("space it works");
4047
},
4148
sanitizeResources: false,
4249
sanitizeOps: false,
@@ -481,6 +488,12 @@ Deno.test({
481488
const res = await fetch(`${demoServer.address()}/test_static/foo`);
482489
const text = await res.text();
483490
expect(text).toContain("<h1>ok</h1>");
491+
492+
const resWithSpace = await fetch(
493+
`${demoServer.address()}/test%20%2520encodeUri/`,
494+
);
495+
const textWithSpace = await resWithSpace.text();
496+
expect(textWithSpace).toContain("<h1>ok</h1>");
484497
},
485498
sanitizeOps: false,
486499
sanitizeResources: false,

0 commit comments

Comments
 (0)