@astrojs/cloudflare 0.0.0-node-standalone-20221011210529 → 0.0.0-prerender-20221215200617

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.
@@ -1,5 +1,5 @@
1
- @astrojs/cloudflare:build: cache hit, replaying output 9b3ab4075bdae816
2
- @astrojs/cloudflare:build: 
3
- @astrojs/cloudflare:build: > @astrojs/cloudflare@0.0.0-node-standalone-20221011210529 build /home/runner/work/astro/astro/packages/integrations/cloudflare
4
- @astrojs/cloudflare:build: > astro-scripts build "src/**/*.ts" && tsc
5
- @astrojs/cloudflare:build: 
1
+ @astrojs/cloudflare:build: cache hit, replaying output d6b00155b7fccb91
2
+ @astrojs/cloudflare:build: 
3
+ @astrojs/cloudflare:build: > @astrojs/cloudflare@0.0.0-prerender-20221215200617 build /home/runner/work/astro/astro/packages/integrations/cloudflare
4
+ @astrojs/cloudflare:build: > astro-scripts build "src/**/*.ts" && tsc
5
+ @astrojs/cloudflare:build: 
package/CHANGELOG.md CHANGED
@@ -1,12 +1,77 @@
1
1
  # @astrojs/cloudflare
2
2
 
3
- ## 0.0.0-node-standalone-20221011210529
3
+ ## 0.0.0-prerender-20221215200617
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`f3af698e2`](https://github.com/withastro/astro/commit/f3af698e2c18b415b2445c6dd0b8f2e388f84577), [`31ec84797`](https://github.com/withastro/astro/commit/31ec8479721a1cd65538ec041458c5ffe8f50ee9), [`dced4a8a2`](https://github.com/withastro/astro/commit/dced4a8a2657887ec569860d9862d20f695dc23a), [`6b156dd3b`](https://github.com/withastro/astro/commit/6b156dd3b467884839a571c53114aadf26fa4b0b)]:
8
+ - astro@0.0.0-prerender-20221215200617
9
+
10
+ ## 4.1.1
11
+
12
+ ### Patch Changes
13
+
14
+ - [#5534](https://github.com/withastro/astro/pull/5534) [`fabd9124b`](https://github.com/withastro/astro/commit/fabd9124bd3e654e885054f30e9c0d01eabf0470) Thanks [@bluwy](https://github.com/bluwy)! - Update esbuild dependency
15
+
16
+ - Updated dependencies [[`9082a850e`](https://github.com/withastro/astro/commit/9082a850eef4ab0187fc3bfdd5a377f0c7040070), [`4f7f20616`](https://github.com/withastro/astro/commit/4f7f20616ed2b63f94ebf43bc5fdc1be55062a94), [`05915fec0`](https://github.com/withastro/astro/commit/05915fec01a51f27ab5051644f01e6112ecf06bc), [`1aeabe417`](https://github.com/withastro/astro/commit/1aeabe417077505bc0cdb8d2e47366ddbc616072), [`795f00f73`](https://github.com/withastro/astro/commit/795f00f73c549727e05d5b7bf0e39cce87add4e7), [`2c836b9d1`](https://github.com/withastro/astro/commit/2c836b9d1283a0707128d172e92ee2bba767486c), [`8f3f67c96`](https://github.com/withastro/astro/commit/8f3f67c96aee63be64de77f374293761ff73f6ce)]:
17
+ - astro@1.6.14
18
+
19
+ ## 4.1.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [#5347](https://github.com/withastro/astro/pull/5347) [`743000cc7`](https://github.com/withastro/astro/commit/743000cc70274a2d2fed01c72e2ac51aa6b876a6) Thanks [@AirBorne04](https://github.com/AirBorne04)! - Now building for Cloudflare directory mode takes advantage of the standard asset handling from Cloudflare Pages, and therefore does not call a function script to deliver static assets anymore.
24
+ Also supports the use of `_routes.json`, `_redirects` and `_headers` files when placed into the `public` folder.
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [[`936c1e411`](https://github.com/withastro/astro/commit/936c1e411d77c69b2b60a061c54704200716800a), [`4b188132e`](https://github.com/withastro/astro/commit/4b188132ef68f8d9951cec86418ef50bb4df4a96), [`f5ed630bc`](https://github.com/withastro/astro/commit/f5ed630bca05ebbfcc6ac994ced3911e41daedcc)]:
29
+ - astro@1.6.11
30
+
31
+ ## 4.0.1
32
+
33
+ ### Patch Changes
34
+
35
+ - [#5301](https://github.com/withastro/astro/pull/5301) [`a79a37cad`](https://github.com/withastro/astro/commit/a79a37cad549b21f91599ff86899e456d9dcc7df) Thanks [@bluwy](https://github.com/bluwy)! - Fix environment variables usage in worker output and warn if environment variables are accessedd too early
36
+
37
+ - Updated dependencies [[`88c1bbe3a`](https://github.com/withastro/astro/commit/88c1bbe3a71f85e92f42f13d0f310c6b2a264ade), [`a79a37cad`](https://github.com/withastro/astro/commit/a79a37cad549b21f91599ff86899e456d9dcc7df)]:
38
+ - astro@1.6.5
39
+
40
+ ## 4.0.0
41
+
42
+ ### Major Changes
43
+
44
+ - [#5290](https://github.com/withastro/astro/pull/5290) [`b2b291d29`](https://github.com/withastro/astro/commit/b2b291d29143703cece0d12c8e74b2e1151d2061) Thanks [@matthewp](https://github.com/matthewp)! - Handle base configuration in adapters
45
+
46
+ This allows adapters to correctly handle `base` configuration. Internally Astro now matches routes when the URL includes the `base`.
47
+
48
+ Adapters now also have access to the `removeBase` method which will remove the `base` from a pathname. This is useful to look up files for static assets.
49
+
50
+ ### Patch Changes
51
+
52
+ - Updated dependencies [[`b2b291d29`](https://github.com/withastro/astro/commit/b2b291d29143703cece0d12c8e74b2e1151d2061), [`97e2b6ad7`](https://github.com/withastro/astro/commit/97e2b6ad7a6fa23e82be28b2f57cdf3f85fab112), [`4af4d8fa0`](https://github.com/withastro/astro/commit/4af4d8fa0035130fbf31c82d72777c3679bc1ca5), [`f6add3924`](https://github.com/withastro/astro/commit/f6add3924d5cd59925a6ea4bf7f2f731709bc893), [`247eb7411`](https://github.com/withastro/astro/commit/247eb7411f429317e5cd7d401a6660ee73641313)]:
53
+ - astro@1.6.4
54
+
55
+ ## 3.1.2
56
+
57
+ ### Patch Changes
58
+
59
+ - [#5230](https://github.com/withastro/astro/pull/5230) [`69a532ab6`](https://github.com/withastro/astro/commit/69a532ab60a85d30c2395969593c4d38f9a2fbbe) Thanks [@matthewp](https://github.com/matthewp)! - Exports new runtime entrypoint's types
60
+
61
+ ## 3.1.1
62
+
63
+ ### Patch Changes
64
+
65
+ - [#5103](https://github.com/withastro/astro/pull/5103) [`d151d9f3f`](https://github.com/withastro/astro/commit/d151d9f3f29c0a57c59b8029a18717808ccc7f8f) Thanks [@AirBorne04](https://github.com/AirBorne04)! - enable access to Cloudflare runtime [KV, R2, Durable Objects]
66
+ - access native Cloudflare runtime through `import { getRuntime } from "@astrojs/cloudflare/runtime"`; now you can call `getRuntime(Astro.request)` and get access to the runtime environment.
67
+
68
+ ## 3.1.0
4
69
 
5
70
  ### Minor Changes
6
71
 
7
- - [#5056](https://github.com/withastro/astro/pull/5056) [`69e32cbba`](https://github.com/withastro/astro/commit/69e32cbba2ee8537a002755c34598dab834898c9) Thanks [@matthewp](https://github.com/matthewp)! - # New build configuration
72
+ - [#5056](https://github.com/withastro/astro/pull/5056) [`e55af8a23`](https://github.com/withastro/astro/commit/e55af8a23233b6335f45b7a04b9d026990fb616c) Thanks [@matthewp](https://github.com/matthewp)! - # New build configuration
8
73
 
9
- The ability to customize SSR build configuration more granular is now available in Astro. You can now customize the output folder for `server` (the server code for SSR), `client` (your client-side JavaScript and assets), and `serverEntry` (the name of the entrypoint server module). Here are the defaults:
74
+ The ability to customize SSR build configuration more granularly is now available in Astro. You can now customize the output folder for `server` (the server code for SSR), `client` (your client-side JavaScript and assets), and `serverEntry` (the name of the entrypoint server module). Here are the defaults:
10
75
 
11
76
  ```js
12
77
  import { defineConfig } from 'astro/config';
@@ -25,7 +90,7 @@
25
90
 
26
91
  ## Integration hook change
27
92
 
28
- The integration hook `astro:build:start` includes a param `buildConfig` which includes all of these same options. You can continue to use this param in Astro 1.x, but it is deprecated in favor of the new `build.config` options. All if the built-in adapters have been updated to the new format. If you have an integration that depends on this param we suggest upgrading to do this instead:
93
+ The integration hook `astro:build:start` includes a param `buildConfig` which includes all of these same options. You can continue to use this param in Astro 1.x, but it is deprecated in favor of the new `build.config` options. All of the built-in adapters have been updated to the new format. If you have an integration that depends on this param we suggest upgrading to do this instead:
29
94
 
30
95
  ```js
31
96
  export default function myIntegration() {
package/README.md CHANGED
@@ -25,7 +25,8 @@ npm install @astrojs/cloudflare
25
25
 
26
26
  2. Add the following to your `astro.config.mjs` file:
27
27
 
28
- ```js title="astro.config.mjs" ins={2, 5-6}
28
+ ```js ins={3, 6-7}
29
+ // astro.config.mjs
29
30
  import { defineConfig } from 'astro/config';
30
31
  import cloudflare from '@astrojs/cloudflare';
31
32
 
@@ -46,9 +47,9 @@ default `"advanced"`
46
47
 
47
48
  Cloudflare Pages has 2 different modes for deploying functions, `advanced` mode which picks up the `_worker.js` in `dist`, or a directory mode where pages will compile the worker out of a functions folder in the project root.
48
49
 
49
- For most projects the adaptor default of `advanced` will be sufficiant, when in this mode the `dist` folder will contain your compiled project. However if you'd like to use [pages plugins](https://developers.cloudflare.com/pages/platform/functions/plugins/) such as [Sentry](https://developers.cloudflare.com/pages/platform/functions/plugins/sentry/) for example to enable logging, you'll need to use directory mode.
50
+ For most projects the adaptor default of `advanced` will be sufficient; the `dist` folder will contain your compiled project. Switching to directory mode allows you to use [pages plugins](https://developers.cloudflare.com/pages/platform/functions/plugins/) such as [Sentry](https://developers.cloudflare.com/pages/platform/functions/plugins/sentry/) or write custom code to enable logging.
50
51
 
51
- In directory mode the adaptor will compile the client side part of you app the same way, but it will move the worker script into a `functions` folder in the project root. The adaptor will only ever place a `[[path]].js` in that folder, allowing you to add additional plugins and pages middleware which can be checked into version control.
52
+ In directory mode the adaptor will compile the client side part of you app the same way, but moves the worker script into a `functions` folder in the project root. The adaptor will only ever place a `[[path]].js` in that folder, allowing you to add additional plugins and pages middleware which can be checked into version control. Cloudflare documentation contains more information about [writing custom functions](https://developers.cloudflare.com/pages/platform/functions/).
52
53
 
53
54
  ```ts
54
55
  // directory mode
@@ -66,31 +67,55 @@ In order for preview to work you must install `wrangler`
66
67
  $ pnpm install wrangler --save-dev
67
68
  ```
68
69
 
69
- It's then possible to update the preview script in your `package.json` to `"preview": "wrangler pages dev ./dist"`.This will allow you run your entire application locally with [Wrangler](https://github.com/cloudflare/wrangler2), which supports secrets, environment variables, KV namespaces, Durable Objects and [all other supported Cloudflare bindings](https://developers.cloudflare.com/pages/platform/functions/#adding-bindings).
70
+ It's then possible to update the preview script in your `package.json` to `"preview": "wrangler pages dev ./dist"`. This will allow you run your entire application locally with [Wrangler](https://github.com/cloudflare/wrangler2), which supports secrets, environment variables, KV namespaces, Durable Objects and [all other supported Cloudflare bindings](https://developers.cloudflare.com/pages/platform/functions/#adding-bindings).
71
+
72
+ ## Access to the Cloudflare runtime
73
+
74
+ You can access all the Cloudflare bindings and environment variables from Astro components and API routes through the adapter API.
75
+
76
+ ```js
77
+ import { getRuntime } from "@astrojs/cloudflare/runtime";
78
+
79
+ getRuntime(Astro.request);
80
+ ```
81
+
82
+ Depending on your adapter mode (advanced = worker, directory = pages), the runtime object will look a little different due to differences in the Cloudflare API.
70
83
 
71
84
  ## Streams
72
85
 
73
- Some integrations such as [React](https://github.com/withastro/astro/tree/main/packages/integrations/react) rely on web streams. Currently Cloudflare Pages functions are in beta and don't support the `streams_enable_constructors` feature flag.
86
+ Some integrations such as [React](https://github.com/withastro/astro/tree/main/packages/integrations/react) rely on web streams. Currently Cloudflare Pages Functions require enabling a flag to support Streams.
74
87
 
75
- In order to work around this:
76
- - install the `"web-streams-polyfill"` package
77
- - add `import "web-streams-polyfill/es2018";` to the top of the front matter of every page which requires streams, such as server rendering a React component.
88
+ To do this:
89
+ - go to the Cloudflare Pages project
90
+ - click on Settings in the top bar, then Functions in the sidebar
91
+ - scroll down to Compatibility Flags, click Configure Production Compatibility Flags, and add `streams_enable_constructors`
92
+ - do this for both the Production Compatibility Flags and Preview Compatibility Flags
78
93
 
79
94
  ## Environment Variables
80
95
 
81
- As Cloudflare Pages Functions [provides environment variables differently](https://developers.cloudflare.com/pages/platform/functions/#adding-environment-variables-locally), private environment variables needs to be set through [`vite.define`](https://vitejs.dev/config/shared-options.html#define) to work in builds.
96
+ As Cloudflare Pages Functions [provides environment variables per request](https://developers.cloudflare.com/pages/platform/functions/#adding-environment-variables-locally), you can only access private environment variables when a request has happened. Usually, this means moving environment variable access inside a function.
82
97
 
83
98
  ```js
84
- // astro.config.mjs
85
- export default {
86
- vite: {
87
- define: {
88
- 'process.env.MY_SECRET': JSON.stringify(process.env.MY_SECRET),
89
- },
90
- },
99
+ // pages/[id].json.js
100
+
101
+ export function get({ params }) {
102
+ // Access environment variables per request inside a function
103
+ const serverUrl = import.meta.env.SERVER_URL;
104
+ const result = await fetch(serverUrl + "/user/" + params.id);
105
+ return {
106
+ body: await result.text(),
107
+ };
91
108
  }
92
109
  ```
93
110
 
111
+ ## Headers, Redirects and function invocation routes
112
+
113
+ Cloudflare has support for adding custom [headers](https://developers.cloudflare.com/pages/platform/headers/), configuring static [redirects](https://developers.cloudflare.com/pages/platform/redirects/) and defining which routes should [invoke functions](https://developers.cloudflare.com/pages/platform/functions/routing/#function-invocation-routes). Cloudflare looks for `_headers`, `_redirects`, and `_routes.json` files in your build output directory to configure these features. This means they should be placed in your Astro project’s `public/` directory.
114
+
115
+ ### Custom `_routes.json`
116
+
117
+ By default, `@astrojs/cloudflare` will generate a `_routes.json` file that lists all files from your `dist/` folder and redirects from the `_redirects` file in the `exclude` array. 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 and, if not configured manually, cause function invocations that will count against the request limits of your Cloudflare plan.
118
+
94
119
  ## Troubleshooting
95
120
 
96
121
  For help, check out the `#support` channel on [Discord](https://astro.build/chat). Our friendly Support Squad members are here to help!
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import esbuild from "esbuild";
2
2
  import * as fs from "fs";
3
+ import * as os from "os";
4
+ import glob from "tiny-glob";
3
5
  import { fileURLToPath } from "url";
4
6
  function getAdapter(isModeDirectory) {
5
7
  return isModeDirectory ? {
@@ -16,6 +18,7 @@ const SHIM = `globalThis.process = {
16
18
  argv: [],
17
19
  env: {},
18
20
  };`;
21
+ const SERVER_BUILD_FOLDER = "/$server_build/";
19
22
  function createIntegration(args) {
20
23
  let _config;
21
24
  let _buildConfig;
@@ -28,8 +31,8 @@ function createIntegration(args) {
28
31
  needsBuildConfig = !config.build.client;
29
32
  updateConfig({
30
33
  build: {
31
- client: new URL("./static/", config.outDir),
32
- server: new URL("./", config.outDir),
34
+ client: new URL(`.${config.base}`, config.outDir),
35
+ server: new URL(`.${SERVER_BUILD_FOLDER}`, config.outDir),
33
36
  serverEntry: "_worker.js"
34
37
  }
35
38
  });
@@ -44,6 +47,10 @@ function createIntegration(args) {
44
47
 
45
48
  `);
46
49
  }
50
+ if (config.base === SERVER_BUILD_FOLDER) {
51
+ throw new Error(`
52
+ [@astrojs/cloudflare] \`base: "${SERVER_BUILD_FOLDER}"\` is not allowed. Please change your \`base\` config to something else.`);
53
+ }
47
54
  },
48
55
  "astro:build:setup": ({ vite, target }) => {
49
56
  if (target === "server") {
@@ -63,19 +70,18 @@ function createIntegration(args) {
63
70
  },
64
71
  "astro:build:start": ({ buildConfig }) => {
65
72
  if (needsBuildConfig) {
66
- buildConfig.client = new URL("./static/", _config.outDir);
67
- buildConfig.server = new URL("./", _config.outDir);
73
+ buildConfig.client = new URL(`.${_config.base}`, _config.outDir);
74
+ buildConfig.server = new URL(`.${SERVER_BUILD_FOLDER}`, _config.outDir);
68
75
  buildConfig.serverEntry = "_worker.js";
69
76
  }
70
77
  },
71
78
  "astro:build:done": async () => {
72
- const entryUrl = new URL(_buildConfig.serverEntry, _buildConfig.server);
73
- const pkg = fileURLToPath(entryUrl);
79
+ const entryPath = fileURLToPath(new URL(_buildConfig.serverEntry, _buildConfig.server)), entryUrl = new URL(_buildConfig.serverEntry, _config.outDir), buildPath = fileURLToPath(entryUrl);
74
80
  await esbuild.build({
75
81
  target: "es2020",
76
82
  platform: "browser",
77
- entryPoints: [pkg],
78
- outfile: pkg,
83
+ entryPoints: [entryPath],
84
+ outfile: buildPath,
79
85
  allowOverwrite: true,
80
86
  format: "esm",
81
87
  bundle: true,
@@ -84,8 +90,55 @@ function createIntegration(args) {
84
90
  js: SHIM
85
91
  }
86
92
  });
87
- const chunksUrl = new URL("./chunks", _buildConfig.server);
88
- await fs.promises.rm(chunksUrl, { recursive: true, force: true });
93
+ const serverUrl = new URL(_buildConfig.server);
94
+ await fs.promises.rm(serverUrl, { recursive: true, force: true });
95
+ const cloudflareSpecialFiles = ["_headers", "_redirects", "_routes.json"];
96
+ if (_config.base !== "/") {
97
+ for (const file of cloudflareSpecialFiles) {
98
+ try {
99
+ await fs.promises.rename(
100
+ new URL(file, _buildConfig.client),
101
+ new URL(file, _config.outDir)
102
+ );
103
+ } catch (e) {
104
+ }
105
+ }
106
+ }
107
+ const routesExists = await fs.promises.stat(new URL("./_routes.json", _config.outDir)).then((stat) => stat.isFile()).catch(() => false);
108
+ if (!routesExists) {
109
+ const staticPathList = (await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, {
110
+ cwd: fileURLToPath(_config.outDir),
111
+ filesOnly: true
112
+ })).filter((file) => cloudflareSpecialFiles.indexOf(file) < 0).map((file) => `/${file}`);
113
+ const redirectsExists = await fs.promises.stat(new URL("./_redirects", _config.outDir)).then((stat) => stat.isFile()).catch(() => false);
114
+ if (redirectsExists) {
115
+ const redirects = (await fs.promises.readFile(new URL("./_redirects", _config.outDir), "utf-8")).split(os.EOL).map((line) => {
116
+ const parts = line.split(" ");
117
+ if (parts.length < 2) {
118
+ return null;
119
+ } else {
120
+ return parts[0].replace(/\/:.*?(?=\/|$)/g, "/*").replace(/\?.*$/, "");
121
+ }
122
+ }).filter(
123
+ (line, index, arr) => line !== null && arr.indexOf(line) === index
124
+ );
125
+ if (redirects.length > 0) {
126
+ staticPathList.push(...redirects);
127
+ }
128
+ }
129
+ await fs.promises.writeFile(
130
+ new URL("./_routes.json", _config.outDir),
131
+ JSON.stringify(
132
+ {
133
+ version: 1,
134
+ include: ["/*"],
135
+ exclude: staticPathList
136
+ },
137
+ null,
138
+ 2
139
+ )
140
+ );
141
+ }
89
142
  if (isModeDirectory) {
90
143
  const functionsUrl = new URL(`file://${process.cwd()}/functions/`);
91
144
  await fs.promises.mkdir(functionsUrl, { recursive: true });
@@ -0,0 +1,16 @@
1
+ export declare type WorkerRuntime<T = unknown> = {
2
+ name: 'cloudflare';
3
+ env: T;
4
+ waitUntil(promise: Promise<any>): void;
5
+ passThroughOnException(): void;
6
+ };
7
+ export declare type PagesRuntime<T = unknown, U = unknown> = {
8
+ name: 'cloudflare';
9
+ env: T;
10
+ functionPath: string;
11
+ params: Record<string, string>;
12
+ data: U;
13
+ waitUntil(promise: Promise<any>): void;
14
+ next(request: Request): void;
15
+ };
16
+ export declare function getRuntime<T = unknown, U = unknown>(request: Request): WorkerRuntime<T> | PagesRuntime<T, U>;
@@ -0,0 +1,12 @@
1
+ function getRuntime(request) {
2
+ if (!!request) {
3
+ return Reflect.get(request, Symbol.for("runtime"));
4
+ } else {
5
+ throw new Error(
6
+ "To retrieve the current cloudflare runtime you need to pass in the Astro request object"
7
+ );
8
+ }
9
+ }
10
+ export {
11
+ getRuntime
12
+ };
@@ -1,13 +1,13 @@
1
- import './shim.js';
2
1
  import type { SSRManifest } from 'astro';
3
2
  declare type Env = {
4
3
  ASSETS: {
5
4
  fetch: (req: Request) => Promise<Response>;
6
5
  };
6
+ name: string;
7
7
  };
8
8
  export declare function createExports(manifest: SSRManifest): {
9
9
  default: {
10
- fetch: (request: Request, env: Env) => Promise<Response>;
10
+ fetch: (request: Request, env: Env, context: any) => Promise<Response>;
11
11
  };
12
12
  };
13
13
  export {};
@@ -1,12 +1,13 @@
1
- import "./shim.js";
2
1
  import { App } from "astro/app";
2
+ import { getProcessEnvProxy } from "./util.js";
3
+ process.env = getProcessEnvProxy();
3
4
  function createExports(manifest) {
4
5
  const app = new App(manifest, false);
5
- const fetch = async (request, env) => {
6
- const { origin, pathname } = new URL(request.url);
6
+ const fetch = async (request, env, context) => {
7
+ process.env = env;
8
+ const { pathname } = new URL(request.url);
7
9
  if (manifest.assets.has(pathname)) {
8
- const assetRequest = new Request(`${origin}/static${pathname}`, request);
9
- return env.ASSETS.fetch(assetRequest);
10
+ return env.ASSETS.fetch(request);
10
11
  }
11
12
  let routeData = app.match(request, { matchNotFound: true });
12
13
  if (routeData) {
@@ -15,6 +16,7 @@ function createExports(manifest) {
15
16
  Symbol.for("astro.clientAddress"),
16
17
  request.headers.get("cf-connecting-ip")
17
18
  );
19
+ Reflect.set(request, Symbol.for("runtime"), { env, name: "cloudflare", ...context });
18
20
  let response = await app.render(request, routeData);
19
21
  if (app.setCookieHeaders) {
20
22
  for (const setCookieHeader of app.setCookieHeaders(response)) {
@@ -1,8 +1,7 @@
1
- import './shim.js';
2
1
  import type { SSRManifest } from 'astro';
3
2
  export declare function createExports(manifest: SSRManifest): {
4
- onRequest: ({ request, next, }: {
3
+ onRequest: ({ request, next, ...runtimeEnv }: {
5
4
  request: Request;
6
5
  next: (request: Request) => void;
7
- }) => Promise<void | Response>;
6
+ } & Record<string, unknown>) => Promise<void | Response>;
8
7
  };
@@ -1,15 +1,17 @@
1
- import "./shim.js";
2
1
  import { App } from "astro/app";
2
+ import { getProcessEnvProxy } from "./util.js";
3
+ process.env = getProcessEnvProxy();
3
4
  function createExports(manifest) {
4
5
  const app = new App(manifest, false);
5
6
  const onRequest = async ({
6
7
  request,
7
- next
8
+ next,
9
+ ...runtimeEnv
8
10
  }) => {
9
- const { origin, pathname } = new URL(request.url);
11
+ process.env = runtimeEnv.env;
12
+ const { pathname } = new URL(request.url);
10
13
  if (manifest.assets.has(pathname)) {
11
- const assetRequest = new Request(`${origin}/static${pathname}`, request);
12
- return next(assetRequest);
14
+ return next(request);
13
15
  }
14
16
  let routeData = app.match(request, { matchNotFound: true });
15
17
  if (routeData) {
@@ -18,6 +20,11 @@ function createExports(manifest) {
18
20
  Symbol.for("astro.clientAddress"),
19
21
  request.headers.get("cf-connecting-ip")
20
22
  );
23
+ Reflect.set(request, Symbol.for("runtime"), {
24
+ ...runtimeEnv,
25
+ name: "cloudflare",
26
+ next
27
+ });
21
28
  let response = await app.render(request, routeData);
22
29
  if (app.setCookieHeaders) {
23
30
  for (const setCookieHeader of app.setCookieHeaders(response)) {
package/dist/util.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function getProcessEnvProxy(): {};
package/dist/util.js ADDED
@@ -0,0 +1,15 @@
1
+ function getProcessEnvProxy() {
2
+ return new Proxy(
3
+ {},
4
+ {
5
+ get: (target, prop) => {
6
+ console.warn(
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.`
8
+ );
9
+ }
10
+ }
11
+ );
12
+ }
13
+ export {
14
+ getProcessEnvProxy
15
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@astrojs/cloudflare",
3
- "description": "Deploy your site to cloudflare pages functions",
4
- "version": "0.0.0-node-standalone-20221011210529",
3
+ "description": "Deploy your site to cloudflare workers or cloudflare pages",
4
+ "version": "0.0.0-prerender-20221215200617",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "withastro",
@@ -19,16 +19,24 @@
19
19
  "homepage": "https://docs.astro.build/en/guides/integrations-guide/cloudflare/",
20
20
  "exports": {
21
21
  ".": "./dist/index.js",
22
+ "./runtime": {
23
+ "types": "./runtime.d.ts",
24
+ "default": "./dist/runtime.js"
25
+ },
22
26
  "./server.advanced.js": "./dist/server.advanced.js",
23
27
  "./server.directory.js": "./dist/server.directory.js",
24
28
  "./package.json": "./package.json"
25
29
  },
26
30
  "dependencies": {
27
- "esbuild": "^0.14.42"
31
+ "esbuild": "^0.15.18",
32
+ "tiny-glob": "^0.2.9"
33
+ },
34
+ "peerDependencies": {
35
+ "astro": "0.0.0-prerender-20221215200617"
28
36
  },
29
37
  "devDependencies": {
30
- "astro": "0.0.0-node-standalone-20221011210529",
31
- "astro-scripts": "0.0.8",
38
+ "astro": "0.0.0-prerender-20221215200617",
39
+ "astro-scripts": "0.0.9",
32
40
  "chai": "^4.3.6",
33
41
  "cheerio": "^1.0.0-rc.11",
34
42
  "mocha": "^9.2.2",
package/runtime.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type { WorkerRuntime, PagesRuntime } from './dist/runtime';
2
+
3
+ export { getRuntime } from './dist/runtime';
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
2
2
  import esbuild from 'esbuild';
3
3
  import * as fs from 'fs';
4
+ import * as os from 'os';
5
+ import glob from 'tiny-glob';
4
6
  import { fileURLToPath } from 'url';
5
7
 
6
8
  type Options = {
@@ -32,6 +34,8 @@ const SHIM = `globalThis.process = {
32
34
  env: {},
33
35
  };`;
34
36
 
37
+ const SERVER_BUILD_FOLDER = '/$server_build/';
38
+
35
39
  export default function createIntegration(args?: Options): AstroIntegration {
36
40
  let _config: AstroConfig;
37
41
  let _buildConfig: BuildConfig;
@@ -45,10 +49,10 @@ export default function createIntegration(args?: Options): AstroIntegration {
45
49
  needsBuildConfig = !config.build.client;
46
50
  updateConfig({
47
51
  build: {
48
- client: new URL('./static/', config.outDir),
49
- server: new URL('./', config.outDir),
52
+ client: new URL(`.${config.base}`, config.outDir),
53
+ server: new URL(`.${SERVER_BUILD_FOLDER}`, config.outDir),
50
54
  serverEntry: '_worker.js',
51
- }
55
+ },
52
56
  });
53
57
  },
54
58
  'astro:config:done': ({ setAdapter, config }) => {
@@ -62,6 +66,11 @@ export default function createIntegration(args?: Options): AstroIntegration {
62
66
 
63
67
  `);
64
68
  }
69
+
70
+ if (config.base === SERVER_BUILD_FOLDER) {
71
+ throw new Error(`
72
+ [@astrojs/cloudflare] \`base: "${SERVER_BUILD_FOLDER}"\` is not allowed. Please change your \`base\` config to something else.`);
73
+ }
65
74
  },
66
75
  'astro:build:setup': ({ vite, target }) => {
67
76
  if (target === 'server') {
@@ -83,20 +92,21 @@ export default function createIntegration(args?: Options): AstroIntegration {
83
92
  },
84
93
  'astro:build:start': ({ buildConfig }) => {
85
94
  // Backwards compat
86
- if(needsBuildConfig) {
87
- buildConfig.client = new URL('./static/', _config.outDir);
88
- buildConfig.server = new URL('./', _config.outDir);
95
+ if (needsBuildConfig) {
96
+ buildConfig.client = new URL(`.${_config.base}`, _config.outDir);
97
+ buildConfig.server = new URL(`.${SERVER_BUILD_FOLDER}`, _config.outDir);
89
98
  buildConfig.serverEntry = '_worker.js';
90
99
  }
91
100
  },
92
101
  'astro:build:done': async () => {
93
- const entryUrl = new URL(_buildConfig.serverEntry, _buildConfig.server);
94
- const pkg = fileURLToPath(entryUrl);
102
+ const entryPath = fileURLToPath(new URL(_buildConfig.serverEntry, _buildConfig.server)),
103
+ entryUrl = new URL(_buildConfig.serverEntry, _config.outDir),
104
+ buildPath = fileURLToPath(entryUrl);
95
105
  await esbuild.build({
96
106
  target: 'es2020',
97
107
  platform: 'browser',
98
- entryPoints: [pkg],
99
- outfile: pkg,
108
+ entryPoints: [entryPath],
109
+ outfile: buildPath,
100
110
  allowOverwrite: true,
101
111
  format: 'esm',
102
112
  bundle: true,
@@ -107,8 +117,90 @@ export default function createIntegration(args?: Options): AstroIntegration {
107
117
  });
108
118
 
109
119
  // throw the server folder in the bin
110
- const chunksUrl = new URL('./chunks', _buildConfig.server);
111
- await fs.promises.rm(chunksUrl, { recursive: true, force: true });
120
+ const serverUrl = new URL(_buildConfig.server);
121
+ await fs.promises.rm(serverUrl, { recursive: true, force: true });
122
+
123
+ // move cloudflare specific files to the root
124
+ const cloudflareSpecialFiles = ['_headers', '_redirects', '_routes.json'];
125
+ if (_config.base !== '/') {
126
+ for (const file of cloudflareSpecialFiles) {
127
+ try {
128
+ await fs.promises.rename(
129
+ new URL(file, _buildConfig.client),
130
+ new URL(file, _config.outDir)
131
+ );
132
+ } catch (e) {
133
+ // ignore
134
+ }
135
+ }
136
+ }
137
+
138
+ const routesExists = await fs.promises
139
+ .stat(new URL('./_routes.json', _config.outDir))
140
+ .then((stat) => stat.isFile())
141
+ .catch(() => false);
142
+
143
+ // this creates a _routes.json, in case there is none present to enable
144
+ // cloudflare to handle static files and support _redirects configuration
145
+ // (without calling the function)
146
+ if (!routesExists) {
147
+ const staticPathList: Array<string> = (
148
+ await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, {
149
+ cwd: fileURLToPath(_config.outDir),
150
+ filesOnly: true,
151
+ })
152
+ )
153
+ .filter((file: string) => cloudflareSpecialFiles.indexOf(file) < 0)
154
+ .map((file: string) => `/${file}`);
155
+
156
+ const redirectsExists = await fs.promises
157
+ .stat(new URL('./_redirects', _config.outDir))
158
+ .then((stat) => stat.isFile())
159
+ .catch(() => false);
160
+
161
+ // convert all redirect source paths into a list of routes
162
+ // and add them to the static path
163
+ if (redirectsExists) {
164
+ const redirects = (
165
+ await fs.promises.readFile(new URL('./_redirects', _config.outDir), 'utf-8')
166
+ )
167
+ .split(os.EOL)
168
+ .map((line) => {
169
+ const parts = line.split(' ');
170
+ if (parts.length < 2) {
171
+ return null;
172
+ } else {
173
+ // convert /products/:id to /products/*
174
+ return (
175
+ parts[0]
176
+ .replace(/\/:.*?(?=\/|$)/g, '/*')
177
+ // remove query params as they are not supported by cloudflare
178
+ .replace(/\?.*$/, '')
179
+ );
180
+ }
181
+ })
182
+ .filter(
183
+ (line, index, arr) => line !== null && arr.indexOf(line) === index
184
+ ) as string[];
185
+
186
+ if (redirects.length > 0) {
187
+ staticPathList.push(...redirects);
188
+ }
189
+ }
190
+
191
+ await fs.promises.writeFile(
192
+ new URL('./_routes.json', _config.outDir),
193
+ JSON.stringify(
194
+ {
195
+ version: 1,
196
+ include: ['/*'],
197
+ exclude: staticPathList,
198
+ },
199
+ null,
200
+ 2
201
+ )
202
+ );
203
+ }
112
204
 
113
205
  if (isModeDirectory) {
114
206
  const functionsUrl = new URL(`file://${process.cwd()}/functions/`);
package/src/runtime.ts ADDED
@@ -0,0 +1,28 @@
1
+ export type WorkerRuntime<T = unknown> = {
2
+ name: 'cloudflare';
3
+ env: T;
4
+ waitUntil(promise: Promise<any>): void;
5
+ passThroughOnException(): void;
6
+ };
7
+
8
+ export type PagesRuntime<T = unknown, U = unknown> = {
9
+ name: 'cloudflare';
10
+ env: T;
11
+ functionPath: string;
12
+ params: Record<string, string>;
13
+ data: U;
14
+ waitUntil(promise: Promise<any>): void;
15
+ next(request: Request): void;
16
+ };
17
+
18
+ export function getRuntime<T = unknown, U = unknown>(
19
+ request: Request
20
+ ): WorkerRuntime<T> | PagesRuntime<T, U> {
21
+ if (!!request) {
22
+ return Reflect.get(request, Symbol.for('runtime'));
23
+ } else {
24
+ throw new Error(
25
+ 'To retrieve the current cloudflare runtime you need to pass in the Astro request object'
26
+ );
27
+ }
28
+ }
@@ -1,22 +1,25 @@
1
- import './shim.js';
2
-
3
1
  import type { SSRManifest } from 'astro';
4
2
  import { App } from 'astro/app';
3
+ import { getProcessEnvProxy } from './util.js';
4
+
5
+ process.env = getProcessEnvProxy();
5
6
 
6
7
  type Env = {
7
8
  ASSETS: { fetch: (req: Request) => Promise<Response> };
9
+ name: string;
8
10
  };
9
11
 
10
12
  export function createExports(manifest: SSRManifest) {
11
13
  const app = new App(manifest, false);
12
14
 
13
- const fetch = async (request: Request, env: Env) => {
14
- const { origin, pathname } = new URL(request.url);
15
+ const fetch = async (request: Request, env: Env, context: any) => {
16
+ process.env = env as any;
17
+
18
+ const { pathname } = new URL(request.url);
15
19
 
16
- // static assets
20
+ // static assets fallback, in case default _routes.json is not used
17
21
  if (manifest.assets.has(pathname)) {
18
- const assetRequest = new Request(`${origin}/static${pathname}`, request);
19
- return env.ASSETS.fetch(assetRequest);
22
+ return env.ASSETS.fetch(request);
20
23
  }
21
24
 
22
25
  let routeData = app.match(request, { matchNotFound: true });
@@ -26,6 +29,7 @@ export function createExports(manifest: SSRManifest) {
26
29
  Symbol.for('astro.clientAddress'),
27
30
  request.headers.get('cf-connecting-ip')
28
31
  );
32
+ Reflect.set(request, Symbol.for('runtime'), { env, name: 'cloudflare', ...context });
29
33
  let response = await app.render(request, routeData);
30
34
 
31
35
  if (app.setCookieHeaders) {
@@ -1,7 +1,8 @@
1
- import './shim.js';
2
-
3
1
  import type { SSRManifest } from 'astro';
4
2
  import { App } from 'astro/app';
3
+ import { getProcessEnvProxy } from './util.js';
4
+
5
+ process.env = getProcessEnvProxy();
5
6
 
6
7
  export function createExports(manifest: SSRManifest) {
7
8
  const app = new App(manifest, false);
@@ -9,16 +10,17 @@ export function createExports(manifest: SSRManifest) {
9
10
  const onRequest = async ({
10
11
  request,
11
12
  next,
13
+ ...runtimeEnv
12
14
  }: {
13
15
  request: Request;
14
16
  next: (request: Request) => void;
15
- }) => {
16
- const { origin, pathname } = new URL(request.url);
17
+ } & Record<string, unknown>) => {
18
+ process.env = runtimeEnv.env as any;
17
19
 
18
- // static assets
20
+ const { pathname } = new URL(request.url);
21
+ // static assets fallback, in case default _routes.json is not used
19
22
  if (manifest.assets.has(pathname)) {
20
- const assetRequest = new Request(`${origin}/static${pathname}`, request);
21
- return next(assetRequest);
23
+ return next(request);
22
24
  }
23
25
 
24
26
  let routeData = app.match(request, { matchNotFound: true });
@@ -28,6 +30,11 @@ export function createExports(manifest: SSRManifest) {
28
30
  Symbol.for('astro.clientAddress'),
29
31
  request.headers.get('cf-connecting-ip')
30
32
  );
33
+ Reflect.set(request, Symbol.for('runtime'), {
34
+ ...runtimeEnv,
35
+ name: 'cloudflare',
36
+ next,
37
+ });
31
38
  let response = await app.render(request, routeData);
32
39
 
33
40
  if (app.setCookieHeaders) {
package/src/util.ts ADDED
@@ -0,0 +1,16 @@
1
+ export function getProcessEnvProxy() {
2
+ return new Proxy(
3
+ {},
4
+ {
5
+ get: (target, prop) => {
6
+ console.warn(
7
+ // NOTE: \0 prevents Vite replacement
8
+ `Unable to access \`import.meta\0.env.${prop.toString()}\` on initialization ` +
9
+ `as the Cloudflare platform only provides the environment variables per request. ` +
10
+ `Please move the environment variable access inside a function ` +
11
+ `that's only called after a request has been received.`
12
+ );
13
+ },
14
+ }
15
+ );
16
+ }
@@ -24,6 +24,7 @@ describe.skip('Basic app', () => {
24
24
  let html = await res.text();
25
25
  let $ = cheerio.load(html);
26
26
  expect($('h1').text()).to.equal('Testing');
27
+ expect($('#env').text()).to.equal('secret');
27
28
  } finally {
28
29
  stop();
29
30
  }
@@ -1,6 +1,9 @@
1
1
  import { defineConfig } from 'astro/config';
2
2
  import cloudflare from '@astrojs/cloudflare';
3
3
 
4
+ // test env var
5
+ process.env.SECRET_STUFF = 'secret'
6
+
4
7
  export default defineConfig({
5
8
  adapter: cloudflare(),
6
9
  output: 'server',
@@ -4,5 +4,6 @@
4
4
  </head>
5
5
  <body>
6
6
  <h1>Testing</h1>
7
+ <div id="env">{import.meta.env.SECRET_STUFF}</div>
7
8
  </body>
8
9
  </html>
@@ -0,0 +1,4 @@
1
+ # for tests only
2
+
3
+ [vars]
4
+ SECRET_STUFF = "secret"
package/dist/shim.d.ts DELETED
File without changes
package/dist/shim.js DELETED
@@ -1,4 +0,0 @@
1
- globalThis.process = {
2
- argv: [],
3
- env: {}
4
- };
package/src/shim.ts DELETED
@@ -1,4 +0,0 @@
1
- (globalThis as any).process = {
2
- argv: [],
3
- env: {},
4
- };