@astrojs/cloudflare 10.4.1 → 11.0.0

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,4 +1,5 @@
1
1
  import { App } from 'astro/app';
2
+ import { setGetEnv } from 'astro/env/setup';
2
3
  import { createGetEnv } from '../utils/env.js';
3
4
  export function createExports(manifest) {
4
5
  const app = new App(manifest);
@@ -29,16 +30,14 @@ export function createExports(manifest) {
29
30
  caches: caches,
30
31
  ctx: {
31
32
  waitUntil: (promise) => context.waitUntil(promise),
32
- passThroughOnException: () => context.passThroughOnException(),
33
+ // Currently not available: https://developers.cloudflare.com/pages/platform/known-issues/#pages-functions
34
+ passThroughOnException: () => {
35
+ throw new Error('`passThroughOnException` is currently not available in Cloudflare Pages. See https://developers.cloudflare.com/pages/platform/known-issues/#pages-functions.');
36
+ },
33
37
  },
34
38
  },
35
39
  };
36
- // Won't throw if the virtual module is not available because it's not supported in
37
- // the users's astro version or if astro:env is not enabled in the project
38
- const setupModule = 'astro/env/setup';
39
- await import(/* @vite-ignore */ setupModule)
40
- .then((mod) => mod.setGetEnv(createGetEnv(env)))
41
- .catch(() => { });
40
+ setGetEnv(createGetEnv(env));
42
41
  const response = await app.render(request, { routeData, locals });
43
42
  if (app.setCookieHeaders) {
44
43
  for (const setCookieHeader of app.setCookieHeaders(response)) {
package/dist/index.d.ts CHANGED
@@ -31,7 +31,7 @@ export type Options = {
31
31
  * Proxy configuration for the platform.
32
32
  */
33
33
  platformProxy?: {
34
- /** Toggle the proxy. Default `undefined`, which equals to `false`. */
34
+ /** Toggle the proxy. Default `undefined`, which equals to `true`. */
35
35
  enabled?: boolean;
36
36
  /** Path to the configuration file. Default `wrangler.toml`. */
37
37
  configPath?: string;
@@ -50,7 +50,5 @@ export type Options = {
50
50
  * for reference on how these file types are exported
51
51
  */
52
52
  cloudflareModules?: boolean;
53
- /** @deprecated - use `cloudflareModules`, which defaults to true. You can set `cloudflareModuleLoading: false` to disable */
54
- wasmModuleImports?: boolean;
55
53
  };
56
54
  export default function createIntegration(args?: Options): AstroIntegration;
package/dist/index.js CHANGED
@@ -1,95 +1,66 @@
1
1
  import { createReadStream } from 'node:fs';
2
- import { appendFile, rename, stat, unlink } from 'node:fs/promises';
2
+ import { appendFile, rename, stat } from 'node:fs/promises';
3
3
  import { createInterface } from 'node:readline/promises';
4
4
  import { appendForwardSlash, prependForwardSlash, removeLeadingForwardSlash, } from '@astrojs/internal-helpers/path';
5
5
  import { createRedirectsFromAstroRoutes } from '@astrojs/underscore-redirects';
6
6
  import { AstroError } from 'astro/errors';
7
- import { walk } from 'estree-walker';
8
- import MagicString from 'magic-string';
9
7
  import { getPlatformProxy } from 'wrangler';
10
8
  import { cloudflareModuleLoader, } from './utils/cloudflare-module-loader.js';
11
9
  import { createGetEnv } from './utils/env.js';
12
10
  import { createRoutesFile, getParts } from './utils/generate-routes-json.js';
13
11
  import { setImageConfig } from './utils/image-config.js';
14
- import { mutateDynamicPageImportsInPlace, mutatePageMapInPlace } from './utils/index.js';
15
- import { NonServerChunkDetector } from './utils/non-server-chunk-detector.js';
12
+ function wrapWithSlashes(path) {
13
+ return prependForwardSlash(appendForwardSlash(path));
14
+ }
15
+ function setProcessEnv(config, env) {
16
+ const getEnv = createGetEnv(env);
17
+ if (config.experimental.env?.schema) {
18
+ for (const key of Object.keys(config.experimental.env.schema)) {
19
+ const value = getEnv(key);
20
+ if (value !== undefined) {
21
+ process.env[key] = value;
22
+ }
23
+ }
24
+ }
25
+ }
26
+ function createPlatformProxy(platformProxy) {
27
+ return getPlatformProxy({
28
+ configPath: platformProxy?.configPath ?? 'wrangler.toml',
29
+ experimentalJsonConfig: platformProxy?.experimentalJsonConfig ?? false,
30
+ persist: platformProxy?.persist ?? true,
31
+ });
32
+ }
16
33
  export default function createIntegration(args) {
17
34
  let _config;
18
- const cloudflareModulePlugin = cloudflareModuleLoader(args?.cloudflareModules ?? args?.wasmModuleImports ?? true);
19
- // Initialize the unused chunk analyzer as a shared state between hooks.
20
- // The analyzer is used on earlier hooks to collect information about used hooks on a Vite plugin
21
- // and then later after the full build to clean up unused chunks, so it has to be shared between them.
22
- const chunkAnalyzer = new NonServerChunkDetector();
35
+ const cloudflareModulePlugin = cloudflareModuleLoader(args?.cloudflareModules ?? true);
23
36
  return {
24
37
  name: '@astrojs/cloudflare',
25
38
  hooks: {
26
- 'astro:config:setup': ({ command, config, updateConfig, logger }) => {
39
+ 'astro:config:setup': ({ command, config, updateConfig, logger, addWatchFile }) => {
27
40
  updateConfig({
28
41
  build: {
29
- client: new URL(`.${prependForwardSlash(appendForwardSlash(config.base))}`, config.outDir),
42
+ client: new URL(`.${wrapWithSlashes(config.base)}`, config.outDir),
30
43
  server: new URL('./_worker.js/', config.outDir),
31
44
  serverEntry: 'index.js',
32
45
  redirects: false,
33
46
  },
34
47
  vite: {
35
- // load .wasm files as WebAssembly modules
36
48
  plugins: [
49
+ // https://developers.cloudflare.com/pages/functions/module-support/
50
+ // Allows imports of '.wasm', '.bin', and '.txt' file types
37
51
  cloudflareModulePlugin,
38
- chunkAnalyzer.getPlugin(),
39
- {
40
- name: 'dynamic-imports-analyzer',
41
- enforce: 'post',
42
- generateBundle(_, bundle) {
43
- let astrojsSSRVirtualEntryAST;
44
- const prerenderImports = [];
45
- let entryChunk;
46
- // Find all pages (ignore the ssr entrypoint) which are prerendered based on the dynamic imports of the prerender chunk
47
- for (const chunk of Object.values(bundle)) {
48
- if (chunk.type !== 'chunk')
49
- continue;
50
- if (chunk.name === '_@astrojs-ssr-virtual-entry') {
51
- astrojsSSRVirtualEntryAST = this.parse(chunk.code);
52
- entryChunk = chunk;
53
- continue;
54
- }
55
- const isPrerendered = chunk.dynamicImports.some((entry) => entry.includes('prerender'));
56
- if (isPrerendered) {
57
- prerenderImports.push(chunk.fileName);
58
- }
59
- }
60
- if (!astrojsSSRVirtualEntryAST)
61
- return;
62
- if (!entryChunk)
63
- return;
64
- const s = new MagicString(entryChunk.code);
65
- const constsToRemove = [];
66
- walk(astrojsSSRVirtualEntryAST, {
67
- leave(node) {
68
- // We are only looking for VariableDeclarations, since both (dynamic imports and pageMap) are declared as constants in the code
69
- if (node.type !== 'VariableDeclaration')
70
- return;
71
- if (!node.declarations[0] ||
72
- node.declarations[0].type !== 'VariableDeclarator')
73
- return;
74
- // This function will remove the dynamic imports from the entrypoint
75
- mutateDynamicPageImportsInPlace(node, prerenderImports, constsToRemove, s);
76
- // This function will remove the pageMap entries which are invalid now
77
- mutatePageMapInPlace(node, constsToRemove, s);
78
- },
79
- });
80
- entryChunk.code = s.toString();
81
- },
82
- },
83
52
  ],
84
53
  },
85
- image: setImageConfig(args?.imageService ?? 'DEFAULT', config.image, command, logger),
54
+ image: setImageConfig(args?.imageService ?? 'compile', config.image, command, logger),
86
55
  });
56
+ addWatchFile(new URL('./wrangler.toml', config.root));
57
+ addWatchFile(new URL('./wrangler.json', config.root));
87
58
  },
88
59
  'astro:config:done': ({ setAdapter, config }) => {
89
- _config = config;
90
60
  if (config.output === 'static') {
91
61
  throw new AstroError('[@astrojs/cloudflare] `output: "server"` or `output: "hybrid"` is required to use this adapter. Otherwise, this adapter is not necessary to deploy a static site to Cloudflare.');
92
62
  }
63
+ _config = config;
93
64
  setAdapter({
94
65
  name: '@astrojs/cloudflare',
95
66
  serverEntrypoint: '@astrojs/cloudflare/entrypoints/server.js',
@@ -113,21 +84,9 @@ export default function createIntegration(args) {
113
84
  });
114
85
  },
115
86
  'astro:server:setup': async ({ server }) => {
116
- if (args?.platformProxy?.enabled === true) {
117
- const platformProxy = await getPlatformProxy({
118
- configPath: args.platformProxy.configPath ?? 'wrangler.toml',
119
- experimentalJsonConfig: args.platformProxy.experimentalJsonConfig ?? false,
120
- persist: args.platformProxy.persist ?? true,
121
- });
122
- const getEnv = createGetEnv(platformProxy.env);
123
- if (_config.experimental.env?.schema) {
124
- for (const key of Object.keys(_config.experimental.env.schema)) {
125
- const value = getEnv(key);
126
- if (value !== undefined) {
127
- process.env[key] = value;
128
- }
129
- }
130
- }
87
+ if ((args?.platformProxy?.enabled ?? true) === true) {
88
+ const platformProxy = await createPlatformProxy(args?.platformProxy);
89
+ setProcessEnv(_config, platformProxy.env);
131
90
  const clientLocalsSymbol = Symbol.for('astro.locals');
132
91
  server.middlewares.use(async function middleware(req, res, next) {
133
92
  Reflect.set(req, clientLocalsSymbol, {
@@ -137,7 +96,10 @@ export default function createIntegration(args) {
137
96
  caches: platformProxy.caches,
138
97
  ctx: {
139
98
  waitUntil: (promise) => platformProxy.ctx.waitUntil(promise),
140
- passThroughOnException: () => platformProxy.ctx.passThroughOnException(),
99
+ // Currently not available: https://developers.cloudflare.com/pages/platform/known-issues/#pages-functions
100
+ passThroughOnException: () => {
101
+ throw new AstroError('`passThroughOnException` is currently not available in Cloudflare Pages. See https://developers.cloudflare.com/pages/platform/known-issues/#pages-functions.');
102
+ },
141
103
  },
142
104
  },
143
105
  });
@@ -186,20 +148,6 @@ export default function createIntegration(args) {
186
148
  // @ts-expect-error
187
149
  vite.build.rollupOptions.output.banner ||=
188
150
  'globalThis.process ??= {}; globalThis.process.env ??= {};';
189
- // @ts-expect-error
190
- vite.build.rollupOptions.output.manualChunks = (id) => {
191
- if (id.includes('node_modules')) {
192
- if (id.indexOf('node_modules') !== -1) {
193
- const basic = id.toString().split('node_modules/')[1];
194
- const sub1 = basic.split('/')[0];
195
- if (sub1 !== '.pnpm') {
196
- return sub1.toString();
197
- }
198
- const name2 = basic.split('/')[1];
199
- return name2.split('@')[name2[0] === '@' ? 1 : 0].toString();
200
- }
201
- }
202
- };
203
151
  vite.build.rollupOptions.external = _config.vite.build?.rollupOptions?.external ?? [];
204
152
  // Cloudflare env is only available per request. This isn't feasible for code that access env vars
205
153
  // in a global way, so we shim their access as `process.env.*`. This is not the recommended way for users to access environment variables. But we'll add this for compatibility for chosen variables. Mainly to support `@astrojs/db`
@@ -219,8 +167,6 @@ export default function createIntegration(args) {
219
167
  vite.build ||= {};
220
168
  vite.build.rollupOptions ||= {};
221
169
  vite.build.rollupOptions.output ||= {};
222
- // @ts-expect-error
223
- vite.build.rollupOptions.output.manualChunks = undefined;
224
170
  }
225
171
  },
226
172
  'astro:build:done': async ({ pages, routes, dir, logger }) => {
@@ -300,18 +246,6 @@ export default function createIntegration(args) {
300
246
  logger.error('Failed to write _redirects file');
301
247
  }
302
248
  }
303
- // Get chunks from the bundle that are not needed on the server and delete them
304
- // Those modules are build only for prerendering routes.
305
- const chunksToDelete = chunkAnalyzer.getNonServerChunks();
306
- for (const chunk of chunksToDelete) {
307
- try {
308
- // Chunks are located on `./_worker.js` directory inside of the output directory
309
- await unlink(new URL(`./_worker.js/${chunk}`, _config.outDir));
310
- }
311
- catch (error) {
312
- logger.warn(`Issue while trying to delete unused file from server bundle: ${new URL(`./_worker.js/${chunk}`, _config.outDir).toString()}`);
313
- }
314
- }
315
249
  },
316
250
  },
317
251
  };
@@ -51,7 +51,7 @@ export function cloudflareModuleLoader(enabled) {
51
51
  return;
52
52
  }
53
53
  if (!enabled) {
54
- throw new Error(`Cloudflare module loading is experimental. The ${maybeExtension} module cannot be loaded unless you add \`wasmModuleImports: true\` to your astro config.`);
54
+ throw new Error(`Cloudflare module loading is experimental. The ${maybeExtension} module cannot be loaded unless you add \`cloudflareModules: true\` to your astro config.`);
55
55
  }
56
56
  const moduleLoader = renderers[moduleType];
57
57
  const filePath = id.replace(/\?\w+$/, '');
@@ -126,7 +126,10 @@ export function cloudflareModuleLoader(enabled) {
126
126
  for (const chunk of Object.values(bundle)) {
127
127
  const repls = chunk.name && replacementsByChunkName.get(chunk.name);
128
128
  for (const replacement of repls || []) {
129
- replacement.fileName = chunk.fileName;
129
+ if (!replacement.fileName) {
130
+ replacement.fileName = [];
131
+ }
132
+ replacement.fileName.push(chunk.fileName);
130
133
  }
131
134
  }
132
135
  },
@@ -140,11 +143,13 @@ export function cloudflareModuleLoader(enabled) {
140
143
  if (!replacement.fileName) {
141
144
  continue;
142
145
  }
143
- const repls = replacementsByFileName.get(replacement.fileName) || [];
144
- if (!repls.length) {
145
- replacementsByFileName.set(replacement.fileName, repls);
146
+ for (const fileName of replacement.fileName) {
147
+ const repls = replacementsByFileName.get(fileName) || [];
148
+ if (!repls.length) {
149
+ replacementsByFileName.set(fileName, repls);
150
+ }
151
+ repls.push(replacement);
146
152
  }
147
- repls.push(replacement);
148
153
  }
149
154
  for (const [fileName, repls] of replacementsByFileName.entries()) {
150
155
  const filepath = path.join(baseDir, '_worker.js', fileName);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@astrojs/cloudflare",
3
3
  "description": "Deploy your site to Cloudflare Workers/Pages",
4
- "version": "10.4.1",
4
+ "version": "11.0.0",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "withastro",
@@ -39,10 +39,10 @@
39
39
  "wrangler": "^3.39.0"
40
40
  },
41
41
  "peerDependencies": {
42
- "astro": "^4.2.0"
42
+ "astro": "^4.10.3"
43
43
  },
44
44
  "devDependencies": {
45
- "astro": "^4.10.1",
45
+ "astro": "^4.10.3",
46
46
  "cheerio": "1.0.0-rc.12",
47
47
  "execa": "^8.0.1",
48
48
  "fast-glob": "^3.3.2",
@@ -1,8 +0,0 @@
1
- import type { Node } from 'estree-walker';
2
- import type MagicString from 'magic-string';
3
- export declare function mutatePageMapInPlace(node: Extract<Node, {
4
- type: 'VariableDeclaration';
5
- }>, constsToRemove: string[], s: MagicString): void;
6
- export declare function mutateDynamicPageImportsInPlace(node: Extract<Node, {
7
- type: 'VariableDeclaration';
8
- }>, prerenderImports: string[], constsToRemove: string[], s: MagicString): void;
@@ -1,55 +0,0 @@
1
- export function mutatePageMapInPlace(node, constsToRemove, s) {
2
- const declarator = node.declarations[0];
3
- if (declarator.id.type !== 'Identifier')
4
- return;
5
- if (declarator.id.name !== 'pageMap')
6
- return;
7
- if (!declarator.init || declarator.init.type !== 'NewExpression')
8
- return;
9
- if (!declarator.init.arguments[0] || declarator.init.arguments[0].type !== 'ArrayExpression')
10
- return;
11
- for (const arrayExpression of declarator.init.arguments[0].elements) {
12
- if (!arrayExpression || arrayExpression.type !== 'ArrayExpression')
13
- continue;
14
- if (!arrayExpression.elements[1] || arrayExpression.elements[1].type !== 'Identifier')
15
- continue;
16
- if (constsToRemove.includes(arrayExpression.elements[1].name)) {
17
- // @ts-expect-error - @types/estree seem to be wrong
18
- if (arrayExpression.start && arrayExpression.end) {
19
- // @ts-expect-error - @types/estree seem to be wrong
20
- s.remove(arrayExpression.start, arrayExpression.end);
21
- // We need to check if there are any leftover commas, which are not part of the `ArrayExpression` node
22
- // @ts-expect-error - @types/estree seem to be wrong
23
- const endChar = s.slice(arrayExpression.end, arrayExpression.end + 1);
24
- if (endChar.includes(',')) {
25
- // @ts-expect-error - @types/estree seem to be wrong
26
- s.remove(arrayExpression.end, arrayExpression.end + 1);
27
- }
28
- }
29
- }
30
- }
31
- }
32
- export function mutateDynamicPageImportsInPlace(node, prerenderImports, constsToRemove, s) {
33
- const declarator = node.declarations[0];
34
- if (declarator.id.type !== 'Identifier')
35
- return;
36
- if (!declarator.id.name.startsWith('_page'))
37
- return;
38
- if (!declarator.init || declarator.init.type !== 'ArrowFunctionExpression')
39
- return;
40
- if (!declarator.init.body || declarator.init.body.type !== 'ImportExpression')
41
- return;
42
- if (!declarator.init.body.source || declarator.init.body.source.type !== 'Literal')
43
- return;
44
- const sourceValue = declarator.init.body.source.value;
45
- if (typeof sourceValue !== 'string')
46
- return;
47
- if (prerenderImports.some((importItem) => sourceValue.includes(importItem))) {
48
- // @ts-expect-error - @types/estree seem to be wrong
49
- if (node.start && node.end) {
50
- constsToRemove.push(declarator.id.name);
51
- // @ts-expect-error - @types/estree seem to be wrong
52
- s.remove(node.start, node.end);
53
- }
54
- }
55
- }