diff --git a/src/rollup/plugins/handlers.ts b/src/rollup/plugins/handlers.ts index 2451ab25e8..b66931b411 100644 --- a/src/rollup/plugins/handlers.ts +++ b/src/rollup/plugins/handlers.ts @@ -1,18 +1,15 @@ import { hash } from "ohash"; -import type { Nitro } from "../../types"; +import type { Nitro, NitroRouteRules, NitroEventHandler } from "../../types"; import { virtual } from "./virtual"; -const unique = (arr: any[]) => [...new Set(arr)]; -const getImportId = (p: string, lazy?: boolean) => - (lazy ? "_lazy_" : "_") + hash(p).slice(0, 6); - -const GLOB_RE = /\/\*\*.*$/; - export function handlers(nitro: Nitro) { return virtual( { "#internal/nitro/virtual/server-handlers": () => { - const handlers = [...nitro.scannedHandlers, ...nitro.options.handlers]; + const handlers: NitroEventHandler[] = [ + ...nitro.scannedHandlers, + ...nitro.options.handlers, + ]; if (nitro.options.serveStatic) { handlers.unshift({ middleware: true, @@ -28,45 +25,7 @@ export function handlers(nitro: Nitro) { } // If this handler would render a cached route rule then we can also inject a cached event handler - const rules = Object.entries(nitro.options.routeRules); - for (const [path, rule] of rules) { - // We can ignore this rule if it is not cached and it isn't nested in a cached route - if (!rule.cache) { - // If we are nested 'within' a cached route, we want to inject a non-cached event handler - const isNested = rules.some( - ([p, r]) => - r.cache && - GLOB_RE.test(p) && - path.startsWith(p.replace(GLOB_RE, "")) - ); - if (!isNested) { - continue; - } - } - for (const [index, handler] of handlers.entries()) { - // skip middleware - if (!handler.route) { - continue; - } - // we will correctly register this rule as a cached route anyway - if (handler.route === path) { - break; - } - // We are looking for handlers that will render a route _despite_ not - // having an identical path to it - if (!GLOB_RE.test(handler.route)) { - continue; - } - if (!path.startsWith(handler.route.replace(GLOB_RE, ""))) { - continue; - } - handlers.splice(index, 0, { - ...handler, - route: path, - }); - break; - } - } + extendMiddlewareWithRuleOverlaps(handlers, nitro.options.routeRules); // Imports take priority const imports = unique( @@ -111,3 +70,58 @@ ${handlers nitro.vfs ); } + +function unique(arr: any[]) { + return [...new Set(arr)]; +} + +function getImportId(p: string, lazy?: boolean) { + return (lazy ? "_lazy_" : "_") + hash(p).slice(0, 6); +} + +const WILDCARD_PATH_RE = /\/\*\*.*$/; + +function extendMiddlewareWithRuleOverlaps( + handlers: NitroEventHandler[], + routeRules: Record +) { + const rules = Object.entries(routeRules); + for (const [path, rule] of rules) { + // We can ignore this rule if it is not cached and it isn't nested in a cached route + if (!rule.cache) { + // If we are nested 'within' a cached route, we want to inject a non-cached event handler + const isNested = rules.some( + ([p, r]) => + r.cache && + WILDCARD_PATH_RE.test(p) && + path.startsWith(p.replace(WILDCARD_PATH_RE, "")) + ); + if (!isNested) { + continue; + } + } + for (const [index, handler] of handlers.entries()) { + // Skip middleware + if (!handler.route || handler.middleware) { + continue; + } + // We will correctly register this rule as a cached route anyway + if (handler.route === path) { + break; + } + // We are looking for handlers that will render a route _despite_ not + // having an identical path to it + if (!WILDCARD_PATH_RE.test(handler.route)) { + continue; + } + if (!path.startsWith(handler.route.replace(WILDCARD_PATH_RE, ""))) { + continue; + } + handlers.splice(index, 0, { + ...handler, + route: path, + }); + break; + } + } +}