chore(agent): configure AI agents and tools
Add configuration for BMad, Claude, OpenCode, and other AI agent tools and workflows.
This commit is contained in:
320
.agent/skills/astro-cloudflare-deploy/SKILL.md
Normal file
320
.agent/skills/astro-cloudflare-deploy/SKILL.md
Normal file
@@ -0,0 +1,320 @@
|
||||
---
|
||||
name: astro-cloudflare-deploy
|
||||
description: Deploy Astro 6 frontend applications to Cloudflare Workers. This skill should be used when deploying an Astro project to Cloudflare, whether as a static site, hybrid rendering, or full SSR. Handles setup of @astrojs/cloudflare adapter, wrangler.jsonc configuration, environment variables, and CI/CD deployment workflows.
|
||||
---
|
||||
|
||||
# Astro 6 to Cloudflare Workers Deployment
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides a complete workflow for deploying Astro 6 applications to Cloudflare Workers. It covers static sites, hybrid rendering, and full SSR deployments using the official @astrojs/cloudflare adapter.
|
||||
|
||||
**Key Requirements:**
|
||||
- Astro 6.x (requires Node.js 22.12.0+)
|
||||
- @astrojs/cloudflare adapter v13+
|
||||
- Wrangler CLI v4+
|
||||
|
||||
## Deployment Decision Tree
|
||||
|
||||
First, determine the deployment mode based on project requirements:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ DEPLOYMENT MODE DECISION │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. Static Site? │
|
||||
│ └─ Marketing sites, blogs, documentation │
|
||||
│ └─ No server-side rendering needed │
|
||||
│ └─ Go to: Static Deployment │
|
||||
│ │
|
||||
│ 2. Mixed static + dynamic pages? │
|
||||
│ └─ Some pages need SSR (dashboard, user-specific content) │
|
||||
│ └─ Most pages are static │
|
||||
│ └─ Go to: Hybrid Deployment │
|
||||
│ │
|
||||
│ 3. All pages need server rendering? │
|
||||
│ └─ Web app with authentication, dynamic content │
|
||||
│ └─ Real-time data on all pages │
|
||||
│ └─ Go to: Full SSR Deployment │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Step 1: Verify Prerequisites
|
||||
|
||||
Before deployment, verify the following:
|
||||
|
||||
```bash
|
||||
# Check Node.js version (must be 22.12.0+)
|
||||
node --version
|
||||
|
||||
# If Node.js is outdated, upgrade to v22 LTS or latest
|
||||
# Check Astro version
|
||||
npm list astro
|
||||
|
||||
# If upgrading to Astro 6:
|
||||
npx @astrojs/upgrade@beta
|
||||
```
|
||||
|
||||
**Important:** Astro 6 requires Node.js 22.12.0 or higher. Verify both local and CI/CD environments meet this requirement.
|
||||
|
||||
## Step 2: Install Dependencies
|
||||
|
||||
Install the Cloudflare adapter and Wrangler:
|
||||
|
||||
```bash
|
||||
# Automated installation (recommended)
|
||||
npx astro add cloudflare
|
||||
|
||||
# Manual installation
|
||||
npm install @astrojs/cloudflare wrangler --save-dev
|
||||
```
|
||||
|
||||
The automated command will:
|
||||
- Install `@astrojs/cloudflare`
|
||||
- Update `astro.config.mjs` with the adapter
|
||||
- Prompt for deployment mode selection
|
||||
|
||||
## Step 3: Configure Astro
|
||||
|
||||
Edit `astro.config.mjs` or `astro.config.ts` based on the deployment mode.
|
||||
|
||||
### Static Deployment
|
||||
|
||||
For purely static sites (no adapter needed):
|
||||
|
||||
```javascript
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'static',
|
||||
});
|
||||
```
|
||||
|
||||
### Hybrid Deployment (Recommended for Most Projects)
|
||||
|
||||
```javascript
|
||||
import { defineConfig } from 'astro/config';
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'hybrid',
|
||||
adapter: cloudflare({
|
||||
imageService: 'passthrough', // or 'compile' for optimization
|
||||
platformProxy: {
|
||||
enabled: true,
|
||||
configPath: './wrangler.jsonc',
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
Mark specific pages for SSR with `export const prerender = false`.
|
||||
|
||||
### Full SSR Deployment
|
||||
|
||||
```javascript
|
||||
import { defineConfig } from 'astro/config';
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: cloudflare({
|
||||
mode: 'directory', // or 'standalone' for single worker
|
||||
imageService: 'passthrough',
|
||||
platformProxy: {
|
||||
enabled: true,
|
||||
configPath: './wrangler.jsonc',
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
## Step 4: Create wrangler.jsonc
|
||||
|
||||
Cloudflare now recommends `wrangler.jsonc` (JSON with comments) over `wrangler.toml`. Use the template in `assets/wrangler.jsonc` as a starting point.
|
||||
|
||||
Key configuration:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||
"name": "your-app-name",
|
||||
"compatibility_date": "2025-01-19",
|
||||
"assets": {
|
||||
"directory": "./dist",
|
||||
"binding": "ASSETS"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Copy the template from:**
|
||||
```
|
||||
assets/wrangler-static.jsonc - For static sites
|
||||
assets/wrangler-hybrid.jsonc - For hybrid rendering
|
||||
assets/wrangler-ssr.jsonc - For full SSR
|
||||
```
|
||||
|
||||
## Step 5: Configure TypeScript Types
|
||||
|
||||
For TypeScript projects, create or update `src/env.d.ts`:
|
||||
|
||||
```typescript
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
|
||||
interface Env {
|
||||
// Add your Cloudflare bindings here
|
||||
MY_KV_NAMESPACE: KVNamespace;
|
||||
MY_D1_DATABASE: D1Database;
|
||||
API_URL: string;
|
||||
}
|
||||
|
||||
type Runtime = import('@astrojs/cloudflare').Runtime<Env>;
|
||||
|
||||
declare namespace App {
|
||||
interface Locals extends Runtime {}
|
||||
}
|
||||
```
|
||||
|
||||
Update `tsconfig.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["@cloudflare/workers-types"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Step 6: Deploy
|
||||
|
||||
### Local Development
|
||||
|
||||
```bash
|
||||
# Build the project
|
||||
npm run build
|
||||
|
||||
# Local development with Wrangler
|
||||
npx wrangler dev
|
||||
|
||||
# Remote development (test against production environment)
|
||||
npx wrangler dev --remote
|
||||
```
|
||||
|
||||
### Production Deployment
|
||||
|
||||
```bash
|
||||
# Deploy to Cloudflare Workers
|
||||
npx wrangler deploy
|
||||
|
||||
# Deploy to specific environment
|
||||
npx wrangler deploy --env staging
|
||||
```
|
||||
|
||||
### Using GitHub Actions
|
||||
|
||||
See `assets/github-actions-deploy.yml` for a complete CI/CD workflow template.
|
||||
|
||||
## Step 7: Configure Bindings (Optional)
|
||||
|
||||
For advanced features, add bindings in `wrangler.jsonc`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"kv_namespaces": [
|
||||
{ "binding": "MY_KV", "id": "your-kv-id" }
|
||||
],
|
||||
"d1_databases": [
|
||||
{ "binding": "DB", "database_name": "my-db", "database_id": "your-d1-id" }
|
||||
],
|
||||
"r2_buckets": [
|
||||
{ "binding": "BUCKET", "bucket_name": "my-bucket" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Access bindings in Astro code:
|
||||
|
||||
```javascript
|
||||
---
|
||||
const kv = Astro.locals.runtime.env.MY_KV;
|
||||
const value = await kv.get("key");
|
||||
---
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Non-Sensitive Variables
|
||||
|
||||
Define in `wrangler.jsonc`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"vars": {
|
||||
"API_URL": "https://api.example.com",
|
||||
"ENVIRONMENT": "production"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sensitive Secrets
|
||||
|
||||
```bash
|
||||
# Add a secret (encrypted, not stored in config)
|
||||
npx wrangler secret put API_KEY
|
||||
|
||||
# Add environment-specific secret
|
||||
npx wrangler secret put API_KEY --env staging
|
||||
|
||||
# List all secrets
|
||||
npx wrangler secret list
|
||||
```
|
||||
|
||||
### Local Development Secrets
|
||||
|
||||
Create `.dev.vars` (add to `.gitignore`):
|
||||
|
||||
```bash
|
||||
API_KEY=local_dev_key
|
||||
DATABASE_URL=postgresql://localhost:5432/mydb
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Refer to `references/troubleshooting.md` for common issues and solutions.
|
||||
|
||||
Common problems:
|
||||
|
||||
1. **"MessageChannel is not defined"** - React 19 compatibility issue
|
||||
- Solution: See troubleshooting guide
|
||||
|
||||
2. **Build fails with Node.js version error**
|
||||
- Solution: Upgrade to Node.js 22.12.0+
|
||||
|
||||
3. **Styling lost in Astro 6 beta dev mode**
|
||||
- Solution: Known bug, check GitHub issue status
|
||||
|
||||
4. **404 errors on deployment**
|
||||
- Solution: Check `_routes.json` configuration
|
||||
|
||||
## Resources
|
||||
|
||||
### references/
|
||||
- `troubleshooting.md` - Common issues and solutions
|
||||
- `configuration-guide.md` - Detailed configuration options
|
||||
- `upgrade-guide.md` - Migrating from older versions
|
||||
|
||||
### assets/
|
||||
- `wrangler-static.jsonc` - Static site configuration template
|
||||
- `wrangler-hybrid.jsonc` - Hybrid rendering configuration template
|
||||
- `wrangler-ssr.jsonc` - Full SSR configuration template
|
||||
- `github-actions-deploy.yml` - CI/CD workflow template
|
||||
- `dev.vars.example` - Local secrets template
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Astro Cloudflare Adapter](https://docs.astro.build/en/guides/integrations-guide/cloudflare/)
|
||||
- [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/)
|
||||
- [Wrangler CLI Reference](https://developers.cloudflare.com/workers/wrangler/)
|
||||
- [Astro 6 Beta Announcement](https://astro.build/blog/astro-6-beta/)
|
||||
@@ -0,0 +1,40 @@
|
||||
// Hybrid rendering configuration - Recommended for most projects
|
||||
// Static pages by default, SSR where needed with `export const prerender = false`
|
||||
|
||||
import { defineConfig } from 'astro/config';
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'hybrid',
|
||||
|
||||
adapter: cloudflare({
|
||||
// Mode: 'directory' (default) = separate function per route
|
||||
// 'standalone' = single worker for all routes
|
||||
mode: 'directory',
|
||||
|
||||
// Image service: 'passthrough' (default) or 'compile'
|
||||
imageService: 'passthrough',
|
||||
|
||||
// Platform proxy for local development with Cloudflare bindings
|
||||
platformProxy: {
|
||||
enabled: true,
|
||||
configPath: './wrangler.jsonc',
|
||||
},
|
||||
}),
|
||||
|
||||
// Optional: Add integrations
|
||||
// integrations: [
|
||||
// tailwind(),
|
||||
// react(),
|
||||
// sitemap(),
|
||||
// ],
|
||||
|
||||
vite: {
|
||||
build: {
|
||||
chunkSizeWarningLimit: 1000,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Usage: Add to pages that need SSR:
|
||||
// export const prerender = false;
|
||||
@@ -0,0 +1,35 @@
|
||||
// Full SSR configuration - All routes server-rendered
|
||||
// Use this for web apps with authentication, dynamic content on all pages
|
||||
|
||||
import { defineConfig } from 'astro/config';
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
|
||||
adapter: cloudflare({
|
||||
mode: 'directory',
|
||||
imageService: 'passthrough',
|
||||
platformProxy: {
|
||||
enabled: true,
|
||||
configPath: './wrangler.jsonc',
|
||||
},
|
||||
}),
|
||||
|
||||
// Optional: Add integrations
|
||||
// integrations: [
|
||||
// tailwind(),
|
||||
// react(),
|
||||
// viewTransitions(),
|
||||
// ],
|
||||
|
||||
vite: {
|
||||
build: {
|
||||
chunkSizeWarningLimit: 1000,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// All pages are server-rendered by default.
|
||||
// Access Cloudflare bindings with:
|
||||
// const env = Astro.locals.runtime.env;
|
||||
@@ -0,0 +1,22 @@
|
||||
// Static site configuration - No adapter needed
|
||||
// Use this for purely static sites (blogs, marketing sites, documentation)
|
||||
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'static',
|
||||
|
||||
// Optional: Add integrations
|
||||
// integrations: [
|
||||
// tailwind(),
|
||||
// sitemap(),
|
||||
// ],
|
||||
|
||||
// Vite configuration
|
||||
vite: {
|
||||
build: {
|
||||
// Adjust chunk size warning limit
|
||||
chunkSizeWarningLimit: 1000,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
# .dev.vars - Local development secrets
|
||||
# Copy this file to .dev.vars and fill in your values
|
||||
# IMPORTANT: Add .dev.vars to .gitignore!
|
||||
|
||||
# Cloudflare Account
|
||||
CLOUDFLARE_ACCOUNT_ID=your-account-id-here
|
||||
|
||||
# API Keys
|
||||
API_KEY=your-local-api-key
|
||||
API_SECRET=your-local-api-secret
|
||||
|
||||
# Database URLs
|
||||
DATABASE_URL=postgresql://localhost:5432/mydb
|
||||
REDIS_URL=redis://localhost:6379
|
||||
|
||||
# Third-party Services
|
||||
STRIPE_SECRET_KEY=sk_test_your_key
|
||||
SENDGRID_API_KEY=your_sendgrid_key
|
||||
|
||||
# OAuth (if using authentication)
|
||||
GITHUB_CLIENT_ID=your_github_client_id
|
||||
GITHUB_CLIENT_SECRET=your_github_client_secret
|
||||
|
||||
# Feature Flags
|
||||
ENABLE_ANALYTICS=false
|
||||
ENABLE_BETA_FEATURES=true
|
||||
40
.agent/skills/astro-cloudflare-deploy/assets/env.d.ts
vendored
Normal file
40
.agent/skills/astro-cloudflare-deploy/assets/env.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
|
||||
// TypeScript type definitions for Cloudflare bindings
|
||||
// Update this file with your actual binding names
|
||||
|
||||
interface Env {
|
||||
// Environment Variables (from wrangler.jsonc vars section)
|
||||
ENVIRONMENT: string;
|
||||
PUBLIC_SITE_URL: string;
|
||||
API_URL?: string;
|
||||
|
||||
// Cloudflare Bindings (configure in wrangler.jsonc)
|
||||
CACHE?: KVNamespace;
|
||||
DB?: D1Database;
|
||||
STORAGE?: R2Bucket;
|
||||
|
||||
// Add your custom bindings here
|
||||
// MY_KV_NAMESPACE: KVNamespace;
|
||||
// MY_D1_DATABASE: D1Database;
|
||||
// MY_R2_BUCKET: R2Bucket;
|
||||
|
||||
// Sensitive secrets (use wrangler secret put)
|
||||
API_KEY?: string;
|
||||
DATABASE_URL?: string;
|
||||
}
|
||||
|
||||
// Runtime type for Astro
|
||||
type Runtime = import('@astrojs/cloudflare').Runtime<Env>;
|
||||
|
||||
// Extend Astro's interfaces
|
||||
declare namespace App {
|
||||
interface Locals extends Runtime {}
|
||||
}
|
||||
|
||||
declare namespace Astro {
|
||||
interface Locals extends Runtime {}
|
||||
}
|
||||
|
||||
// For API endpoints
|
||||
export type { Env, Runtime };
|
||||
@@ -0,0 +1,94 @@
|
||||
name: Deploy to Cloudflare Workers
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and Deploy
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Wrangler
|
||||
run: npm install -g wrangler@latest
|
||||
|
||||
- name: Build Astro
|
||||
run: npm run build
|
||||
env:
|
||||
# Build-time environment variables
|
||||
NODE_ENV: production
|
||||
|
||||
- name: Deploy to Cloudflare Workers
|
||||
run: wrangler deploy
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
|
||||
deploy-staging:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy to Staging
|
||||
if: github.ref == 'refs/heads/staging'
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Wrangler
|
||||
run: npm install -g wrangler@latest
|
||||
|
||||
- name: Build Astro
|
||||
run: npm run build
|
||||
|
||||
- name: Deploy to Staging
|
||||
run: wrangler deploy --env staging
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
|
||||
# Optional: Run tests before deployment
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Run Tests
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: npm test
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||
"// Comment": "Hybrid rendering configuration for Astro on Cloudflare Workers",
|
||||
"name": "your-app-name",
|
||||
"compatibility_date": "2025-01-19",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
"assets": {
|
||||
"directory": "./dist",
|
||||
"binding": "ASSETS"
|
||||
},
|
||||
"vars": {
|
||||
"ENVIRONMENT": "production",
|
||||
"PUBLIC_SITE_URL": "https://your-app-name.workers.dev"
|
||||
},
|
||||
"// Comment env": "Environment-specific configurations",
|
||||
"env": {
|
||||
"staging": {
|
||||
"name": "your-app-name-staging",
|
||||
"vars": {
|
||||
"ENVIRONMENT": "staging",
|
||||
"PUBLIC_SITE_URL": "https://staging-your-app-name.workers.dev"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"name": "your-app-name-production",
|
||||
"vars": {
|
||||
"ENVIRONMENT": "production",
|
||||
"PUBLIC_SITE_URL": "https://your-app-name.workers.dev"
|
||||
}
|
||||
}
|
||||
},
|
||||
"// Comment bindings_examples": "Uncomment and configure as needed",
|
||||
"// kv_namespaces": [
|
||||
// {
|
||||
// "binding": "MY_KV",
|
||||
// "id": "your-kv-namespace-id"
|
||||
// }
|
||||
// ],
|
||||
"// d1_databases": [
|
||||
// {
|
||||
// "binding": "DB",
|
||||
// "database_name": "my-database",
|
||||
// "database_id": "your-d1-database-id"
|
||||
// }
|
||||
// ],
|
||||
"// r2_buckets": [
|
||||
// {
|
||||
// "binding": "BUCKET",
|
||||
// "bucket_name": "my-bucket"
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||
"// Comment": "Full SSR configuration for Astro on Cloudflare Workers",
|
||||
"name": "your-app-name",
|
||||
"compatibility_date": "2025-01-19",
|
||||
"compatibility_flags": ["nodejs_compat", "disable_nodejs_process_v2"],
|
||||
"assets": {
|
||||
"directory": "./dist",
|
||||
"binding": "ASSETS"
|
||||
},
|
||||
"vars": {
|
||||
"ENVIRONMENT": "production",
|
||||
"PUBLIC_SITE_URL": "https://your-app-name.workers.dev",
|
||||
"API_URL": "https://api.example.com"
|
||||
},
|
||||
"env": {
|
||||
"staging": {
|
||||
"name": "your-app-name-staging",
|
||||
"vars": {
|
||||
"ENVIRONMENT": "staging",
|
||||
"PUBLIC_SITE_URL": "https://staging-your-app-name.workers.dev",
|
||||
"API_URL": "https://staging-api.example.com"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"name": "your-app-name-production",
|
||||
"vars": {
|
||||
"ENVIRONMENT": "production",
|
||||
"PUBLIC_SITE_URL": "https://your-app-name.workers.dev",
|
||||
"API_URL": "https://api.example.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"// Comment bindings": "Configure Cloudflare bindings for your SSR app",
|
||||
"kv_namespaces": [
|
||||
{
|
||||
"binding": "CACHE",
|
||||
"id": "your-kv-namespace-id"
|
||||
}
|
||||
],
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
"database_name": "my-database",
|
||||
"database_id": "your-d1-database-id"
|
||||
}
|
||||
],
|
||||
"r2_buckets": [
|
||||
{
|
||||
"binding": "STORAGE",
|
||||
"bucket_name": "my-storage-bucket"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||
"// Comment": "Static site deployment configuration for Astro on Cloudflare Workers",
|
||||
"name": "your-app-name",
|
||||
"compatibility_date": "2025-01-19",
|
||||
"// Comment assets": "Static assets configuration",
|
||||
"assets": {
|
||||
"directory": "./dist",
|
||||
"binding": "ASSETS",
|
||||
"// Comment html_handling": "Options: none, force-trailing-slash, strip-trailing-slash",
|
||||
"html_handling": "none",
|
||||
"// Comment not_found_handling": "Options: none, 404-page, spa-fallback",
|
||||
"not_found_handling": "none"
|
||||
},
|
||||
"// Comment vars": "Non-sensitive environment variables",
|
||||
"vars": {
|
||||
"ENVIRONMENT": "production",
|
||||
"PUBLIC_SITE_URL": "https://your-app-name.workers.dev"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
# Configuration Guide
|
||||
|
||||
Complete reference for all configuration options when deploying Astro to Cloudflare Workers.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [wrangler.jsonc Reference](#wranglerjsonc-reference)
|
||||
2. [Astro Configuration](#astro-configuration)
|
||||
3. [Environment-Specific Configuration](#environment-specific-configuration)
|
||||
4. [Bindings Configuration](#bindings-configuration)
|
||||
5. [Advanced Options](#advanced-options)
|
||||
|
||||
---
|
||||
|
||||
## wrangler.jsonc Reference
|
||||
|
||||
### Core Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `name` | string | Yes | Worker/Project name |
|
||||
| `compatibility_date` | string (YYYY-MM-DD) | Yes | Runtime API version |
|
||||
| `$schema` | string | No | Path to JSON schema for validation |
|
||||
| `main` | string | No | Entry point file (auto-detected for Astro) |
|
||||
| `account_id` | string | No | Cloudflare account ID |
|
||||
|
||||
### Assets Configuration
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"assets": {
|
||||
"directory": "./dist",
|
||||
"binding": "ASSETS",
|
||||
"html_handling": "force-trailing-slash",
|
||||
"not_found_handling": "404-page"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Option | Values | Default | Description |
|
||||
|--------|--------|---------|-------------|
|
||||
| `directory` | path | `"./dist"` | Build output directory |
|
||||
| `binding` | string | `"ASSETS"` | Name to access assets in code |
|
||||
| `html_handling` | `"none"`, `"force-trailing-slash"`, `"strip-trailing-slash"` | `"none"` | URL handling behavior |
|
||||
| `not_found_handling` | `"none"`, `"404-page"`, `"spa-fallback"` | `"none"` | 404 error behavior |
|
||||
|
||||
### Compatibility Flags
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"compatibility_flags": ["nodejs_compat", "disable_nodejs_process_v2"]
|
||||
}
|
||||
```
|
||||
|
||||
| Flag | Purpose |
|
||||
|------|---------|
|
||||
| `nodejs_compat` | Enable Node.js APIs in Workers |
|
||||
| `disable_nodejs_process_v2` | Use legacy process global (for some packages) |
|
||||
|
||||
---
|
||||
|
||||
## Astro Configuration
|
||||
|
||||
### Adapter Options
|
||||
|
||||
```javascript
|
||||
// astro.config.mjs
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
|
||||
export default defineConfig({
|
||||
adapter: cloudflare({
|
||||
// Mode: how routes are deployed
|
||||
mode: 'directory', // 'directory' (default) or 'standalone'
|
||||
|
||||
// Image service handling
|
||||
imageService: 'passthrough', // 'passthrough' (default) or 'compile'
|
||||
|
||||
// Platform proxy for local development
|
||||
platformProxy: {
|
||||
enabled: true,
|
||||
configPath: './wrangler.jsonc',
|
||||
persist: {
|
||||
path: './.cache/wrangler/v3',
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
### Mode Comparison
|
||||
|
||||
| Mode | Description | Use Case |
|
||||
|------|-------------|----------|
|
||||
| `directory` | Separate function per route | Most projects, better caching |
|
||||
| `standalone` | Single worker for all routes | Simple apps, shared state |
|
||||
|
||||
### Image Service Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `passthrough` | Images pass through unchanged (default) |
|
||||
| `compile` | Images optimized at build time using Sharp |
|
||||
|
||||
---
|
||||
|
||||
## Environment-Specific Configuration
|
||||
|
||||
### Multiple Environments
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "my-app",
|
||||
"vars": {
|
||||
"ENVIRONMENT": "production",
|
||||
"API_URL": "https://api.example.com"
|
||||
},
|
||||
|
||||
"env": {
|
||||
"staging": {
|
||||
"name": "my-app-staging",
|
||||
"vars": {
|
||||
"ENVIRONMENT": "staging",
|
||||
"API_URL": "https://staging-api.example.com"
|
||||
}
|
||||
},
|
||||
|
||||
"production": {
|
||||
"name": "my-app-production",
|
||||
"vars": {
|
||||
"ENVIRONMENT": "production",
|
||||
"API_URL": "https://api.example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Deploying to Environment
|
||||
|
||||
```bash
|
||||
# Deploy to staging
|
||||
npx wrangler deploy --env staging
|
||||
|
||||
# Deploy to production
|
||||
npx wrangler deploy --env production
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bindings Configuration
|
||||
|
||||
### KV Namespace
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"kv_namespaces": [
|
||||
{
|
||||
"binding": "MY_KV",
|
||||
"id": "your-kv-namespace-id",
|
||||
"preview_id": "your-preview-kv-id"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in Astro:**
|
||||
```javascript
|
||||
const kv = Astro.locals.runtime.env.MY_KV;
|
||||
const value = await kv.get("key");
|
||||
await kv.put("key", "value", { expirationTtl: 3600 });
|
||||
```
|
||||
|
||||
**Creating KV:**
|
||||
```bash
|
||||
npx wrangler kv:namespace create MY_KV
|
||||
```
|
||||
|
||||
### D1 Database
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
"database_name": "my-database",
|
||||
"database_id": "your-d1-database-id"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in Astro:**
|
||||
```javascript
|
||||
const db = Astro.locals.runtime.env.DB;
|
||||
const result = await db.prepare("SELECT * FROM users").all();
|
||||
```
|
||||
|
||||
**Creating D1:**
|
||||
```bash
|
||||
npx wrangler d1 create my-database
|
||||
npx wrangler d1 execute my-database --file=./schema.sql
|
||||
```
|
||||
|
||||
### R2 Storage
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"r2_buckets": [
|
||||
{
|
||||
"binding": "BUCKET",
|
||||
"bucket_name": "my-bucket"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in Astro:**
|
||||
```javascript
|
||||
const bucket = Astro.locals.runtime.env.BUCKET;
|
||||
await bucket.put("file.txt", "Hello World");
|
||||
const object = await bucket.get("file.txt");
|
||||
```
|
||||
|
||||
**Creating R2:**
|
||||
```bash
|
||||
npx wrangler r2 bucket create my-bucket
|
||||
```
|
||||
|
||||
### Durable Objects
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{
|
||||
"name": "MY_DURABLE_OBJECT",
|
||||
"class_name": "MyDurableObject",
|
||||
"script_name": "durable-object-worker"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Options
|
||||
|
||||
### Custom Routing
|
||||
|
||||
Create `_routes.json` in project root for advanced routing control:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"include": ["/*"],
|
||||
"exclude": ["/api/*", "/admin/*"]
|
||||
}
|
||||
```
|
||||
|
||||
- **include**: Patterns to route to Worker
|
||||
- **exclude**: Patterns to serve as static assets
|
||||
|
||||
### Scheduled Tasks (Cron Triggers)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"triggers": {
|
||||
"crons": [
|
||||
{ "cron": "0 * * * *", "path": "/api/hourly" },
|
||||
{ "cron": "0 0 * * *", "path": "/api/daily" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Create corresponding API routes:
|
||||
|
||||
```javascript
|
||||
// src/pages/api/hourly.js
|
||||
export async function GET({ locals }) {
|
||||
// Runs every hour
|
||||
return new Response("Hourly task complete");
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"routes": [
|
||||
{
|
||||
"pattern": "api.example.com/*",
|
||||
"zone_name": "example.com"
|
||||
}
|
||||
],
|
||||
"limits": {
|
||||
"cpu_ms": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Logging and Monitoring
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"logpush": true,
|
||||
"placement": {
|
||||
"mode": "smart"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**View logs in real-time:**
|
||||
```bash
|
||||
npx wrangler tail
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TypeScript Configuration
|
||||
|
||||
### Complete tsconfig.json
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["@cloudflare/workers-types"],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Type Definition
|
||||
|
||||
```typescript
|
||||
// src/env.d.ts
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
|
||||
interface Env {
|
||||
// Cloudflare bindings
|
||||
MY_KV: KVNamespace;
|
||||
DB: D1Database;
|
||||
BUCKET: R2Bucket;
|
||||
|
||||
// Environment variables
|
||||
API_URL: string;
|
||||
ENVIRONMENT: string;
|
||||
SECRET_VALUE?: string;
|
||||
}
|
||||
|
||||
type Runtime = import('@astrojs/cloudflare').Runtime<Env>;
|
||||
|
||||
declare namespace App {
|
||||
interface Locals extends Runtime {}
|
||||
}
|
||||
|
||||
declare namespace Astro {
|
||||
interface Locals extends Runtime {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build Configuration
|
||||
|
||||
### package.json Scripts
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "wrangler dev",
|
||||
"deploy": "npm run build && wrangler deploy",
|
||||
"deploy:staging": "npm run build && wrangler deploy --env staging",
|
||||
"cf:dev": "wrangler dev",
|
||||
"cf:dev:remote": "wrangler dev --remote",
|
||||
"cf:tail": "wrangler tail"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Vite Configuration
|
||||
|
||||
```javascript
|
||||
// vite.config.js (if needed)
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
// Adjust chunk size warnings
|
||||
chunkSizeWarningLimit: 1000,
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,376 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
This guide covers common issues when deploying Astro 6 to Cloudflare Workers.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Build Errors](#build-errors)
|
||||
2. [Runtime Errors](#runtime-errors)
|
||||
3. [Deployment Issues](#deployment-issues)
|
||||
4. [Performance Issues](#performance-issues)
|
||||
5. [Development Server Issues](#development-server-issues)
|
||||
|
||||
---
|
||||
|
||||
## Build Errors
|
||||
|
||||
### "MessageChannel is not defined"
|
||||
|
||||
**Symptoms:**
|
||||
- Build fails with reference to `MessageChannel`
|
||||
- Occurs when using React 19 with Cloudflare adapter
|
||||
|
||||
**Cause:**
|
||||
React 19 uses `MessageChannel` which is not available in the Cloudflare Workers runtime by default.
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Add compatibility flag** in `wrangler.jsonc`:
|
||||
```jsonc
|
||||
{
|
||||
"compatibility_flags": ["nodejs_compat"]
|
||||
}
|
||||
```
|
||||
|
||||
2. **Use React 18** temporarily if the issue persists:
|
||||
```bash
|
||||
npm install react@18 react-dom@18
|
||||
```
|
||||
|
||||
3. **Check for related GitHub issues:**
|
||||
- [Astro Issue #12824](https://github.com/withastro/astro/issues/12824)
|
||||
|
||||
### "Cannot find module '@astrojs/cloudflare'"
|
||||
|
||||
**Symptoms:**
|
||||
- Import error in `astro.config.mjs`
|
||||
- Type errors in TypeScript
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Install the adapter:**
|
||||
```bash
|
||||
npm install @astrojs/cloudflare
|
||||
```
|
||||
|
||||
2. **Verify installation:**
|
||||
```bash
|
||||
npm list @astrojs/cloudflare
|
||||
```
|
||||
|
||||
3. **For Astro 6, ensure v13+:**
|
||||
```bash
|
||||
npm install @astrojs/cloudflare@beta
|
||||
```
|
||||
|
||||
### "Too many files for webpack"
|
||||
|
||||
**Symptoms:**
|
||||
- Build fails with file limit error
|
||||
- Occurs in large projects
|
||||
|
||||
**Solution:**
|
||||
|
||||
The Cloudflare adapter uses Vite, not webpack. If you see this error, check:
|
||||
|
||||
1. **Ensure adapter is properly configured:**
|
||||
```javascript
|
||||
// astro.config.mjs
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
export default defineConfig({
|
||||
adapter: cloudflare(),
|
||||
});
|
||||
```
|
||||
|
||||
2. **Check for legacy configuration:**
|
||||
- Remove any `@astrojs/vercel` or other adapter references
|
||||
- Ensure `output` mode is set correctly
|
||||
|
||||
---
|
||||
|
||||
## Runtime Errors
|
||||
|
||||
### 404 Errors on Specific Routes
|
||||
|
||||
**Symptoms:**
|
||||
- Some routes return 404 after deployment
|
||||
- Static assets not found
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Check `_routes.json` configuration** (for advanced routing):
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"include": ["/*"],
|
||||
"exclude": ["/api/*"]
|
||||
}
|
||||
```
|
||||
|
||||
2. **Verify build output:**
|
||||
```bash
|
||||
npm run build
|
||||
ls -la dist/
|
||||
```
|
||||
|
||||
3. **Check wrangler.jsonc assets directory:**
|
||||
```jsonc
|
||||
{
|
||||
"assets": {
|
||||
"directory": "./dist",
|
||||
"binding": "ASSETS"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### "env is not defined" or "runtime is not defined"
|
||||
|
||||
**Symptoms:**
|
||||
- Cannot access Cloudflare bindings in Astro code
|
||||
- Runtime errors in server components
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Ensure TypeScript types are configured:**
|
||||
```typescript
|
||||
// src/env.d.ts
|
||||
type Runtime = import('@astrojs/cloudflare').Runtime<Env>;
|
||||
|
||||
declare namespace App {
|
||||
interface Locals extends Runtime {}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Access bindings correctly:**
|
||||
```astro
|
||||
---
|
||||
// Correct
|
||||
const env = Astro.locals.runtime.env;
|
||||
const kv = env.MY_KV_NAMESPACE;
|
||||
|
||||
// Incorrect
|
||||
const kv = Astro.locals.env.MY_KV_NAMESPACE;
|
||||
---
|
||||
```
|
||||
|
||||
3. **Verify platformProxy is enabled:**
|
||||
```javascript
|
||||
// astro.config.mjs
|
||||
adapter: cloudflare({
|
||||
platformProxy: {
|
||||
enabled: true,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment Issues
|
||||
|
||||
### "Authentication required" or "Not logged in"
|
||||
|
||||
**Symptoms:**
|
||||
- `wrangler deploy` fails with authentication error
|
||||
- CI/CD deployment fails
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Authenticate locally:**
|
||||
```bash
|
||||
npx wrangler login
|
||||
```
|
||||
|
||||
2. **For CI/CD, create API token:**
|
||||
- Go to Cloudflare Dashboard → My Profile → API Tokens
|
||||
- Create token with "Edit Cloudflare Workers" template
|
||||
- Set as `CLOUDFLARE_API_TOKEN` in GitHub/GitLab secrets
|
||||
|
||||
3. **Set account ID:**
|
||||
```bash
|
||||
# Get account ID
|
||||
npx wrangler whoami
|
||||
|
||||
# Add to wrangler.jsonc or environment
|
||||
export CLOUDFLARE_ACCOUNT_ID=your-account-id
|
||||
```
|
||||
|
||||
### "Project name already exists"
|
||||
|
||||
**Symptoms:**
|
||||
- Deployment fails due to naming conflict
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Change project name in wrangler.jsonc:**
|
||||
```jsonc
|
||||
{
|
||||
"name": "my-app-production"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Or use environments:**
|
||||
```jsonc
|
||||
{
|
||||
"env": {
|
||||
"staging": {
|
||||
"name": "my-app-staging"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Deployment succeeds but site doesn't update
|
||||
|
||||
**Symptoms:**
|
||||
- `wrangler deploy` reports success
|
||||
- Old version still served
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Clear browser cache** (Ctrl+Shift+R or Cmd+Shift+R)
|
||||
|
||||
2. **Verify deployment:**
|
||||
```bash
|
||||
npx wrangler deployments list
|
||||
```
|
||||
|
||||
3. **Check for cached versions:**
|
||||
```bash
|
||||
npx wrangler versions list
|
||||
```
|
||||
|
||||
4. **Force deployment:**
|
||||
```bash
|
||||
npx wrangler deploy --compatibility-date 2025-01-19
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow initial page load
|
||||
|
||||
**Symptoms:**
|
||||
- First Contentful Paint (FCP) > 2 seconds
|
||||
- Large Time to First Byte (TTFB)
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Use hybrid or static output:**
|
||||
```javascript
|
||||
// Pre-render static pages where possible
|
||||
export const prerender = true;
|
||||
```
|
||||
|
||||
2. **Enable image optimization:**
|
||||
```javascript
|
||||
adapter: cloudflare({
|
||||
imageService: 'compile',
|
||||
})
|
||||
```
|
||||
|
||||
3. **Cache at edge:**
|
||||
```javascript
|
||||
export async function getStaticPaths() {
|
||||
return [{
|
||||
params: { id: '1' },
|
||||
props: { data: await fetchData() },
|
||||
}];
|
||||
}
|
||||
```
|
||||
|
||||
### High cold start latency
|
||||
|
||||
**Symptoms:**
|
||||
- First request after inactivity is slow
|
||||
- Subsequent requests are fast
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Use mode: 'directory'** for better caching:
|
||||
```javascript
|
||||
adapter: cloudflare({
|
||||
mode: 'directory',
|
||||
})
|
||||
```
|
||||
|
||||
2. **Keep bundle size small** - avoid heavy dependencies
|
||||
|
||||
3. **Use Cloudflare KV** for frequently accessed data:
|
||||
```javascript
|
||||
const cached = await env.KV.get('key');
|
||||
if (!cached) {
|
||||
const data = await fetch();
|
||||
await env.KV.put('key', data, { expirationTtl: 3600 });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Server Issues
|
||||
|
||||
### Styling not applied in dev mode (Astro 6 Beta)
|
||||
|
||||
**Symptoms:**
|
||||
- CSS not loading in `astro dev`
|
||||
- Works in production but not locally
|
||||
|
||||
**Status:** Known bug in Astro 6 beta
|
||||
|
||||
**Workarounds:**
|
||||
|
||||
1. **Use production build locally:**
|
||||
```bash
|
||||
npm run build
|
||||
npx wrangler dev --local
|
||||
```
|
||||
|
||||
2. **Check GitHub issue for updates:**
|
||||
- [Astro Issue #15194](https://github.com/withastro/astro/issues/15194)
|
||||
|
||||
### Cannot test bindings locally
|
||||
|
||||
**Symptoms:**
|
||||
- `Astro.locals.runtime.env` is undefined locally
|
||||
- Cloudflare bindings don't work in dev
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Ensure platformProxy is enabled:**
|
||||
```javascript
|
||||
adapter: cloudflare({
|
||||
platformProxy: {
|
||||
enabled: true,
|
||||
configPath: './wrangler.jsonc',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
2. **Create .dev.vars for local secrets:**
|
||||
```bash
|
||||
API_KEY=local_key
|
||||
DATABASE_URL=postgresql://localhost:5432/db
|
||||
```
|
||||
|
||||
3. **Use remote development:**
|
||||
```bash
|
||||
npx wrangler dev --remote
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
If issues persist:
|
||||
|
||||
1. **Check official documentation:**
|
||||
- [Astro Cloudflare Guide](https://docs.astro.build/en/guides/deploy/cloudflare/)
|
||||
- [Cloudflare Workers Docs](https://developers.cloudflare.com/workers/)
|
||||
|
||||
2. **Search existing issues:**
|
||||
- [Astro GitHub Issues](https://github.com/withastro/astro/issues)
|
||||
- [Cloudflare Workers Discussions](https://github.com/cloudflare/workers-sdk/discussions)
|
||||
|
||||
3. **Join community:**
|
||||
- [Astro Discord](https://astro.build/chat)
|
||||
- [Cloudflare Discord](https://discord.gg/cloudflaredev)
|
||||
@@ -0,0 +1,329 @@
|
||||
# Upgrade Guide
|
||||
|
||||
Migrating existing Astro projects to deploy on Cloudflare Workers.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [From Astro 5 to Astro 6](#from-astro-5-to-astro-6)
|
||||
2. [From Other Platforms to Cloudflare](#from-other-platforms-to-cloudflare)
|
||||
3. [Adapter Migration](#adapter-migration)
|
||||
4. [Breaking Changes](#breaking-changes)
|
||||
|
||||
---
|
||||
|
||||
## From Astro 5 to Astro 6
|
||||
|
||||
### Prerequisites Check
|
||||
|
||||
Astro 6 requires:
|
||||
|
||||
| Requirement | Minimum Version | Check Command |
|
||||
|-------------|-----------------|---------------|
|
||||
| Node.js | 22.12.0+ | `node --version` |
|
||||
| Astro | 6.0.0 | `npm list astro` |
|
||||
| Cloudflare Adapter | 13.0.0+ | `npm list @astrojs/cloudflare` |
|
||||
|
||||
### Upgrade Steps
|
||||
|
||||
1. **Backup current state:**
|
||||
```bash
|
||||
git commit -am "Pre-upgrade commit"
|
||||
```
|
||||
|
||||
2. **Run automated upgrade:**
|
||||
```bash
|
||||
npx @astrojs/upgrade@beta
|
||||
```
|
||||
|
||||
3. **Update adapter:**
|
||||
```bash
|
||||
npm install @astrojs/cloudflare@beta
|
||||
```
|
||||
|
||||
4. **Update Node.js** if needed:
|
||||
```bash
|
||||
# Using nvm
|
||||
nvm install 22
|
||||
nvm use 22
|
||||
|
||||
# Or download from nodejs.org
|
||||
```
|
||||
|
||||
5. **Update CI/CD Node.js version:**
|
||||
```yaml
|
||||
# .github/workflows/deploy.yml
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
```
|
||||
|
||||
6. **Test locally:**
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
npm run build
|
||||
npx wrangler dev
|
||||
```
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
#### 1. Vite 7.0
|
||||
|
||||
Vite has been upgraded to Vite 7.0. Check plugin compatibility:
|
||||
|
||||
```bash
|
||||
# Check for outdated plugins
|
||||
npm outdated
|
||||
|
||||
# Update Vite-specific plugins
|
||||
npm update @vitejs/plugin-react
|
||||
```
|
||||
|
||||
#### 2. Hybrid Output Behavior
|
||||
|
||||
The `hybrid` output mode behavior has changed:
|
||||
|
||||
```javascript
|
||||
// Old (Astro 5)
|
||||
export const prerender = true; // Static
|
||||
|
||||
// New (Astro 6) - same, but default behavior changed
|
||||
// Static is now the default for all pages in hybrid mode
|
||||
```
|
||||
|
||||
#### 3. Development Server
|
||||
|
||||
The new dev server runs on the production runtime:
|
||||
|
||||
```javascript
|
||||
// Old: Vite dev server
|
||||
// New: workerd runtime (same as production)
|
||||
|
||||
// Update your code if it relied on Vite-specific behavior
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## From Other Platforms to Cloudflare
|
||||
|
||||
### From Vercel
|
||||
|
||||
**Remove Vercel adapter:**
|
||||
```bash
|
||||
npm uninstall @astrojs/vercel
|
||||
```
|
||||
|
||||
**Install Cloudflare adapter:**
|
||||
```bash
|
||||
npm install @astrojs/cloudflare wrangler --save-dev
|
||||
```
|
||||
|
||||
**Update astro.config.mjs:**
|
||||
```javascript
|
||||
// Before
|
||||
import vercel from '@astrojs/vercel';
|
||||
export default defineConfig({
|
||||
adapter: vercel(),
|
||||
});
|
||||
|
||||
// After
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
export default defineConfig({
|
||||
adapter: cloudflare(),
|
||||
});
|
||||
```
|
||||
|
||||
**Update environment variables:**
|
||||
- Vercel: `process.env.VARIABLE`
|
||||
- Cloudflare: `Astro.locals.runtime.env.VARIABLE` or `env.VARIABLE` in endpoints
|
||||
|
||||
### From Netlify
|
||||
|
||||
**Remove Netlify adapter:**
|
||||
```bash
|
||||
npm uninstall @astrojs/netlify
|
||||
```
|
||||
|
||||
**Install Cloudflare adapter:**
|
||||
```bash
|
||||
npm install @astrojs/cloudflare wrangler --save-dev
|
||||
```
|
||||
|
||||
**Update netlify.toml to wrangler.jsonc:**
|
||||
|
||||
```toml
|
||||
# netlify.toml (old)
|
||||
[build]
|
||||
command = "astro build"
|
||||
publish = "dist"
|
||||
|
||||
[functions]
|
||||
node_bundler = "esbuild"
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// wrangler.jsonc (new)
|
||||
{
|
||||
"name": "my-app",
|
||||
"compatibility_date": "2025-01-19",
|
||||
"assets": {
|
||||
"directory": "./dist"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### From Node.js Server
|
||||
|
||||
**Before (Express/Fastify server):**
|
||||
```javascript
|
||||
// server.js
|
||||
import express from 'express';
|
||||
app.use(express.static('dist'));
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
**After (Cloudflare Workers):**
|
||||
```javascript
|
||||
// astro.config.mjs
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: cloudflare(),
|
||||
});
|
||||
|
||||
// Deploy
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adapter Migration
|
||||
|
||||
### From Astro 4 to 5/6
|
||||
|
||||
**Old adapter syntax:**
|
||||
```javascript
|
||||
// Astro 4
|
||||
adapter: cloudflare({
|
||||
functionPerRoute: true,
|
||||
})
|
||||
```
|
||||
|
||||
**New adapter syntax:**
|
||||
```javascript
|
||||
// Astro 5/6
|
||||
adapter: cloudflare({
|
||||
mode: 'directory', // equivalent to functionPerRoute: true
|
||||
})
|
||||
```
|
||||
|
||||
### Mode Migration Guide
|
||||
|
||||
| Old Option | New Option | Notes |
|
||||
|------------|------------|-------|
|
||||
| `functionPerRoute: true` | `mode: 'directory'` | Recommended |
|
||||
| `functionPerRoute: false` | `mode: 'standalone'` | Single worker |
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Removed APIs
|
||||
|
||||
1. **`Astro.locals` changes:**
|
||||
```javascript
|
||||
// Old
|
||||
const env = Astro.locals.env;
|
||||
|
||||
// New
|
||||
const env = Astro.locals.runtime.env;
|
||||
```
|
||||
|
||||
2. **Endpoint API changes:**
|
||||
```javascript
|
||||
// Old
|
||||
export async function get({ locals }) {
|
||||
const { env } = locals;
|
||||
}
|
||||
|
||||
// New
|
||||
export async function GET({ locals }) {
|
||||
const env = locals.runtime.env;
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript Changes
|
||||
|
||||
```typescript
|
||||
// Old type imports
|
||||
import type { Runtime } from '@astrojs/cloudflare';
|
||||
|
||||
// New type imports
|
||||
import type { Runtime } from '@astrojs/cloudflare/virtual';
|
||||
|
||||
// Or use the adapter export
|
||||
import cloudflare from '@astrojs/cloudflare';
|
||||
type Runtime = typeof cloudflare.Runtime;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rollback Procedures
|
||||
|
||||
### If Deployment Fails
|
||||
|
||||
1. **Keep old version deployed:**
|
||||
```bash
|
||||
npx wrangler versions list
|
||||
npx wrangler versions rollback <version-id>
|
||||
```
|
||||
|
||||
2. **Or rollback git changes:**
|
||||
```bash
|
||||
git revert HEAD
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
### If Build Fails
|
||||
|
||||
1. **Clear cache:**
|
||||
```bash
|
||||
rm -rf node_modules .astro dist
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
2. **Check for incompatible dependencies:**
|
||||
```bash
|
||||
npm ls
|
||||
```
|
||||
|
||||
3. **Temporarily pin to previous version:**
|
||||
```bash
|
||||
npm install astro@5
|
||||
npm install @astrojs/cloudflare@12
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After upgrading, verify:
|
||||
|
||||
- [ ] Local dev server starts without errors
|
||||
- [ ] Build completes successfully
|
||||
- [ ] `wrangler dev` works locally
|
||||
- [ ] Static assets load correctly
|
||||
- [ ] SSR routes render properly
|
||||
- [ ] Environment variables are accessible
|
||||
- [ ] Cloudflare bindings (KV/D1/R2) work
|
||||
- [ ] TypeScript types are correct
|
||||
- [ ] CI/CD pipeline succeeds
|
||||
- [ ] Production deployment works
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
- [Astro Discord](https://astro.build/chat)
|
||||
- [Cloudflare Discord](https://discord.gg/cloudflaredev)
|
||||
- [Astro GitHub Issues](https://github.com/withastro/astro/issues)
|
||||
Reference in New Issue
Block a user