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

Update and modernize adapter-cloudflare-workers #4576

Closed
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/tidy-paws-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-cloudflare-workers': patch
---

Update adapter to use modules API and match the standard adapter-cloudflare implementation more closely
3 changes: 2 additions & 1 deletion packages/adapter-cloudflare-workers/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
node_modules
target
target
/files
37 changes: 27 additions & 10 deletions packages/adapter-cloudflare-workers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,19 @@ export default {

**You will need [Wrangler](https://developers.cloudflare.com/workers/cli-wrangler/install-update) installed on your system**

This adapter expects to find a [wrangler.toml](https://developers.cloudflare.com/workers/platform/sites/configuration) file in the project root. It will determine where to write static assets and the worker based on the `site.bucket` and `site.entry-point` settings.
This adapter expects to find a [wrangler.toml](https://developers.cloudflare.com/workers/platform/sites/configuration) file in the project root. It will determine where to write static assets and the worker based on the `site.bucket` and `build.upload` settings. These values must be set to the following:

Generate this file using `wrangler` from your project directory
```toml
[build.upload]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This approach is more opinionated than the last one, which IMO is a better developer experience. Given adapter-cloudflare builds to .svelte-kit/cloudflare and requires the user to set it to that path in Pages, I don't think this leaking any more details. That said, I can revert it if the old style, user-provided config is preferred.

Copy link
Member

Choose a reason for hiding this comment

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

I think it's better if it respects the values in wrangler.toml — the example config we provide in the README/error messages could be the values specified here, but I don't think there's any real benefit to preventing them going somewhere else. For one thing, outDir is configurable

format = "modules"
dir = "./.svelte-kit/cloudflare"
main = "./_worker.mjs"

[site]
bucket = "./.svelte-kit/cloudflare-bucket"
```

To get started, generate this file using `wrangler` from your project directory

```sh
wrangler init --site my-site-name
Expand All @@ -48,24 +58,31 @@ Now you should get some details from Cloudflare. You should get your:

Get them by visiting your [Cloudflare dashboard](https://dash.cloudflare.com) and click on any domain. There, you can scroll down and on the left, you can see your details under **API**.

Then configure your sites build directory and your account-details in the config file:
Then configure your account-details in the config file:

```toml
account_id = 'YOUR ACCOUNT_ID'
zone_id = 'YOUR ZONE_ID' # optional, if you don't specify this a workers.dev subdomain will be used.
site = {bucket = "./build", entry-point = "./workers-site"}

name = "<your-site-name>"
type = "javascript"
account_id = "<your-account-id>"
workers_dev = true
route = ""
zone_id = ""

compatibility_date = "2022-02-09"

[build]
# Assume it's already been built. You can make this "npm run build" to ensure a build before publishing
command = ""

# All values below here are required by adapter-cloudflare-workers and should not change
[build.upload]
format = "service-worker"
```
format = "modules"
dir = "./.svelte-kit/cloudflare"
main = "./_worker.mjs"

It's recommended that you add the `build` and `workers-site` folders (or whichever other folders you specify) to your `.gitignore`.
[site]
bucket = "./.svelte-kit/cloudflare-bucket"
```

Now, log in with wrangler:

Expand Down
10 changes: 4 additions & 6 deletions packages/adapter-cloudflare-workers/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ declare module 'MANIFEST' {
import { SSRManifest } from '@sveltejs/kit';

export const manifest: SSRManifest;
export const prerendered: Set<string>;
export const prerendered: Map<string, { file: string }>;
}

declare abstract class FetchEvent extends Event {
readonly request: Request;
respondWith(promise: Response | Promise<Response>): void;
passThroughOnException(): void;
waitUntil(promise: Promise<any>): void;
declare module '__STATIC_CONTENT_MANIFEST' {
const value: string;
export = value;
}
61 changes: 0 additions & 61 deletions packages/adapter-cloudflare-workers/files/entry.js

This file was deleted.

194 changes: 126 additions & 68 deletions packages/adapter-cloudflare-workers/index.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { posix } from 'path';
import { execSync } from 'child_process';
import esbuild from 'esbuild';
import * as esbuild from 'esbuild';
import toml from '@iarna/toml';
import { fileURLToPath } from 'url';

/** @type {import('.')} */
export default function () {
export default function (options = {}) {
return {
name: '@sveltejs/adapter-cloudflare-workers',

async adapt(builder) {
const { site } = validate_config(builder);

// @ts-ignore
const { bucket } = site;
validate_config(builder);

// @ts-ignore
const entrypoint = site['entry-point'] || 'workers-site';

const files = fileURLToPath(new URL('./files', import.meta.url).href);
const tmp = builder.getBuildDirectory('cloudflare-workers-tmp');
const dest = builder.getBuildDirectory('cloudflare');
const bucket = builder.getBuildDirectory('cloudflare-bucket');
const tmp = builder.getBuildDirectory('cloudflare-tmp');

builder.rimraf(dest);
builder.rimraf(bucket);
builder.rimraf(entrypoint);
builder.rimraf(tmp);

builder.mkdirp(tmp);

builder.writeStatic(bucket);
builder.writeClient(bucket);
builder.writePrerendered(bucket);

const relativePath = posix.relative(tmp, builder.getServerDirectory());

builder.log.info('Installing worker dependencies...');
builder.copy(`${files}/_package.json`, `${tmp}/package.json`);
Expand All @@ -33,83 +38,136 @@ export default function () {
builder.log.info(stdout.toString());

builder.log.minor('Generating worker...');
const relativePath = posix.relative(tmp, builder.getServerDirectory());

builder.copy(`${files}/entry.js`, `${tmp}/entry.js`, {
writeFileSync(
`${tmp}/manifest.js`,
`export const manifest = ${builder.generateManifest({
relativePath
})};\n\nexport const prerendered = new Map(${JSON.stringify(
Array.from(builder.prerendered.pages.entries())
)});\n`
);

builder.copy(`${files}/worker.js`, `${tmp}/_worker.js`, {
replace: {
SERVER: `${relativePath}/index.js`,
MANIFEST: './manifest.js'
}
});

writeFileSync(
`${tmp}/manifest.js`,
`export const manifest = ${builder.generateManifest({
relativePath
})};\n\nexport const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n`
);
const external = ['__STATIC_CONTENT_MANIFEST'];

if (options.external) {
external.push(...options.external);
}

await esbuild.build({
entryPoints: [`${tmp}/entry.js`],
outfile: `${entrypoint}/index.js`,
bundle: true,
target: 'es2020',
platform: 'browser'
platform: 'browser',
...options,
entryPoints: [`${tmp}/_worker.js`],
external,
outfile: `${dest}/_worker.mjs`,
allowOverwrite: true,
format: 'esm',
bundle: true
});

writeFileSync(`${entrypoint}/package.json`, JSON.stringify({ main: 'index.js' }));

builder.log.minor('Copying assets...');
builder.writeClient(bucket);
builder.writeStatic(bucket);
builder.writePrerendered(bucket);
}
};
}

/** @param {import('@sveltejs/kit').Builder} builder */
function validate_config(builder) {
if (existsSync('wrangler.toml')) {
let wrangler_config;

try {
wrangler_config = toml.parse(readFileSync('wrangler.toml', 'utf-8'));
} catch (err) {
err.message = `Error parsing wrangler.toml: ${err.message}`;
throw err;
}
if (!existsSync('wrangler.toml')) {
builder.log.error(
'Consult https://developers.cloudflare.com/workers/platform/sites/configuration on how to setup your site'
);

builder.log(
`
Sample wrangler.toml:

name = "<your-site-name>"
type = "javascript"
account_id = "<your-account-id>"
workers_dev = true
route = ""
zone_id = ""

compatibility_date = "2022-02-09"

[build]
# Assume it's already been built. You can make this "npm run build" to ensure a build before publishing
command = ""

# All values below here are required by adapter-cloudflare-workers and should not change
[build.upload]
format = "modules"
dir = "./.svelte-kit/cloudflare"
main = "./_worker.mjs"

[site]
bucket = "./.svelte-kit/cloudflare-bucket"`
.replace(/^\t+/gm, '')
.trim()
);

throw new Error('Missing a wrangler.toml file');
}

let wrangler_config;

try {
wrangler_config = toml.parse(readFileSync('wrangler.toml', 'utf-8'));
} catch (err) {
err.message = `Error parsing wrangler.toml: ${err.message}`;
throw err;
}

// @ts-ignore
if (!wrangler_config.site || wrangler_config.site.bucket !== './.svelte-kit/cloudflare-bucket') {
throw new Error(
'You must specify site.bucket in wrangler.toml, and it must equal "./.svelte-kit/cloudflare-bucket"'
);
}

// @ts-ignore
if (
// @ts-ignore
if (!wrangler_config.site || !wrangler_config.site.bucket) {
throw new Error(
'You must specify site.bucket in wrangler.toml. Consult https://developers.cloudflare.com/workers/platform/sites/configuration'
);
}
!wrangler_config.build ||
// @ts-ignore
!wrangler_config.build.upload ||
// @ts-ignore
wrangler_config.build.upload.format !== 'modules'
) {
throw new Error(
'You must specify build.upload.format in wrangler.toml, and it must equal "modules"'
);
}

return wrangler_config;
if (
// @ts-ignore
!wrangler_config.build ||
// @ts-ignore
!wrangler_config.build.upload ||
// @ts-ignore
wrangler_config.build.upload.dir !== './.svelte-kit/cloudflare'
) {
throw new Error(
'You must specify build.upload.dir in wrangler.toml, and it must equal "./.svelte-kit/cloudflare"'
);
}

builder.log.error(
'Consult https://developers.cloudflare.com/workers/platform/sites/configuration on how to setup your site'
);

builder.log(
`
Sample wrangler.toml:

name = "<your-site-name>"
type = "javascript"
account_id = "<your-account-id>"
workers_dev = true
route = ""
zone_id = ""

[site]
bucket = "./.cloudflare/assets"
entry-point = "./.cloudflare/worker"`
.replace(/^\t+/gm, '')
.trim()
);

throw new Error('Missing a wrangler.toml file');
if (
// @ts-ignore
!wrangler_config.build ||
// @ts-ignore
!wrangler_config.build.upload ||
// @ts-ignore
wrangler_config.build.upload.main !== './_worker.mjs'
) {
throw new Error(
'You must specify build.upload.main in wrangler.toml, and it must equal "./_worker.mjs"'
);
}
}
Loading