@astrojs/cloudflare 9.2.0 → 10.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.
package/dist/index.js CHANGED
@@ -1,37 +1,24 @@
1
- import * as fs from 'node:fs';
2
- import * as os from 'node:os';
3
- import { dirname, relative, sep } from 'node:path';
4
- import { fileURLToPath, pathToFileURL } from 'node:url';
1
+ import { createReadStream } from 'node:fs';
2
+ import { appendFile, rename, stat } from 'node:fs/promises';
3
+ import { createInterface } from 'node:readline/promises';
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 esbuild from 'esbuild';
8
- import glob from 'tiny-glob';
9
- import { getAdapter } from './getAdapter.js';
10
- import { deduplicatePatterns } from './utils/deduplicatePatterns.js';
11
- import { prepareImageConfig } from './utils/image-config.js';
12
- import { getLocalRuntime, getRuntimeConfig } from './utils/local-runtime.js';
13
- import { prependForwardSlash } from './utils/prependForwardSlash.js';
14
- import { rewriteWasmImportPath } from './utils/rewriteWasmImportPath.js';
15
- import { patchSharpBundle } from './utils/sharpBundlePatch.js';
7
+ import { getPlatformProxy } from 'wrangler';
8
+ import { createRoutesFile, getParts } from './utils/generate-routes-json.js';
9
+ import { setImageConfig } from './utils/image-config.js';
16
10
  import { wasmModuleLoader } from './utils/wasm-module-loader.js';
17
11
  export default function createIntegration(args) {
18
12
  let _config;
19
- let _buildConfig;
20
- let _localRuntime;
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 = getRuntimeConfig(args?.runtime);
26
13
  return {
27
14
  name: '@astrojs/cloudflare',
28
15
  hooks: {
29
16
  'astro:config:setup': ({ command, config, updateConfig, logger }) => {
30
17
  updateConfig({
31
18
  build: {
32
- client: new URL(`.${config.base}`, config.outDir),
33
- server: new URL(`.${SERVER_BUILD_FOLDER}`, config.outDir),
34
- serverEntry: '_worker.mjs',
19
+ client: new URL(`.${prependForwardSlash(appendForwardSlash(config.base))}`, config.outDir),
20
+ server: new URL('./_worker.js/', config.outDir),
21
+ serverEntry: 'index.js',
35
22
  redirects: false,
36
23
  },
37
24
  vite: {
@@ -39,57 +26,59 @@ export default function createIntegration(args) {
39
26
  plugins: [
40
27
  wasmModuleLoader({
41
28
  disabled: !args?.wasmModuleImports,
42
- assetsDirectory: config.build.assets,
43
29
  }),
44
30
  ],
45
31
  },
46
- image: prepareImageConfig(args?.imageService ?? 'DEFAULT', config.image, command, logger),
32
+ image: setImageConfig(args?.imageService ?? 'DEFAULT', config.image, command, logger),
47
33
  });
48
34
  },
49
35
  'astro:config:done': ({ setAdapter, config }) => {
50
- setAdapter(getAdapter({ isModeDirectory, functionPerRoute }));
51
36
  _config = config;
52
- _buildConfig = config.build;
53
- if (_config.output === 'static') {
37
+ if (config.output === 'static') {
54
38
  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.');
55
39
  }
56
- if (_config.base === SERVER_BUILD_FOLDER) {
57
- throw new AstroError('[@astrojs/cloudflare] `base: "${SERVER_BUILD_FOLDER}"` is not allowed. Please change your `base` config to something else.');
58
- }
40
+ setAdapter({
41
+ name: '@astrojs/cloudflare',
42
+ serverEntrypoint: '@astrojs/cloudflare/entrypoints/server.advanced.js',
43
+ exports: ['default'],
44
+ adapterFeatures: {
45
+ functionPerRoute: false,
46
+ edgeMiddleware: false,
47
+ },
48
+ supportedAstroFeatures: {
49
+ serverOutput: 'stable',
50
+ hybridOutput: 'stable',
51
+ staticOutput: 'unsupported',
52
+ i18nDomains: 'experimental',
53
+ assets: {
54
+ supportKind: 'stable',
55
+ isSharpCompatible: false,
56
+ isSquooshCompatible: false,
57
+ },
58
+ },
59
+ });
59
60
  },
60
- 'astro:server:setup': ({ server, logger }) => {
61
- if (runtimeMode.mode === 'local') {
61
+ 'astro:server:setup': async ({ server }) => {
62
+ if (args?.platformProxy?.enabled === true) {
63
+ const platformProxy = await getPlatformProxy({
64
+ configPath: args.platformProxy.configPath ?? 'wrangler.toml',
65
+ experimentalJsonConfig: args.platformProxy.experimentalJsonConfig ?? false,
66
+ persist: args.platformProxy.persist ?? true,
67
+ });
68
+ const clientLocalsSymbol = Symbol.for('astro.locals');
62
69
  server.middlewares.use(async function middleware(req, res, next) {
63
- _localRuntime = getLocalRuntime(_config, runtimeMode, logger);
64
- const bindings = await _localRuntime.getBindings();
65
- const secrets = await _localRuntime.getSecrets();
66
- const caches = await _localRuntime.getCaches();
67
- const cf = await _localRuntime.getCF();
68
- const clientLocalsSymbol = Symbol.for('astro.locals');
69
70
  Reflect.set(req, clientLocalsSymbol, {
70
71
  runtime: {
71
- env: {
72
- CF_PAGES_URL: `http://${req.headers.host}`,
73
- ...bindings,
74
- ...secrets,
75
- },
76
- cf: cf,
77
- caches: caches,
78
- waitUntil: (_promise) => {
79
- return;
80
- },
72
+ env: platformProxy.env,
73
+ cf: platformProxy.cf,
74
+ caches: platformProxy.caches,
75
+ ctx: platformProxy.ctx,
81
76
  },
82
77
  });
83
78
  next();
84
79
  });
85
80
  }
86
81
  },
87
- 'astro:server:done': async ({ logger }) => {
88
- if (_localRuntime) {
89
- logger.info('Cleaning up the local Cloudflare runtime.');
90
- await _localRuntime.dispose();
91
- }
92
- },
93
82
  'astro:build:setup': ({ vite, target }) => {
94
83
  if (target === 'server') {
95
84
  vite.resolve ||= {};
@@ -99,6 +88,14 @@ export default function createIntegration(args) {
99
88
  find: 'react-dom/server',
100
89
  replacement: 'react-dom/server.browser',
101
90
  },
91
+ {
92
+ find: 'solid-js/web',
93
+ replacement: 'solid-js/web/dist/server',
94
+ },
95
+ {
96
+ find: 'solid-js',
97
+ replacement: 'solid-js/dist/server',
98
+ },
102
99
  ];
103
100
  if (Array.isArray(vite.resolve.alias)) {
104
101
  vite.resolve.alias = [...vite.resolve.alias, ...aliases];
@@ -110,345 +107,97 @@ export default function createIntegration(args) {
110
107
  }
111
108
  vite.ssr ||= {};
112
109
  vite.ssr.target = 'webworker';
110
+ vite.ssr.noExternal = true;
111
+ vite.ssr.external = _config.vite.ssr?.external ?? [];
112
+ vite.build ||= {};
113
+ vite.build.rollupOptions ||= {};
114
+ vite.build.rollupOptions.output ||= {};
115
+ // @ts-expect-error
116
+ vite.build.rollupOptions.output.banner ||=
117
+ 'globalThis.process ??= {}; globalThis.process.env ??= {};';
118
+ vite.build.rollupOptions.external = _config.vite.build?.rollupOptions?.external ?? [];
113
119
  // Cloudflare env is only available per request. This isn't feasible for code that access env vars
114
- // in a global way, so we shim their access as `process.env.*`. We will populate `process.env` later
115
- // in its fetch handler.
120
+ // 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`
116
121
  vite.define = {
117
122
  'process.env': 'process.env',
118
123
  ...vite.define,
119
124
  };
120
125
  }
121
126
  },
122
- 'astro:build:ssr': ({ entryPoints }) => {
123
- _entryPoints = entryPoints;
124
- },
125
- 'astro:build:done': async ({ pages, routes, dir }) => {
126
- const functionsUrl = new URL('functions/', _config.root);
127
- const assetsUrl = new URL(_buildConfig.assets, _buildConfig.client);
128
- if (isModeDirectory) {
129
- await fs.promises.mkdir(functionsUrl, { recursive: true });
130
- }
131
- if (isModeDirectory && functionPerRoute) {
132
- const entryPointsURL = [..._entryPoints.values()];
133
- const entryPaths = entryPointsURL.map((entry) => fileURLToPath(entry));
134
- const outputUrl = new URL('$astro', _buildConfig.server);
135
- const outputDir = fileURLToPath(outputUrl);
136
- //
137
- // Sadly, when wasmModuleImports is enabled, this needs to build esbuild for each depth of routes/entrypoints
138
- // independently so that relative import paths to the assets are the correct depth of '../' traversals
139
- // This is inefficient, so wasmModuleImports is opt-in. This could potentially be improved in the future by
140
- // taking advantage of the esbuild "onEnd" hook to rewrite import code per entry point relative to where the final
141
- // destination of the entrypoint is
142
- const entryPathsGroupedByDepth = !args.wasmModuleImports
143
- ? [entryPaths]
144
- : entryPaths
145
- .reduce((sum, thisPath) => {
146
- const depthFromRoot = thisPath.split(sep).length;
147
- sum.set(depthFromRoot, (sum.get(depthFromRoot) || []).concat(thisPath));
148
- return sum;
149
- }, new Map())
150
- .values();
151
- for (const pathsGroup of entryPathsGroupedByDepth) {
152
- // for some reason this exports to "entry.pages" on windows instead of "pages" on unix environments.
153
- // This deduces the name of the "pages" build directory
154
- const pagesDirname = relative(fileURLToPath(_buildConfig.server), pathsGroup[0]).split(sep)[0];
155
- const absolutePagesDirname = fileURLToPath(new URL(pagesDirname, _buildConfig.server));
156
- const urlWithinFunctions = new URL(relative(absolutePagesDirname, pathsGroup[0]), functionsUrl);
157
- const esbuildPlugins = [];
158
- if (args?.imageService === 'compile') {
159
- esbuildPlugins.push(patchSharpBundle());
160
- }
161
- const relativePathToAssets = relative(dirname(fileURLToPath(urlWithinFunctions)), fileURLToPath(assetsUrl));
162
- if (args?.wasmModuleImports) {
163
- esbuildPlugins.push(rewriteWasmImportPath({ relativePathToAssets }));
164
- }
165
- await esbuild.build({
166
- target: 'es2022',
167
- platform: 'browser',
168
- conditions: ['workerd', 'worker', 'browser'],
169
- external: [
170
- 'node:assert',
171
- 'node:async_hooks',
172
- 'node:buffer',
173
- 'node:crypto',
174
- 'node:diagnostics_channel',
175
- 'node:events',
176
- 'node:path',
177
- 'node:process',
178
- 'node:stream',
179
- 'node:string_decoder',
180
- 'node:util',
181
- 'cloudflare:*',
182
- ],
183
- entryPoints: pathsGroup,
184
- outbase: absolutePagesDirname,
185
- outdir: outputDir,
186
- allowOverwrite: true,
187
- format: 'esm',
188
- bundle: true,
189
- minify: _config.vite?.build?.minify !== false,
190
- banner: {
191
- js: `globalThis.process = {
192
- argv: [],
193
- env: {},
194
- };`,
195
- },
196
- logOverride: {
197
- 'ignored-bare-import': 'silent',
198
- },
199
- plugins: esbuildPlugins,
200
- });
201
- }
202
- const outputFiles = await glob('**/*', {
203
- cwd: outputDir,
204
- filesOnly: true,
205
- });
206
- // move the files into the functions folder
207
- // & make sure the file names match Cloudflare syntax for routing
208
- for (const outputFile of outputFiles) {
209
- const path = outputFile.split(sep);
210
- const finalSegments = path.map((segment) => segment
211
- .replace(/(\_)(\w+)(\_)/g, (_, __, prop) => {
212
- return `[${prop}]`;
213
- })
214
- .replace(/(\_\-\-\-)(\w+)(\_)/g, (_, __, prop) => {
215
- return `[[${prop}]]`;
216
- }));
217
- finalSegments[finalSegments.length - 1] = finalSegments[finalSegments.length - 1]
218
- .replace('entry.', '')
219
- .replace(/(.*)\.(\w+)\.(\w+)$/g, (_, fileName, __, newExt) => {
220
- return `${fileName}.${newExt}`;
221
- });
222
- const finalDirPath = finalSegments.slice(0, -1).join(sep);
223
- const finalPath = finalSegments.join(sep);
224
- const newDirUrl = new URL(finalDirPath, functionsUrl);
225
- await fs.promises.mkdir(newDirUrl, { recursive: true });
226
- const oldFileUrl = new URL(`$astro/${outputFile}`, outputUrl);
227
- const newFileUrl = new URL(finalPath, functionsUrl);
228
- await fs.promises.rename(oldFileUrl, newFileUrl);
229
- }
230
- }
231
- else {
232
- const entryPath = fileURLToPath(new URL(_buildConfig.serverEntry, _buildConfig.server));
233
- const entryUrl = new URL(_buildConfig.serverEntry, _config.outDir);
234
- const buildPath = fileURLToPath(entryUrl);
235
- // A URL for the final build path after renaming
236
- const finalBuildUrl = pathToFileURL(buildPath.replace(/\.mjs$/, '.js'));
237
- const esbuildPlugins = [];
238
- if (args?.imageService === 'compile') {
239
- esbuildPlugins.push(patchSharpBundle());
240
- }
241
- if (args?.wasmModuleImports) {
242
- esbuildPlugins.push(rewriteWasmImportPath({
243
- relativePathToAssets: isModeDirectory
244
- ? relative(fileURLToPath(functionsUrl), fileURLToPath(assetsUrl))
245
- : relative(fileURLToPath(_buildConfig.client), fileURLToPath(assetsUrl)),
246
- }));
247
- }
248
- await esbuild.build({
249
- target: 'es2022',
250
- platform: 'browser',
251
- conditions: ['workerd', 'worker', 'browser'],
252
- external: [
253
- 'node:assert',
254
- 'node:async_hooks',
255
- 'node:buffer',
256
- 'node:crypto',
257
- 'node:diagnostics_channel',
258
- 'node:events',
259
- 'node:path',
260
- 'node:process',
261
- 'node:stream',
262
- 'node:string_decoder',
263
- 'node:util',
264
- 'cloudflare:*',
265
- ],
266
- entryPoints: [entryPath],
267
- outfile: buildPath,
268
- allowOverwrite: true,
269
- format: 'esm',
270
- bundle: true,
271
- minify: _config.vite?.build?.minify !== false,
272
- banner: {
273
- js: `globalThis.process = {
274
- argv: [],
275
- env: {},
276
- };`,
277
- },
278
- logOverride: {
279
- 'ignored-bare-import': 'silent',
280
- },
281
- plugins: esbuildPlugins,
282
- });
283
- // Rename to worker.js
284
- await fs.promises.rename(buildPath, finalBuildUrl);
285
- if (isModeDirectory) {
286
- const directoryUrl = new URL('[[path]].js', functionsUrl);
287
- await fs.promises.rename(finalBuildUrl, directoryUrl);
288
- }
289
- }
290
- // throw the server folder in the bin
291
- const serverUrl = new URL(_buildConfig.server);
292
- await fs.promises.rm(serverUrl, { recursive: true, force: true });
293
- // move cloudflare specific files to the root
294
- const cloudflareSpecialFiles = ['_headers', '_redirects', '_routes.json'];
127
+ 'astro:build:done': async ({ pages, routes, dir, logger }) => {
128
+ const PLATFORM_FILES = ['_headers', '_redirects', '_routes.json'];
295
129
  if (_config.base !== '/') {
296
- for (const file of cloudflareSpecialFiles) {
130
+ for (const file of PLATFORM_FILES) {
297
131
  try {
298
- await fs.promises.rename(new URL(file, _buildConfig.client), new URL(file, _config.outDir));
132
+ await rename(new URL(file, _config.build.client), new URL(file, _config.outDir));
299
133
  }
300
134
  catch (e) {
301
- // ignore
135
+ logger.error(`There was an error moving ${file} to the root of the output directory.`);
302
136
  }
303
137
  }
304
138
  }
305
- // Add also the worker file so it's excluded from the _routes.json generation
306
- if (!isModeDirectory) {
307
- cloudflareSpecialFiles.push('_worker.js');
139
+ let redirectsExists = false;
140
+ try {
141
+ const redirectsStat = await stat(new URL('./_redirects', _config.outDir));
142
+ if (redirectsStat.isFile()) {
143
+ redirectsExists = true;
144
+ }
308
145
  }
309
- const routesExists = await fs.promises
310
- .stat(new URL('./_routes.json', _config.outDir))
311
- .then((stat) => stat.isFile())
312
- .catch(() => false);
313
- // this creates a _routes.json, in case there is none present to enable
314
- // cloudflare to handle static files and support _redirects configuration
315
- if (!routesExists) {
316
- /**
317
- * These route types are candiates for being part of the `_routes.json` `include` array.
318
- */
319
- let notFoundIsSSR = false;
320
- const potentialFunctionRouteTypes = ['endpoint', 'page'];
321
- const functionEndpoints = routes
322
- // Certain route types, when their prerender option is set to false, run on the server as function invocations
323
- .filter((route) => potentialFunctionRouteTypes.includes(route.type) && !route.prerender)
324
- .map((route) => {
325
- if (route.component === 'src/pages/404.astro' && route.prerender === false)
326
- notFoundIsSSR = true;
327
- const includePattern = `/${route.segments
328
- .flat()
329
- .map((segment) => (segment.dynamic ? '*' : segment.content))
330
- .join('/')}`;
331
- const regexp = new RegExp(`^\\/${route.segments
332
- .flat()
333
- .map((segment) => (segment.dynamic ? '(.*)' : segment.content))
334
- .join('\\/')}$`);
335
- return {
336
- includePattern,
337
- regexp,
338
- };
146
+ catch (error) {
147
+ redirectsExists = false;
148
+ }
149
+ const redirects = [];
150
+ if (redirectsExists) {
151
+ const rl = createInterface({
152
+ input: createReadStream(new URL('./_redirects', _config.outDir)),
153
+ crlfDelay: Number.POSITIVE_INFINITY,
339
154
  });
340
- const staticPathList = (await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, {
341
- cwd: fileURLToPath(_config.outDir),
342
- filesOnly: true,
343
- dot: true,
344
- }))
345
- .filter((file) => cloudflareSpecialFiles.indexOf(file) < 0)
346
- .map((file) => `/${file.replace(/\\/g, '/')}`);
347
- for (const page of pages) {
348
- let pagePath = prependForwardSlash(page.pathname);
349
- if (_config.base !== '/') {
350
- const base = _config.base.endsWith('/') ? _config.base.slice(0, -1) : _config.base;
351
- pagePath = `${base}${pagePath}`;
155
+ for await (const line of rl) {
156
+ const parts = line.split(' ');
157
+ if (parts.length >= 2) {
158
+ const p = removeLeadingForwardSlash(parts[0])
159
+ .split('/')
160
+ .filter(Boolean)
161
+ .map((s) => {
162
+ const syntax = s
163
+ .replace(/\/:.*?(?=\/|$)/g, '/*')
164
+ // remove query params as they are not supported by cloudflare
165
+ .replace(/\?.*$/, '');
166
+ return getParts(syntax);
167
+ });
168
+ redirects.push(p);
352
169
  }
353
- staticPathList.push(pagePath);
354
170
  }
355
- const redirectsExists = await fs.promises
356
- .stat(new URL('./_redirects', _config.outDir))
357
- .then((stat) => stat.isFile())
358
- .catch(() => false);
359
- // convert all redirect source paths into a list of routes
360
- // and add them to the static path
361
- if (redirectsExists) {
362
- const redirects = (await fs.promises.readFile(new URL('./_redirects', _config.outDir), 'utf-8'))
363
- .split(os.EOL)
364
- .map((line) => {
365
- const parts = line.split(' ');
366
- if (parts.length < 2) {
367
- return null;
368
- }
369
- // convert /products/:id to /products/*
370
- return (parts[0]
371
- .replace(/\/:.*?(?=\/|$)/g, '/*')
372
- // remove query params as they are not supported by cloudflare
373
- .replace(/\?.*$/, ''));
374
- })
375
- .filter((line, index, arr) => line !== null && arr.indexOf(line) === index);
376
- if (redirects.length > 0) {
377
- staticPathList.push(...redirects);
378
- }
379
- }
380
- const redirectRoutes = routes
381
- .filter((r) => r.type === 'redirect')
382
- .map((r) => {
383
- return [r, ''];
384
- });
385
- const trueRedirects = createRedirectsFromAstroRoutes({
386
- config: _config,
387
- routeToDynamicTargetMap: new Map(Array.from(redirectRoutes)),
388
- dir,
389
- });
390
- if (!trueRedirects.empty()) {
391
- await fs.promises.appendFile(new URL('./_redirects', _config.outDir), trueRedirects.print());
171
+ }
172
+ let routesExists = false;
173
+ try {
174
+ const routesStat = await stat(new URL('./_routes.json', _config.outDir));
175
+ if (routesStat.isFile()) {
176
+ routesExists = true;
392
177
  }
393
- staticPathList.push(...routes.filter((r) => r.type === 'redirect').map((r) => r.route));
394
- const strategy = args?.routes?.strategy ?? 'auto';
395
- // Strategy `include`: include all function endpoints, and then exclude static paths that would be matched by an include pattern
396
- const includeStrategy = strategy === 'exclude'
397
- ? undefined
398
- : {
399
- include: deduplicatePatterns(functionEndpoints
400
- .map((endpoint) => endpoint.includePattern)
401
- .concat(args?.routes?.include ?? [])),
402
- exclude: deduplicatePatterns(staticPathList
403
- .filter((file) => functionEndpoints.some((endpoint) => endpoint.regexp.test(file)))
404
- .concat(args?.routes?.exclude ?? [])),
405
- };
406
- // Cloudflare requires at least one include pattern:
407
- // https://developers.cloudflare.com/pages/platform/functions/routing/#limits
408
- // So we add a pattern that we immediately exclude again
409
- if (includeStrategy?.include.length === 0) {
410
- includeStrategy.include = ['/'];
411
- includeStrategy.exclude = ['/'];
178
+ }
179
+ catch (error) {
180
+ routesExists = false;
181
+ }
182
+ if (!routesExists) {
183
+ await createRoutesFile(_config, logger, routes, pages, redirects, args?.routes?.extend?.include, args?.routes?.extend?.exclude);
184
+ }
185
+ const redirectRoutes = [];
186
+ for (const route of routes) {
187
+ if (route.type === 'redirect')
188
+ redirectRoutes.push([route, '']);
189
+ }
190
+ const trueRedirects = createRedirectsFromAstroRoutes({
191
+ config: _config,
192
+ routeToDynamicTargetMap: new Map(Array.from(redirectRoutes)),
193
+ dir,
194
+ });
195
+ if (!trueRedirects.empty()) {
196
+ try {
197
+ await appendFile(new URL('./_redirects', _config.outDir), trueRedirects.print());
412
198
  }
413
- // Strategy `exclude`: include everything, and then exclude all static paths
414
- const excludeStrategy = strategy === 'include'
415
- ? undefined
416
- : {
417
- include: ['/*'],
418
- exclude: deduplicatePatterns(staticPathList.concat(args?.routes?.exclude ?? [])),
419
- };
420
- switch (args?.routes?.strategy) {
421
- case 'include':
422
- await fs.promises.writeFile(new URL('./_routes.json', _config.outDir), JSON.stringify({
423
- version: 1,
424
- ...includeStrategy,
425
- }, null, 2));
426
- break;
427
- case 'exclude':
428
- await fs.promises.writeFile(new URL('./_routes.json', _config.outDir), JSON.stringify({
429
- version: 1,
430
- ...excludeStrategy,
431
- }, null, 2));
432
- break;
433
- default:
434
- {
435
- const includeStrategyLength = includeStrategy
436
- ? includeStrategy.include.length + includeStrategy.exclude.length
437
- : Number.POSITIVE_INFINITY;
438
- const excludeStrategyLength = excludeStrategy
439
- ? excludeStrategy.include.length + excludeStrategy.exclude.length
440
- : Number.POSITIVE_INFINITY;
441
- const winningStrategy = notFoundIsSSR
442
- ? excludeStrategy
443
- : includeStrategyLength <= excludeStrategyLength
444
- ? includeStrategy
445
- : excludeStrategy;
446
- await fs.promises.writeFile(new URL('./_routes.json', _config.outDir), JSON.stringify({
447
- version: 1,
448
- ...winningStrategy,
449
- }, null, 2));
450
- }
451
- break;
199
+ catch (error) {
200
+ logger.error('Failed to write _redirects file');
452
201
  }
453
202
  }
454
203
  },
@@ -1,6 +1,5 @@
1
1
  import type { AstroConfig, ImageMetadata, RemotePattern } from 'astro';
2
2
  export declare function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata;
3
- export declare function isRemotePath(src: string): boolean;
4
3
  export declare function matchHostname(url: URL, hostname?: string, allowWildcard?: boolean): boolean;
5
4
  export declare function matchPort(url: URL, port?: string): boolean;
6
5
  export declare function matchProtocol(url: URL, protocol?: string): boolean;
@@ -8,7 +7,3 @@ export declare function matchPathname(url: URL, pathname?: string, allowWildcard
8
7
  export declare function matchPattern(url: URL, remotePattern: RemotePattern): boolean;
9
8
  export declare function isRemoteAllowed(src: string, { domains, remotePatterns, }: Partial<Pick<AstroConfig['image'], 'domains' | 'remotePatterns'>>): boolean;
10
9
  export declare function isString(path: unknown): path is string;
11
- export declare function removeTrailingForwardSlash(path: string): string;
12
- export declare function removeLeadingForwardSlash(path: string): string;
13
- export declare function trimSlashes(path: string): string;
14
- export declare function joinPaths(...paths: (string | undefined)[]): string;
@@ -1,9 +1,7 @@
1
+ import { isRemotePath } from '@astrojs/internal-helpers/path';
1
2
  export function isESMImportedImage(src) {
2
3
  return typeof src === 'object';
3
4
  }
4
- export function isRemotePath(src) {
5
- return /^(http|ftp|https|ws):?\/\//.test(src) || src.startsWith('data:');
6
- }
7
5
  export function matchHostname(url, hostname, allowWildcard) {
8
6
  if (!hostname) {
9
7
  return true;
@@ -68,26 +66,3 @@ export function isRemoteAllowed(src, { domains = [], remotePatterns = [], }) {
68
66
  export function isString(path) {
69
67
  return typeof path === 'string' || path instanceof String;
70
68
  }
71
- export function removeTrailingForwardSlash(path) {
72
- return path.endsWith('/') ? path.slice(0, path.length - 1) : path;
73
- }
74
- export function removeLeadingForwardSlash(path) {
75
- return path.startsWith('/') ? path.substring(1) : path;
76
- }
77
- export function trimSlashes(path) {
78
- return path.replace(/^\/|\/$/g, '');
79
- }
80
- export function joinPaths(...paths) {
81
- return paths
82
- .filter(isString)
83
- .map((path, i) => {
84
- if (i === 0) {
85
- return removeTrailingForwardSlash(path);
86
- }
87
- if (i === paths.length - 1) {
88
- return removeLeadingForwardSlash(path);
89
- }
90
- return trimSlashes(path);
91
- })
92
- .join('/');
93
- }
@@ -0,0 +1,9 @@
1
+ import type { AstroConfig, AstroIntegrationLogger, RouteData, RoutePart } from 'astro';
2
+ export declare function getParts(part: string): RoutePart[];
3
+ export declare function createRoutesFile(_config: AstroConfig, logger: AstroIntegrationLogger, routes: RouteData[], pages: {
4
+ pathname: string;
5
+ }[], redirects: RouteData['segments'][], includeExtends: {
6
+ pattern: string;
7
+ }[] | undefined, excludeExtends: {
8
+ pattern: string;
9
+ }[] | undefined): Promise<void>;