Skip to content

Commit

Permalink
feat: add cloudflare-pages preset (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniFoldi authored Aug 24, 2022
1 parent 5356f62 commit ed7d0db
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/content/2.deploy/0.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ When running Nitro in development mode, Nitro will always use a special preset c
When deploying to the production using CI/CD, Nitro tries to automatically detect the provider environment and set the right one without any additional configuration. Currently, providers below can be auto-detected with zero config.

- [azure](/deploy/providers/azure)
- [cloudflare_pages](/deploy/providers/cloudflare_pages)
- [netlify](/deploy/providers/netlify)
- [stormkit](/deploy/providers/stormkit)
- [vercel](/deploy/providers/vercel)
Expand Down
55 changes: 52 additions & 3 deletions docs/content/2.deploy/providers/cloudflare.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@ title: Cloudflare
description: 'Discover Cloudflare preset for Nitro!'
---

## Cloudflare Workers

**Preset:** `cloudflare` ([switch to this preset](/deploy/#changing-the-deployment-preset))


::alert{type="info"}
**Note:** This preset uses [service-worker syntax](https://developers.cloudflare.com/workers/learning/service-worker/) for deployment.
::

Login to your [Cloudflare Workers](https://workers.cloudflare.com) account and obtain your `account_id` from the sidebar.

Create a `wrangler.toml` in your root directory:
Expand All @@ -27,7 +34,7 @@ command = ""
upload.format = "service-worker"
```

## Testing locally
### Testing locally

You can use [miniflare](https://miniflare.dev/), a local Cloudflare Workers development server, to test your app locally:

Expand All @@ -36,7 +43,7 @@ NITRO_PRESET=cloudflare yarn build
npx miniflare .output/server/index.mjs --site .output/public
```

## Deploy from your local machine using wrangler
### Deploy from your local machine using wrangler

Install [wrangler](https://github.com/cloudflare/wrangler) and login to your Cloudflare account:

Expand All @@ -63,7 +70,7 @@ Publish:
wrangler publish
```

## Deploy within CI/CD using GitHub Actions
### Deploy within CI/CD using GitHub Actions

Create a token according to [the wrangler action docs](https://github.com/marketplace/actions/deploy-to-cloudflare-workers-with-wrangler#authentication) and set `CF_API_TOKEN` in your repository config on GitHub.

Expand Down Expand Up @@ -119,3 +126,45 @@ jobs:
```
## Cloudflare Pages
**Preset:** `cloudflare_pages` ([switch to this preset](/deploy/#changing-the-deployment-preset))

::alert{type="warning"}
**Note:** This is an experimental preset.
::

::alert
**Zero Config Provider**
:br
Integration with this provider is possible with zero configuration. ([Learn More](/deploy/#zero-config-providers))
::

### Git integration

If you use the GitHub/GitLab [integration](https://developers.cloudflare.com/pages/get-started/#connect-your-git-provider-to-pages) with Pages, Nitro does not require any configuration. When you push to the repository, Pages will automatically build your project, and Nitro will detect the environment.

### Direct Upload

Alternatively, you can use [wrangler](https://github.com/cloudflare/wrangler2) to upload your project to Cloudflare. In this case, you will have to set the preset manually:

### Deploy from your local machine using wrangler

Install [wrangler](https://github.com/cloudflare/wrangler) and login to your Cloudflare account:

```bash
npm i wrangler -g
wrangler login
```

Create project:

```bash
wrangler pages project create <project-name>
```

Publish:

```bash
wrangler pages publish
```
25 changes: 25 additions & 0 deletions src/presets/cloudflare.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { resolve } from 'pathe'
import { move } from 'fs-extra'
import { writeFile } from '../utils'
import { defineNitroPreset } from '../preset'
import type { Nitro } from '../types'
Expand All @@ -17,3 +18,27 @@ export const cloudflare = defineNitroPreset({
}
}
})

export const cloudflarePages = defineNitroPreset({
extends: 'cloudflare',
entry: '#internal/nitro/entries/cloudflare-pages',
commands: {
preview: 'npx wrangler pages dev .output/public',
deploy: 'npx wrangler pages publish .output/public'
},
output: {
serverDir: '{{ rootDir }}/functions'
},
rollupConfig: {
output: {
entryFileNames: 'path.js',
format: 'esm'
}
},
hooks: {
async 'compiled' (nitro: Nitro) {
await move(resolve(nitro.options.output.serverDir, 'path.js'), resolve(nitro.options.output.serverDir, '[[path]].js'))
await move(resolve(nitro.options.output.serverDir, 'path.js.map'), resolve(nitro.options.output.serverDir, '[[path]].js.map'))
}
}
})
60 changes: 60 additions & 0 deletions src/runtime/entries/cloudflare-pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import '#internal/nitro/virtual/polyfill'
import { requestHasBody, useRequestBody } from '../utils'
import { nitroApp } from '../app'

/** @see https://developers.cloudflare.com/pages/platform/functions/#writing-your-first-function */
interface CFRequestContext {
/** same as existing Worker API */
request: any
/** same as existing Worker API */
env: any
/** if filename includes [id] or [[path]] **/
params: any
/** Same as ctx.waitUntil in existing Worker API */
waitUntil: any
/** Used for middleware or to fetch assets */
next: any
/** Arbitrary space for passing data between middlewares */
data: any
}

export async function onRequest (ctx: CFRequestContext) {
try {
// const asset = await env.ASSETS.fetch(request, { cacheControl: assetsCacheControl })
const asset = await ctx.next()
if (asset.status !== 404) {
return asset
}
} catch (_err) {
// Ignore
}

const url = new URL(ctx.request.url)
let body
if (requestHasBody(ctx.request)) {
body = await useRequestBody(ctx.request)
}

const r = await nitroApp.localCall({
url: url.pathname + url.search,
method: ctx.request.method,
headers: ctx.request.headers,
host: url.hostname,
protocol: url.protocol,
body
// TODO: Allow passing custom context
// cf: ctx,
// TODO: Handle redirects?
// redirect: ctx.request.redirect
})

return new Response(r.body, {
headers: normalizeOutgoingHeaders(r.headers),
status: r.status,
statusText: r.statusText
})
}

function normalizeOutgoingHeaders (headers: Record<string, string | string[] | undefined>) {
return Object.entries(headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(',') : v])
}
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export function replaceAll (input: string, from: string, to: string) {

const autodetectableProviders = {
azure_static: 'azure',
cloudflare_pages: 'cloudflare_pages',
netlify: 'netlify',
stormkit: 'stormkit',
vercel: 'vercel'
Expand Down

0 comments on commit ed7d0db

Please sign in to comment.