Skip to content

Commit 2723110

Browse files
dario-piotrowiczbmuenzenmeyerIgorMinar
committed
make site work with the Cloudflare OpenNext adapter
update the site application so that it can be build using the Cloudflare OpenNext adapter (`@opennextjs/cloudflare`) and thus deployed on Cloudflare Workers > [!Note] > This is very experimental and full of hacks > it's very much a work-in-progress right now ___ Co-authored-by: Brian Muenzenmeyer <[email protected]> Co-authored-by: Igor Minar <[email protected]>
1 parent 6fcbf5b commit 2723110

27 files changed

+30136
-18088
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,10 @@ cache
3232
tsconfig.tsbuildinfo
3333

3434
dist/
35+
36+
# Ignore worker artifacts
37+
apps/site/.open-next
38+
apps/site/.wrangler
39+
40+
# Pre-build generated files
41+
apps/site/.generated

apps/site/.cloudflare/empty.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default {};

apps/site/.cloudflare/node/fs.mjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { files } from '../../.generated/next.helpers.mjs';
2+
3+
export function readdir(params, cb) {
4+
console.log('fs#readdir', params);
5+
cb(null, []);
6+
}
7+
8+
export function exists(path, cb) {
9+
const result =
10+
files.includes(path) || files.includes(path.replace(/^\//, ''));
11+
console.log('fs#exists', path, result);
12+
cb(result);
13+
}
14+
15+
export default {
16+
readdir,
17+
exists,
18+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { getCloudflareContext } from '@opennextjs/cloudflare';
2+
3+
export async function readFile(path) {
4+
console.log('fs/promies#readFile', path);
5+
6+
const { env } = await getCloudflareContext();
7+
8+
const text = await env.ASSETS.fetch(
9+
new URL(path, 'https://jamesrocks/')
10+
).then(response => response.text());
11+
return text;
12+
}
13+
14+
export async function readdir(params) {
15+
console.log('fs/promises#readdir', params);
16+
return Promise.resolve([]);
17+
}
18+
19+
export async function exists(...args) {
20+
console.log('fs/promises#exists', args);
21+
return Promise.resolve(false);
22+
}
23+
24+
export default {
25+
readdir,
26+
exists,
27+
readFile,
28+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// we shim @opentelemetry/api to the throwing shim so that it will throw right away, this is so that we throw inside the
2+
// try block here: https://github.com/vercel/next.js/blob/9e8266a7/packages/next/src/server/lib/trace/tracer.ts#L27-L31
3+
// causing the code to require the 'next/dist/compiled/@opentelemetry/api' module instead (which properly works)
4+
5+
// IMPORTANT: we already do that in the open-next Cloudflare adapter, it shouldn't be necessary here too
6+
// (https://github.com/opennextjs/opennextjs-cloudflare/issues/219 seems to be the same issue)
7+
throw new Error();

apps/site/.cloudflare/server-only.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// In our aliased fs code: apps/site/.cloudflare/node/fs/promises.mjs we are importing `getCloudflareContext`
2+
// from `@opennextjs/cloudflare`, this in turn imports from `server-only`, this aliasing makes it so that
3+
// server-only is not actually removed from the final bundle as it would otherwise cause an incorrect server
4+
// internal error

apps/site/CLOUDFLARE.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# nodejs.org on OpenNext for Cloudflare
2+
3+
## Getting started
4+
5+
To develop, build, preview, and deploy nodejs.org, execute the following commands to get started:
6+
7+
```
8+
nvm use
9+
npm install
10+
cd apps/site
11+
```
12+
13+
## Developing locally
14+
15+
To develop locally, run the usual:
16+
17+
```
18+
npm run dev
19+
```
20+
21+
## Build nodejs.org production distribution using OpenNext
22+
23+
To build you need connection to the Internet because the build system will try to fetch the following files:
24+
25+
- https://nodejs.org/dist/index.json
26+
- https://raw.githubusercontent.com/nodejs/Release/master/schedule.json
27+
28+
```
29+
npm run cf:build
30+
```
31+
32+
## Preview a production build locally
33+
34+
You can preview production build locally using [wrangler](https://developers.cloudflare.com/workers/wrangler/):
35+
36+
```
37+
npm run cf:preview
38+
```
39+
40+
## Deploying a build to production
41+
42+
To build and deploy the application run:
43+
44+
```
45+
npm run cf:deploy
46+
```
47+
48+
The build is currently deployed to a dedicated "nodejs.org" (Cloudflare account id: 8ed4d03ac99f77561d0e8c9cbcc76cb6): https://nodejs-website.web-experiments.workers.dev
49+
50+
You can monitor and configure the project at https://dash.cloudflare.com/8ed4d03ac99f77561d0e8c9cbcc76cb6/workers/services/view/nodejs-website/production
51+
52+
## TODOs
53+
54+
The following is an incomplete list of tasks and problems that still need to be resolved:
55+
56+
- [x] update `@opennextjs/cloudflare` to the latest in `/apps/site/package.json`
57+
- [x] sort out issues with `eval` and MDX (Claudio is looking into this one)
58+
- [x] and undo edits in `./app/[locale]/[[...path]]/page.tsx`
59+
- [x] reimplement `getMarkdownFiles` in `next.helpers.mjs` to be generated at build time
60+
- this can be accomplished either via a npm/turbo prebuild task, or possibly as part of next.js SSG/staticProps but
61+
- [ ] we need to ensure that we don't end up accidentally downloading this big file to the client as part of hydration
62+
- [x] once we have easy access to the list of files, we should roll back changes to `next-data/providers/blogData.ts`
63+
- [x] back out most changes from `next.dynamic.mjs`
64+
- [x] instead of using runtime detection via `globalThis.navigator?.userAgent`, we should instead use `alias` feature in `wrangler.toml` to override the implementation of `node:fs` calls but only when running in workerd as we need the build to keep on running in node.js for SSG to work
65+
- [x] could we reimplement the `existsAsync` call as sync `exists` which consults `getMarkdownFiles` from the task above?
66+
- [ ] remove symlink hack in `package.json#build:cloudflare`
67+
- would it be possible to make the pages directory part of assets in a less hacky way?
68+
- [ ] move these files under `.open-next/assets/cdn-cgi/pages` so that these raw md files are not publicly accessible as that could become a maintenance burden down the road.
69+
- [ ] wire up the changes with turborepo (right now just plain npm scripts are used)
70+
- [ ] reenable minification in `next.config.mjs`
71+
- [ ] remove as many `alias`es as possible from the `wrangler.toml` file
72+
(the `alias`es that can't be removed should be fully investigated and documented)
73+
- [ ] fix flashes of unstyled content present on hard navigation
74+
- [x] enable caching
75+
- [x] fix routes for languages besides `en` 404ing

apps/site/app/[locale]/next-data/api-data/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const getPathnameForApiFile = (name: string, version: string) =>
2121
// for a digest and metadata of all API pages from the Node.js Website
2222
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
2323
export const GET = async () => {
24-
const releases = provideReleaseData();
24+
const releases = await provideReleaseData();
2525

2626
const { versionWithPrefix } = releases.find(
2727
release => release.status === 'LTS'

apps/site/app/[locale]/next-data/release-data/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { defaultLocale } from '@/next.locales.mjs';
55
// for generating static data related to the Node.js Release Data
66
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
77
export const GET = async () => {
8-
const releaseData = provideReleaseData();
8+
const releaseData = await provideReleaseData();
99

1010
return Response.json(releaseData);
1111
};

apps/site/instrumentation.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)