Skip to content

Commit

Permalink
Adding basic routing support
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuhaow committed Aug 26, 2022
1 parent f1cf0a6 commit d5727ac
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 1 deletion.
35 changes: 35 additions & 0 deletions src/server/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {RequestData} from 'next/dist/server/web/types';

declare const nextConfig: RequestData['nextConfig'];

// From https://developers.cloudflare.com/fundamentals/get-started/reference/http-request-headers/
// and https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties
export function getRequestData(request: Request): RequestData {
return {
url: request.url,
method: request.method,
headers: toPlainHeaders(request.headers),
ip: request.headers.get('CF-Connecting-IP') ?? undefined,
geo: {
country: request.cf?.country,
city: request.cf?.city,
region: request.cf?.region,
latitude: request.cf?.latitude,
longitude: request.cf?.longitude,
},
nextConfig,
// Should no longer be needed
page: undefined,
body: request.body,
};
}

function toPlainHeaders(headers: Headers): Record<string, string> {
const result: Record<string, string> = {};

for (const [value, key] of headers.entries()) {
result[key] = value;
}

return result;
}
66 changes: 66 additions & 0 deletions src/server/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// The implementation depends deciphering the source code
// of Next.js instead of following its document as there is none.
// Should expect breakage during Next.js version change.

import {RequestData} from 'next/dist/server/web/types';
import {getRequestData} from './request';

// eslint-disable-next-line @typescript-eslint/naming-convention
declare const _ENTRIES: Record<
string,
{
default: (parameters: {
request: RequestData;
}) => Promise<{response: Response; waitUntil: Promise<any>}>;
}
>;

interface Route {
page: string;
regex: string;
routeKeys: Record<string, string>;
namedRegex: string;
}

declare const routesManifest: {
dynamicRoutes: Route[];
};

export const onRequest: PagesFunction = async ({
request,
waitUntil,
env: {ASSETS},
}) => {
// For now we only support serve static then dynamic with no basepath or locale.
// https://nextjs.org/docs/advanced-features/middleware#matching-paths is the full route order we need to handle

const assetsResponse = await ASSETS.fetch(request);
if (assetsResponse.ok) {
return assetsResponse;
}

const url = new URL(request.url);

for (const route of routesManifest.dynamicRoutes) {
if (new RegExp(route.regex).test(url.pathname)) {
// eslint-disable-next-line no-await-in-loop
const {response, waitUntil: promise} = await _ENTRIES[
`middleware_${route.page}`
].default({request: getRequestData(request)});

waitUntil(promise);

return response;
}
}

const notFoundResponse = await ASSETS.fetch(
new URL('/404', request.url).toString(),
);

return new Response(notFoundResponse.body, {
...notFoundResponse,
status: 404,
statusText: 'Not found',
});
};
5 changes: 4 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
"esModuleInterop": true,
"allowJs": true,
"noEmit": true,
"resolveJsonModule": true
"resolveJsonModule": true,
"lib": [
"ES2020"
]
},
"include": [
"**/*.ts",
Expand Down

0 comments on commit d5727ac

Please sign in to comment.