Skip to content

Commit 30374b9

Browse files
authoredFeb 5, 2025··
fix: Drop the module condition from ESBuild (#339)
Because Next (via nft) does not use the module condition, ESBuild should not use it. Otherwise we might end up with missing files and a broken build
1 parent a1fcbb6 commit 30374b9

25 files changed

+4982
-1994
lines changed
 

‎.changeset/tender-hotels-thank.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
fix: Drop the module condition from ESBuild
6+
7+
Because Next (via nft) does not use the module condition, ESBuild should not use it.
8+
Otherwise we might end up with missing files and a broken build.

‎examples/bugs/gh-219/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@cloudflare/workers-types": "^4.20241224.0",
4545
"@eslint/eslintrc": "^3",
4646
"@opennextjs/cloudflare": "workspace:*",
47+
"@playwright/test": "catalog:",
4748
"@types/better-sqlite3": "^7.6.12",
4849
"@types/node": "^20",
4950
"@types/react-dom": "^19",

‎examples/bugs/gh-223/.eslintrc.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "next/core-web-vitals"
3+
}

‎examples/bugs/gh-223/.gitignore

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts
37+
38+
39+
# Cloudflare related
40+
/.open-next
41+
/.wrangler
42+
43+
# wrangler files
44+
.wrangler
45+
.dev.vars
46+
47+
/.vscode
48+
49+
# playwright
50+
/test-results/
51+
/playwright-report/
52+
/blob-report/
53+
/playwright/.cache/

‎examples/bugs/gh-223/README.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
# or
14+
bun dev
15+
```
16+
17+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18+
19+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20+
21+
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22+
23+
## Learn More
24+
25+
To learn more about Next.js, take a look at the following resources:
26+
27+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29+
30+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31+
32+
## Deploy on Vercel
33+
34+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35+
36+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
3+
import { getImageUrl } from "../../../src/utils/s3Bucket";
4+
5+
export async function GET(request: NextRequest) {
6+
const searchParams = request.nextUrl.searchParams;
7+
const fileName = searchParams.get("fileName");
8+
return NextResponse.json(
9+
{
10+
image: fileName ? await getImageUrl(fileName) : "",
11+
},
12+
{
13+
status: 200,
14+
}
15+
);
16+
}

‎examples/bugs/gh-223/app/favicon.ico

25.3 KB
Binary file not shown.

‎examples/bugs/gh-223/app/globals.css

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
:root {
6+
--foreground-rgb: 0, 0, 0;
7+
--background-start-rgb: 214, 219, 220;
8+
--background-end-rgb: 255, 255, 255;
9+
}
10+
11+
@media (prefers-color-scheme: dark) {
12+
:root {
13+
--foreground-rgb: 255, 255, 255;
14+
--background-start-rgb: 0, 0, 0;
15+
--background-end-rgb: 0, 0, 0;
16+
}
17+
}
18+
19+
body {
20+
color: rgb(var(--foreground-rgb));
21+
background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb)))
22+
rgb(var(--background-start-rgb));
23+
}
24+
25+
@layer utilities {
26+
.text-balance {
27+
text-wrap: balance;
28+
}
29+
}

‎examples/bugs/gh-223/app/layout.tsx

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Metadata } from "next";
2+
import { Inter } from "next/font/google";
3+
import "./globals.css";
4+
5+
const inter = Inter({ subsets: ["latin"] });
6+
7+
export const metadata: Metadata = {
8+
title: "Create Next App",
9+
description: "Generated by create next app",
10+
};
11+
12+
export default function RootLayout({
13+
children,
14+
}: Readonly<{
15+
children: React.ReactNode;
16+
}>) {
17+
return (
18+
<html lang="en">
19+
<body className={inter.className}>{children}</body>
20+
</html>
21+
);
22+
}

‎examples/bugs/gh-223/app/page.tsx

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"use client";
2+
3+
import Image from "next/image";
4+
5+
export default function Home() {
6+
return (
7+
<main className="flex min-h-screen flex-col items-center justify-between p-24">
8+
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
9+
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
10+
Get started by editing&nbsp;
11+
<code className="font-mono font-bold">src/app/page.tsx</code>
12+
</p>
13+
<div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:size-auto lg:bg-none">
14+
<a
15+
className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
16+
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
17+
target="_blank"
18+
rel="noopener noreferrer"
19+
>
20+
By{" "}
21+
<Image
22+
src="/vercel.svg"
23+
alt="Vercel Logo"
24+
className="dark:invert"
25+
width={100}
26+
height={24}
27+
priority
28+
/>
29+
</a>
30+
</div>
31+
</div>
32+
33+
<div className="relative z-[-1] flex place-items-center before:absolute before:h-[300px] before:w-full before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-full after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 sm:before:w-[480px] sm:after:w-[240px] before:lg:h-[360px]">
34+
<Image
35+
className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
36+
src="/next.svg"
37+
alt="Next.js Logo"
38+
width={180}
39+
height={37}
40+
priority
41+
/>
42+
</div>
43+
44+
<div className="mb-32 grid text-center lg:mb-0 lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">
45+
<a
46+
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
47+
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
48+
target="_blank"
49+
rel="noopener noreferrer"
50+
>
51+
<h2 className="mb-3 text-2xl font-semibold">
52+
Docs{" "}
53+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
54+
-&gt;
55+
</span>
56+
</h2>
57+
<p className="m-0 max-w-[30ch] text-sm opacity-50">
58+
Find in-depth information about Next.js features and API.
59+
</p>
60+
</a>
61+
62+
<a
63+
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
64+
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
65+
target="_blank"
66+
rel="noopener noreferrer"
67+
>
68+
<h2 className="mb-3 text-2xl font-semibold">
69+
Learn{" "}
70+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
71+
-&gt;
72+
</span>
73+
</h2>
74+
<p className="m-0 max-w-[30ch] text-sm opacity-50">
75+
Learn about Next.js in an interactive course with&nbsp;quizzes!
76+
</p>
77+
</a>
78+
79+
<a
80+
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
81+
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
82+
target="_blank"
83+
rel="noopener noreferrer"
84+
>
85+
<h2 className="mb-3 text-2xl font-semibold">
86+
Templates{" "}
87+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
88+
-&gt;
89+
</span>
90+
</h2>
91+
<p className="m-0 max-w-[30ch] text-sm opacity-50">Explore starter templates for Next.js.</p>
92+
</a>
93+
94+
<a
95+
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
96+
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
97+
target="_blank"
98+
rel="noopener noreferrer"
99+
>
100+
<h2 className="mb-3 text-2xl font-semibold">
101+
Deploy{" "}
102+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
103+
-&gt;
104+
</span>
105+
</h2>
106+
<p className="m-0 max-w-[30ch] text-balance text-sm opacity-50">
107+
Instantly deploy your Next.js site to a shareable URL with Vercel.
108+
</p>
109+
</a>
110+
</div>
111+
</main>
112+
);
113+
}

‎examples/bugs/gh-223/e2e/base.spec.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { test, expect } from "@playwright/test";
2+
3+
test("api route", async ({ page }) => {
4+
const res = await page.request.get("/api/image");
5+
expect(res.status()).toEqual(200);
6+
expect((await res.json()).image).toEqual("");
7+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { defineConfig, devices } from "@playwright/test";
2+
import type nodeProcess from "node:process";
3+
4+
declare const process: typeof nodeProcess;
5+
6+
/**
7+
* See https://playwright.dev/docs/test-configuration.
8+
*/
9+
export default defineConfig({
10+
testDir: "./",
11+
/* Run tests in files in parallel */
12+
fullyParallel: true,
13+
/* Fail the build on CI if you accidentally left test.only in the source code. */
14+
forbidOnly: !!process.env.CI,
15+
/* Retry on CI only */
16+
retries: process.env.CI ? 2 : 0,
17+
/* Opt out of parallel tests on CI. */
18+
workers: process.env.CI ? 1 : undefined,
19+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
20+
reporter: "html",
21+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
22+
use: {
23+
/* Base URL to use in actions like `await page.goto('/')`. */
24+
baseURL: "http://localhost:8752",
25+
26+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
27+
trace: "on-first-retry",
28+
},
29+
30+
/* Configure projects for major browsers */
31+
projects: [
32+
{
33+
name: "chromium",
34+
use: { ...devices["Desktop Chrome"] },
35+
},
36+
],
37+
38+
/* Run your local dev server before starting the tests */
39+
webServer: {
40+
command: "pnpm preview",
41+
url: "http://localhost:8752",
42+
reuseExistingServer: !process.env.CI,
43+
timeout: 500_000,
44+
},
45+
});

‎examples/bugs/gh-223/next.config.mjs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
typescript: {
4+
ignoreBuildErrors: true,
5+
},
6+
};
7+
8+
export default nextConfig;
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { OpenNextConfig } from "@opennextjs/aws/types/open-next";
2+
3+
const config: OpenNextConfig = {
4+
default: {
5+
override: {
6+
wrapper: "cloudflare-node",
7+
converter: "edge",
8+
incrementalCache: "dummy",
9+
tagCache: "dummy",
10+
queue: "dummy",
11+
},
12+
},
13+
14+
middleware: {
15+
external: true,
16+
override: {
17+
wrapper: "cloudflare-edge",
18+
converter: "edge",
19+
proxyExternalRequest: "fetch",
20+
},
21+
},
22+
};
23+
24+
export default config;

‎examples/bugs/gh-223/package.json

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "gh-223",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"lint": "next lint",
10+
"build:worker": "opennextjs-cloudflare",
11+
"dev:worker": "wrangler dev --port 8752 --inspector-port 9372",
12+
"preview": "npm run build:worker && npm run dev:worker",
13+
"e2e": "playwright test -c e2e/playwright.config.ts",
14+
"deploy:worker": "npm run build:worker && wrangler deploy"
15+
},
16+
"dependencies": {
17+
"@aws-sdk/client-s3": "^3.721.0",
18+
"@aws-sdk/s3-request-presigner": "^3.721.0",
19+
"next": "15.1.3",
20+
"react": "^19.0.0",
21+
"react-dom": "^19.0.0"
22+
},
23+
"devDependencies": {
24+
"@cloudflare/workers-types": "^4.20241224.0",
25+
"@opennextjs/cloudflare": "workspace:*",
26+
"@playwright/test": "catalog:",
27+
"@types/node": "^22.10.2",
28+
"@types/react": "^19.0.2",
29+
"@types/react-dom": "^19.0.2",
30+
"eslint": "^9.17.0",
31+
"eslint-config-next": "15.1.3",
32+
"postcss": "^8.4.49",
33+
"tailwindcss": "^3.4.17",
34+
"typescript": "^5.7.2",
35+
"wrangler": "^3.107.0"
36+
}
37+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @type {import('postcss-load-config').Config} */
2+
const config = {
3+
plugins: {
4+
tailwindcss: {},
5+
},
6+
};
7+
8+
export default config;

‎examples/bugs/gh-223/public/next.svg

+1
Loading
+1
Loading
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Optional: Check file size (e.g., max 5MB)
2+
export const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
3+
4+
export const validateImageFile = (file: File): void => {
5+
const allowedImageTypes = ["image/jpeg", "image/png", "image/gif", "image/jpg"];
6+
7+
// Check file type
8+
if (!allowedImageTypes.includes(file.type)) {
9+
throw new Error("Invalid file type. Please upload a valid image file.");
10+
}
11+
12+
if (file.size > MAX_FILE_SIZE) {
13+
throw new Error("File size exceeds the maximum limit of 5MB.");
14+
}
15+
};
16+
17+
export const getImageUrlFromS3 = async (fileName: string) => {
18+
try {
19+
const url = await fetch(`/api/image?fileName=${fileName}`, {
20+
method: "GET",
21+
});
22+
//@ts-ignore
23+
const { image } = await url.json();
24+
return image;
25+
} catch (error) {
26+
console.log({ error });
27+
throw new Error("Failed to get image");
28+
}
29+
};
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
2+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
3+
4+
/**
5+
* This function should only be used inside api calls
6+
*/
7+
export const getImageUrl = async (fileName: string) => {
8+
try {
9+
const s3Client = new S3Client({
10+
region: "REGION",
11+
endpoint: "ENDPOINT",
12+
credentials: {
13+
accessKeyId: "ACCESS_KEY_ID",
14+
secretAccessKey: "SECRET_ACCESS_KEY",
15+
},
16+
});
17+
18+
const command = new GetObjectCommand({
19+
Key: fileName.trim().toLowerCase().replace(/ /g, "-"),
20+
Bucket: process.env.CLOUDFLARE_R2_BUCKET || "",
21+
ResponseExpires: new Date(Date.now() + 3600),
22+
});
23+
const presignedUrl = await getSignedUrl(s3Client, command);
24+
25+
return presignedUrl;
26+
} catch (error) {
27+
console.log({ error });
28+
throw new Error("Failed to get image");
29+
}
30+
};
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { Config } from "tailwindcss";
2+
3+
const config: Config = {
4+
content: [
5+
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6+
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7+
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8+
],
9+
theme: {
10+
extend: {
11+
backgroundImage: {
12+
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13+
"gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
14+
},
15+
},
16+
},
17+
plugins: [],
18+
};
19+
export default config;

‎examples/bugs/gh-223/tsconfig.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"compilerOptions": {
3+
"lib": ["dom", "dom.iterable", "esnext"],
4+
"allowJs": true,
5+
"skipLibCheck": true,
6+
"strict": true,
7+
"noEmit": true,
8+
"esModuleInterop": true,
9+
"module": "esnext",
10+
"moduleResolution": "bundler",
11+
"resolveJsonModule": true,
12+
"isolatedModules": true,
13+
"jsx": "preserve",
14+
"incremental": true,
15+
"plugins": [
16+
{
17+
"name": "next"
18+
}
19+
],
20+
"paths": {
21+
"@/*": ["./src/*"]
22+
},
23+
"types": ["@cloudflare/workers-types/2023-07-01"],
24+
"target": "ES2017"
25+
},
26+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27+
"exclude": ["node_modules", "open-next.config.ts"]
28+
}

‎examples/bugs/gh-223/wrangler.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"$schema": "node_modules/wrangler/config-schema.json",
3+
"main": ".open-next/worker.js",
4+
"name": "gh-219",
5+
"compatibility_date": "2024-12-30",
6+
"compatibility_flags": ["nodejs_compat"],
7+
"assets": {
8+
"directory": ".open-next/assets",
9+
"binding": "ASSETS"
10+
}
11+
}

‎packages/cloudflare/src/cli/build/bundle-server.ts

+10
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ export async function bundleServer(buildOpts: BuildOptions): Promise<void> {
6565
target: "esnext",
6666
minify: false,
6767
metafile: true,
68+
// Next traces files using the default conditions from `nft` (`node`, `require`, `import` and `default`)
69+
//
70+
// Because we use the `node` platform for this build, the "module" condition is used when no conditions are defined.
71+
// We explicitly set the conditions to an empty array to disable the "module" condition in order to match Next tracing.
72+
//
73+
// See:
74+
// - default nft conditions: https://github.com/vercel/nft/blob/2b55b01/readme.md#exports--imports
75+
// - Next no explicit override: https://github.com/vercel/next.js/blob/2efcf11/packages/next/src/build/collect-build-traces.ts#L287
76+
// - ESBuild `node` platform: https://esbuild.github.io/api/#platform
77+
conditions: [],
6878
plugins: [
6979
createFixRequiresESBuildPlugin(buildOpts),
7080
inlineRequirePagePlugin(buildOpts),

‎pnpm-lock.yaml

+4,443-1,994
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.