Cloudflare Pages!
We better discuss this with you all! please give me your suggestions!
After struggling with multiple different bundlers, I have managed to find a way to do this 🙃
- Repository: https://github.com/thetre97/vite-plugin-ssr-cloudflare-pages
- Preview: https://vite-plugin-ssr-cloudflare-pages.pages.dev
I have used esbuild to bundle the worker, and it's then just a case of configuring the pages site correctly.
Dev Packages
esbuild
@esbuild-plugins/node-modules-polyfill
wrangler@beta # Used for local previews
@cloudflare/workers-types # Optional
Worker
worker/index.ts
import { createPageRenderer } from 'vite-plugin-ssr'
import '../dist/server/importBuild.js'
const renderPage = createPageRenderer({ isProduction: true })
interface EnvironmentVariables {
EXAMPLE_SECRET: string
}
type FetchFunction = EventContext<EnvironmentVariables, '', unknown>
export default {
async fetch (request: FetchFunction['request'], env: FetchFunction['env']) {
// Keep browser requests happy during testing
if (request.url.includes('favicon')) return new Response('', { status: 200 })
if (request.url.includes('/api/')) {
// TODO: Add your custom /api/* logic here.
return new Response('Ok')
}
// Handle Asset requests
if (request.url.includes('/assets')) return env.ASSETS.fetch(request)
// Otherwise pass to SSR handler
const pageContextInit = {
url: request.url,
fetch: (...args: [RequestInfo, RequestInit]) => fetch(...args)
}
const pageContext = await renderPage(pageContextInit)
const { httpResponse } = pageContext
if (httpResponse) {
const { body, statusCode, contentType } = httpResponse
return new Response(body, {
headers: { 'content-type': contentType },
status: statusCode
})
}
}
}
Build Script
build.js
const esbuild = require('esbuild')
const { default: nodeModulesPolyfills } = require('@esbuild-plugins/node-modules-polyfill')
esbuild.build({
entryPoints: ['./worker/index.js'],
sourcemap: false,
outfile: './dist/client/_worker.js',
minify: true,
logLevel: 'info',
platform: 'browser',
plugins: [nodeModulesPolyfills()],
format: 'esm',
target: 'es2020',
bundle: true
}).then(() => {
console.log(`Successfully built worker.`)
}).catch(error => {
console.error(`There was an error whilst building this worker:`)
console.error(error)
})
Pages
Make sure to set the Build output directory to /dist/client, as otherwise asset requests will fail because the browser is requesting /assets/some.js, whereas the actual stored asset path would be /client/assets/some.js.
You will then need to add a build script in package.json to first build the Vite app, run vite-plugin-ssr, and then run the build.js script.
"scripts": {
"build": "npm run build:site && npm run build:worker",
"build:site": "vite build && vite build --ssr",
"build:worker": "node build.js",
"serve": "wrangler pages dev ./dist/client"
}
You can also preview the site locally, by running the build script and then the serve script.
Potential Issues
The esbuild --minify option is pretty decent, but even with this basic project the built _worker.js file comes out around 600kB - the Worker limit being 1MB. So any larger projects, with a number of pages/packages etc. will probably go over this limit quite quickly. There is talk of this limit being raised however, and there is a form to request an increase (although I don't know if this is just for the Workers platform, or whether it includes Pages Functions (which is what the above uses).
Thank you @thetre97, I appreciate this!
I'll read it precisely in the next days, so I can improve this package! I'll let you know if I needed anything!