diff --git a/src/utils/getFilenameFromUrl.js b/src/utils/getFilenameFromUrl.js index 82a58115a..4ebaa9a5f 100644 --- a/src/utils/getFilenameFromUrl.js +++ b/src/utils/getFilenameFromUrl.js @@ -10,12 +10,14 @@ const getPaths = require("./getPaths"); const cacheStore = new WeakMap(); /** + * @template T * @param {Function} fn - * @param {{ cache?: Map }} [cache] + * @param {{ cache?: Map } | undefined} cache + * @param {(value: T) => T} callback * @returns {any} */ // @ts-ignore -const mem = (fn, { cache = new Map() } = {}) => { +const mem = (fn, { cache = new Map() } = {}, callback) => { /** * @param {any} arguments_ * @return {any} @@ -28,7 +30,8 @@ const mem = (fn, { cache = new Map() } = {}) => { return cacheItem.data; } - const result = fn.apply(this, arguments_); + let result = fn.apply(this, arguments_); + result = callback(result); cache.set(key, { data: result, @@ -41,7 +44,15 @@ const mem = (fn, { cache = new Map() } = {}) => { return memoized; }; -const memoizedParse = mem(parse); +// eslint-disable-next-line no-undefined +const memoizedParse = mem(parse, undefined, (value) => { + if (value.pathname) { + // eslint-disable-next-line no-param-reassign + value.pathname = decode(value.pathname); + } + + return value; +}); const UP_PATH_REGEXP = /(?:^|[\\/])\.\.(?:[\\/]|$)/; @@ -77,7 +88,9 @@ function getFilenameFromUrl(context, url, extra = {}) { const { options } = context; const paths = getPaths(context); + /** @type {string | undefined} */ let foundFilename; + /** @type {URL} */ let urlObject; try { @@ -88,7 +101,9 @@ function getFilenameFromUrl(context, url, extra = {}) { } for (const { publicPath, outputPath } of paths) { + /** @type {string | undefined} */ let filename; + /** @type {URL} */ let publicPathObject; try { @@ -102,8 +117,8 @@ function getFilenameFromUrl(context, url, extra = {}) { continue; } - const pathname = decode(urlObject.pathname); - const publicPathPathname = decode(publicPathObject.pathname); + const { pathname } = urlObject; + const { pathname: publicPathPathname } = publicPathObject; if (pathname && pathname.startsWith(publicPathPathname)) { // Null byte(s) diff --git a/test/middleware.test.js b/test/middleware.test.js index 79288d0b5..ead7886fc 100644 --- a/test/middleware.test.js +++ b/test/middleware.test.js @@ -859,6 +859,39 @@ describe.each([ }, ], }, + { + file: "windows.txt", + data: "windows.txt content", + urls: [ + { + value: "windows.txt", + contentType: "text/plain; charset=utf-8", + code: 200, + }, + ], + }, + { + file: "windows 2.txt", + data: "windows 2.txt content", + urls: [ + { + value: "windows%202.txt", + contentType: "text/plain; charset=utf-8", + code: 200, + }, + ], + }, + { + file: "test & test & %20.txt", + data: "test & test & %20.txt content", + urls: [ + { + value: "test%20%26%20test%20%26%20%2520.txt", + contentType: "text/plain; charset=utf-8", + code: 200, + }, + ], + }, ]; const configurations = [ @@ -934,71 +967,28 @@ describe.each([ }, publicPathForRequest: "/", }, - ]; - - const isWindows = process.platform === "win32"; - - if (isWindows) { - fixtures.push( - { - file: "windows.txt", - data: "windows.txt content", - urls: [ - { - value: "windows.txt", - contentType: "text/plain; charset=utf-8", - code: 200, - }, - ], - }, - { - file: "windows 2.txt", - data: "windows 2.txt content", - urls: [ - { - value: "windows%202.txt", - contentType: "text/plain; charset=utf-8", - code: 200, - }, - ], - }, - { - file: "test & test & %20.txt", - data: "test & test & %20.txt content", - urls: [ - { - value: "test%20%26%20test%20%26%20%2520.txt", - contentType: "text/plain; charset=utf-8", - code: 200, - }, - ], - }, - ); - - configurations.push( - { - output: { - path: path.join(basicOutputPath, "my static"), - publicPath: "/static/", - }, - publicPathForRequest: "/static/", + { + output: { + path: path.join(basicOutputPath, "my static"), + publicPath: "/static/", }, - { - output: { - path: path.join(basicOutputPath, "my%20static"), - publicPath: "/static/", - }, - publicPathForRequest: "/static/", + publicPathForRequest: "/static/", + }, + { + output: { + path: path.join(basicOutputPath, "my%20static"), + publicPath: "/static/", }, - { - output: { - path: path.join(basicOutputPath, "my %20 static"), - publicPath: "/my%20static/", - }, - publicPathForRequest: "/my%20static/", + publicPathForRequest: "/static/", + }, + { + output: { + path: path.join(basicOutputPath, "my %20 static"), + publicPath: "/my%20static/", }, - ); - } + publicPathForRequest: "/my%20static/", + }, + ]; for (const configuration of configurations) { // eslint-disable-next-line no-loop-func