Skip to content
Merged
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
12 changes: 0 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { mimes } from "mrmime";

import middleware from "./middleware.js";
import getFilenameFromUrl from "./utils/getFilenameFromUrl.js";
import ready from "./utils/ready.js";
Expand Down Expand Up @@ -199,16 +197,6 @@ const noop = () => {};
* @returns {API<RequestInternal, ResponseInternal>} rspack dev middleware
*/
function rdm(compiler, options = {}) {
const { mimeTypes } = options;

if (mimeTypes) {
// mrmime.lookup closes over the exported dictionary, so overrides
// need to update the object in place.
for (const [extension, type] of Object.entries(mimeTypes)) {
mimes[extension.toLowerCase()] = type;
}
}

/**
* @type {WithOptional<Context<RequestInternal, ResponseInternal>, "watching" | "outputFileSystem">}
*/
Expand Down
22 changes: 15 additions & 7 deletions src/middleware.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from "node:path";
import { finished } from "node:stream";

import { lookup, mimes } from "mrmime";
import { mimes } from "mrmime";
import rangeParser from "range-parser";

import {
Expand Down Expand Up @@ -47,14 +47,14 @@ const UTF8_CHARSET_MIME_TYPES = new Set([
"application/manifest+json",
]);

mimes.usdz = "model/vnd.usdz+zip";

/**
* @param {string} filename filename or extension
* @param {string} extension file extension including the leading dot
* @param {Map<string, string>} mimeTypes instance MIME mappings
* @returns {string | undefined} content type header value
*/
function getContentType(filename) {
const mimeType = lookup(filename);
function getContentType(extension, mimeTypes) {
const ext = extension.trim().slice(1).toLowerCase();
const mimeType = mimeTypes.get(ext) ?? mimes[ext];

if (!mimeType) {
return undefined;
Expand Down Expand Up @@ -172,6 +172,14 @@ const MAX_MAX_AGE = 31536000000;
* @returns {import("./index.js").Middleware<Request, Response>} wrapper
*/
function wrapper(context) {
const mimeTypes = new Map([["usdz", "model/vnd.usdz+zip"]]);

for (const [extension, type] of Object.entries(
context.options.mimeTypes || {},
)) {
mimeTypes.set(extension.toLowerCase(), type);
}

return async function middleware(req, res, next) {
/**
* @param {NodeJS.ErrnoException=} err an error
Expand Down Expand Up @@ -733,7 +741,7 @@ function wrapper(context) {
) {
removeResponseHeader(res, "Content-Type");
// content-type name (like application/javascript; charset=utf-8) or false
const contentType = getContentType(path.extname(filename));
const contentType = getContentType(path.extname(filename), mimeTypes);

// Only set content-type header if media type is known
// https://tools.ietf.org/html/rfc7231#section-3.1.1.5
Expand Down
43 changes: 40 additions & 3 deletions test/middleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import finalhandler from "finalhandler";
import { Hono } from "hono";
import koa from "koa";
import memfs, { Volume, createFsFromVolume } from "memfs";
import { lookup, mimes } from "mrmime";
import { lookup } from "mrmime";
import router from "router";
import request from "supertest";

Expand Down Expand Up @@ -47,8 +47,6 @@ const UTF8_CHARSET_MIME_TYPES = new Set([
"application/manifest+json",
]);

mimes.usdz = "model/vnd.usdz+zip";

async function startServer(name, app) {
return new Promise((resolve, reject) => {
if (name === "router") {
Expand Down Expand Up @@ -3907,6 +3905,45 @@ describe.each([
});

describe("mimeTypes option", () => {
it("should isolate MIME overrides between instances", async () => {
const defaultType = lookup("js");
const expectedTypes = [
"text/plain; charset=utf-8",
"text/html; charset=utf-8",
getContentTypeHeader(),
];
const instances = [];

try {
for (const mimeTypes of [
{ JS: "text/plain" },
{ js: "text/html" },
undefined,
]) {
instances.push(
await frameworkFactory(
name,
framework,
getCompiler(webpackConfig),
{
mimeTypes,
},
),
);
}

for (const [index, [, request]] of instances.entries()) {
const response = await request.get("/bundle.js").expect(200);
expect(response.headers["content-type"]).toBe(expectedTypes[index]);
}
expect(lookup("js")).toBe(defaultType);
} finally {
await Promise.all(
instances.map(([server, , instance]) => close(server, instance)),
);
}
});

describe('should set the correct value for "Content-Type" header to known MIME type', () => {
beforeAll(async () => {
const outputPath = path.resolve(__dirname, "./outputs/basic");
Expand Down
Loading