Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/prereleases.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: Publish prereleases

on: pull_request
on:
push:
branches: [main, experimental]
pull_request:
branches: [main, experimental]

jobs:
release:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ node_modules

output
.worker-next
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shall we remove .worker-next if it's not used anymore?

.save.next
.open-next
.wrangler
dist
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ The repository contains two directories:
- `packages` containing a cloudflare package that can be used to build a Cloudflare Workers-compatible output for Next.js applications.
- `examples` containing Next.js applications that use the above mentioned cloudflare package.

### How to try out/develop in the repository
### How to try out the `@opennextjs/cloudflare` package

You can simply install the package from npm as specified in the [OpenNext documentation](https://opennext.js.org/cloudflare/get-started).

#### Preleases

Besides the standard npm releases we also automatically publish prerelease packages on branch pushes (using [`pkg.pr.new`](https://github.com/stackblitz-labs/pkg.pr.new)):

- `https://pkg.pr.new/@opennextjs/cloudflare@main`:
Updated with every push to the `main` branch, this prerelease contains the most up to date yet (reasonably) stable version of the package.
- `https://pkg.pr.new/@opennextjs/cloudflare@experimental`
Updated with every push to the `experimental` branch, this prerelease contains the latest experimental version of the package (containing features that we want to test/experiment on before committing to).

Which you can simply install directly with your package manager of choice, for example:

```bash
npm i https://pkg.pr.new/@opennextjs/cloudflare@main
```

### How to develop in the repository

See the [CONTRIBUTING](./CONTRIBUTING.md) page for how to get started with this repository.
15 changes: 15 additions & 0 deletions examples/api/open-next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { OpenNextConfig } from "@opennextjs/aws/types/open-next";

const config: OpenNextConfig = {
default: {},

middleware: {
external: true,
override: {
wrapper: "cloudflare",
converter: "edge",
},
},
};

export default config;
2 changes: 1 addition & 1 deletion examples/api/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
]
},
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx", "worker-configuration.d.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "open-next.config.ts"]
}
8 changes: 4 additions & 4 deletions examples/api/wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#:schema node_modules/wrangler/config-schema.json
name = "api"
main = ".worker-next/index.mjs"
compatibility_date = "2024-09-16"
compatibility_flags = ["nodejs_compat_v2"]
main = ".open-next/index.mjs"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]

assets = { directory = ".worker-next/assets", binding = "ASSETS" }
assets = { directory = ".open-next/assets", binding = "ASSETS" }

[vars]
hello = 'Hello World from the cloudflare context!'
15 changes: 15 additions & 0 deletions examples/create-next-app/open-next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { OpenNextConfig } from "@opennextjs/aws/types/open-next";

const config: OpenNextConfig = {
default: {},

middleware: {
external: true,
override: {
wrapper: "cloudflare",
converter: "edge",
},
},
};

export default config;
2 changes: 1 addition & 1 deletion examples/create-next-app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "open-next.config.ts"]
}
8 changes: 4 additions & 4 deletions examples/create-next-app/wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#:schema node_modules/wrangler/config-schema.json
name = "create-next-app"
main = ".worker-next/index.mjs"
main = ".open-next/index.mjs"

compatibility_date = "2024-08-29"
compatibility_flags = ["nodejs_compat_v2"]
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]

# Use the new Workers + Assets to host the static frontend files
assets = { directory = ".worker-next/assets", binding = "ASSETS" }
assets = { directory = ".open-next/assets", binding = "ASSETS" }
42 changes: 42 additions & 0 deletions examples/middleware/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
31 changes: 31 additions & 0 deletions examples/middleware/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Middleware

This example shows how to use [Middleware in Next.js](https://nextjs.org/docs/app/building-your-application/routing/middleware) to run code before a request is completed.

The index page ([`app/page.tsx`](app/page.tsx)) has a list of links to pages with `redirect`, `rewrite`, or normal behavior.

On the Middleware file ([`middleware.ts`](middleware.ts)) the routes are already being filtered by defining a `matcher` on the exported config. If you want the Middleware to run for every request, you can remove the `matcher`.

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/middleware&project-name=middleware&repository-name=middleware)

## How to use

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:

```bash
npx create-next-app --example middleware middleware-app
```

```bash
yarn create next-app --example middleware middleware-app
```

```bash
pnpm create next-app --example middleware middleware-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
Comment on lines +9 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Deploy your own
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/middleware&project-name=middleware&repository-name=middleware)
## How to use
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
```bash
npx create-next-app --example middleware middleware-app
```
```bash
yarn create next-app --example middleware middleware-app
```
```bash
pnpm create next-app --example middleware middleware-app
```
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the changes here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just removing the vercel specific documentation

3 changes: 3 additions & 0 deletions examples/middleware/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function AboutPage() {
return <h1>About</h1>;
}
3 changes: 3 additions & 0 deletions examples/middleware/app/about2/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function About2Page() {
return <h1>About 2</h1>;
}
3 changes: 3 additions & 0 deletions examples/middleware/app/another/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function AnotherPage() {
return <h1>Another</h1>;
}
18 changes: 18 additions & 0 deletions examples/middleware/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Metadata } from "next";

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}

export const metadata: Metadata = {
title: "Next.js Middleware example",
description: "Redirect and rewrite pages using Next.js Middleware.",
};
3 changes: 3 additions & 0 deletions examples/middleware/app/middleware/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function MiddlewarePage() {
return <h1>Via middleware</h1>;
}
21 changes: 21 additions & 0 deletions examples/middleware/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Link from "next/link";

export default function Home() {
return (
<div>
<h1>Index</h1>
<p>
<Link href="/about">Go to about page (will redirect)</Link>
</p>
<p>
<Link href="/another">Go to another page (will rewrite)</Link>
</p>
<p>
<Link href="/about2">Go to about 2 page (no redirect or rewrite)</Link>
</p>
<p>
<Link href="/middleware">Go to middleware page (using NextResponse.next())</Link>
</p>
</div>
);
}
3 changes: 3 additions & 0 deletions examples/middleware/app/redirected/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function RedirectedPage() {
return <h1>Redirected from /about</h1>;
}
3 changes: 3 additions & 0 deletions examples/middleware/app/rewrite/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function RewritePage() {
return <h1>Rewrite</h1>;
}
29 changes: 29 additions & 0 deletions examples/middleware/e2e/base.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { test, expect } from "@playwright/test";

test("redirect", async ({ page }) => {
await page.goto("/");
await page.click('[href="/about"]');
expect(page.waitForURL("**/redirected"));
expect(await page.textContent("h1")).toContain("Redirected");
});

test("rewrite", async ({ page }) => {
await page.goto("/");
await page.click('[href="/another"]');
expect(page.waitForURL("**/another"));
expect(await page.textContent("h1")).toContain("Rewrite");
});

test("no matching middleware", async ({ page }) => {
await page.goto("/");
await page.click('[href="/about2"]');
expect(page.waitForURL("**/about2"));
expect(await page.textContent("h1")).toContain("About 2");
});

test("matching noop middleware", async ({ page }) => {
await page.goto("/");
await page.click('[href="/middleware"]');
expect(page.waitForURL("**/middleware"));
expect(await page.textContent("h1")).toContain("Via middleware");
});
53 changes: 53 additions & 0 deletions examples/middleware/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { defineConfig, devices } from "@playwright/test";

declare const process: { env: Record<string, string> };

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:8774",

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
},

/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},

{
name: "firefox",
use: { ...devices["Desktop Firefox"] },
},

{
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
],

/* Run your local dev server before starting the tests */
webServer: {
command: "pnpm preview:worker",
url: "http://localhost:8774",
reuseExistingServer: !process.env.CI,
},
});
16 changes: 16 additions & 0 deletions examples/middleware/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NextRequest, NextResponse } from "next/server";

export function middleware(request: NextRequest) {
console.log("middleware");
if (request.nextUrl.pathname === "/about") {
return NextResponse.redirect(new URL("/redirected", request.url));
}
if (request.nextUrl.pathname === "/another") {
return NextResponse.rewrite(new URL("/rewrite", request.url));
}
return NextResponse.next();
}

export const config = {
matcher: ["/about/:path*", "/another/:path*", "/middleware/:path*"],
};
4 changes: 4 additions & 0 deletions examples/middleware/next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};

export default nextConfig;
15 changes: 15 additions & 0 deletions examples/middleware/open-next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { OpenNextConfig } from "@opennextjs/aws/types/open-next";

const config: OpenNextConfig = {
default: {},

middleware: {
external: true,
override: {
wrapper: "cloudflare",
converter: "edge",
},
},
};

export default config;
Loading