Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions src/utils/getFilenameFromUrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ class FilenameError extends Error {
}
}

// TODO fix redirect logic when `/` at the end, like https://github.com/pillarjs/send/blob/master/index.js#L586
/**
* @template {IncomingMessage} Request
* @template {ServerResponse} Response
Expand Down Expand Up @@ -110,13 +109,26 @@ function getFilenameFromUrl(context, url) {
throw new FilenameError("Forbidden", 403);
}

// Strip the `pathname` property from the `publicPath` option from the start of requested url
// `/complex/foo.js` => `foo.js`
// and add outputPath
// `foo.js` => `/home/user/my-project/dist/foo.js`
let index;

if (pathname && pathname.endsWith("/")) {
if (options.index === false) {
return;
}
index =
typeof options.index === "string" ? options.index : "index.html";
}

// Builds the absolute path of the file to serve:
// - If the URL ends with '/', appends the index file (index.html or custom) to the directory path.
// - If the URL does not end with '/', only joins the relative path to outputPath.
// Example:
// URL: /complex/foo.js => outputPath/complex/foo.js
// URL: /complex/ => outputPath/complex/index.html (or the configured index file)
filename = path.join(
outputPath,
pathname.slice(publicPathPathname.length),
index || "",
);

try {
Expand Down
59 changes: 58 additions & 1 deletion test/middleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,10 @@ describe.each([
value: "noextension",
code: 200,
},
{
value: "noextension/",
code: 404,
},
],
},
{
Expand Down Expand Up @@ -1780,6 +1784,11 @@ describe.each([
contentType: "text/plain; charset=utf-8",
code: 200,
},
{
value: "windows%202.txt/",
contentType: get404ContentTypeHeader(name),
code: 404,
},
],
},
{
Expand Down Expand Up @@ -1945,7 +1954,7 @@ describe.each([

expect(response.statusCode).toEqual(code);

if (data) {
if (data && code !== 404) {
expect(response.headers["content-length"]).toEqual(
String(data.length),
);
Expand Down Expand Up @@ -5100,6 +5109,15 @@ describe.each([
);
});

it('should return the "404" code for the "GET" request to the "index.html" file', async () => {
const response = await req.get("/index.html/");

expect(response.statusCode).toBe(404);
expect(response.headers["content-type"]).toEqual(
get404ContentTypeHeader(name),
);
});

it('should return the "200" code for the "GET" request to the "index.html" file', async () => {
const response = await req.get("/index.html");

Expand Down Expand Up @@ -5169,10 +5187,22 @@ describe.each([
instance.context.outputFileSystem.mkdirSync(outputPath, {
recursive: true,
});

instance.context.outputFileSystem.mkdirSync(
path.resolve(outputPath, "slug"),
{
recursive: true,
},
);
instance.context.outputFileSystem.writeFileSync(
path.resolve(outputPath, "default.html"),
"hello",
);

instance.context.outputFileSystem.writeFileSync(
path.resolve(outputPath, "slug", "default.html"),
"hello",
);
});

afterAll(async () => {
Expand All @@ -5187,6 +5217,33 @@ describe.each([
"text/html; charset=utf-8",
);
});

it('should return the "200" code for the "GET" request to the "/slug/" path', async () => {
const response = await req.get("/slug/");

expect(response.statusCode).toBe(200);
expect(response.headers["content-type"]).toBe(
"text/html; charset=utf-8",
);
});

it('should return the "200" code for the "GET" request to the "/slug" path', async () => {
const response = await req.get("/slug");

expect(response.statusCode).toBe(200);
expect(response.headers["content-type"]).toBe(
"text/html; charset=utf-8",
);
});

it('should return the "404" code for the "GET" request with a non-existent file', async () => {
const response = await req.get("/default.html/");

expect(response.statusCode).toBe(404);
expect(response.headers["content-type"]).toBe(
get404ContentTypeHeader(name),
);
});
});

describe('should work with "string" value with a custom extension', () => {
Expand Down