Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add SSR adaptor for cloudflare pages functions #3600

Merged
merged 1 commit into from
Jun 16, 2022
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
5 changes: 5 additions & 0 deletions .changeset/nine-dots-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/cloudflare': minor
---

add SSR adaptor for Cloudflare Pages functions
24 changes: 24 additions & 0 deletions packages/integrations/cloudflare/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# @astrojs/cloudflare

An SSR adapter for use with Cloudflare Pages Functions targets. Write your code in Astro/Node and deploy to Cloudflare Pages.

In your astro.config.mjs use:

```js
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
adapter: cloudflare()
});
```

## Enabling Preview

In order for preview to work you must install `wrangler`

```sh
$ pnpm install wrangler --save-dev
```

It's then possible to update the preview script in your `package.json` to `"preview": "wrangler pages dev ./dist"`
33 changes: 33 additions & 0 deletions packages/integrations/cloudflare/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@astrojs/cloudflare",
"description": "Deploy your site to cloudflare pages functions",
"version": "0.1.0",
"type": "module",
"types": "./dist/index.d.ts",
"author": "withastro",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/withastro/astro.git",
"directory": "packages/integrations/cloudflare"
},
"bugs": "https://github.com/withastro/astro/issues",
"homepage": "https://astro.build",
"exports": {
".": "./dist/index.js",
"./server.js": "./dist/server.js",
"./package.json": "./package.json"
},
"scripts": {
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
"build:ci": "astro-scripts build \"src/**/*.ts\"",
"dev": "astro-scripts dev \"src/**/*.ts\""
},
"dependencies": {
"esbuild": "^0.14.42"
},
"devDependencies": {
"astro": "workspace:*",
"astro-scripts": "workspace:*"
}
}
73 changes: 73 additions & 0 deletions packages/integrations/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { AstroAdapter, AstroConfig, AstroIntegration, BuildConfig } from 'astro';
import esbuild from 'esbuild';
import * as fs from 'fs';
import { fileURLToPath } from 'url';

export function getAdapter(): AstroAdapter {
return {
name: '@astrojs/cloudflare',
serverEntrypoint: '@astrojs/cloudflare/server.js',
exports: ['default'],
};
}

export default function createIntegration(): AstroIntegration {
let _config: AstroConfig;
let _buildConfig: BuildConfig;

return {
name: '@astrojs/cloudflare',
hooks: {
'astro:config:done': ({ setAdapter, config }) => {
setAdapter(getAdapter());
_config = config;
},
'astro:build:start': ({ buildConfig }) => {
_buildConfig = buildConfig;
buildConfig.serverEntry = '_worker.js';
buildConfig.client = new URL('./static/', _config.outDir);
buildConfig.server = new URL('./', _config.outDir);
},
'astro:build:setup': ({ vite, target }) => {
if (target === 'server') {
vite.resolve = vite.resolve || {};
vite.resolve.alias = vite.resolve.alias || {};

const aliases = [{ find: 'react-dom/server', replacement: 'react-dom/server.browser' }];

if (Array.isArray(vite.resolve.alias)) {
vite.resolve.alias = [...vite.resolve.alias, ...aliases];
} else {
for (const alias of aliases) {
(vite.resolve.alias as Record<string, string>)[alias.find] = alias.replacement;
}
}

vite.ssr = {
target: 'webworker',
noExternal: true,
};
}
},
'astro:build:done': async () => {
const entryUrl = new URL(_buildConfig.serverEntry, _buildConfig.server);
const pkg = fileURLToPath(entryUrl);

await esbuild.build({
target: 'es2020',
platform: 'browser',
entryPoints: [pkg],
outfile: pkg,
allowOverwrite: true,
format: 'esm',
bundle: true,
minify: true,
});

// throw the server folder in the bin
const chunksUrl = new URL('./chunks', _buildConfig.server);
await fs.promises.rm(chunksUrl, { recursive: true, force: true });
},
},
};
}
34 changes: 34 additions & 0 deletions packages/integrations/cloudflare/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import './shim.js';

import type { SSRManifest } from 'astro';
import { App } from 'astro/app';

type Env = {
ASSETS: { fetch: (req: Request) => Promise<Response> };
};

export function createExports(manifest: SSRManifest) {
const app = new App(manifest);

const fetch = async (request: Request, env: Env) => {
const { origin, pathname } = new URL(request.url);

// static assets
if (manifest.assets.has(pathname)) {
const assetRequest = new Request(`${origin}/static${pathname}`, request);
return env.ASSETS.fetch(assetRequest);
}

if (app.match(request)) {
return app.render(request);
}

// 404
return new Response(null, {
status: 404,
statusText: 'Not found',
});
};

return { default: { fetch } };
}
4 changes: 4 additions & 0 deletions packages/integrations/cloudflare/src/shim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(globalThis as any).process = {
argv: [],
env: {},
};
10 changes: 10 additions & 0 deletions packages/integrations/cloudflare/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["src"],
"compilerOptions": {
"allowJs": true,
"module": "ES2020",
"outDir": "./dist",
"target": "ES2020"
}
}
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.