Skip to content

Commit 848d6aa

Browse files
committed
Add an asset resolver override
1 parent f2c86ca commit 848d6aa

File tree

6 files changed

+96
-24
lines changed

6 files changed

+96
-24
lines changed

packages/open-next/src/adapters/middleware.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { OpenNextHandlerOptions } from "types/overrides";
99
import { debug, error } from "../adapters/logger";
1010
import { createGenericHandler } from "../core/createGenericHandler";
1111
import {
12+
resolveAssetResolver,
1213
resolveIncrementalCache,
1314
resolveOriginResolver,
1415
resolveProxyRequest,
@@ -29,25 +30,22 @@ const defaultHandler = async (
2930
internalEvent: InternalEvent,
3031
options?: OpenNextHandlerOptions,
3132
): Promise<InternalResult | MiddlewareResult> => {
32-
const originResolver = await resolveOriginResolver(
33-
globalThis.openNextConfig.middleware?.originResolver,
34-
);
33+
const config = globalThis.openNextConfig.middleware;
34+
const originResolver = await resolveOriginResolver(config?.originResolver);
3535

3636
const externalRequestProxy = await resolveProxyRequest(
37-
globalThis.openNextConfig.middleware?.override?.proxyExternalRequest,
37+
config?.override?.proxyExternalRequest,
3838
);
3939

40+
const assetResolver = await resolveAssetResolver(config?.assetResolver);
41+
4042
//#override includeCacheInMiddleware
41-
globalThis.tagCache = await resolveTagCache(
42-
globalThis.openNextConfig.middleware?.override?.tagCache,
43-
);
43+
globalThis.tagCache = await resolveTagCache(config?.override?.tagCache);
4444

45-
globalThis.queue = await resolveQueue(
46-
globalThis.openNextConfig.middleware?.override?.queue,
47-
);
45+
globalThis.queue = await resolveQueue(config?.override?.queue);
4846

4947
globalThis.incrementalCache = await resolveIncrementalCache(
50-
globalThis.openNextConfig.middleware?.override?.incrementalCache,
48+
config?.override?.incrementalCache,
5149
);
5250
//#endOverride
5351

@@ -61,7 +59,7 @@ const defaultHandler = async (
6159
requestId,
6260
},
6361
async () => {
64-
const result = await routingHandler(internalEvent);
62+
const result = await routingHandler(internalEvent, { assetResolver });
6563
if ("internalEvent" in result) {
6664
debug("Middleware intercepted event", internalEvent);
6765
if (!result.isExternalRewrite) {

packages/open-next/src/core/resolve.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,20 @@ export async function resolveOriginResolver(
116116
return m_1.default;
117117
}
118118

119+
/**
120+
* @returns
121+
* @__PURE__
122+
*/
123+
export async function resolveAssetResolver(
124+
assetResolver: RemoveUndefined<OpenNextConfig["middleware"]>["assetResolver"],
125+
) {
126+
if (typeof assetResolver === "function") {
127+
return assetResolver();
128+
}
129+
const m_1 = await import("../overrides/assetResolver/dummy.js");
130+
return m_1.default;
131+
}
132+
119133
/**
120134
* @__PURE__
121135
*/

packages/open-next/src/core/routingHandler.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
RoutingResult,
1313
} from "types/open-next";
1414

15+
import type { AssetResolver } from "types/overrides";
1516
import { debug, error } from "../adapters/logger";
1617
import { cacheInterceptor } from "./routing/cacheInterceptor";
1718
import { detectLocale } from "./routing/i18n";
@@ -64,6 +65,7 @@ function applyMiddlewareHeaders(
6465

6566
export default async function routingHandler(
6667
event: InternalEvent,
68+
{ assetResolver }: { assetResolver?: AssetResolver } = {},
6769
): Promise<InternalResult | RoutingResult> {
6870
try {
6971
// Add Next geo headers
@@ -89,11 +91,13 @@ export default async function routingHandler(
8991

9092
const nextHeaders = getNextConfigHeaders(event, ConfigHeaders);
9193

92-
let internalEvent = fixDataPage(event, BuildId);
93-
if ("statusCode" in internalEvent) {
94-
return internalEvent;
94+
const internalEventOrResult = fixDataPage(event, BuildId);
95+
if ("statusCode" in internalEventOrResult) {
96+
return internalEventOrResult;
9597
}
9698

99+
let internalEvent: InternalEvent | InternalResult = internalEventOrResult;
100+
97101
const redirect = handleRedirects(internalEvent, RoutesManifest.redirects);
98102
if (redirect) {
99103
// We need to encode the value in the Location header to make sure it is valid according to RFC
@@ -105,21 +109,20 @@ export default async function routingHandler(
105109
return redirect;
106110
}
107111

108-
const eventOrResult = await handleMiddleware(
112+
const middlewareEventOrResult = await handleMiddleware(
109113
internalEvent,
110114
// We need to pass the initial search without any decoding
111115
// TODO: we'd need to refactor InternalEvent to include the initial querystring directly
112116
// Should be done in another PR because it is a breaking change
113117
new URL(event.url).search,
114118
);
115-
const isResult = "statusCode" in eventOrResult;
116-
if (isResult) {
117-
return eventOrResult;
119+
if ("statusCode" in middlewareEventOrResult) {
120+
return middlewareEventOrResult;
118121
}
119-
const middlewareResponseHeaders = eventOrResult.responseHeaders;
120-
let isExternalRewrite = eventOrResult.isExternalRewrite ?? false;
121-
// internalEvent is `InternalEvent | MiddlewareEvent`
122-
internalEvent = eventOrResult;
122+
123+
const middlewareResponseHeaders = middlewareEventOrResult.responseHeaders;
124+
let isExternalRewrite = middlewareEventOrResult.isExternalRewrite ?? false;
125+
internalEvent = middlewareEventOrResult;
123126

124127
if (!isExternalRewrite) {
125128
// First rewrite to be applied
@@ -184,7 +187,22 @@ export default async function routingHandler(
184187
dynamicRouteMatcher(internalEvent.rawPath).length > 0
185188
)
186189
) {
187-
internalEvent = {
190+
const assetEventOrResult =
191+
await assetResolver?.onRouteNotFound(internalEvent);
192+
193+
if (assetEventOrResult && "statusCode" in assetEventOrResult) {
194+
applyMiddlewareHeaders(
195+
assetEventOrResult.headers,
196+
{
197+
...middlewareResponseHeaders,
198+
...nextHeaders,
199+
},
200+
false,
201+
);
202+
return assetEventOrResult;
203+
}
204+
205+
internalEvent = assetEventOrResult ?? {
188206
...internalEvent,
189207
rawPath: "/404",
190208
url: constructNextUrl(internalEvent.url, "/404"),
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { InternalEvent } from "types/open-next";
2+
import type { AssetResolver } from "types/overrides";
3+
4+
const resolver: AssetResolver = {
5+
name: "dummy",
6+
// @returns `undefined` to preserve the routing layer default behavior (404 page)
7+
onRouteNotFound: (_event: InternalEvent) => undefined,
8+
};
9+
10+
export default resolver;

packages/open-next/src/types/open-next.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { ReadableStream } from "node:stream/web";
33
import type { Writable } from "node:stream";
44
import type { WarmerEvent, WarmerResponse } from "../adapters/warmer-function";
55
import type {
6+
AssetResolver,
67
CDNInvalidationHandler,
78
Converter,
89
ImageLoader,
@@ -186,6 +187,9 @@ export type IncludedWarmer = "aws-lambda" | "dummy";
186187
export type IncludedProxyExternalRequest = "node" | "fetch" | "dummy";
187188

188189
export type IncludedCDNInvalidationHandler = "cloudfront" | "dummy";
190+
191+
export type IncludedAssetResolver = "dummy";
192+
189193
export interface DefaultOverrideOptions<
190194
E extends BaseEventOrResult = InternalEvent,
191195
R extends BaseEventOrResult = InternalResult,
@@ -396,6 +400,13 @@ export interface OpenNextConfig {
396400
originResolver?:
397401
| IncludedOriginResolver
398402
| LazyLoadedOverride<OriginResolver>;
403+
404+
/**
405+
* The assetResolver is used to resolve assets in the routing layer.
406+
*
407+
* @default "dummy"
408+
*/
409+
assetResolver?: IncludedAssetResolver | LazyLoadedOverride<AssetResolver>;
399410
};
400411

401412
/**

packages/open-next/src/types/overrides.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ export interface Queue {
3131
name: string;
3232
}
3333

34+
/**
35+
* Resolves assets in the routing layer.
36+
*/
37+
export interface AssetResolver {
38+
// TODO: add onCatchAllRoute, called when a catchAllRoute matches as it could potentially match an asset
39+
// onCatchAllRoute
40+
41+
/**
42+
* Called when no matching route is found.
43+
*
44+
* @param event The internal event from the routing layer
45+
* @returns - `Promise<InternalEvent>` replaces the internal event in the routing layer
46+
* - `Promise<InternalResult>` will get the routing layer to return the result, i.e. an asset
47+
* - `Promise<undefined> | undefined` does not change the routing default behavior of returning a 404
48+
*/
49+
onRouteNotFound(
50+
event: InternalEvent,
51+
): Promise<InternalEvent | InternalResult | undefined> | undefined;
52+
name: string;
53+
}
54+
3455
// Incremental cache
3556

3657
export type CachedFile =

0 commit comments

Comments
 (0)