@astrojs/cloudflare 7.5.3 → 7.5.4

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/dist/index.js CHANGED
@@ -1,431 +1,484 @@
1
- import { createRedirectsFromAstroRoutes } from "@astrojs/underscore-redirects";
2
- import { AstroError } from "astro/errors";
3
- import esbuild from "esbuild";
4
- import { Miniflare } from "miniflare";
5
- import * as fs from "node:fs";
6
- import * as os from "node:os";
7
- import { dirname, relative, sep } from "node:path";
8
- import { fileURLToPath, pathToFileURL } from "node:url";
9
- import glob from "tiny-glob";
10
- import { getAdapter } from "./getAdapter.js";
11
- import { deduplicatePatterns } from "./utils/deduplicatePatterns.js";
12
- import { getCFObject } from "./utils/getCFObject.js";
13
- import {
14
- getD1Bindings,
15
- getDOBindings,
16
- getEnvVars,
17
- getKVBindings,
18
- getR2Bindings
19
- } from "./utils/parser.js";
20
- import { prependForwardSlash } from "./utils/prependForwardSlash.js";
21
- import { rewriteWasmImportPath } from "./utils/rewriteWasmImportPath.js";
22
- import { wasmModuleLoader } from "./utils/wasm-module-loader.js";
23
- function createIntegration(args) {
24
- let _config;
25
- let _buildConfig;
26
- let _mf;
27
- let _entryPoints = /* @__PURE__ */ new Map();
28
- const SERVER_BUILD_FOLDER = "/$server_build/";
29
- const isModeDirectory = args?.mode === "directory";
30
- const functionPerRoute = args?.functionPerRoute ?? false;
31
- const runtimeMode = args?.runtime ?? "off";
32
- return {
33
- name: "@astrojs/cloudflare",
34
- hooks: {
35
- "astro:config:setup": ({ config, updateConfig }) => {
36
- updateConfig({
37
- build: {
38
- client: new URL(`.${config.base}`, config.outDir),
39
- server: new URL(`.${SERVER_BUILD_FOLDER}`, config.outDir),
40
- serverEntry: "_worker.mjs",
41
- redirects: false
42
- },
43
- vite: {
44
- // load .wasm files as WebAssembly modules
45
- plugins: [
46
- wasmModuleLoader({
47
- disabled: !args?.wasmModuleImports,
48
- assetsDirectory: config.build.assets
49
- })
50
- ]
51
- }
52
- });
53
- },
54
- "astro:config:done": ({ setAdapter, config }) => {
55
- setAdapter(getAdapter({ isModeDirectory, functionPerRoute }));
56
- _config = config;
57
- _buildConfig = config.build;
58
- if (_config.output === "static") {
59
- throw new AstroError(
60
- '[@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.'
61
- );
62
- }
63
- if (_config.base === SERVER_BUILD_FOLDER) {
64
- throw new AstroError(
65
- '[@astrojs/cloudflare] `base: "${SERVER_BUILD_FOLDER}"` is not allowed. Please change your `base` config to something else.'
66
- );
67
- }
68
- },
69
- "astro:server:setup": ({ server }) => {
70
- if (runtimeMode !== "off") {
71
- server.middlewares.use(async function middleware(req, res, next) {
72
- try {
73
- const cf = await getCFObject(runtimeMode);
74
- const vars = await getEnvVars();
75
- const D1Bindings = await getD1Bindings();
76
- const R2Bindings = await getR2Bindings();
77
- const KVBindings = await getKVBindings();
78
- const DOBindings = await getDOBindings();
79
- let bindingsEnv = new Object({});
80
- const originalPWD = process.env.PWD;
81
- process.env.PWD = process.cwd();
82
- _mf = new Miniflare({
83
- modules: true,
84
- script: "",
85
- cache: true,
86
- cachePersist: true,
87
- cacheWarnUsage: true,
88
- d1Databases: D1Bindings,
89
- d1Persist: true,
90
- r2Buckets: R2Bindings,
91
- r2Persist: true,
92
- kvNamespaces: KVBindings,
93
- kvPersist: true,
94
- durableObjects: DOBindings,
95
- durableObjectsPersist: true
96
- });
97
- await _mf.ready;
98
- for (const D1Binding of D1Bindings) {
99
- const db = await _mf.getD1Database(D1Binding);
100
- Reflect.set(bindingsEnv, D1Binding, db);
101
- }
102
- for (const R2Binding of R2Bindings) {
103
- const bucket = await _mf.getR2Bucket(R2Binding);
104
- Reflect.set(bindingsEnv, R2Binding, bucket);
105
- }
106
- for (const KVBinding of KVBindings) {
107
- const namespace = await _mf.getKVNamespace(KVBinding);
108
- Reflect.set(bindingsEnv, KVBinding, namespace);
109
- }
110
- for (const key in DOBindings) {
111
- if (Object.prototype.hasOwnProperty.call(DOBindings, key)) {
112
- const DO = await _mf.getDurableObjectNamespace(key);
113
- Reflect.set(bindingsEnv, key, DO);
1
+ import { createRedirectsFromAstroRoutes } from '@astrojs/underscore-redirects';
2
+ import { AstroError } from 'astro/errors';
3
+ import esbuild from 'esbuild';
4
+ import { Miniflare } from 'miniflare';
5
+ import * as fs from 'node:fs';
6
+ import * as os from 'node:os';
7
+ import { dirname, relative, sep } from 'node:path';
8
+ import { fileURLToPath, pathToFileURL } from 'node:url';
9
+ import glob from 'tiny-glob';
10
+ import { getAdapter } from './getAdapter.js';
11
+ import { deduplicatePatterns } from './utils/deduplicatePatterns.js';
12
+ import { getCFObject } from './utils/getCFObject.js';
13
+ import { getD1Bindings, getDOBindings, getEnvVars, getKVBindings, getR2Bindings, } from './utils/parser.js';
14
+ import { prependForwardSlash } from './utils/prependForwardSlash.js';
15
+ import { rewriteWasmImportPath } from './utils/rewriteWasmImportPath.js';
16
+ import { wasmModuleLoader } from './utils/wasm-module-loader.js';
17
+ export default function createIntegration(args) {
18
+ let _config;
19
+ let _buildConfig;
20
+ let _mf;
21
+ let _entryPoints = new Map();
22
+ const SERVER_BUILD_FOLDER = '/$server_build/';
23
+ const isModeDirectory = args?.mode === 'directory';
24
+ const functionPerRoute = args?.functionPerRoute ?? false;
25
+ const runtimeMode = args?.runtime ?? 'off';
26
+ return {
27
+ name: '@astrojs/cloudflare',
28
+ hooks: {
29
+ 'astro:config:setup': ({ config, updateConfig }) => {
30
+ updateConfig({
31
+ build: {
32
+ client: new URL(`.${config.base}`, config.outDir),
33
+ server: new URL(`.${SERVER_BUILD_FOLDER}`, config.outDir),
34
+ serverEntry: '_worker.mjs',
35
+ redirects: false,
36
+ },
37
+ vite: {
38
+ // load .wasm files as WebAssembly modules
39
+ plugins: [
40
+ wasmModuleLoader({
41
+ disabled: !args?.wasmModuleImports,
42
+ assetsDirectory: config.build.assets,
43
+ }),
44
+ ],
45
+ },
46
+ });
47
+ },
48
+ 'astro:config:done': ({ setAdapter, config }) => {
49
+ setAdapter(getAdapter({ isModeDirectory, functionPerRoute }));
50
+ _config = config;
51
+ _buildConfig = config.build;
52
+ if (_config.output === 'static') {
53
+ 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.');
54
+ }
55
+ if (_config.base === SERVER_BUILD_FOLDER) {
56
+ throw new AstroError('[@astrojs/cloudflare] `base: "${SERVER_BUILD_FOLDER}"` is not allowed. Please change your `base` config to something else.');
57
+ }
58
+ },
59
+ 'astro:server:setup': ({ server }) => {
60
+ if (runtimeMode !== 'off') {
61
+ server.middlewares.use(async function middleware(req, res, next) {
62
+ try {
63
+ const cf = await getCFObject(runtimeMode);
64
+ const vars = await getEnvVars();
65
+ const D1Bindings = await getD1Bindings();
66
+ const R2Bindings = await getR2Bindings();
67
+ const KVBindings = await getKVBindings();
68
+ const DOBindings = await getDOBindings();
69
+ let bindingsEnv = new Object({});
70
+ // fix for the error "kj/filesystem-disk-unix.c++:1709: warning: PWD environment variable doesn't match current directory."
71
+ // note: This mismatch might be primarily due to the test runner.
72
+ const originalPWD = process.env.PWD;
73
+ process.env.PWD = process.cwd();
74
+ _mf = new Miniflare({
75
+ modules: true,
76
+ script: '',
77
+ cache: true,
78
+ cachePersist: true,
79
+ cacheWarnUsage: true,
80
+ d1Databases: D1Bindings,
81
+ d1Persist: true,
82
+ r2Buckets: R2Bindings,
83
+ r2Persist: true,
84
+ kvNamespaces: KVBindings,
85
+ kvPersist: true,
86
+ durableObjects: DOBindings,
87
+ durableObjectsPersist: true,
88
+ });
89
+ await _mf.ready;
90
+ for (const D1Binding of D1Bindings) {
91
+ const db = await _mf.getD1Database(D1Binding);
92
+ Reflect.set(bindingsEnv, D1Binding, db);
93
+ }
94
+ for (const R2Binding of R2Bindings) {
95
+ const bucket = await _mf.getR2Bucket(R2Binding);
96
+ Reflect.set(bindingsEnv, R2Binding, bucket);
97
+ }
98
+ for (const KVBinding of KVBindings) {
99
+ const namespace = await _mf.getKVNamespace(KVBinding);
100
+ Reflect.set(bindingsEnv, KVBinding, namespace);
101
+ }
102
+ for (const key in DOBindings) {
103
+ if (Object.prototype.hasOwnProperty.call(DOBindings, key)) {
104
+ const DO = await _mf.getDurableObjectNamespace(key);
105
+ Reflect.set(bindingsEnv, key, DO);
106
+ }
107
+ }
108
+ const mfCache = await _mf.getCaches();
109
+ process.env.PWD = originalPWD;
110
+ const clientLocalsSymbol = Symbol.for('astro.locals');
111
+ Reflect.set(req, clientLocalsSymbol, {
112
+ runtime: {
113
+ env: {
114
+ // default binding for static assets will be dynamic once we support mocking of bindings
115
+ ASSETS: {},
116
+ // this is just a VAR for CF to change build behavior, on dev it should be 0
117
+ CF_PAGES: '0',
118
+ // will be fetched from git dynamically once we support mocking of bindings
119
+ CF_PAGES_BRANCH: 'TBA',
120
+ // will be fetched from git dynamically once we support mocking of bindings
121
+ CF_PAGES_COMMIT_SHA: 'TBA',
122
+ CF_PAGES_URL: `http://${req.headers.host}`,
123
+ ...bindingsEnv,
124
+ ...vars,
125
+ },
126
+ cf: cf,
127
+ waitUntil: (_promise) => {
128
+ return;
129
+ },
130
+ caches: mfCache,
131
+ },
132
+ });
133
+ next();
134
+ }
135
+ catch {
136
+ next();
137
+ }
138
+ });
139
+ }
140
+ },
141
+ 'astro:server:done': async ({ logger }) => {
142
+ if (_mf) {
143
+ logger.info('Cleaning up the Miniflare instance, and shutting down the workerd server.');
144
+ await _mf.dispose();
145
+ }
146
+ },
147
+ 'astro:build:setup': ({ vite, target }) => {
148
+ if (target === 'server') {
149
+ vite.resolve ||= {};
150
+ vite.resolve.alias ||= {};
151
+ const aliases = [
152
+ {
153
+ find: 'react-dom/server',
154
+ replacement: 'react-dom/server.browser',
155
+ },
156
+ ];
157
+ if (Array.isArray(vite.resolve.alias)) {
158
+ vite.resolve.alias = [...vite.resolve.alias, ...aliases];
159
+ }
160
+ else {
161
+ for (const alias of aliases) {
162
+ vite.resolve.alias[alias.find] = alias.replacement;
163
+ }
164
+ }
165
+ vite.ssr ||= {};
166
+ vite.ssr.target = 'webworker';
167
+ // Cloudflare env is only available per request. This isn't feasible for code that access env vars
168
+ // in a global way, so we shim their access as `process.env.*`. We will populate `process.env` later
169
+ // in its fetch handler.
170
+ vite.define = {
171
+ 'process.env': 'process.env',
172
+ ...vite.define,
173
+ };
114
174
  }
115
- }
116
- const mfCache = await _mf.getCaches();
117
- process.env.PWD = originalPWD;
118
- const clientLocalsSymbol = Symbol.for("astro.locals");
119
- Reflect.set(req, clientLocalsSymbol, {
120
- runtime: {
121
- env: {
122
- // default binding for static assets will be dynamic once we support mocking of bindings
123
- ASSETS: {},
124
- // this is just a VAR for CF to change build behavior, on dev it should be 0
125
- CF_PAGES: "0",
126
- // will be fetched from git dynamically once we support mocking of bindings
127
- CF_PAGES_BRANCH: "TBA",
128
- // will be fetched from git dynamically once we support mocking of bindings
129
- CF_PAGES_COMMIT_SHA: "TBA",
130
- CF_PAGES_URL: `http://${req.headers.host}`,
131
- ...bindingsEnv,
132
- ...vars
133
- },
134
- cf,
135
- waitUntil: (_promise) => {
136
- return;
137
- },
138
- caches: mfCache
175
+ },
176
+ 'astro:build:ssr': ({ entryPoints }) => {
177
+ _entryPoints = entryPoints;
178
+ },
179
+ 'astro:build:done': async ({ pages, routes, dir }) => {
180
+ const functionsUrl = new URL('functions/', _config.root);
181
+ const assetsUrl = new URL(_buildConfig.assets, _buildConfig.client);
182
+ if (isModeDirectory) {
183
+ await fs.promises.mkdir(functionsUrl, { recursive: true });
139
184
  }
140
- });
141
- next();
142
- } catch {
143
- next();
144
- }
145
- });
146
- }
147
- },
148
- "astro:server:done": async ({ logger }) => {
149
- if (_mf) {
150
- logger.info("Cleaning up the Miniflare instance, and shutting down the workerd server.");
151
- await _mf.dispose();
152
- }
153
- },
154
- "astro:build:setup": ({ vite, target }) => {
155
- if (target === "server") {
156
- vite.resolve ||= {};
157
- vite.resolve.alias ||= {};
158
- const aliases = [{ find: "react-dom/server", replacement: "react-dom/server.browser" }];
159
- if (Array.isArray(vite.resolve.alias)) {
160
- vite.resolve.alias = [...vite.resolve.alias, ...aliases];
161
- } else {
162
- for (const alias of aliases) {
163
- vite.resolve.alias[alias.find] = alias.replacement;
164
- }
165
- }
166
- vite.ssr ||= {};
167
- vite.ssr.target = "webworker";
168
- vite.define = {
169
- "process.env": "process.env",
170
- ...vite.define
171
- };
172
- }
173
- },
174
- "astro:build:ssr": ({ entryPoints }) => {
175
- _entryPoints = entryPoints;
176
- },
177
- "astro:build:done": async ({ pages, routes, dir }) => {
178
- const functionsUrl = new URL("functions/", _config.root);
179
- const assetsUrl = new URL(_buildConfig.assets, _buildConfig.client);
180
- if (isModeDirectory) {
181
- await fs.promises.mkdir(functionsUrl, { recursive: true });
182
- }
183
- if (isModeDirectory && (_buildConfig.split || functionPerRoute)) {
184
- const entryPointsURL = [..._entryPoints.values()];
185
- const entryPaths = entryPointsURL.map((entry) => fileURLToPath(entry));
186
- const outputUrl = new URL("$astro", _buildConfig.server);
187
- const outputDir = fileURLToPath(outputUrl);
188
- const entryPathsGroupedByDepth = !args.wasmModuleImports ? [entryPaths] : entryPaths.reduce((sum, thisPath) => {
189
- const depthFromRoot = thisPath.split(sep).length;
190
- sum.set(depthFromRoot, (sum.get(depthFromRoot) || []).concat(thisPath));
191
- return sum;
192
- }, /* @__PURE__ */ new Map()).values();
193
- for (const pathsGroup of entryPathsGroupedByDepth) {
194
- const pagesDirname = relative(fileURLToPath(_buildConfig.server), pathsGroup[0]).split(
195
- sep
196
- )[0];
197
- const absolutePagesDirname = fileURLToPath(new URL(pagesDirname, _buildConfig.server));
198
- const urlWithinFunctions = new URL(
199
- relative(absolutePagesDirname, pathsGroup[0]),
200
- functionsUrl
201
- );
202
- const relativePathToAssets = relative(
203
- dirname(fileURLToPath(urlWithinFunctions)),
204
- fileURLToPath(assetsUrl)
205
- );
206
- await esbuild.build({
207
- target: "es2022",
208
- platform: "browser",
209
- conditions: ["workerd", "worker", "browser"],
210
- external: [
211
- "node:assert",
212
- "node:async_hooks",
213
- "node:buffer",
214
- "node:crypto",
215
- "node:diagnostics_channel",
216
- "node:events",
217
- "node:path",
218
- "node:process",
219
- "node:stream",
220
- "node:string_decoder",
221
- "node:util",
222
- "cloudflare:*"
223
- ],
224
- entryPoints: pathsGroup,
225
- outbase: absolutePagesDirname,
226
- outdir: outputDir,
227
- allowOverwrite: true,
228
- format: "esm",
229
- bundle: true,
230
- minify: _config.vite?.build?.minify !== false,
231
- banner: {
232
- js: `globalThis.process = {
185
+ // TODO: remove _buildConfig.split in Astro 4.0
186
+ if (isModeDirectory && (_buildConfig.split || functionPerRoute)) {
187
+ const entryPointsURL = [..._entryPoints.values()];
188
+ const entryPaths = entryPointsURL.map((entry) => fileURLToPath(entry));
189
+ const outputUrl = new URL('$astro', _buildConfig.server);
190
+ const outputDir = fileURLToPath(outputUrl);
191
+ //
192
+ // Sadly, when wasmModuleImports is enabled, this needs to build esbuild for each depth of routes/entrypoints
193
+ // independently so that relative import paths to the assets are the correct depth of '../' traversals
194
+ // This is inefficient, so wasmModuleImports is opt-in. This could potentially be improved in the future by
195
+ // taking advantage of the esbuild "onEnd" hook to rewrite import code per entry point relative to where the final
196
+ // destination of the entrypoint is
197
+ const entryPathsGroupedByDepth = !args.wasmModuleImports
198
+ ? [entryPaths]
199
+ : entryPaths
200
+ .reduce((sum, thisPath) => {
201
+ const depthFromRoot = thisPath.split(sep).length;
202
+ sum.set(depthFromRoot, (sum.get(depthFromRoot) || []).concat(thisPath));
203
+ return sum;
204
+ }, new Map())
205
+ .values();
206
+ for (const pathsGroup of entryPathsGroupedByDepth) {
207
+ // for some reason this exports to "entry.pages" on windows instead of "pages" on unix environments.
208
+ // This deduces the name of the "pages" build directory
209
+ const pagesDirname = relative(fileURLToPath(_buildConfig.server), pathsGroup[0]).split(sep)[0];
210
+ const absolutePagesDirname = fileURLToPath(new URL(pagesDirname, _buildConfig.server));
211
+ const urlWithinFunctions = new URL(relative(absolutePagesDirname, pathsGroup[0]), functionsUrl);
212
+ const relativePathToAssets = relative(dirname(fileURLToPath(urlWithinFunctions)), fileURLToPath(assetsUrl));
213
+ await esbuild.build({
214
+ target: 'es2022',
215
+ platform: 'browser',
216
+ conditions: ['workerd', 'worker', 'browser'],
217
+ external: [
218
+ 'node:assert',
219
+ 'node:async_hooks',
220
+ 'node:buffer',
221
+ 'node:crypto',
222
+ 'node:diagnostics_channel',
223
+ 'node:events',
224
+ 'node:path',
225
+ 'node:process',
226
+ 'node:stream',
227
+ 'node:string_decoder',
228
+ 'node:util',
229
+ 'cloudflare:*',
230
+ ],
231
+ entryPoints: pathsGroup,
232
+ outbase: absolutePagesDirname,
233
+ outdir: outputDir,
234
+ allowOverwrite: true,
235
+ format: 'esm',
236
+ bundle: true,
237
+ minify: _config.vite?.build?.minify !== false,
238
+ banner: {
239
+ js: `globalThis.process = {
233
240
  argv: [],
234
241
  env: {},
235
- };`
236
- },
237
- logOverride: {
238
- "ignored-bare-import": "silent"
239
- },
240
- plugins: !args?.wasmModuleImports ? [] : [rewriteWasmImportPath({ relativePathToAssets })]
241
- });
242
- }
243
- const outputFiles = await glob(`**/*`, {
244
- cwd: outputDir,
245
- filesOnly: true
246
- });
247
- for (const outputFile of outputFiles) {
248
- const path = outputFile.split(sep);
249
- const finalSegments = path.map(
250
- (segment) => segment.replace(/(\_)(\w+)(\_)/g, (_, __, prop) => {
251
- return `[${prop}]`;
252
- }).replace(/(\_\-\-\-)(\w+)(\_)/g, (_, __, prop) => {
253
- return `[[${prop}]]`;
254
- })
255
- );
256
- finalSegments[finalSegments.length - 1] = finalSegments[finalSegments.length - 1].replace("entry.", "").replace(/(.*)\.(\w+)\.(\w+)$/g, (_, fileName, __, newExt) => {
257
- return `${fileName}.${newExt}`;
258
- });
259
- const finalDirPath = finalSegments.slice(0, -1).join(sep);
260
- const finalPath = finalSegments.join(sep);
261
- const newDirUrl = new URL(finalDirPath, functionsUrl);
262
- await fs.promises.mkdir(newDirUrl, { recursive: true });
263
- const oldFileUrl = new URL(`$astro/${outputFile}`, outputUrl);
264
- const newFileUrl = new URL(finalPath, functionsUrl);
265
- await fs.promises.rename(oldFileUrl, newFileUrl);
266
- }
267
- } else {
268
- const entryPath = fileURLToPath(new URL(_buildConfig.serverEntry, _buildConfig.server));
269
- const entryUrl = new URL(_buildConfig.serverEntry, _config.outDir);
270
- const buildPath = fileURLToPath(entryUrl);
271
- const finalBuildUrl = pathToFileURL(buildPath.replace(/\.mjs$/, ".js"));
272
- await esbuild.build({
273
- target: "es2022",
274
- platform: "browser",
275
- conditions: ["workerd", "worker", "browser"],
276
- external: [
277
- "node:assert",
278
- "node:async_hooks",
279
- "node:buffer",
280
- "node:crypto",
281
- "node:diagnostics_channel",
282
- "node:events",
283
- "node:path",
284
- "node:process",
285
- "node:stream",
286
- "node:string_decoder",
287
- "node:util",
288
- "cloudflare:*"
289
- ],
290
- entryPoints: [entryPath],
291
- outfile: buildPath,
292
- allowOverwrite: true,
293
- format: "esm",
294
- bundle: true,
295
- minify: _config.vite?.build?.minify !== false,
296
- banner: {
297
- js: `globalThis.process = {
242
+ };`,
243
+ },
244
+ logOverride: {
245
+ 'ignored-bare-import': 'silent',
246
+ },
247
+ plugins: !args?.wasmModuleImports
248
+ ? []
249
+ : [rewriteWasmImportPath({ relativePathToAssets })],
250
+ });
251
+ }
252
+ const outputFiles = await glob(`**/*`, {
253
+ cwd: outputDir,
254
+ filesOnly: true,
255
+ });
256
+ // move the files into the functions folder
257
+ // & make sure the file names match Cloudflare syntax for routing
258
+ for (const outputFile of outputFiles) {
259
+ const path = outputFile.split(sep);
260
+ const finalSegments = path.map((segment) => segment
261
+ .replace(/(\_)(\w+)(\_)/g, (_, __, prop) => {
262
+ return `[${prop}]`;
263
+ })
264
+ .replace(/(\_\-\-\-)(\w+)(\_)/g, (_, __, prop) => {
265
+ return `[[${prop}]]`;
266
+ }));
267
+ finalSegments[finalSegments.length - 1] = finalSegments[finalSegments.length - 1]
268
+ .replace('entry.', '')
269
+ .replace(/(.*)\.(\w+)\.(\w+)$/g, (_, fileName, __, newExt) => {
270
+ return `${fileName}.${newExt}`;
271
+ });
272
+ const finalDirPath = finalSegments.slice(0, -1).join(sep);
273
+ const finalPath = finalSegments.join(sep);
274
+ const newDirUrl = new URL(finalDirPath, functionsUrl);
275
+ await fs.promises.mkdir(newDirUrl, { recursive: true });
276
+ const oldFileUrl = new URL(`$astro/${outputFile}`, outputUrl);
277
+ const newFileUrl = new URL(finalPath, functionsUrl);
278
+ await fs.promises.rename(oldFileUrl, newFileUrl);
279
+ }
280
+ }
281
+ else {
282
+ const entryPath = fileURLToPath(new URL(_buildConfig.serverEntry, _buildConfig.server));
283
+ const entryUrl = new URL(_buildConfig.serverEntry, _config.outDir);
284
+ const buildPath = fileURLToPath(entryUrl);
285
+ // A URL for the final build path after renaming
286
+ const finalBuildUrl = pathToFileURL(buildPath.replace(/\.mjs$/, '.js'));
287
+ await esbuild.build({
288
+ target: 'es2022',
289
+ platform: 'browser',
290
+ conditions: ['workerd', 'worker', 'browser'],
291
+ external: [
292
+ 'node:assert',
293
+ 'node:async_hooks',
294
+ 'node:buffer',
295
+ 'node:crypto',
296
+ 'node:diagnostics_channel',
297
+ 'node:events',
298
+ 'node:path',
299
+ 'node:process',
300
+ 'node:stream',
301
+ 'node:string_decoder',
302
+ 'node:util',
303
+ 'cloudflare:*',
304
+ ],
305
+ entryPoints: [entryPath],
306
+ outfile: buildPath,
307
+ allowOverwrite: true,
308
+ format: 'esm',
309
+ bundle: true,
310
+ minify: _config.vite?.build?.minify !== false,
311
+ banner: {
312
+ js: `globalThis.process = {
298
313
  argv: [],
299
314
  env: {},
300
- };`
301
- },
302
- logOverride: {
303
- "ignored-bare-import": "silent"
315
+ };`,
316
+ },
317
+ logOverride: {
318
+ 'ignored-bare-import': 'silent',
319
+ },
320
+ plugins: !args?.wasmModuleImports
321
+ ? []
322
+ : [
323
+ rewriteWasmImportPath({
324
+ relativePathToAssets: isModeDirectory
325
+ ? relative(fileURLToPath(functionsUrl), fileURLToPath(assetsUrl))
326
+ : relative(fileURLToPath(_buildConfig.client), fileURLToPath(assetsUrl)),
327
+ }),
328
+ ],
329
+ });
330
+ // Rename to worker.js
331
+ await fs.promises.rename(buildPath, finalBuildUrl);
332
+ if (isModeDirectory) {
333
+ const directoryUrl = new URL('[[path]].js', functionsUrl);
334
+ await fs.promises.rename(finalBuildUrl, directoryUrl);
335
+ }
336
+ }
337
+ // throw the server folder in the bin
338
+ const serverUrl = new URL(_buildConfig.server);
339
+ await fs.promises.rm(serverUrl, { recursive: true, force: true });
340
+ // move cloudflare specific files to the root
341
+ const cloudflareSpecialFiles = ['_headers', '_redirects', '_routes.json'];
342
+ if (_config.base !== '/') {
343
+ for (const file of cloudflareSpecialFiles) {
344
+ try {
345
+ await fs.promises.rename(new URL(file, _buildConfig.client), new URL(file, _config.outDir));
346
+ }
347
+ catch (e) {
348
+ // ignore
349
+ }
350
+ }
351
+ }
352
+ // Add also the worker file so it's excluded from the _routes.json generation
353
+ if (!isModeDirectory) {
354
+ cloudflareSpecialFiles.push('_worker.js');
355
+ }
356
+ const routesExists = await fs.promises
357
+ .stat(new URL('./_routes.json', _config.outDir))
358
+ .then((stat) => stat.isFile())
359
+ .catch(() => false);
360
+ // this creates a _routes.json, in case there is none present to enable
361
+ // cloudflare to handle static files and support _redirects configuration
362
+ if (!routesExists) {
363
+ /**
364
+ * These route types are candiates for being part of the `_routes.json` `include` array.
365
+ */
366
+ const potentialFunctionRouteTypes = ['endpoint', 'page'];
367
+ const functionEndpoints = routes
368
+ // Certain route types, when their prerender option is set to false, run on the server as function invocations
369
+ .filter((route) => potentialFunctionRouteTypes.includes(route.type) && !route.prerender)
370
+ .map((route) => {
371
+ const includePattern = '/' +
372
+ route.segments
373
+ .flat()
374
+ .map((segment) => (segment.dynamic ? '*' : segment.content))
375
+ .join('/');
376
+ const regexp = new RegExp('^\\/' +
377
+ route.segments
378
+ .flat()
379
+ .map((segment) => (segment.dynamic ? '(.*)' : segment.content))
380
+ .join('\\/') +
381
+ '$');
382
+ return {
383
+ includePattern,
384
+ regexp,
385
+ };
386
+ });
387
+ const staticPathList = (await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, {
388
+ cwd: fileURLToPath(_config.outDir),
389
+ filesOnly: true,
390
+ dot: true,
391
+ }))
392
+ .filter((file) => cloudflareSpecialFiles.indexOf(file) < 0)
393
+ .map((file) => `/${file.replace(/\\/g, '/')}`);
394
+ for (let page of pages) {
395
+ let pagePath = prependForwardSlash(page.pathname);
396
+ if (_config.base !== '/') {
397
+ const base = _config.base.endsWith('/') ? _config.base.slice(0, -1) : _config.base;
398
+ pagePath = `${base}${pagePath}`;
399
+ }
400
+ staticPathList.push(pagePath);
401
+ }
402
+ const redirectsExists = await fs.promises
403
+ .stat(new URL('./_redirects', _config.outDir))
404
+ .then((stat) => stat.isFile())
405
+ .catch(() => false);
406
+ // convert all redirect source paths into a list of routes
407
+ // and add them to the static path
408
+ if (redirectsExists) {
409
+ const redirects = (await fs.promises.readFile(new URL('./_redirects', _config.outDir), 'utf-8'))
410
+ .split(os.EOL)
411
+ .map((line) => {
412
+ const parts = line.split(' ');
413
+ if (parts.length < 2) {
414
+ return null;
415
+ }
416
+ else {
417
+ // convert /products/:id to /products/*
418
+ return (parts[0]
419
+ .replace(/\/:.*?(?=\/|$)/g, '/*')
420
+ // remove query params as they are not supported by cloudflare
421
+ .replace(/\?.*$/, ''));
422
+ }
423
+ })
424
+ .filter((line, index, arr) => line !== null && arr.indexOf(line) === index);
425
+ if (redirects.length > 0) {
426
+ staticPathList.push(...redirects);
427
+ }
428
+ }
429
+ const redirectRoutes = routes
430
+ .filter((r) => r.type === 'redirect')
431
+ .map((r) => {
432
+ return [r, ''];
433
+ });
434
+ const trueRedirects = createRedirectsFromAstroRoutes({
435
+ config: _config,
436
+ routeToDynamicTargetMap: new Map(Array.from(redirectRoutes)),
437
+ dir,
438
+ });
439
+ if (!trueRedirects.empty()) {
440
+ await fs.promises.appendFile(new URL('./_redirects', _config.outDir), trueRedirects.print());
441
+ }
442
+ staticPathList.push(...routes.filter((r) => r.type === 'redirect').map((r) => r.route));
443
+ const strategy = args?.routes?.strategy ?? 'auto';
444
+ // Strategy `include`: include all function endpoints, and then exclude static paths that would be matched by an include pattern
445
+ const includeStrategy = strategy === 'exclude'
446
+ ? undefined
447
+ : {
448
+ include: deduplicatePatterns(functionEndpoints
449
+ .map((endpoint) => endpoint.includePattern)
450
+ .concat(args?.routes?.include ?? [])),
451
+ exclude: deduplicatePatterns(staticPathList
452
+ .filter((file) => functionEndpoints.some((endpoint) => endpoint.regexp.test(file)))
453
+ .concat(args?.routes?.exclude ?? [])),
454
+ };
455
+ // Cloudflare requires at least one include pattern:
456
+ // https://developers.cloudflare.com/pages/platform/functions/routing/#limits
457
+ // So we add a pattern that we immediately exclude again
458
+ if (includeStrategy?.include.length === 0) {
459
+ includeStrategy.include = ['/'];
460
+ includeStrategy.exclude = ['/'];
461
+ }
462
+ // Strategy `exclude`: include everything, and then exclude all static paths
463
+ const excludeStrategy = strategy === 'include'
464
+ ? undefined
465
+ : {
466
+ include: ['/*'],
467
+ exclude: deduplicatePatterns(staticPathList.concat(args?.routes?.exclude ?? [])),
468
+ };
469
+ const includeStrategyLength = includeStrategy
470
+ ? includeStrategy.include.length + includeStrategy.exclude.length
471
+ : Infinity;
472
+ const excludeStrategyLength = excludeStrategy
473
+ ? excludeStrategy.include.length + excludeStrategy.exclude.length
474
+ : Infinity;
475
+ const winningStrategy = includeStrategyLength <= excludeStrategyLength ? includeStrategy : excludeStrategy;
476
+ await fs.promises.writeFile(new URL('./_routes.json', _config.outDir), JSON.stringify({
477
+ version: 1,
478
+ ...winningStrategy,
479
+ }, null, 2));
480
+ }
304
481
  },
305
- plugins: !args?.wasmModuleImports ? [] : [
306
- rewriteWasmImportPath({
307
- relativePathToAssets: isModeDirectory ? relative(fileURLToPath(functionsUrl), fileURLToPath(assetsUrl)) : relative(fileURLToPath(_buildConfig.client), fileURLToPath(assetsUrl))
308
- })
309
- ]
310
- });
311
- await fs.promises.rename(buildPath, finalBuildUrl);
312
- if (isModeDirectory) {
313
- const directoryUrl = new URL("[[path]].js", functionsUrl);
314
- await fs.promises.rename(finalBuildUrl, directoryUrl);
315
- }
316
- }
317
- const serverUrl = new URL(_buildConfig.server);
318
- await fs.promises.rm(serverUrl, { recursive: true, force: true });
319
- const cloudflareSpecialFiles = ["_headers", "_redirects", "_routes.json"];
320
- if (_config.base !== "/") {
321
- for (const file of cloudflareSpecialFiles) {
322
- try {
323
- await fs.promises.rename(
324
- new URL(file, _buildConfig.client),
325
- new URL(file, _config.outDir)
326
- );
327
- } catch (e) {
328
- }
329
- }
330
- }
331
- if (!isModeDirectory) {
332
- cloudflareSpecialFiles.push("_worker.js");
333
- }
334
- const routesExists = await fs.promises.stat(new URL("./_routes.json", _config.outDir)).then((stat) => stat.isFile()).catch(() => false);
335
- if (!routesExists) {
336
- const potentialFunctionRouteTypes = ["endpoint", "page"];
337
- const functionEndpoints = routes.filter((route) => potentialFunctionRouteTypes.includes(route.type) && !route.prerender).map((route) => {
338
- const includePattern = "/" + route.segments.flat().map((segment) => segment.dynamic ? "*" : segment.content).join("/");
339
- const regexp = new RegExp(
340
- "^\\/" + route.segments.flat().map((segment) => segment.dynamic ? "(.*)" : segment.content).join("\\/") + "$"
341
- );
342
- return {
343
- includePattern,
344
- regexp
345
- };
346
- });
347
- const staticPathList = (await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, {
348
- cwd: fileURLToPath(_config.outDir),
349
- filesOnly: true,
350
- dot: true
351
- })).filter((file) => cloudflareSpecialFiles.indexOf(file) < 0).map((file) => `/${file.replace(/\\/g, "/")}`);
352
- for (let page of pages) {
353
- let pagePath = prependForwardSlash(page.pathname);
354
- if (_config.base !== "/") {
355
- const base = _config.base.endsWith("/") ? _config.base.slice(0, -1) : _config.base;
356
- pagePath = `${base}${pagePath}`;
357
- }
358
- staticPathList.push(pagePath);
359
- }
360
- const redirectsExists = await fs.promises.stat(new URL("./_redirects", _config.outDir)).then((stat) => stat.isFile()).catch(() => false);
361
- if (redirectsExists) {
362
- const redirects = (await fs.promises.readFile(new URL("./_redirects", _config.outDir), "utf-8")).split(os.EOL).map((line) => {
363
- const parts = line.split(" ");
364
- if (parts.length < 2) {
365
- return null;
366
- } else {
367
- return parts[0].replace(/\/:.*?(?=\/|$)/g, "/*").replace(/\?.*$/, "");
368
- }
369
- }).filter(
370
- (line, index, arr) => line !== null && arr.indexOf(line) === index
371
- );
372
- if (redirects.length > 0) {
373
- staticPathList.push(...redirects);
374
- }
375
- }
376
- const redirectRoutes = routes.filter((r) => r.type === "redirect").map((r) => {
377
- return [r, ""];
378
- });
379
- const trueRedirects = createRedirectsFromAstroRoutes({
380
- config: _config,
381
- routeToDynamicTargetMap: new Map(Array.from(redirectRoutes)),
382
- dir
383
- });
384
- if (!trueRedirects.empty()) {
385
- await fs.promises.appendFile(
386
- new URL("./_redirects", _config.outDir),
387
- trueRedirects.print()
388
- );
389
- }
390
- staticPathList.push(...routes.filter((r) => r.type === "redirect").map((r) => r.route));
391
- const strategy = args?.routes?.strategy ?? "auto";
392
- const includeStrategy = strategy === "exclude" ? void 0 : {
393
- include: deduplicatePatterns(
394
- functionEndpoints.map((endpoint) => endpoint.includePattern).concat(args?.routes?.include ?? [])
395
- ),
396
- exclude: deduplicatePatterns(
397
- staticPathList.filter(
398
- (file) => functionEndpoints.some((endpoint) => endpoint.regexp.test(file))
399
- ).concat(args?.routes?.exclude ?? [])
400
- )
401
- };
402
- if (includeStrategy?.include.length === 0) {
403
- includeStrategy.include = ["/"];
404
- includeStrategy.exclude = ["/"];
405
- }
406
- const excludeStrategy = strategy === "include" ? void 0 : {
407
- include: ["/*"],
408
- exclude: deduplicatePatterns(staticPathList.concat(args?.routes?.exclude ?? []))
409
- };
410
- const includeStrategyLength = includeStrategy ? includeStrategy.include.length + includeStrategy.exclude.length : Infinity;
411
- const excludeStrategyLength = excludeStrategy ? excludeStrategy.include.length + excludeStrategy.exclude.length : Infinity;
412
- const winningStrategy = includeStrategyLength <= excludeStrategyLength ? includeStrategy : excludeStrategy;
413
- await fs.promises.writeFile(
414
- new URL("./_routes.json", _config.outDir),
415
- JSON.stringify(
416
- {
417
- version: 1,
418
- ...winningStrategy
419
- },
420
- null,
421
- 2
422
- )
423
- );
424
- }
425
- }
426
- }
427
- };
482
+ },
483
+ };
428
484
  }
429
- export {
430
- createIntegration as default
431
- };