diff --git a/website/flags/audience.ts b/website/flags/audience.ts index 8e1ac9774..2d921c224 100644 --- a/website/flags/audience.ts +++ b/website/flags/audience.ts @@ -22,6 +22,7 @@ export interface Route { * @description higher priority means that this route will be used in favor of other routes with less or none priority */ highPriority?: boolean; + supportedExtensions?: string[]; } /** * @title Routes diff --git a/website/handlers/fresh.ts b/website/handlers/fresh.ts index 7e61896a8..69f30312f 100644 --- a/website/handlers/fresh.ts +++ b/website/handlers/fresh.ts @@ -109,6 +109,7 @@ export default function Fresh( const timing = appContext?.monitoring?.timings?.start?.("load-data"); let didFinish = false; const url = new URL(req.url); + const startedAt = Date.now(); const asJson = url.searchParams.get("asJson"); const delayFromProps = appContext.firstByteThresholdMS ? 1 : 0; diff --git a/website/handlers/router.ts b/website/handlers/router.ts index 95c3f6b10..d8700d124 100644 --- a/website/handlers/router.ts +++ b/website/handlers/router.ts @@ -8,6 +8,7 @@ import { RouteContext, } from "@deco/deco"; import { isAwaitable } from "@deco/deco/utils"; +import { extname } from "@std/path/extname"; import { weakcache } from "../../utils/weakcache.ts"; import { Route, Routes } from "../flags/audience.ts"; import { isFreshCtx } from "../handlers/fresh.ts"; @@ -19,6 +20,7 @@ export interface SelectionConfig { } interface MaybePriorityHandler { func: Resolvable; + supportedExtensions?: string[]; highPriority?: boolean; } const HIGH_PRIORITY_ROUTE_RANK_BASE_VALUE = 1000; @@ -38,6 +40,7 @@ const rankRoute = (pattern: string): number => return acc + 3; }, 0); const urlPatternCache: Record = {}; + export const router = ( routes: Route[], hrefRoutes: Record> = {}, @@ -77,7 +80,14 @@ export const router = ( if (handler) { return route(handler, `${url.pathname}${url.search || ""}`); } - for (const { pathTemplate: routePath, handler } of routes) { + for ( + const { pathTemplate: routePath, handler, supportedExtensions } of routes + ) { + // Skip catch-all routes for paths with file extensions + const ext = extname(url.pathname).slice(1); + if (ext && !supportedExtensions?.includes(ext)) { + continue; + } const pattern = urlPatternCache[routePath] ??= (() => { let url; if (URL.canParse(routePath)) { @@ -110,12 +120,21 @@ export const buildRoutes = (audiences: Routes[]): [ // We should tackle this problem elsewhere // check if the audience matches with the given context considering the `isMatch` provided by the cookies. for (const audience of audiences.filter(Boolean).flat()) { - const { pathTemplate, isHref, highPriority, handler: { value: handler } } = - audience; + const { + pathTemplate, + isHref, + highPriority, + handler: { value: handler }, + supportedExtensions, + } = audience; if (isHref) { hrefRoutes[pathTemplate] = handler; } else { - routeMap[pathTemplate] = { func: handler, highPriority }; + routeMap[pathTemplate] = { + func: handler, + highPriority, + supportedExtensions, + }; } } return [routeMap, hrefRoutes]; @@ -155,6 +174,7 @@ const prepareRoutes = (audiences: Routes[], ctx: AppContext) => { routes: builtRoutes.map((route) => ({ pathTemplate: route[0], handler: { value: route[1].func }, + supportedExtensions: route[1].supportedExtensions, })), hrefRoutes, }; diff --git a/website/loaders/pages.ts b/website/loaders/pages.ts index 347fff625..e416b0737 100644 --- a/website/loaders/pages.ts +++ b/website/loaders/pages.ts @@ -3,7 +3,7 @@ import { Route } from "../flags/audience.ts"; import { AppContext } from "../mod.ts"; import Page from "../pages/Page.tsx"; -async function getAllPages(ctx: AppContext): Promise { +async function getAllPages(ctx: AppContext, props: Props): Promise { const allPages = await ctx.get< Record[0]> >({ @@ -21,6 +21,7 @@ async function getAllPages(ctx: AppContext): Promise { } routes.push({ pathTemplate, + supportedExtensions: props.supportedExtensions, handler: { value: { __resolveType: "website/handlers/fresh.ts", @@ -45,6 +46,12 @@ export interface Props { * @description Deco routes that will ignore the previous rule. If the same route exists on other routes loader, the deco page will be used. */ alwaysVisiblePages?: SiteRoute[]; + + /** + * @title Supported extensions + * @description Extensions that will be supported by pages routes. + */ + supportedExtensions?: string[]; } /** @@ -58,7 +65,7 @@ export default async function Pages( const allPages = await ctx.get< Route[] >({ - func: () => getAllPages(ctx), + func: () => getAllPages(ctx, props), __resolveType: "once", }); diff --git a/website/mod.ts b/website/mod.ts index db07a2783..825709fff 100644 --- a/website/mod.ts +++ b/website/mod.ts @@ -12,7 +12,6 @@ import { type FnContext, type Site, } from "@deco/deco"; -import { VTEX_PATHS_THAT_REQUIRES_SAME_REFERER } from "../vtex/loaders/proxy.ts"; declare global { interface Window { @@ -77,6 +76,7 @@ export interface AbTesting { includeScriptsToBody?: { includes?: Script[]; }; + pathsThatRequireSameReferer?: string[]; } /** @titleBy framework */ interface Fresh { @@ -209,7 +209,9 @@ const getAbTestAudience = (abTesting: AbTesting) => { includeScriptsToHead: abTesting.includeScriptsToHead, includeScriptsToBody: abTesting.includeScriptsToBody, replaces: abTesting.replaces, - pathsThatRequireSameReferer: [...VTEX_PATHS_THAT_REQUIRES_SAME_REFERER], + pathsThatRequireSameReferer: [ + ...(abTesting.pathsThatRequireSameReferer ?? []), + ], }, }; if (abTesting.enabled) {