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
28 changes: 28 additions & 0 deletions .changeset/twenty-signs-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
'@astrojs/cloudflare': minor
---

Adds support for more `@cloudflare/vite-plugin` options

The adapter now accepts the following [options from Cloudflare's Vite plugin](https://developers.cloudflare.com/workers/vite-plugin/reference/api/):

- `auxiliaryWorkers`
- `configPath`
- `inspectorPort`
- `persistState`
- `remoteBindings`
- `experimental.headersAndRedirectsDevModeSupport`

For example, you can now set `inspectorPort` to provide a custom port for debugging your Workers:

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

export default defineConfig({
adapter: cloudflare({
inspectorPort: 3456
})
})
```
11 changes: 4 additions & 7 deletions packages/integrations/cloudflare/src/entrypoints/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import {
type ResolvedServerUrls,
} from 'vite';
import { fileURLToPath } from 'node:url';
import { cloudflare as cfVitePlugin, type PluginConfig } from '@cloudflare/vite-plugin';
import { cloudflare as cfVitePlugin } from '@cloudflare/vite-plugin';
import type * as http from 'node:http';
import colors from 'piccolore';
import { performance } from 'node:perf_hooks';
import { cloudflareConfigCustomizer } from '../wrangler.js';

const createPreviewServer: CreatePreviewServer = async ({
logger,
Expand All @@ -23,10 +22,6 @@ const createPreviewServer: CreatePreviewServer = async ({
}) => {
const startServerTime = performance.now();
let previewServer: VitePreviewServer;
const cfPluginConfig: PluginConfig = {
viteEnvironment: { name: 'ssr' },
config: cloudflareConfigCustomizer(),
};

try {
previewServer = await preview({
Expand All @@ -44,7 +39,9 @@ const createPreviewServer: CreatePreviewServer = async ({
open: false,
allowedHosts: [],
},
plugins: [cfVitePlugin(cfPluginConfig)],
plugins: [
cfVitePlugin({ ...globalThis.astroCloudflareOptions, viteEnvironment: { name: 'ssr' } }),
],
});
} catch (err) {
if (err instanceof Error) {
Expand Down
54 changes: 38 additions & 16 deletions packages/integrations/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ import {
import { parseEnv } from 'node:util';
import { sessionDrivers } from 'astro/config';
import { createCloudflarePrerenderer } from './prerenderer.js';
import { createRequire } from 'node:module';

export type { Runtime } from './utils/handler.js';

export type Options = {
export interface Options
extends Pick<
PluginConfig,
'auxiliaryWorkers' | 'configPath' | 'inspectorPort' | 'persistState' | 'remoteBindings'
> {
/** Options for handling images. */
imageService?: ImageServiceConfig;

Expand All @@ -49,18 +54,26 @@ export type Options = {
* See https://developers.cloudflare.com/images/transform-images/bindings/ for more details.
*/
imagesBindingName?: string;
};

export default function createIntegration(args?: Options): AstroIntegration {
experimental?: Pick<
NonNullable<PluginConfig['experimental']>,
'headersAndRedirectsDevModeSupport'
>;
}

export default function createIntegration({
imageService,
sessionKVBindingName = DEFAULT_SESSION_KV_BINDING_NAME,
imagesBindingName = DEFAULT_IMAGES_BINDING_NAME,
...cloudflareOptions
}: Options = {}): AstroIntegration {
let _config: AstroConfig;

let _routes: IntegrationResolvedRoute[];
let _isFullyStatic = false;
let cfPluginConfig: PluginConfig;

const sessionKVBindingName = args?.sessionKVBindingName ?? DEFAULT_SESSION_KV_BINDING_NAME;
const imagesBindingName = args?.imagesBindingName ?? DEFAULT_IMAGES_BINDING_NAME;

const { buildService, runtimeService } = normalizeImageServiceConfig(args?.imageService);
const { buildService, runtimeService } = normalizeImageServiceConfig(imageService);
const needsImagesBinding = runtimeService === 'cloudflare-binding';

return {
Expand Down Expand Up @@ -97,17 +110,17 @@ export default function createIntegration(args?: Options): AstroIntegration {
ttl: session?.ttl,
};
}

// In dev, `compile` needs the IMAGES binding for real transforms
// (the image-transform-endpoint uses it). At build time,
// `compile` uses Sharp on the Node side instead.
const needsImagesBindingForDev = isCompile && command === 'dev';

const cfPluginConfig: PluginConfig = {
viteEnvironment: { name: 'ssr' },
cfPluginConfig = {
config: cloudflareConfigCustomizer({
sessionKVBindingName: args?.sessionKVBindingName,
sessionKVBindingName,
imagesBindingName:
needsImagesBinding || needsImagesBindingForDev ? args?.imagesBindingName : false,
needsImagesBinding || needsImagesBindingForDev ? imagesBindingName : false,
}),
experimental: {
prerenderWorker: {
Expand All @@ -125,14 +138,20 @@ export default function createIntegration(args?: Options): AstroIntegration {
},
};

// The preview entrypoint uses Cloudflare's vite plugin and so it needs access
// to the config. But there's no proper API for this so we use globalThis.
if (command === 'preview') {
globalThis.astroCloudflareOptions = cfPluginConfig;
}

updateConfig({
build: {
redirects: false,
},
session,
vite: {
plugins: [
cfVitePlugin(cfPluginConfig),
cfVitePlugin({ ...cfPluginConfig, viteEnvironment: { name: 'ssr' } }),
{
name: '@astrojs/cloudflare:cf-imports',
enforce: 'pre',
Expand Down Expand Up @@ -229,9 +248,13 @@ export default function createIntegration(args?: Options): AstroIntegration {
}),
],
},
image: setImageConfig(args?.imageService, config.image, command, logger),
image: setImageConfig(imageService, config.image, command, logger),
});

if (cloudflareOptions.configPath) {
addWatchFile(createRequire(import.meta.url).resolve(cloudflareOptions.configPath));
}

addWatchFile(new URL('./wrangler.toml', config.root));
addWatchFile(new URL('./wrangler.json', config.root));
addWatchFile(new URL('./wrangler.jsonc', config.root));
Expand Down Expand Up @@ -298,6 +321,7 @@ export default function createIntegration(args?: Options): AstroIntegration {
clientDir: _config.build.client,
base: _config.base,
trailingSlash: _config.trailingSlash,
cfPluginConfig,
hasCompileImageService: buildService === 'compile',
}),
);
Expand All @@ -322,9 +346,7 @@ export default function createIntegration(args?: Options): AstroIntegration {
// in a global way, so we shim their access as `process.env.*`. This is not the recommended way for users to access environment variables. But we'll add this for compatibility for chosen variables. Mainly to support `@astrojs/db`
vite.define = {
'process.env': 'process.env',
'globalThis.__ASTRO_IMAGES_BINDING_NAME': JSON.stringify(
args?.imagesBindingName ?? 'IMAGES',
),
'globalThis.__ASTRO_IMAGES_BINDING_NAME': JSON.stringify(imagesBindingName),
...vite.define,
};
}
Expand Down
10 changes: 3 additions & 7 deletions packages/integrations/cloudflare/src/prerenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { preview, type PreviewServer as VitePreviewServer } from 'vite';
import { fileURLToPath } from 'node:url';
import { mkdir } from 'node:fs/promises';
import { cloudflare as cfVitePlugin, type PluginConfig } from '@cloudflare/vite-plugin';
import { cloudflareConfigCustomizer } from './wrangler.js';
import { serializeRouteData, deserializeRouteData } from 'astro/app/manifest';
import type {
StaticPathsResponse,
Expand All @@ -27,6 +26,7 @@ interface CloudflarePrerendererOptions {
clientDir: AstroConfig['build']['client'];
base: AstroConfig['base'];
trailingSlash: AstroConfig['trailingSlash'];
cfPluginConfig: PluginConfig;
hasCompileImageService: boolean;
}

Expand All @@ -40,6 +40,7 @@ export function createCloudflarePrerenderer({
clientDir,
base,
trailingSlash,
cfPluginConfig,
hasCompileImageService,
}: CloudflarePrerendererOptions): AstroPrerenderer {
let previewServer: VitePreviewServer | undefined;
Expand All @@ -52,11 +53,6 @@ export function createCloudflarePrerenderer({
// Ensure client dir exists (CF plugin expects it for assets)
await mkdir(clientDir, { recursive: true });

const cfPluginConfig: PluginConfig = {
viteEnvironment: { name: 'prerender' },
config: cloudflareConfigCustomizer(),
};

previewServer = await preview({
configFile: false,
base,
Expand All @@ -70,7 +66,7 @@ export function createCloudflarePrerenderer({
port: 0, // Let the OS pick a free port
open: false,
},
plugins: [cfVitePlugin(cfPluginConfig)],
plugins: [cfVitePlugin({ ...cfPluginConfig, viteEnvironment: { name: 'prerender' } })],
});

const address = previewServer.httpServer.address();
Expand Down
6 changes: 3 additions & 3 deletions packages/integrations/cloudflare/src/wrangler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ export const DEFAULT_IMAGES_BINDING_NAME = 'IMAGES';
export const DEFAULT_ASSETS_BINDING_NAME = 'ASSETS';

interface CloudflareConfigOptions {
sessionKVBindingName?: string;
imagesBindingName?: string | false;
sessionKVBindingName: string | undefined;
imagesBindingName: string | false | undefined;
}

/**
* Returns a config customizer that sets up the Astro Cloudflare defaults.
* Sets the main entrypoint and adds bindings for auto-provisioning.
*/
export function cloudflareConfigCustomizer(
options?: CloudflareConfigOptions,
options: CloudflareConfigOptions,
): PluginConfig['config'] {
const sessionKVBindingName = options?.sessionKVBindingName ?? DEFAULT_SESSION_KV_BINDING_NAME;
const imagesBindingName =
Expand Down
2 changes: 2 additions & 0 deletions packages/integrations/cloudflare/virtual.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ declare namespace Cloudflare {
interface Env extends Cloudflare.Env {}
type ImagesBinding = import('@cloudflare/workers-types').ImagesBinding;
type Fetcher = import('@cloudflare/workers-types').Fetcher;

declare var astroCloudflareOptions: import('@cloudflare/vite-plugin').PluginConfig;