@astrojs/cloudflare 8.0.0 → 8.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -508
- package/dist/entrypoints/server.advanced.js +2 -2
- package/dist/entrypoints/server.directory.js +2 -2
- package/dist/index.js +15 -20
- package/dist/util.js +1 -4
- package/dist/utils/assets.js +8 -10
- package/dist/utils/local-runtime.js +6 -5
- package/dist/utils/parser.js +7 -8
- package/dist/utils/prependForwardSlash.js +1 -1
- package/dist/utils/wasm-module-loader.js +20 -22
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,524 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
An SSR adapter for use with Cloudflare Pages Functions targets. Write your code in Astro/Javascript and deploy to Cloudflare Pages.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Documentation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Read the [`@astrojs/cloudflare` docs][docs]
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
# Using NPM
|
|
11
|
-
npx astro add cloudflare
|
|
12
|
-
# Using Yarn
|
|
13
|
-
yarn astro add cloudflare
|
|
14
|
-
# Using PNPM
|
|
15
|
-
pnpm astro add cloudflare
|
|
16
|
-
```
|
|
9
|
+
## Support
|
|
17
10
|
|
|
18
|
-
|
|
11
|
+
- Get help in the [Astro Discord][discord]. Post questions in our `#support` forum, or visit our dedicated `#dev` channel to discuss current development and more!
|
|
19
12
|
|
|
20
|
-
|
|
13
|
+
- Check our [Astro Integration Documentation][astro-integration] for more on integrations.
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
npm install @astrojs/cloudflare
|
|
24
|
-
```
|
|
15
|
+
- Submit bug reports and feature requests as [GitHub issues][issues].
|
|
25
16
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```diff lang="js"
|
|
29
|
-
// astro.config.mjs
|
|
30
|
-
import { defineConfig } from 'astro/config';
|
|
31
|
-
+ import cloudflare from '@astrojs/cloudflare';
|
|
32
|
-
|
|
33
|
-
export default defineConfig({
|
|
34
|
-
+ output: 'server',
|
|
35
|
-
+ adapter: cloudflare(),
|
|
36
|
-
});
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Options
|
|
40
|
-
|
|
41
|
-
### `mode`
|
|
42
|
-
|
|
43
|
-
`mode: "advanced" | "directory"`
|
|
44
|
-
|
|
45
|
-
default `"advanced"`
|
|
46
|
-
|
|
47
|
-
This configuration option defines how your Astro project is deployed to Cloudflare Pages.
|
|
48
|
-
|
|
49
|
-
- `advanced` mode picks up the `_worker.js` file in the `dist` folder
|
|
50
|
-
- `directory` mode picks up the files in the `functions` folder, by default only one `[[path]].js` file is generated
|
|
51
|
-
|
|
52
|
-
Switching to directory mode allows you to add additional files manually such as [Cloudflare Pages Plugins](https://developers.cloudflare.com/pages/platform/functions/plugins/), [Cloudflare Pages Middleware](https://developers.cloudflare.com/pages/platform/functions/middleware/) or custom functions using [Cloudflare Pages Functions Routing](https://developers.cloudflare.com/pages/platform/functions/routing/).
|
|
53
|
-
|
|
54
|
-
```js
|
|
55
|
-
// astro.config.mjs
|
|
56
|
-
export default defineConfig({
|
|
57
|
-
adapter: cloudflare({ mode: 'directory' }),
|
|
58
|
-
});
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
To compile a separate bundle for each page, set the `functionPerRoute` option in your Cloudflare adapter config. This option requires some manual maintenance of the `functions` folder. Files emitted by Astro will overwrite existing files with identical names in the `functions` folder, so you must choose unique file names for each file you manually add. Additionally, the adapter will never empty the `functions` folder of outdated files, so you must clean up the folder manually when you remove pages.
|
|
62
|
-
|
|
63
|
-
```diff lang="js"
|
|
64
|
-
// astro.config.mjs
|
|
65
|
-
import {defineConfig} from "astro/config";
|
|
66
|
-
import cloudflare from '@astrojs/cloudflare';
|
|
67
|
-
|
|
68
|
-
export default defineConfig({
|
|
69
|
-
adapter: cloudflare({
|
|
70
|
-
mode: 'directory',
|
|
71
|
-
+ functionPerRoute: true
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
This adapter doesn't support the [`edgeMiddleware`](https://docs.astro.build/en/reference/adapter-reference/#edgemiddleware) option.
|
|
77
|
-
|
|
78
|
-
### `routes.strategy`
|
|
79
|
-
|
|
80
|
-
`routes.strategy: "auto" | "include" | "exclude"`
|
|
81
|
-
|
|
82
|
-
default `"auto"`
|
|
83
|
-
|
|
84
|
-
Determines how `routes.json` will be generated if no [custom `_routes.json`](#custom-_routesjson) is provided.
|
|
85
|
-
|
|
86
|
-
There are three options available:
|
|
87
|
-
|
|
88
|
-
- **`"auto"` (default):** Will automatically select the strategy that generates the fewest entries. This should almost always be sufficient, so choose this option unless you have a specific reason not to.
|
|
89
|
-
|
|
90
|
-
- **`include`:** Pages and endpoints that are not pre-rendered are listed as `include` entries, telling Cloudflare to invoke these routes as functions. `exclude` entries are only used to resolve conflicts. Usually the best strategy when your website has mostly static pages and only a few dynamic pages or endpoints.
|
|
91
|
-
|
|
92
|
-
Example: For `src/pages/index.astro` (static), `src/pages/company.astro` (static), `src/pages/users/faq.astro` (static) and `/src/pages/users/[id].astro` (SSR) this will produce the following `_routes.json`:
|
|
93
|
-
|
|
94
|
-
```json
|
|
95
|
-
{
|
|
96
|
-
"version": 1,
|
|
97
|
-
"include": [
|
|
98
|
-
"/_image", // Astro's image endpoint
|
|
99
|
-
"/users/*" // Dynamic route
|
|
100
|
-
],
|
|
101
|
-
"exclude": [
|
|
102
|
-
// Static routes that needs to be exempted from the dynamic wildcard route above
|
|
103
|
-
"/users/faq/",
|
|
104
|
-
"/users/faq/index.html"
|
|
105
|
-
]
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
- **`exclude`:** Pre-rendered pages are listed as `exclude` entries (telling Cloudflare to handle these routes as static assets). Usually the best strategy when your website has mostly dynamic pages or endpoints and only a few static pages.
|
|
110
|
-
|
|
111
|
-
Example: For the same pages as in the previous example this will produce the following `_routes.json`:
|
|
112
|
-
|
|
113
|
-
```json
|
|
114
|
-
{
|
|
115
|
-
"version": 1,
|
|
116
|
-
"include": [
|
|
117
|
-
"/*" // Handle everything as function except the routes below
|
|
118
|
-
],
|
|
119
|
-
"exclude": [
|
|
120
|
-
// All static assets
|
|
121
|
-
"/",
|
|
122
|
-
"/company/",
|
|
123
|
-
"/index.html",
|
|
124
|
-
"/users/faq/",
|
|
125
|
-
"/favicon.png",
|
|
126
|
-
"/company/index.html",
|
|
127
|
-
"/users/faq/index.html"
|
|
128
|
-
]
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### `routes.include`
|
|
133
|
-
|
|
134
|
-
`routes.include: string[]`
|
|
135
|
-
|
|
136
|
-
default `[]`
|
|
137
|
-
|
|
138
|
-
If you want to use the automatic `_routes.json` generation, but want to include additional routes (e.g. when having custom functions in the `functions` folder), you can use the `routes.include` option to add additional routes to the `include` array.
|
|
139
|
-
|
|
140
|
-
### `routes.exclude`
|
|
141
|
-
|
|
142
|
-
`routes.exclude: string[]`
|
|
143
|
-
|
|
144
|
-
default `[]`
|
|
145
|
-
|
|
146
|
-
If you want to use the automatic `_routes.json` generation, but want to exclude additional routes, you can use the `routes.exclude` option to add additional routes to the `exclude` array.
|
|
147
|
-
|
|
148
|
-
The following example automatically generates `_routes.json` while including and excluding additional routes. Note that that is only necessary if you have custom functions in the `functions` folder that are not handled by Astro.
|
|
149
|
-
|
|
150
|
-
```diff lang="js"
|
|
151
|
-
// astro.config.mjs
|
|
152
|
-
export default defineConfig({
|
|
153
|
-
adapter: cloudflare({
|
|
154
|
-
mode: 'directory',
|
|
155
|
-
+ routes: {
|
|
156
|
-
+ strategy: 'include',
|
|
157
|
-
+ include: ['/users/*'], // handled by custom function: functions/users/[id].js
|
|
158
|
-
+ exclude: ['/users/faq'], // handled by static page: pages/users/faq.astro
|
|
159
|
-
+ },
|
|
160
|
-
}),
|
|
161
|
-
});
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### `imageService`
|
|
165
|
-
|
|
166
|
-
`imageService: "passthrough" | "cloudflare"`
|
|
167
|
-
|
|
168
|
-
Determines which image service is used by the adapter. The adapter will default to `passthrough` mode when an incompatible image service is configured. Otherwise, it will use the globally configured image service:
|
|
169
|
-
|
|
170
|
-
- **`cloudflare`:** Uses the [Cloudflare Image Resizing](https://developers.cloudflare.com/images/image-resizing/) service.
|
|
171
|
-
- **`passthrough`:** Uses the existing [`noop`](https://docs.astro.build/en/guides/images/#configure-no-op-passthrough-service) service.
|
|
172
|
-
|
|
173
|
-
### `wasmModuleImports`
|
|
174
|
-
|
|
175
|
-
`wasmModuleImports: boolean`
|
|
176
|
-
|
|
177
|
-
default: `false`
|
|
178
|
-
|
|
179
|
-
Whether or not to import `.wasm` files [directly as ES modules](https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration) using the `.wasm?module` import syntax.
|
|
180
|
-
|
|
181
|
-
Add `wasmModuleImports: true` to `astro.config.mjs` to enable this functionality in both the Cloudflare build and the Astro dev server. Read more about [using Wasm modules](#use-wasm-modules).
|
|
182
|
-
|
|
183
|
-
```diff lang="js"
|
|
184
|
-
// astro.config.mjs
|
|
185
|
-
import {defineConfig} from "astro/config";
|
|
186
|
-
import cloudflare from '@astrojs/cloudflare';
|
|
187
|
-
|
|
188
|
-
export default defineConfig({
|
|
189
|
-
adapter: cloudflare({
|
|
190
|
-
+ wasmModuleImports: true
|
|
191
|
-
}),
|
|
192
|
-
output: 'server'
|
|
193
|
-
})
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### `runtime`
|
|
197
|
-
|
|
198
|
-
See Cloudflare's repository for more type information about [CF_BINDING](https://github.com/cloudflare/workers-sdk/blob/eaa16db25103d26ef31499634a31b86caccdf7aa/packages/wrangler/src/api/startDevWorker/types.ts#L190-L219)
|
|
199
|
-
|
|
200
|
-
`runtime: { mode: 'off' } | { mode: 'local'; type: 'pages'; persistTo?: string; bindings?: Record<string, CF_BINDING> } | { mode: 'local'; type: 'workers'; persistTo?: string; };`
|
|
201
|
-
|
|
202
|
-
default `{ mode: 'off', persistTo: '' }`
|
|
203
|
-
|
|
204
|
-
Determines whether and how the Cloudflare Runtime is added to `astro dev`.
|
|
205
|
-
Read more about [the Cloudflare Runtime](#cloudflare-runtime).
|
|
206
|
-
|
|
207
|
-
The `type` property defines where your Astro project is deployed to:
|
|
208
|
-
|
|
209
|
-
- `pages`: Deployed to [Cloudflare Pages](https://pages.cloudflare.com/)
|
|
210
|
-
- `workers`: Deployed to [Cloudflare Workers](https://workers.cloudflare.com/)
|
|
211
|
-
|
|
212
|
-
The `mode` property defines what you want the runtime to support in `astro dev`:
|
|
213
|
-
|
|
214
|
-
- `off`: no access to the runtime using `astro dev`. You can choose [Preview with Wrangler](#preview-with-wrangler) when you need access to the runtime, to simulate the production environment locally.
|
|
215
|
-
- `local`: uses a local runtime powered by miniflare and workerd, which supports Cloudflare's Bindings. Only if you want to use unsupported features, such as `eval`, bindings with no local support choose [Preview with Wrangler](#preview-with-wrangler)
|
|
216
|
-
|
|
217
|
-
In `mode: local`, you have access to the `persistTo` property which defines where the local bindings state is saved. This avoids fresh bindings on every restart of the dev server. This value is a directory relative to your `astro dev` execution path. By default it is set to `.wrangler/state/v3` to allow usage of `wrangler` cli commands (e.g. for migrations). Add this path to your `.gitignore`.
|
|
218
|
-
|
|
219
|
-
## Cloudflare runtime
|
|
220
|
-
|
|
221
|
-
The Cloudflare runtime gives you access to environment variables and Cloudflare bindings. You can find more information in Cloudflare's [Workers](https://developers.cloudflare.com/workers/configuration/bindings/) and [Pages](https://developers.cloudflare.com/pages/platform/functions/bindings/) docs. Depending on your deployment type (`pages` or `workers`), you need to configure the bindings differently.
|
|
222
|
-
|
|
223
|
-
Currently supported bindings:
|
|
224
|
-
|
|
225
|
-
- Environment Variables
|
|
226
|
-
- [Cloudflare Workers KV](https://developers.cloudflare.com/kv/)
|
|
227
|
-
- [Cloudflare D1](https://developers.cloudflare.com/d1/)
|
|
228
|
-
- [Cloudflare R2](https://developers.cloudflare.com/r2/)
|
|
229
|
-
- [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/)
|
|
230
|
-
|
|
231
|
-
### Config
|
|
232
|
-
|
|
233
|
-
#### Cloudflare Pages
|
|
234
|
-
|
|
235
|
-
Cloudflare Pages does not support a configuration file.
|
|
236
|
-
|
|
237
|
-
To deploy your pages project to production, you need to configure the bindings using Cloudflare's Dashboard. To be able to access bindings locally, you need to configure them using the adpater's `runtime` option.
|
|
238
|
-
|
|
239
|
-
```diff lang="js"
|
|
240
|
-
// astro.config.mjs
|
|
241
|
-
import { defineConfig } from 'astro/config';
|
|
242
|
-
import cloudflare from '@astrojs/cloudflare';
|
|
243
|
-
|
|
244
|
-
export default defineConfig({
|
|
245
|
-
output: 'server',
|
|
246
|
-
adapter: cloudflare({
|
|
247
|
-
+ runtime: {
|
|
248
|
-
+ mode: 'local',
|
|
249
|
-
+ type: 'pages',
|
|
250
|
-
+ bindings: {
|
|
251
|
-
// example of a var binding (environment variable)
|
|
252
|
-
+ "URL": {
|
|
253
|
-
+ type: "var",
|
|
254
|
-
+ value: "https://example.com",
|
|
255
|
-
+ },
|
|
256
|
-
// example of a KV binding
|
|
257
|
-
+ "KV": {
|
|
258
|
-
+ type: "kv",
|
|
259
|
-
+ },
|
|
260
|
-
// example of a D1 binding
|
|
261
|
-
+ "D1": {
|
|
262
|
-
+ type: "d1",
|
|
263
|
-
+ },
|
|
264
|
-
// example of a R2 binding
|
|
265
|
-
+ "R2": {
|
|
266
|
-
+ type: "r2",
|
|
267
|
-
+ },
|
|
268
|
-
// example of a Durable Object binding
|
|
269
|
-
+ "DO": {
|
|
270
|
-
+ type: "durable-object",
|
|
271
|
-
+ className: "DO",
|
|
272
|
-
+ },
|
|
273
|
-
+ },
|
|
274
|
-
+ },
|
|
275
|
-
}),
|
|
276
|
-
});
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
If you also need to define `secrets` in addition to enviroment variables, you need to add a `.dev.vars` file to the root of the Astro project:
|
|
280
|
-
|
|
281
|
-
```
|
|
282
|
-
# .dev.vars
|
|
283
|
-
DB_PASSWORD=myPassword
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
If you want to use `wrangler` for cli commands, e.g. D1 migrations, you also need to add a `wrangler.toml` to the root of the Astro project with the correct content. Consult [Cloudflare's documentation](https://developers.cloudflare.com/) for further details.
|
|
287
|
-
|
|
288
|
-
```toml
|
|
289
|
-
# wrangler.toml
|
|
290
|
-
name = "example"
|
|
291
|
-
compatibility_date = "2023-06-14"
|
|
292
|
-
|
|
293
|
-
# example for D1 Binding
|
|
294
|
-
[[d1_databases]]
|
|
295
|
-
binding = "D1"
|
|
296
|
-
database_name = "D1"
|
|
297
|
-
database_id = "D1"
|
|
298
|
-
preview_database_id = "D1"
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
#### Cloudflare Workers
|
|
302
|
-
|
|
303
|
-
To deploy your workers project to production, you need to configure the bindings using a `wrangler.toml` config file in the root directory of your Astro project. To be able to access bindings locally, the `@astrojs/cloudflare` adapter will also read the `wrangler.toml` file.
|
|
304
|
-
|
|
305
|
-
```toml
|
|
306
|
-
# wrangler.toml
|
|
307
|
-
name = "example"
|
|
308
|
-
|
|
309
|
-
# example of a KV Binding
|
|
310
|
-
kv_namespaces = [
|
|
311
|
-
{ binding = "KV", id = "KV", preview_id = "KV" },
|
|
312
|
-
]
|
|
313
|
-
|
|
314
|
-
# example of a var binding (environment variables)
|
|
315
|
-
[vars]
|
|
316
|
-
URL = "example.com"
|
|
317
|
-
|
|
318
|
-
# example of a D1 Binding
|
|
319
|
-
[[d1_databases]]
|
|
320
|
-
binding = "D1"
|
|
321
|
-
database_name = "D1"
|
|
322
|
-
database_id = "D1"
|
|
323
|
-
preview_database_id = "D1"
|
|
324
|
-
|
|
325
|
-
# example of a R2 Binding
|
|
326
|
-
[[r2_buckets]]
|
|
327
|
-
binding = 'R2'
|
|
328
|
-
bucket_name = 'R2'
|
|
329
|
-
|
|
330
|
-
# example of a Durable Object Binding
|
|
331
|
-
[[durable_objects.bindings]]
|
|
332
|
-
name = "DO"
|
|
333
|
-
class_name = "DO"
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
If you also need to define `secrets` in addition to enviroment variables, you need to add a `.dev.vars` file to the root of the Astro project:
|
|
337
|
-
|
|
338
|
-
```
|
|
339
|
-
# .dev.vars
|
|
340
|
-
DB_PASSWORD=myPassword
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
### Usage
|
|
344
|
-
|
|
345
|
-
You can access the runtime from Astro components through `Astro.locals` inside any `.astro` file.
|
|
346
|
-
|
|
347
|
-
```astro
|
|
348
|
-
---
|
|
349
|
-
// src/pages/index.astro
|
|
350
|
-
const runtime = Astro.locals.runtime;
|
|
351
|
-
---
|
|
352
|
-
|
|
353
|
-
<pre>{JSON.stringify(runtime.env)}</pre>
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
You can access the runtime from API endpoints through `context.locals`:
|
|
357
|
-
|
|
358
|
-
```js
|
|
359
|
-
// src/pages/api/someFile.js
|
|
360
|
-
export function GET(context) {
|
|
361
|
-
const runtime = context.locals.runtime;
|
|
362
|
-
|
|
363
|
-
return new Response('Some body');
|
|
364
|
-
}
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
### Typing
|
|
368
|
-
|
|
369
|
-
If you have configured `mode: advanced`, you can type the `runtime` object using `AdvancedRuntime`:
|
|
370
|
-
|
|
371
|
-
```ts
|
|
372
|
-
// src/env.d.ts
|
|
373
|
-
/// <reference types="astro/client" />
|
|
374
|
-
|
|
375
|
-
type KVNamespace = import('@cloudflare/workers-types/experimental').KVNamespace;
|
|
376
|
-
type ENV = {
|
|
377
|
-
SERVER_URL: string;
|
|
378
|
-
KV_BINDING: KVNamespace;
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
type Runtime = import('@astrojs/cloudflare').AdvancedRuntime<ENV>;
|
|
382
|
-
|
|
383
|
-
declare namespace App {
|
|
384
|
-
interface Locals extends Runtime {
|
|
385
|
-
user: {
|
|
386
|
-
name: string;
|
|
387
|
-
surname: string;
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
If you have configured `mode: directory`, you can type the `runtime` object using `DirectoryRuntime`:
|
|
394
|
-
|
|
395
|
-
```ts
|
|
396
|
-
// src/env.d.ts
|
|
397
|
-
/// <reference types="astro/client" />
|
|
398
|
-
|
|
399
|
-
type KVNamespace = import('@cloudflare/workers-types/experimental').KVNamespace;
|
|
400
|
-
type ENV = {
|
|
401
|
-
SERVER_URL: string;
|
|
402
|
-
KV_BINDING: KVNamespace;
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
type Runtime = import('@astrojs/cloudflare').DirectoryRuntime<ENV>;
|
|
406
|
-
|
|
407
|
-
declare namespace App {
|
|
408
|
-
interface Locals extends Runtime {
|
|
409
|
-
user: {
|
|
410
|
-
name: string;
|
|
411
|
-
surname: string;
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
## Platform
|
|
418
|
-
|
|
419
|
-
### Headers
|
|
420
|
-
|
|
421
|
-
You can attach [custom headers](https://developers.cloudflare.com/pages/platform/headers/) to your responses by adding a `_headers` file in your Astro project's `public/` folder. This file will be copied to your build output directory.
|
|
422
|
-
|
|
423
|
-
### Redirects
|
|
424
|
-
|
|
425
|
-
You can declare [custom redirects](https://developers.cloudflare.com/pages/platform/redirects/) using Cloudflare Pages. This allows you to redirect requests to a different URL. You can add a `_redirects` file in your Astro project's `public/` folder. This file will be copied to your build output directory.
|
|
426
|
-
|
|
427
|
-
### Routes
|
|
428
|
-
|
|
429
|
-
You can define which routes are invoking functions and which are static assets, using [Cloudflare routing](https://developers.cloudflare.com/pages/platform/functions/routing/#functions-invocation-routes) via a `_routes.json` file. This file is automatically generated by Astro.
|
|
430
|
-
|
|
431
|
-
#### Custom `_routes.json`
|
|
432
|
-
|
|
433
|
-
By default, `@astrojs/cloudflare` will generate a `_routes.json` file with `include` and `exclude` rules based on your applications's dynamic and static routes.
|
|
434
|
-
This will enable Cloudflare to serve files and process static redirects without a function invocation. Creating a custom `_routes.json` will override this automatic optimization. See [Cloudflare's documentation on creating a custom `routes.json`](https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file) for more details.
|
|
435
|
-
|
|
436
|
-
## Use Wasm modules
|
|
437
|
-
|
|
438
|
-
The following is an example of importing a Wasm module that then responds to requests by adding the request's number parameters together.
|
|
439
|
-
|
|
440
|
-
```js
|
|
441
|
-
// pages/add/[a]/[b].js
|
|
442
|
-
import mod from '../util/add.wasm?module';
|
|
443
|
-
|
|
444
|
-
// instantiate ahead of time to share module
|
|
445
|
-
const addModule: any = new WebAssembly.Instance(mod);
|
|
446
|
-
|
|
447
|
-
export async function GET(context) {
|
|
448
|
-
const a = Number.parseInt(context.params.a);
|
|
449
|
-
const b = Number.parseInt(context.params.b);
|
|
450
|
-
return new Response(`${addModule.exports.add(a, b)}`);
|
|
451
|
-
}
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
While this example is trivial, Wasm can be used to accelerate computationally intensive operations which do not involve significant I/O such as embedding an image processing library.
|
|
455
|
-
|
|
456
|
-
## Node.js compatibility
|
|
457
|
-
|
|
458
|
-
Astro's Cloudflare adapter allows you to use any Node.js runtime API supported by Cloudflare:
|
|
459
|
-
|
|
460
|
-
- assert
|
|
461
|
-
- AsyncLocalStorage
|
|
462
|
-
- Buffer
|
|
463
|
-
- Crypto
|
|
464
|
-
- Diagnostics Channel
|
|
465
|
-
- EventEmitter
|
|
466
|
-
- path
|
|
467
|
-
- process
|
|
468
|
-
- Streams
|
|
469
|
-
- StringDecoder
|
|
470
|
-
- util
|
|
471
|
-
|
|
472
|
-
To use these APIs, your page or endpoint must be server-side rendered (not pre-rendered) and must use the the `import {} from 'node:*'` import syntax.
|
|
473
|
-
|
|
474
|
-
```js
|
|
475
|
-
// pages/api/endpoint.js
|
|
476
|
-
export const prerender = false;
|
|
477
|
-
import { Buffer } from 'node:buffer';
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
Additionally, you'll need to enable the Compatibility Flag in Cloudflare. The configuration for this flag may vary based on where you deploy your Astro site. For detailed guidance, please refer to the [Cloudflare documentation on enabling Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs).
|
|
481
|
-
|
|
482
|
-
## Cloudflare module support
|
|
483
|
-
|
|
484
|
-
All Cloudflare namespaced packages (e.g. `cloudflare:sockets`) are allowlisted for use. Note that the package `cloudflare:sockets` does not work locally without using Wrangler dev mode.
|
|
485
|
-
|
|
486
|
-
## Preview with Wrangler
|
|
487
|
-
|
|
488
|
-
To use [`wrangler`](https://developers.cloudflare.com/workers/wrangler/) to run your application locally, update the preview script:
|
|
489
|
-
|
|
490
|
-
```json
|
|
491
|
-
//package.json
|
|
492
|
-
"preview": "wrangler pages dev ./dist"
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
[`wrangler`](https://developers.cloudflare.com/workers/wrangler/) gives you access to [Cloudflare bindings](https://developers.cloudflare.com/pages/platform/functions/bindings), [environment variables](https://developers.cloudflare.com/pages/platform/functions/bindings/#environment-variables), and the [cf object](https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties). Getting hot reloading or the astro dev server to work with Wrangler might require custom setup. See [community examples](https://github.com/withastro/roadmap/discussions/590).
|
|
496
|
-
|
|
497
|
-
### Meaningful error messages
|
|
498
|
-
|
|
499
|
-
Currently, errors during running your application in Wrangler are not very useful, due to the minification of your code. For better debugging, you can add `vite.build.minify = false` setting to your `astro.config.mjs`.
|
|
500
|
-
|
|
501
|
-
```diff lang="js"
|
|
502
|
-
// astro.config.mjs
|
|
503
|
-
export default defineConfig({
|
|
504
|
-
adapter: cloudflare(),
|
|
505
|
-
output: 'server',
|
|
506
|
-
|
|
507
|
-
+ vite: {
|
|
508
|
-
+ build: {
|
|
509
|
-
+ minify: false,
|
|
510
|
-
+ },
|
|
511
|
-
+ },
|
|
512
|
-
});
|
|
513
|
-
```
|
|
17
|
+
## Contributing
|
|
514
18
|
|
|
515
|
-
|
|
19
|
+
This package is maintained by Astro's Core team. You're welcome to submit an issue or PR! These links will help you get started:
|
|
516
20
|
|
|
517
|
-
|
|
21
|
+
- [Contributor Manual][contributing]
|
|
22
|
+
- [Code of Conduct][coc]
|
|
23
|
+
- [Community Guide][community]
|
|
518
24
|
|
|
519
|
-
|
|
25
|
+
## License
|
|
520
26
|
|
|
521
|
-
|
|
27
|
+
MIT
|
|
522
28
|
|
|
523
|
-
|
|
29
|
+
Copyright (c) 2023–present [Astro][astro]
|
|
524
30
|
|
|
31
|
+
[astro]: https://astro.build/
|
|
32
|
+
[docs]: https://docs.astro.build/en/guides/integrations-guide/cloudflare/
|
|
33
|
+
[contributing]: https://github.com/withastro/astro/blob/main/CONTRIBUTING.md
|
|
34
|
+
[coc]: https://github.com/withastro/.github/blob/main/CODE_OF_CONDUCT.md
|
|
35
|
+
[community]: https://github.com/withastro/.github/blob/main/COMMUNITY_GUIDE.md
|
|
36
|
+
[discord]: https://astro.build/chat/
|
|
37
|
+
[issues]: https://github.com/withastro/adapter/issues
|
|
525
38
|
[astro-integration]: https://docs.astro.build/en/guides/integrations-guide/
|
|
@@ -14,7 +14,7 @@ export function createExports(manifest) {
|
|
|
14
14
|
if (manifest.assets.has(pathname)) {
|
|
15
15
|
return env.ASSETS.fetch(request);
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
const routeData = app.match(request);
|
|
18
18
|
Reflect.set(request, Symbol.for('astro.clientAddress'), request.headers.get('cf-connecting-ip'));
|
|
19
19
|
const locals = {
|
|
20
20
|
runtime: {
|
|
@@ -26,7 +26,7 @@ export function createExports(manifest) {
|
|
|
26
26
|
caches: caches,
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
|
-
|
|
29
|
+
const response = await app.render(request, routeData, locals);
|
|
30
30
|
if (app.setCookieHeaders) {
|
|
31
31
|
for (const setCookieHeader of app.setCookieHeaders(response)) {
|
|
32
32
|
response.headers.append('Set-Cookie', setCookieHeader);
|
|
@@ -16,7 +16,7 @@ export function createExports(manifest) {
|
|
|
16
16
|
if (manifest.assets.has(pathname)) {
|
|
17
17
|
return env.ASSETS.fetch(request);
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
const routeData = app.match(request);
|
|
20
20
|
Reflect.set(request, Symbol.for('astro.clientAddress'), request.headers.get('cf-connecting-ip'));
|
|
21
21
|
const locals = {
|
|
22
22
|
runtime: {
|
|
@@ -28,7 +28,7 @@ export function createExports(manifest) {
|
|
|
28
28
|
caches: caches,
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
|
-
|
|
31
|
+
const response = await app.render(request, routeData, locals);
|
|
32
32
|
if (app.setCookieHeaders) {
|
|
33
33
|
for (const setCookieHeader of app.setCookieHeaders(response)) {
|
|
34
34
|
response.headers.append('Set-Cookie', setCookieHeader);
|
package/dist/index.js
CHANGED
|
@@ -193,7 +193,7 @@ export default function createIntegration(args) {
|
|
|
193
193
|
: [rewriteWasmImportPath({ relativePathToAssets })],
|
|
194
194
|
});
|
|
195
195
|
}
|
|
196
|
-
const outputFiles = await glob(
|
|
196
|
+
const outputFiles = await glob('**/*', {
|
|
197
197
|
cwd: outputDir,
|
|
198
198
|
filesOnly: true,
|
|
199
199
|
});
|
|
@@ -315,17 +315,14 @@ export default function createIntegration(args) {
|
|
|
315
315
|
.map((route) => {
|
|
316
316
|
if (route.component === 'src/pages/404.astro' && route.prerender === false)
|
|
317
317
|
notFoundIsSSR = true;
|
|
318
|
-
const includePattern =
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
.map((segment) => (segment.dynamic ? '(.*)' : segment.content))
|
|
327
|
-
.join('\\/') +
|
|
328
|
-
'$');
|
|
318
|
+
const includePattern = `/${route.segments
|
|
319
|
+
.flat()
|
|
320
|
+
.map((segment) => (segment.dynamic ? '*' : segment.content))
|
|
321
|
+
.join('/')}`;
|
|
322
|
+
const regexp = new RegExp(`^\\/${route.segments
|
|
323
|
+
.flat()
|
|
324
|
+
.map((segment) => (segment.dynamic ? '(.*)' : segment.content))
|
|
325
|
+
.join('\\/')}$`);
|
|
329
326
|
return {
|
|
330
327
|
includePattern,
|
|
331
328
|
regexp,
|
|
@@ -338,7 +335,7 @@ export default function createIntegration(args) {
|
|
|
338
335
|
}))
|
|
339
336
|
.filter((file) => cloudflareSpecialFiles.indexOf(file) < 0)
|
|
340
337
|
.map((file) => `/${file.replace(/\\/g, '/')}`);
|
|
341
|
-
for (
|
|
338
|
+
for (const page of pages) {
|
|
342
339
|
let pagePath = prependForwardSlash(page.pathname);
|
|
343
340
|
if (_config.base !== '/') {
|
|
344
341
|
const base = _config.base.endsWith('/') ? _config.base.slice(0, -1) : _config.base;
|
|
@@ -360,13 +357,11 @@ export default function createIntegration(args) {
|
|
|
360
357
|
if (parts.length < 2) {
|
|
361
358
|
return null;
|
|
362
359
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
.replace(/\?.*$/, ''));
|
|
369
|
-
}
|
|
360
|
+
// convert /products/:id to /products/*
|
|
361
|
+
return (parts[0]
|
|
362
|
+
.replace(/\/:.*?(?=\/|$)/g, '/*')
|
|
363
|
+
// remove query params as they are not supported by cloudflare
|
|
364
|
+
.replace(/\?.*$/, ''));
|
|
370
365
|
})
|
|
371
366
|
.filter((line, index, arr) => line !== null && arr.indexOf(line) === index);
|
|
372
367
|
if (redirects.length > 0) {
|
package/dist/util.js
CHANGED
|
@@ -4,10 +4,7 @@ export function getProcessEnvProxy() {
|
|
|
4
4
|
get: (target, prop) => {
|
|
5
5
|
console.warn(
|
|
6
6
|
// NOTE: \0 prevents Vite replacement
|
|
7
|
-
`Unable to access \`import.meta\0.env.${prop.toString()}\` on initialization
|
|
8
|
-
`as the Cloudflare platform only provides the environment variables per request. ` +
|
|
9
|
-
`Please move the environment variable access inside a function ` +
|
|
10
|
-
`that's only called after a request has been received.`);
|
|
7
|
+
`Unable to access \`import.meta\0.env.${prop.toString()}\` on initialization as the Cloudflare platform only provides the environment variables per request. Please move the environment variable access inside a function that's only called after a request has been received.`);
|
|
11
8
|
},
|
|
12
9
|
});
|
|
13
10
|
}
|
package/dist/utils/assets.js
CHANGED
|
@@ -8,14 +8,14 @@ export function matchHostname(url, hostname, allowWildcard) {
|
|
|
8
8
|
if (!hostname) {
|
|
9
9
|
return true;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
if (!allowWildcard || !hostname.startsWith('*')) {
|
|
12
12
|
return hostname === url.hostname;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
if (hostname.startsWith('**.')) {
|
|
15
15
|
const slicedHostname = hostname.slice(2); // ** length
|
|
16
16
|
return slicedHostname !== url.hostname && url.hostname.endsWith(slicedHostname);
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
if (hostname.startsWith('*.')) {
|
|
19
19
|
const slicedHostname = hostname.slice(1); // * length
|
|
20
20
|
const additionalSubdomains = url.hostname
|
|
21
21
|
.replace(slicedHostname, '')
|
|
@@ -35,14 +35,14 @@ export function matchPathname(url, pathname, allowWildcard) {
|
|
|
35
35
|
if (!pathname) {
|
|
36
36
|
return true;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
if (!allowWildcard || !pathname.endsWith('*')) {
|
|
39
39
|
return pathname === url.pathname;
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
if (pathname.endsWith('/**')) {
|
|
42
42
|
const slicedPathname = pathname.slice(0, -2); // ** length
|
|
43
43
|
return slicedPathname !== url.pathname && url.pathname.startsWith(slicedPathname);
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
if (pathname.endsWith('/*')) {
|
|
46
46
|
const slicedPathname = pathname.slice(0, -1); // * length
|
|
47
47
|
const additionalPathChunks = url.pathname
|
|
48
48
|
.replace(slicedPathname, '')
|
|
@@ -84,12 +84,10 @@ export function joinPaths(...paths) {
|
|
|
84
84
|
if (i === 0) {
|
|
85
85
|
return removeTrailingForwardSlash(path);
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
if (i === paths.length - 1) {
|
|
88
88
|
return removeLeadingForwardSlash(path);
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
return trimSlashes(path);
|
|
92
|
-
}
|
|
90
|
+
return trimSlashes(path);
|
|
93
91
|
})
|
|
94
92
|
.join('/');
|
|
95
93
|
}
|
|
@@ -15,11 +15,11 @@ class LocalRuntime {
|
|
|
15
15
|
constructor(astroConfig, runtimeConfig, logger) {
|
|
16
16
|
this._astroConfig = astroConfig;
|
|
17
17
|
this._logger = logger;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
const varBindings = {};
|
|
19
|
+
const kvBindings = [];
|
|
20
|
+
const d1Bindings = [];
|
|
21
|
+
const r2Bindings = [];
|
|
22
|
+
const durableObjectBindings = {};
|
|
23
23
|
for (const bindingName in runtimeConfig.bindings) {
|
|
24
24
|
const bindingData = runtimeConfig.bindings[bindingName];
|
|
25
25
|
switch (bindingData.type) {
|
|
@@ -185,6 +185,7 @@ export class LocalWorkersRuntime extends LocalRuntime {
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
export class LocalPagesRuntime extends LocalRuntime {
|
|
188
|
+
// biome-ignore lint/complexity/noUselessConstructor: not types information yet, so we need to disable the rule for the time being
|
|
188
189
|
constructor(astroConfig, runtimeConfig, logger) {
|
|
189
190
|
super(astroConfig, runtimeConfig, logger);
|
|
190
191
|
}
|
package/dist/utils/parser.js
CHANGED
|
@@ -14,10 +14,10 @@ import { findUpSync } from 'find-up';
|
|
|
14
14
|
let _wrangler;
|
|
15
15
|
function findWranglerToml(referencePath = process.cwd(), preferJson = false) {
|
|
16
16
|
if (preferJson) {
|
|
17
|
-
return (findUpSync(
|
|
18
|
-
findUpSync(
|
|
17
|
+
return (findUpSync('wrangler.json', { cwd: referencePath }) ??
|
|
18
|
+
findUpSync('wrangler.toml', { cwd: referencePath }));
|
|
19
19
|
}
|
|
20
|
-
return findUpSync(
|
|
20
|
+
return findUpSync('wrangler.toml', { cwd: referencePath });
|
|
21
21
|
}
|
|
22
22
|
class ParseError extends Error {
|
|
23
23
|
text;
|
|
@@ -84,9 +84,7 @@ function getVarsForDev(config, configPath) {
|
|
|
84
84
|
...loaded.parsed,
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
return config.vars;
|
|
89
|
-
}
|
|
87
|
+
return config.vars;
|
|
90
88
|
}
|
|
91
89
|
function parseConfig() {
|
|
92
90
|
if (_wrangler)
|
|
@@ -139,10 +137,11 @@ export function getDOBindings() {
|
|
|
139
137
|
const { rawConfig } = parseConfig();
|
|
140
138
|
if (!rawConfig)
|
|
141
139
|
return {};
|
|
142
|
-
if (!rawConfig
|
|
140
|
+
if (!rawConfig.durable_objects)
|
|
143
141
|
return {};
|
|
144
142
|
const output = new Object({});
|
|
145
|
-
|
|
143
|
+
const bindings = rawConfig.durable_objects.bindings;
|
|
144
|
+
for (const binding of bindings) {
|
|
146
145
|
Reflect.set(output, binding.name, { className: binding.class_name });
|
|
147
146
|
}
|
|
148
147
|
return output;
|
|
@@ -43,31 +43,29 @@ export default wasmModule
|
|
|
43
43
|
// no need to wire up the assets in dev mode, just rewrite
|
|
44
44
|
return base64Module;
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return `
|
|
46
|
+
// just some shared ID
|
|
47
|
+
const hash = hashString(base64);
|
|
48
|
+
// emit the wasm binary as an asset file, to be picked up later by the esbuild bundle for the worker.
|
|
49
|
+
// give it a shared deterministic name to make things easy for esbuild to switch on later
|
|
50
|
+
const assetName = `${path.basename(filePath).split('.')[0]}.${hash}.wasm`;
|
|
51
|
+
this.emitFile({
|
|
52
|
+
type: 'asset',
|
|
53
|
+
// put it explicitly in the _astro assets directory with `fileName` rather than `name` so that
|
|
54
|
+
// vite doesn't give it a random id in its name. We need to be able to easily rewrite from
|
|
55
|
+
// the .mjs loader and the actual wasm asset later in the ESbuild for the worker
|
|
56
|
+
fileName: path.join(assetsDirectory, assetName),
|
|
57
|
+
source: fs.readFileSync(filePath),
|
|
58
|
+
});
|
|
59
|
+
// however, by default, the SSG generator cannot import the .wasm as a module, so embed as a base64 string
|
|
60
|
+
const chunkId = this.emitFile({
|
|
61
|
+
type: 'prebuilt-chunk',
|
|
62
|
+
fileName: `${assetName}.mjs`,
|
|
63
|
+
code: base64Module,
|
|
64
|
+
});
|
|
65
|
+
return `
|
|
67
66
|
import wasmModule from "__WASM_ASSET__${chunkId}.wasm.mjs";
|
|
68
67
|
export default wasmModule;
|
|
69
68
|
`;
|
|
70
|
-
}
|
|
71
69
|
},
|
|
72
70
|
// output original wasm file relative to the chunk
|
|
73
71
|
renderChunk(code, chunk, _) {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"//comment": "test changeset-bot",
|
|
3
3
|
"name": "@astrojs/cloudflare",
|
|
4
4
|
"description": "Deploy your site to Cloudflare Workers/Pages",
|
|
5
|
-
"version": "8.0.
|
|
5
|
+
"version": "8.0.2",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"author": "withastro",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@astrojs/underscore-redirects": "^0.3.3",
|
|
33
33
|
"@cloudflare/workers-types": "^4.20231025.0",
|
|
34
|
-
"miniflare": "3.
|
|
34
|
+
"miniflare": "3.20231030.2",
|
|
35
35
|
"@iarna/toml": "^2.2.5",
|
|
36
36
|
"dotenv": "^16.3.1",
|
|
37
37
|
"esbuild": "^0.19.5",
|