@astrojs/cloudflare 14.0.0-alpha.0 → 14.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/entrypoints/image-passthrough-endpoint.js +5 -1
- package/dist/entrypoints/preview.js +2 -2
- package/dist/esbuild-plugin-astro-frontmatter.js +9 -2
- package/dist/fetch.d.ts +12 -0
- package/dist/fetch.js +37 -0
- package/dist/hono.d.ts +27 -0
- package/dist/hono.js +21 -0
- package/dist/index.js +116 -21
- package/dist/utils/cf-helpers.d.ts +33 -0
- package/dist/utils/cf-helpers.js +63 -0
- package/dist/utils/cf.d.ts +8 -0
- package/dist/utils/cf.js +24 -0
- package/dist/utils/handler.d.ts +2 -4
- package/dist/utils/handler.js +17 -54
- package/dist/utils/headers.d.ts +26 -0
- package/dist/utils/headers.js +63 -0
- package/dist/utils/image-binding-transform.js +16 -3
- package/dist/utils/image-config.d.ts +2 -0
- package/dist/vite-plugin-prism.d.ts +2 -0
- package/dist/vite-plugin-prism.js +56 -0
- package/dist/wrangler.js +1 -5
- package/package.json +15 -12
package/README.md
CHANGED
|
@@ -34,5 +34,5 @@ Copyright (c) 2023–present [Astro][astro]
|
|
|
34
34
|
[coc]: https://github.com/withastro/.github/blob/main/CODE_OF_CONDUCT.md
|
|
35
35
|
[community]: https://github.com/withastro/.github/blob/main/COMMUNITY_GUIDE.md
|
|
36
36
|
[discord]: https://astro.build/chat/
|
|
37
|
-
[issues]: https://github.com/withastro/
|
|
37
|
+
[issues]: https://github.com/withastro/astro/issues
|
|
38
38
|
[astro-integration]: https://docs.astro.build/en/guides/integrations/
|
|
@@ -2,6 +2,7 @@ import { imageConfig } from "astro:assets";
|
|
|
2
2
|
import { isRemotePath } from "@astrojs/internal-helpers/path";
|
|
3
3
|
import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
|
|
4
4
|
import { env } from "cloudflare:workers";
|
|
5
|
+
import { fetchWithRedirects } from "astro/assets";
|
|
5
6
|
const prerender = false;
|
|
6
7
|
const GET = async ({ request }) => {
|
|
7
8
|
try {
|
|
@@ -14,7 +15,10 @@ const GET = async ({ request }) => {
|
|
|
14
15
|
if (!isRemoteAllowed(href, imageConfig)) {
|
|
15
16
|
return new Response("Forbidden", { status: 403 });
|
|
16
17
|
}
|
|
17
|
-
response = await
|
|
18
|
+
response = await fetchWithRedirects({
|
|
19
|
+
url: href,
|
|
20
|
+
imageConfig
|
|
21
|
+
});
|
|
18
22
|
} else {
|
|
19
23
|
const sourceUrl = new URL(href, url.origin);
|
|
20
24
|
if (sourceUrl.origin !== url.origin) {
|
|
@@ -41,7 +41,7 @@ const createPreviewServer = async ({
|
|
|
41
41
|
allowedHosts
|
|
42
42
|
},
|
|
43
43
|
plugins: [
|
|
44
|
-
cfVitePlugin({ ...globalThis.
|
|
44
|
+
cfVitePlugin({ ...globalThis.astroCloudflareConfig, viteEnvironment: { name: "ssr" } })
|
|
45
45
|
]
|
|
46
46
|
});
|
|
47
47
|
} catch (err) {
|
|
@@ -88,7 +88,7 @@ function serverStart({
|
|
|
88
88
|
host,
|
|
89
89
|
base
|
|
90
90
|
}) {
|
|
91
|
-
const version = "14.0.0-
|
|
91
|
+
const version = "14.0.0-beta.2";
|
|
92
92
|
const localPrefix = `${colors.dim("\u2503")} Local `;
|
|
93
93
|
const networkPrefix = `${colors.dim("\u2503")} Network `;
|
|
94
94
|
const emptyPrefix = " ".repeat(11);
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
3
|
+
const RETURN_REPLACE_RE = /(\/\/[^\n]*|\/\*[\s\S]*?\*\/|`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(?<!\.)\breturn(\s*;|\b)/g;
|
|
4
|
+
function replaceTopLevelReturns(code) {
|
|
5
|
+
return code.replace(RETURN_REPLACE_RE, (_match, skip, tail) => {
|
|
6
|
+
if (skip !== void 0) return skip;
|
|
7
|
+
return tail.trim() === ";" ? "throw 0;" : "throw ";
|
|
8
|
+
});
|
|
9
|
+
}
|
|
3
10
|
function astroFrontmatterScanPlugin() {
|
|
4
11
|
return {
|
|
5
12
|
name: "astro-frontmatter-scan",
|
|
6
13
|
setup(build) {
|
|
7
|
-
build.onLoad({ filter: /\.astro
|
|
14
|
+
build.onLoad({ filter: /\.astro$/, namespace: "file" }, async (args) => {
|
|
8
15
|
try {
|
|
9
16
|
const code = await readFile(args.path, "utf-8");
|
|
10
17
|
const frontmatterMatch = FRONTMATTER_RE.exec(code);
|
|
11
18
|
if (frontmatterMatch) {
|
|
12
|
-
const contents = frontmatterMatch[1]
|
|
19
|
+
const contents = replaceTopLevelReturns(frontmatterMatch[1]);
|
|
13
20
|
return {
|
|
14
21
|
contents: contents + "\nexport default {}",
|
|
15
22
|
loader: "ts"
|
package/dist/fetch.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FetchState } from 'astro/fetch';
|
|
2
|
+
/**
|
|
3
|
+
* Applies Cloudflare-specific setup to a `FetchState`:
|
|
4
|
+
* - Injects the SESSION KV binding
|
|
5
|
+
* - Serves static assets via the ASSETS binding
|
|
6
|
+
* - Sets `locals.cfContext`, client address, `waitUntil`, and error page fetch
|
|
7
|
+
*
|
|
8
|
+
* Returns a `Response` if the request was handled by the ASSETS binding
|
|
9
|
+
* (static file hit). Returns `undefined` when the caller should continue
|
|
10
|
+
* to Astro rendering.
|
|
11
|
+
*/
|
|
12
|
+
export declare function cf(state: FetchState, env: Env, ctx: ExecutionContext): Promise<Response | undefined>;
|
package/dist/fetch.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { env as globalEnv } from "cloudflare:workers";
|
|
2
|
+
import { createApp } from "astro/app/entrypoint";
|
|
3
|
+
import { setGetEnv } from "astro/env/setup";
|
|
4
|
+
import { createGetEnv } from "./utils/env.js";
|
|
5
|
+
import {
|
|
6
|
+
injectSessionBinding,
|
|
7
|
+
matchStaticAsset,
|
|
8
|
+
fallbackToAssets,
|
|
9
|
+
createErrorPageFetch,
|
|
10
|
+
createLocals,
|
|
11
|
+
getClientAddress
|
|
12
|
+
} from "./utils/cf.js";
|
|
13
|
+
let app;
|
|
14
|
+
function ensureInitialized() {
|
|
15
|
+
if (!app) {
|
|
16
|
+
setGetEnv(createGetEnv(globalEnv));
|
|
17
|
+
app = createApp();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function cf(state, env, ctx) {
|
|
21
|
+
ensureInitialized();
|
|
22
|
+
injectSessionBinding(app.manifest, env);
|
|
23
|
+
const staticAsset = matchStaticAsset(app.manifest, state.request.url, env);
|
|
24
|
+
if (staticAsset) return staticAsset;
|
|
25
|
+
if (!state.routeData) {
|
|
26
|
+
const asset = await fallbackToAssets(state.request.url, env);
|
|
27
|
+
if (asset) return asset;
|
|
28
|
+
}
|
|
29
|
+
Object.assign(state.locals, createLocals(ctx));
|
|
30
|
+
state.clientAddress = getClientAddress(state.request);
|
|
31
|
+
state.renderOptions.waitUntil = ctx.waitUntil.bind(ctx);
|
|
32
|
+
state.renderOptions.prerenderedErrorPageFetch = createErrorPageFetch(env);
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
cf
|
|
37
|
+
};
|
package/dist/hono.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duck-typed Hono context — matches Hono's `Context` shape for
|
|
3
|
+
* Cloudflare Workers without importing from `hono` at runtime.
|
|
4
|
+
*/
|
|
5
|
+
type HonoCloudflareContextLike = {
|
|
6
|
+
req: {
|
|
7
|
+
raw: Request;
|
|
8
|
+
};
|
|
9
|
+
env: Env;
|
|
10
|
+
executionCtx: ExecutionContext;
|
|
11
|
+
get?: (key: string) => unknown;
|
|
12
|
+
set?: (key: string, value: unknown) => void;
|
|
13
|
+
};
|
|
14
|
+
type HonoMiddlewareHandler = (context: HonoCloudflareContextLike, next: () => Promise<void>) => Promise<Response | void>;
|
|
15
|
+
/**
|
|
16
|
+
* Hono middleware that applies Cloudflare-specific setup.
|
|
17
|
+
*
|
|
18
|
+
* Reads `env` and `executionCtx` from the Hono context (provided
|
|
19
|
+
* automatically by Hono on Cloudflare Workers). Handles static assets
|
|
20
|
+
* via the ASSETS binding, injects the SESSION KV binding, and sets
|
|
21
|
+
* `locals.cfContext`, client address, `waitUntil`, and error page fetch.
|
|
22
|
+
*
|
|
23
|
+
* If the request matches a static asset, returns the asset response
|
|
24
|
+
* directly. Otherwise calls `next()` to continue the middleware chain.
|
|
25
|
+
*/
|
|
26
|
+
export declare function cf(): HonoMiddlewareHandler;
|
|
27
|
+
export {};
|
package/dist/hono.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FetchState } from "astro/fetch";
|
|
2
|
+
import { cf as cfFetch } from "./fetch.js";
|
|
3
|
+
const FETCH_STATE_KEY = "fetchState";
|
|
4
|
+
function getFetchState(context) {
|
|
5
|
+
const state = context.get?.(FETCH_STATE_KEY);
|
|
6
|
+
if (state) return state;
|
|
7
|
+
const nextState = new FetchState(context.req.raw);
|
|
8
|
+
context.set?.(FETCH_STATE_KEY, nextState);
|
|
9
|
+
return nextState;
|
|
10
|
+
}
|
|
11
|
+
function cf() {
|
|
12
|
+
return async (context, next) => {
|
|
13
|
+
const state = getFetchState(context);
|
|
14
|
+
const asset = await cfFetch(state, context.env, context.executionCtx);
|
|
15
|
+
if (asset) return asset;
|
|
16
|
+
await next();
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
cf
|
|
21
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { createReadStream, existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { appendFile, stat } from "node:fs/promises";
|
|
2
|
+
import { appendFile, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { relative } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { normalizePath } from "vite";
|
|
3
6
|
import { createInterface } from "node:readline/promises";
|
|
4
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
removeLeadingForwardSlash,
|
|
9
|
+
removeTrailingForwardSlash
|
|
10
|
+
} from "@astrojs/internal-helpers/path";
|
|
5
11
|
import { createRedirectsFromAstroRoutes, printAsRedirects } from "@astrojs/underscore-redirects";
|
|
6
12
|
import { cloudflare as cfVitePlugin } from "@cloudflare/vite-plugin";
|
|
7
13
|
import { astroFrontmatterScanPlugin } from "./esbuild-plugin-astro-frontmatter.js";
|
|
8
14
|
import { getParts } from "./utils/generate-routes-json.js";
|
|
15
|
+
import { buildAssetsHeadersContent } from "./utils/headers.js";
|
|
9
16
|
import {
|
|
10
17
|
normalizeImageServiceConfig,
|
|
11
18
|
setImageConfig
|
|
@@ -20,6 +27,7 @@ import {
|
|
|
20
27
|
import { parseEnv } from "node:util";
|
|
21
28
|
import { sessionDrivers } from "astro/config";
|
|
22
29
|
import { createCloudflarePrerenderer } from "./prerenderer.js";
|
|
30
|
+
import cfPrismPlugin from "./vite-plugin-prism.js";
|
|
23
31
|
const CLOUDFLARE_KV_SESSION_DRIVER_ENTRYPOINT = sessionDrivers.cloudflareKVBinding().entrypoint;
|
|
24
32
|
function usesCloudflareKVSessionDriver(session) {
|
|
25
33
|
const driver = session?.driver;
|
|
@@ -57,15 +65,16 @@ function createIntegration({
|
|
|
57
65
|
...cloudflareOptions
|
|
58
66
|
} = {}) {
|
|
59
67
|
let _config;
|
|
68
|
+
let _buildOutput;
|
|
69
|
+
let _originalClientDir;
|
|
60
70
|
let _routes;
|
|
61
|
-
let _isFullyStatic = false;
|
|
62
71
|
let cfPluginConfig;
|
|
63
72
|
const { buildService, runtimeService } = normalizeImageServiceConfig(imageService);
|
|
64
73
|
const needsImagesBinding = runtimeService === "cloudflare-binding";
|
|
65
74
|
return {
|
|
66
75
|
name: "@astrojs/cloudflare",
|
|
67
76
|
hooks: {
|
|
68
|
-
"astro:config:setup": ({ command, config, updateConfig, logger, addWatchFile }) => {
|
|
77
|
+
"astro:config:setup": async ({ command, config, updateConfig, logger, addWatchFile }) => {
|
|
69
78
|
if (!!process.versions.webcontainer) {
|
|
70
79
|
throw new Error("`workerd` does not run on Stackblitz.");
|
|
71
80
|
}
|
|
@@ -96,7 +105,7 @@ function createIntegration({
|
|
|
96
105
|
const needsImagesBindingForDev = isCompile && command === "dev";
|
|
97
106
|
const usesContentCollections = hasContentCollectionsConfig(config.srcDir);
|
|
98
107
|
const prebundleContentRuntime = command === "dev" && usesContentCollections;
|
|
99
|
-
|
|
108
|
+
const adapterPluginConfig = {
|
|
100
109
|
config: cloudflareConfigCustomizer({
|
|
101
110
|
needsSessionKVBinding,
|
|
102
111
|
sessionKVBindingName,
|
|
@@ -122,9 +131,17 @@ function createIntegration({
|
|
|
122
131
|
}
|
|
123
132
|
}
|
|
124
133
|
};
|
|
134
|
+
cfPluginConfig = { ...cloudflareOptions, ...adapterPluginConfig };
|
|
125
135
|
if (command === "preview") {
|
|
126
|
-
globalThis.
|
|
136
|
+
globalThis.astroCloudflareConfig = cfPluginConfig;
|
|
127
137
|
}
|
|
138
|
+
const prismFiles = [
|
|
139
|
+
"@astrojs/prism > prismjs",
|
|
140
|
+
"@astrojs/prism > prismjs/components.js",
|
|
141
|
+
"@astrojs/prism > prismjs/dependencies.js"
|
|
142
|
+
];
|
|
143
|
+
const isAstroPrismPackageInstalled = await getIsAstroPrismInstalled(config.root);
|
|
144
|
+
const userOptimizeDeps = config.vite?.optimizeDeps;
|
|
128
145
|
updateConfig({
|
|
129
146
|
build: {
|
|
130
147
|
redirects: false
|
|
@@ -134,9 +151,9 @@ function createIntegration({
|
|
|
134
151
|
plugins: [
|
|
135
152
|
...prerenderEnvironment === "node" && command === "dev" ? [createNodePrerenderPlugin()] : [],
|
|
136
153
|
cfVitePlugin({
|
|
137
|
-
...cloudflareOptions,
|
|
138
154
|
...cfPluginConfig,
|
|
139
|
-
viteEnvironment: { name: "ssr" }
|
|
155
|
+
viteEnvironment: { name: "ssr" },
|
|
156
|
+
assetsOnly: () => _buildOutput === "static"
|
|
140
157
|
}),
|
|
141
158
|
{
|
|
142
159
|
name: "@astrojs/cloudflare:cf-imports",
|
|
@@ -177,6 +194,9 @@ function createIntegration({
|
|
|
177
194
|
"astro > piccolore",
|
|
178
195
|
"astro > picomatch",
|
|
179
196
|
"astro/app",
|
|
197
|
+
"astro/app/fetch/default-handler",
|
|
198
|
+
"astro/fetch",
|
|
199
|
+
"astro/hono",
|
|
180
200
|
"astro/assets",
|
|
181
201
|
"astro/assets/runtime",
|
|
182
202
|
"astro/assets/utils/inferRemoteSize.js",
|
|
@@ -185,7 +205,9 @@ function createIntegration({
|
|
|
185
205
|
"astro/compiler-runtime",
|
|
186
206
|
"astro/jsx-runtime",
|
|
187
207
|
"astro/app/entrypoint/dev",
|
|
188
|
-
"astro/virtual-modules/middleware.js"
|
|
208
|
+
"astro/virtual-modules/middleware.js",
|
|
209
|
+
...isAstroPrismPackageInstalled ? prismFiles : [],
|
|
210
|
+
...Array.isArray(userOptimizeDeps?.include) ? userOptimizeDeps.include : []
|
|
189
211
|
],
|
|
190
212
|
exclude: [
|
|
191
213
|
"unstorage/drivers/cloudflare-kv-binding",
|
|
@@ -193,7 +215,8 @@ function createIntegration({
|
|
|
193
215
|
"virtual:astro:*",
|
|
194
216
|
"virtual:astro-cloudflare:*",
|
|
195
217
|
"virtual:@astrojs/*",
|
|
196
|
-
"@astrojs/starlight"
|
|
218
|
+
"@astrojs/starlight",
|
|
219
|
+
...Array.isArray(userOptimizeDeps?.exclude) ? userOptimizeDeps.exclude : []
|
|
197
220
|
],
|
|
198
221
|
esbuildOptions: {
|
|
199
222
|
// Suppress Vite's `createRequire(import.meta.url)` banner to work around
|
|
@@ -201,7 +224,8 @@ function createIntegration({
|
|
|
201
224
|
// incorrectly rewrites identifiers inside `import.meta` when an imported
|
|
202
225
|
// binding shares the same name (e.g. zod v4 exports `meta`).
|
|
203
226
|
banner: { js: "" },
|
|
204
|
-
plugins: [astroFrontmatterScanPlugin()]
|
|
227
|
+
plugins: [astroFrontmatterScanPlugin()],
|
|
228
|
+
...userOptimizeDeps?.esbuildOptions?.loader ? { loader: userOptimizeDeps.esbuildOptions.loader } : {}
|
|
205
229
|
}
|
|
206
230
|
}
|
|
207
231
|
};
|
|
@@ -237,7 +261,8 @@ function createIntegration({
|
|
|
237
261
|
imageServiceEntrypoint: "@astrojs/cloudflare/image-service-workerd",
|
|
238
262
|
buildAssets: config.build.assets ?? "_astro"
|
|
239
263
|
} : null
|
|
240
|
-
})
|
|
264
|
+
}),
|
|
265
|
+
cfPrismPlugin()
|
|
241
266
|
]
|
|
242
267
|
},
|
|
243
268
|
image: setImageConfig(imageService, config.image, command, logger)
|
|
@@ -251,11 +276,14 @@ function createIntegration({
|
|
|
251
276
|
},
|
|
252
277
|
"astro:routes:resolved": ({ routes }) => {
|
|
253
278
|
_routes = routes;
|
|
254
|
-
const nonInternalRoutes = routes.filter((route) => route.origin !== "internal");
|
|
255
|
-
_isFullyStatic = nonInternalRoutes.length > 0 && nonInternalRoutes.every((route) => route.isPrerendered);
|
|
256
279
|
},
|
|
257
|
-
"astro:config:done": ({ setAdapter, config, injectTypes, logger }) => {
|
|
280
|
+
"astro:config:done": ({ setAdapter, config, injectTypes, logger, buildOutput }) => {
|
|
258
281
|
_config = config;
|
|
282
|
+
_buildOutput = buildOutput;
|
|
283
|
+
_originalClientDir = new URL(config.build.client.href);
|
|
284
|
+
if (config.base !== "/") {
|
|
285
|
+
config.build.client = new URL("." + config.base + "/", config.build.client);
|
|
286
|
+
}
|
|
259
287
|
injectTypes({
|
|
260
288
|
filename: "cloudflare.d.ts",
|
|
261
289
|
content: '/// <reference types="@astrojs/cloudflare/types.d.ts" />'
|
|
@@ -263,9 +291,10 @@ function createIntegration({
|
|
|
263
291
|
setAdapter({
|
|
264
292
|
name: "@astrojs/cloudflare",
|
|
265
293
|
adapterFeatures: {
|
|
266
|
-
buildOutput
|
|
294
|
+
buildOutput,
|
|
267
295
|
middlewareMode: "classic",
|
|
268
|
-
preserveBuildClientDir: true
|
|
296
|
+
preserveBuildClientDir: true,
|
|
297
|
+
preserveBuildServerDir: true
|
|
269
298
|
},
|
|
270
299
|
entrypointResolution: "auto",
|
|
271
300
|
previewEntrypoint: "@astrojs/cloudflare/entrypoints/preview",
|
|
@@ -330,9 +359,65 @@ function createIntegration({
|
|
|
330
359
|
}
|
|
331
360
|
},
|
|
332
361
|
"astro:build:done": async ({ dir, logger, assets }) => {
|
|
362
|
+
if (_config.base !== "/") {
|
|
363
|
+
for (const file of [".assetsignore", "_headers"]) {
|
|
364
|
+
try {
|
|
365
|
+
await rename(
|
|
366
|
+
new URL(`./${file}`, _config.build.client),
|
|
367
|
+
new URL(`./${file}`, _originalClientDir)
|
|
368
|
+
);
|
|
369
|
+
} catch {
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
try {
|
|
373
|
+
const wranglerJsonUrl = new URL("./wrangler.json", _config.build.server);
|
|
374
|
+
const raw = await readFile(wranglerJsonUrl, "utf-8");
|
|
375
|
+
const wranglerConfig = JSON.parse(raw);
|
|
376
|
+
if (wranglerConfig.assets?.directory) {
|
|
377
|
+
wranglerConfig.assets.directory = normalizePath(
|
|
378
|
+
relative(fileURLToPath(_config.build.server), fileURLToPath(_originalClientDir))
|
|
379
|
+
);
|
|
380
|
+
await writeFile(wranglerJsonUrl, JSON.stringify(wranglerConfig));
|
|
381
|
+
}
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (_config.build.assetsPrefix) {
|
|
386
|
+
logger.debug(
|
|
387
|
+
"Skipping Cache-Control injection for assets \u2014 `build.assetsPrefix` is set, so assets are served from a different origin."
|
|
388
|
+
);
|
|
389
|
+
} else {
|
|
390
|
+
const headersPath = new URL("./_headers", _originalClientDir);
|
|
391
|
+
const result = await buildAssetsHeadersContent(
|
|
392
|
+
{
|
|
393
|
+
assetsDir: _config.build.assets,
|
|
394
|
+
basePrefix: removeTrailingForwardSlash(_config.base),
|
|
395
|
+
headersPath
|
|
396
|
+
},
|
|
397
|
+
(path) => readFile(path, "utf-8")
|
|
398
|
+
);
|
|
399
|
+
if (result === null) {
|
|
400
|
+
logger.debug(
|
|
401
|
+
`Skipping Cache-Control injection \u2014 _headers already sets Cache-Control on a matching rule.`
|
|
402
|
+
);
|
|
403
|
+
} else {
|
|
404
|
+
const tempPath = new URL("./_headers.tmp", _originalClientDir);
|
|
405
|
+
try {
|
|
406
|
+
await writeFile(tempPath, result.content);
|
|
407
|
+
await rename(tempPath, headersPath);
|
|
408
|
+
} catch (err) {
|
|
409
|
+
await unlink(tempPath).catch(() => {
|
|
410
|
+
});
|
|
411
|
+
throw err;
|
|
412
|
+
}
|
|
413
|
+
logger.info(
|
|
414
|
+
`Injected immutable Cache-Control for ${result.assetsPattern} into _headers.`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
333
418
|
let redirectsExists = false;
|
|
334
419
|
try {
|
|
335
|
-
const redirectsStat = await stat(new URL("./_redirects",
|
|
420
|
+
const redirectsStat = await stat(new URL("./_redirects", _originalClientDir));
|
|
336
421
|
if (redirectsStat.isFile()) {
|
|
337
422
|
redirectsExists = true;
|
|
338
423
|
}
|
|
@@ -342,7 +427,7 @@ function createIntegration({
|
|
|
342
427
|
const redirects = [];
|
|
343
428
|
if (redirectsExists) {
|
|
344
429
|
const rl = createInterface({
|
|
345
|
-
input: createReadStream(new URL("./_redirects",
|
|
430
|
+
input: createReadStream(new URL("./_redirects", _originalClientDir)),
|
|
346
431
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
347
432
|
});
|
|
348
433
|
for await (const line of rl) {
|
|
@@ -364,13 +449,13 @@ function createIntegration({
|
|
|
364
449
|
)
|
|
365
450
|
),
|
|
366
451
|
dir,
|
|
367
|
-
buildOutput:
|
|
452
|
+
buildOutput: _buildOutput,
|
|
368
453
|
assets
|
|
369
454
|
});
|
|
370
455
|
if (!trueRedirects.empty()) {
|
|
371
456
|
try {
|
|
372
457
|
await appendFile(
|
|
373
|
-
new URL("./_redirects",
|
|
458
|
+
new URL("./_redirects", _originalClientDir),
|
|
374
459
|
printAsRedirects(trueRedirects)
|
|
375
460
|
);
|
|
376
461
|
} catch (_error) {
|
|
@@ -382,6 +467,16 @@ function createIntegration({
|
|
|
382
467
|
}
|
|
383
468
|
};
|
|
384
469
|
}
|
|
470
|
+
async function getIsAstroPrismInstalled(rootURL) {
|
|
471
|
+
try {
|
|
472
|
+
const pkgURL = new URL("./package.json", rootURL);
|
|
473
|
+
const input = await readFile(pkgURL, { encoding: "utf-8" });
|
|
474
|
+
const pkgJson = JSON.parse(input);
|
|
475
|
+
return Object.hasOwn(pkgJson["dependencies"], "@astrojs/prism");
|
|
476
|
+
} catch {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
385
480
|
export {
|
|
386
481
|
createIntegration as default
|
|
387
482
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface Runtime {
|
|
2
|
+
cfContext: ExecutionContext;
|
|
3
|
+
}
|
|
4
|
+
/** Minimal manifest shape needed by the Cloudflare helpers. */
|
|
5
|
+
export interface ManifestLike {
|
|
6
|
+
assets: Set<string>;
|
|
7
|
+
sessionConfig?: {
|
|
8
|
+
options?: Record<string, unknown>;
|
|
9
|
+
} | undefined;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns a `Response` from the ASSETS binding if the request pathname
|
|
13
|
+
* is a known static asset. Returns `undefined` otherwise.
|
|
14
|
+
*/
|
|
15
|
+
export declare function matchStaticAsset(manifest: ManifestLike, requestUrl: string, env: Env): Response | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Tries the ASSETS binding as a fallback for an unmatched route.
|
|
18
|
+
* Returns the asset `Response` if found (non-404), `undefined` otherwise.
|
|
19
|
+
*/
|
|
20
|
+
export declare function fallbackToAssets(requestUrl: string, env: Env): Promise<Response | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a fetch function for prerendered error pages via the ASSETS binding.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createErrorPageFetch(env: Env): (url: string) => Promise<Response>;
|
|
25
|
+
/**
|
|
26
|
+
* Creates the Cloudflare-specific locals object with `cfContext`
|
|
27
|
+
* and deprecated `runtime` property getters.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createLocals(ctx: ExecutionContext): Runtime;
|
|
30
|
+
/**
|
|
31
|
+
* Extracts the client IP address from the `cf-connecting-ip` header.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getClientAddress(request: Request): string | undefined;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getValidatedIpFromHeader } from "@astrojs/internal-helpers/request";
|
|
2
|
+
function matchStaticAsset(manifest, requestUrl, env) {
|
|
3
|
+
const { pathname } = new URL(requestUrl);
|
|
4
|
+
if (manifest.assets.has(pathname)) {
|
|
5
|
+
return env.ASSETS.fetch(requestUrl.replace(/\.html$/, ""));
|
|
6
|
+
}
|
|
7
|
+
return void 0;
|
|
8
|
+
}
|
|
9
|
+
async function fallbackToAssets(requestUrl, env) {
|
|
10
|
+
const asset = await env.ASSETS.fetch(
|
|
11
|
+
requestUrl.replace(/index.html$/, "").replace(/\.html$/, "")
|
|
12
|
+
);
|
|
13
|
+
if (asset.status !== 404) {
|
|
14
|
+
return asset;
|
|
15
|
+
}
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
function createErrorPageFetch(env) {
|
|
19
|
+
return async (url) => {
|
|
20
|
+
return env.ASSETS.fetch(url.replace(/\.html$/, ""));
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function createLocals(ctx) {
|
|
24
|
+
const locals = {
|
|
25
|
+
cfContext: ctx
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(locals, "runtime", {
|
|
28
|
+
enumerable: false,
|
|
29
|
+
value: {
|
|
30
|
+
get env() {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Astro.locals.runtime.env has been removed in Astro v6. Use 'import { env } from "cloudflare:workers"' instead.`
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
get cf() {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Astro.locals.runtime.cf has been removed in Astro v6. Use 'Astro.request.cf' instead.`
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
get caches() {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Astro.locals.runtime.caches has been removed in Astro v6. Use the global 'caches' object instead.`
|
|
43
|
+
);
|
|
44
|
+
},
|
|
45
|
+
get ctx() {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Astro.locals.runtime.ctx has been removed in Astro v6. Use 'Astro.locals.cfContext' instead.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return locals;
|
|
53
|
+
}
|
|
54
|
+
function getClientAddress(request) {
|
|
55
|
+
return getValidatedIpFromHeader(request.headers.get("cf-connecting-ip"));
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
createErrorPageFetch,
|
|
59
|
+
createLocals,
|
|
60
|
+
fallbackToAssets,
|
|
61
|
+
getClientAddress,
|
|
62
|
+
matchStaticAsset
|
|
63
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ManifestLike } from './cf-helpers.js';
|
|
2
|
+
export type { Runtime, ManifestLike } from './cf-helpers.js';
|
|
3
|
+
export { matchStaticAsset, fallbackToAssets, createErrorPageFetch, createLocals, getClientAddress, } from './cf-helpers.js';
|
|
4
|
+
/**
|
|
5
|
+
* Injects the SESSION KV binding into the app manifest's session config.
|
|
6
|
+
* Idempotent — safe to call on every request.
|
|
7
|
+
*/
|
|
8
|
+
export declare function injectSessionBinding(manifest: ManifestLike, env: Env): void;
|
package/dist/utils/cf.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { sessionKVBindingName } from "virtual:astro-cloudflare:config";
|
|
2
|
+
import {
|
|
3
|
+
matchStaticAsset,
|
|
4
|
+
fallbackToAssets,
|
|
5
|
+
createErrorPageFetch,
|
|
6
|
+
createLocals,
|
|
7
|
+
getClientAddress
|
|
8
|
+
} from "./cf-helpers.js";
|
|
9
|
+
function injectSessionBinding(manifest, env) {
|
|
10
|
+
if (env[sessionKVBindingName]) {
|
|
11
|
+
const sessionConfigOptions = manifest.sessionConfig?.options ?? {};
|
|
12
|
+
Object.assign(sessionConfigOptions, {
|
|
13
|
+
binding: env[sessionKVBindingName]
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
createErrorPageFetch,
|
|
19
|
+
createLocals,
|
|
20
|
+
fallbackToAssets,
|
|
21
|
+
getClientAddress,
|
|
22
|
+
injectSessionBinding,
|
|
23
|
+
matchStaticAsset
|
|
24
|
+
};
|
package/dist/utils/handler.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}
|
|
1
|
+
import { type Runtime } from './cf.js';
|
|
2
|
+
export type { Runtime };
|
|
4
3
|
declare global {
|
|
5
4
|
var __ASTRO_IMAGES_BINDING_NAME: string;
|
|
6
5
|
}
|
|
7
6
|
type CfResponse = Awaited<ReturnType<Required<ExportedHandler<Env>>['fetch']>>;
|
|
8
7
|
export declare function handle(request: Request, env: Env, context: ExecutionContext): Promise<CfResponse>;
|
|
9
|
-
export {};
|
package/dist/utils/handler.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { env as globalEnv } from "cloudflare:workers";
|
|
2
|
-
import {
|
|
3
|
-
sessionKVBindingName,
|
|
4
|
-
compileImageConfig,
|
|
5
|
-
isPrerender
|
|
6
|
-
} from "virtual:astro-cloudflare:config";
|
|
2
|
+
import { compileImageConfig, isPrerender } from "virtual:astro-cloudflare:config";
|
|
7
3
|
import { createApp } from "astro/app/entrypoint";
|
|
8
4
|
import { setGetEnv } from "astro/env/setup";
|
|
9
5
|
import { createGetEnv } from "../utils/env.js";
|
|
@@ -15,7 +11,14 @@ import {
|
|
|
15
11
|
isStaticImagesRequest,
|
|
16
12
|
handleStaticImagesRequest
|
|
17
13
|
} from "./prerender.js";
|
|
18
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
injectSessionBinding,
|
|
16
|
+
matchStaticAsset,
|
|
17
|
+
fallbackToAssets,
|
|
18
|
+
createErrorPageFetch,
|
|
19
|
+
createLocals,
|
|
20
|
+
getClientAddress
|
|
21
|
+
} from "./cf.js";
|
|
19
22
|
setGetEnv(createGetEnv(globalEnv));
|
|
20
23
|
const app = createApp();
|
|
21
24
|
async function handle(request, env, context) {
|
|
@@ -34,16 +37,9 @@ async function handle(request, env, context) {
|
|
|
34
37
|
return handleStaticImagesRequest();
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Object.assign(sessionConfigOptions, {
|
|
41
|
-
binding: env[sessionKVBindingName]
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
if (app.manifest.assets.has(requestPathname)) {
|
|
45
|
-
return env.ASSETS.fetch(request.url.replace(/\.html$/, ""));
|
|
46
|
-
}
|
|
40
|
+
injectSessionBinding(app.manifest, env);
|
|
41
|
+
const staticAsset = matchStaticAsset(app.manifest, request.url, env);
|
|
42
|
+
if (staticAsset) return staticAsset;
|
|
47
43
|
let routeData = void 0;
|
|
48
44
|
if (app.isDev()) {
|
|
49
45
|
const result = await app.devMatch(app.getPathnameFromRequest(request));
|
|
@@ -54,50 +50,17 @@ async function handle(request, env, context) {
|
|
|
54
50
|
routeData = app.match(request);
|
|
55
51
|
}
|
|
56
52
|
if (!routeData) {
|
|
57
|
-
const asset = await env
|
|
58
|
-
|
|
59
|
-
);
|
|
60
|
-
if (asset.status !== 404) {
|
|
61
|
-
return asset;
|
|
62
|
-
}
|
|
53
|
+
const asset = await fallbackToAssets(request.url, env);
|
|
54
|
+
if (asset) return asset;
|
|
63
55
|
}
|
|
64
|
-
const locals =
|
|
65
|
-
cfContext: context
|
|
66
|
-
};
|
|
67
|
-
Object.defineProperty(locals, "runtime", {
|
|
68
|
-
enumerable: false,
|
|
69
|
-
value: {
|
|
70
|
-
get env() {
|
|
71
|
-
throw new Error(
|
|
72
|
-
`Astro.locals.runtime.env has been removed in Astro v6. Use 'import { env } from "cloudflare:workers"' instead.`
|
|
73
|
-
);
|
|
74
|
-
},
|
|
75
|
-
get cf() {
|
|
76
|
-
throw new Error(
|
|
77
|
-
`Astro.locals.runtime.cf has been removed in Astro v6. Use 'Astro.request.cf' instead.`
|
|
78
|
-
);
|
|
79
|
-
},
|
|
80
|
-
get caches() {
|
|
81
|
-
throw new Error(
|
|
82
|
-
`Astro.locals.runtime.caches has been removed in Astro v6. Use the global 'caches' object instead.`
|
|
83
|
-
);
|
|
84
|
-
},
|
|
85
|
-
get ctx() {
|
|
86
|
-
throw new Error(
|
|
87
|
-
`Astro.locals.runtime.ctx has been removed in Astro v6. Use 'Astro.locals.cfContext' instead.`
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
});
|
|
56
|
+
const locals = createLocals(context);
|
|
92
57
|
const waitUntil = context.waitUntil.bind(context);
|
|
93
58
|
const response = await app.render(request, {
|
|
94
59
|
routeData,
|
|
95
60
|
locals,
|
|
96
61
|
waitUntil,
|
|
97
|
-
prerenderedErrorPageFetch:
|
|
98
|
-
|
|
99
|
-
},
|
|
100
|
-
clientAddress: getValidatedIpFromHeader(request.headers.get("cf-connecting-ip"))
|
|
62
|
+
prerenderedErrorPageFetch: createErrorPageFetch(env),
|
|
63
|
+
clientAddress: getClientAddress(request)
|
|
101
64
|
});
|
|
102
65
|
if (app.setCookieHeaders) {
|
|
103
66
|
for (const setCookieHeader of app.setCookieHeaders(response)) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true if the given `_headers` content already declares (or detaches)
|
|
3
|
+
* a `Cache-Control` directive on any rule whose URL pattern matches `path`.
|
|
4
|
+
*
|
|
5
|
+
* Used to avoid emitting a second `Cache-Control` rule for hashed assets when
|
|
6
|
+
* the user already has one — Cloudflare merges duplicate header values across
|
|
7
|
+
* matching rules with a comma, which produces contradictory cache directives.
|
|
8
|
+
*/
|
|
9
|
+
export declare function headersFileHasCacheControlForPath(content: string, path: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Computes the content to write to `_headers` to inject an immutable
|
|
12
|
+
* Cache-Control rule for the hashed assets directory.
|
|
13
|
+
*
|
|
14
|
+
* Returns `null` when injection should be skipped because the existing
|
|
15
|
+
* `_headers` already declares `Cache-Control` on a rule matching the assets
|
|
16
|
+
* path — Cloudflare merges duplicate header values with a comma, which would
|
|
17
|
+
* produce contradictory directives.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildAssetsHeadersContent(opts: {
|
|
20
|
+
assetsDir: string;
|
|
21
|
+
basePrefix: string;
|
|
22
|
+
headersPath: URL;
|
|
23
|
+
}, readFile: (path: URL) => Promise<string>): Promise<{
|
|
24
|
+
content: string;
|
|
25
|
+
assetsPattern: string;
|
|
26
|
+
} | null>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
function cfHeadersPatternToRegex(pattern) {
|
|
2
|
+
let regexStr = "";
|
|
3
|
+
let i = 0;
|
|
4
|
+
while (i < pattern.length) {
|
|
5
|
+
const ch = pattern[i];
|
|
6
|
+
if (ch === "*") {
|
|
7
|
+
regexStr += ".*";
|
|
8
|
+
i++;
|
|
9
|
+
} else if (ch === ":" && /[A-Za-z]/.test(pattern[i + 1] ?? "")) {
|
|
10
|
+
i++;
|
|
11
|
+
while (i < pattern.length && /\w/.test(pattern[i])) i++;
|
|
12
|
+
regexStr += "[^/]+";
|
|
13
|
+
} else {
|
|
14
|
+
regexStr += ch.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
15
|
+
i++;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return new RegExp(`^${regexStr}$`);
|
|
19
|
+
}
|
|
20
|
+
function headersFileHasCacheControlForPath(content, path) {
|
|
21
|
+
let matchesCurrentSection = false;
|
|
22
|
+
for (const rawLine of content.split("\n")) {
|
|
23
|
+
const trimmed = rawLine.trim();
|
|
24
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
25
|
+
const isSectionHeader = !/^\s/.test(rawLine);
|
|
26
|
+
if (isSectionHeader) {
|
|
27
|
+
const pathOnly = trimmed.replace(/^https?:\/\/[^/]+/, "");
|
|
28
|
+
try {
|
|
29
|
+
matchesCurrentSection = cfHeadersPatternToRegex(pathOnly).test(path);
|
|
30
|
+
} catch {
|
|
31
|
+
matchesCurrentSection = false;
|
|
32
|
+
}
|
|
33
|
+
} else if (matchesCurrentSection && // Either `Cache-Control: value` (set) or `! Cache-Control` (detach).
|
|
34
|
+
/^\s+(?:!\s+cache-control\s*$|cache-control\s*:)/i.test(rawLine)) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
async function buildAssetsHeadersContent(opts, readFile) {
|
|
41
|
+
const { assetsDir, basePrefix, headersPath } = opts;
|
|
42
|
+
const assetsPattern = `${basePrefix}/${assetsDir}/*`;
|
|
43
|
+
const probePath = `${basePrefix}/${assetsDir}/probe`;
|
|
44
|
+
let existingHeaders = "";
|
|
45
|
+
try {
|
|
46
|
+
existingHeaders = await readFile(headersPath);
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
if (headersFileHasCacheControlForPath(existingHeaders, probePath)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const cacheBlock = `${assetsPattern}
|
|
53
|
+
Cache-Control: public, max-age=31536000, immutable
|
|
54
|
+
`;
|
|
55
|
+
const normalizedExisting = existingHeaders && !existingHeaders.endsWith("\n") ? existingHeaders + "\n" : existingHeaders;
|
|
56
|
+
const content = normalizedExisting ? `${cacheBlock}
|
|
57
|
+
${normalizedExisting}` : cacheBlock;
|
|
58
|
+
return { content, assetsPattern };
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
buildAssetsHeadersContent,
|
|
62
|
+
headersFileHasCacheControlForPath
|
|
63
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { imageConfig } from "astro:assets";
|
|
2
2
|
import { isRemotePath } from "@astrojs/internal-helpers/path";
|
|
3
3
|
import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
|
|
4
|
+
import { fetchWithRedirects } from "astro/assets";
|
|
4
5
|
const qualityTable = {
|
|
5
6
|
low: 25,
|
|
6
7
|
mid: 50,
|
|
@@ -14,9 +15,21 @@ async function transform(rawUrl, images, assets) {
|
|
|
14
15
|
return new Response("Forbidden", { status: 403 });
|
|
15
16
|
}
|
|
16
17
|
const imageSrc = new URL(href, url.origin);
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
|
|
18
|
+
let content;
|
|
19
|
+
if (isRemotePath(href)) {
|
|
20
|
+
try {
|
|
21
|
+
content = await fetchWithRedirects({
|
|
22
|
+
url: imageSrc,
|
|
23
|
+
imageConfig
|
|
24
|
+
});
|
|
25
|
+
if (!isRemoteAllowed(content.url, imageConfig)) {
|
|
26
|
+
return new Response("Forbidden", { status: 403 });
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
return new Response("Not Found", { status: 404 });
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
content = await assets.fetch(imageSrc);
|
|
20
33
|
}
|
|
21
34
|
if (!content.body) {
|
|
22
35
|
return new Response(null, { status: 404 });
|
|
@@ -16,6 +16,7 @@ export declare function setImageConfig(service: ImageServiceConfig | undefined,
|
|
|
16
16
|
endpoint: {
|
|
17
17
|
entrypoint: string;
|
|
18
18
|
};
|
|
19
|
+
dangerouslyProcessSVG: boolean;
|
|
19
20
|
domains: string[];
|
|
20
21
|
remotePatterns: {
|
|
21
22
|
protocol?: string | undefined;
|
|
@@ -34,6 +35,7 @@ export declare function setImageConfig(service: ImageServiceConfig | undefined,
|
|
|
34
35
|
route: string;
|
|
35
36
|
entrypoint?: string | undefined;
|
|
36
37
|
};
|
|
38
|
+
dangerouslyProcessSVG: boolean;
|
|
37
39
|
domains: string[];
|
|
38
40
|
remotePatterns: {
|
|
39
41
|
protocol?: string | undefined;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import components from "prismjs/components.js";
|
|
3
|
+
const MODULE_ID = "virtual:astro-cloudflare:prism";
|
|
4
|
+
const RESOLVED_MODULE_ID = "\0" + MODULE_ID;
|
|
5
|
+
const languages = Object.keys(components.languages).filter((l) => l !== "meta");
|
|
6
|
+
function cfPrismPlugin() {
|
|
7
|
+
return {
|
|
8
|
+
name: "@astrojs/cloudflare:prism",
|
|
9
|
+
configEnvironment(environmentName) {
|
|
10
|
+
if (environmentName === "ssr") {
|
|
11
|
+
return {
|
|
12
|
+
// Because this virtual module adds a large number of dynamic import statements,
|
|
13
|
+
// Vite’s logs will consequently display the message “new dependencies optimized” for all languages.
|
|
14
|
+
// To avoid this, we explicitly specify that the module should be optimized in advance.
|
|
15
|
+
optimizeDeps: {
|
|
16
|
+
include: ["prismjs/components/prism-*.js"]
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
resolveId: {
|
|
22
|
+
filter: {
|
|
23
|
+
id: new RegExp(`^${MODULE_ID}$`)
|
|
24
|
+
},
|
|
25
|
+
handler() {
|
|
26
|
+
return RESOLVED_MODULE_ID;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
load: {
|
|
30
|
+
filter: {
|
|
31
|
+
id: new RegExp(`^${RESOLVED_MODULE_ID}$`)
|
|
32
|
+
},
|
|
33
|
+
async handler() {
|
|
34
|
+
const importerPath = fileURLToPath(import.meta.url);
|
|
35
|
+
const resolvedModules = await Promise.all(
|
|
36
|
+
languages.map(async (lang) => {
|
|
37
|
+
const resolvedId = await this.resolve(
|
|
38
|
+
`prismjs/components/prism-${lang}.js`,
|
|
39
|
+
importerPath
|
|
40
|
+
);
|
|
41
|
+
return { resolvedId: resolvedId?.id, lang };
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
const prismBundledLanguages = resolvedModules.filter(({ resolvedId }) => resolvedId !== void 0).map(
|
|
45
|
+
({ resolvedId, lang }) => `${JSON.stringify(lang)}: () => import(${JSON.stringify(resolvedId)})`
|
|
46
|
+
);
|
|
47
|
+
return `
|
|
48
|
+
export const bundledLanguages = { ${prismBundledLanguages.join(",")} };
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
cfPrismPlugin as default
|
|
56
|
+
};
|
package/dist/wrangler.js
CHANGED
|
@@ -13,11 +13,7 @@ function cloudflareConfigCustomizer(options) {
|
|
|
13
13
|
);
|
|
14
14
|
const hasImagesBinding = nonInheritableConfig?.images?.binding !== void 0;
|
|
15
15
|
return {
|
|
16
|
-
kv_namespaces: !needsSessionKVBinding || hasSessionBinding ? void 0 : [
|
|
17
|
-
{
|
|
18
|
-
binding: sessionKVBindingName
|
|
19
|
-
}
|
|
20
|
-
],
|
|
16
|
+
kv_namespaces: !needsSessionKVBinding || hasSessionBinding ? void 0 : [{ binding: sessionKVBindingName }],
|
|
21
17
|
images: hasImagesBinding || !imagesBindingName ? void 0 : {
|
|
22
18
|
binding: imagesBindingName
|
|
23
19
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/cloudflare",
|
|
3
3
|
"description": "Deploy your site to Cloudflare Workers",
|
|
4
|
-
"version": "14.0.0-
|
|
4
|
+
"version": "14.0.0-beta.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"author": "withastro",
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
"./image-passthrough-endpoint": "./dist/entrypoints/image-passthrough-endpoint.js",
|
|
28
28
|
"./image-service-workerd": "./dist/entrypoints/image-service-workerd.js",
|
|
29
29
|
"./handler": "./dist/utils/handler.js",
|
|
30
|
+
"./fetch": "./dist/fetch.js",
|
|
31
|
+
"./hono": "./dist/hono.js",
|
|
30
32
|
"./types.d.ts": "./types.d.ts",
|
|
31
33
|
"./package.json": "./package.json"
|
|
32
34
|
},
|
|
@@ -35,23 +37,25 @@
|
|
|
35
37
|
"types.d.ts"
|
|
36
38
|
],
|
|
37
39
|
"dependencies": {
|
|
38
|
-
"@cloudflare/vite-plugin": "^1.
|
|
40
|
+
"@cloudflare/vite-plugin": "^1.39.0",
|
|
39
41
|
"piccolore": "^0.1.3",
|
|
40
42
|
"tinyglobby": "^0.2.15",
|
|
41
|
-
"vite": "^8.0.
|
|
42
|
-
"@astrojs/internal-helpers": "0.
|
|
43
|
+
"vite": "^8.0.13",
|
|
44
|
+
"@astrojs/internal-helpers": "0.10.0",
|
|
43
45
|
"@astrojs/underscore-redirects": "1.0.3"
|
|
44
46
|
},
|
|
45
47
|
"peerDependencies": {
|
|
46
|
-
"astro": "^7.0.0-alpha.
|
|
48
|
+
"astro": "^7.0.0-alpha.2",
|
|
47
49
|
"wrangler": "^4.83.0"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
50
|
-
"@cloudflare/workers-types": "^4.
|
|
51
|
-
"@types/node": "^
|
|
52
|
+
"@cloudflare/workers-types": "^4.20260526.1",
|
|
53
|
+
"@types/node": "^22.10.6",
|
|
54
|
+
"@types/prismjs": "1.26.6",
|
|
52
55
|
"cheerio": "1.2.0",
|
|
53
|
-
"devalue": "^5.
|
|
54
|
-
"
|
|
56
|
+
"devalue": "^5.8.1",
|
|
57
|
+
"prismjs": "^1.30.0",
|
|
58
|
+
"astro": "7.0.0-beta.3",
|
|
55
59
|
"astro-scripts": "0.0.14"
|
|
56
60
|
},
|
|
57
61
|
"publishConfig": {
|
|
@@ -59,9 +63,8 @@
|
|
|
59
63
|
},
|
|
60
64
|
"scripts": {
|
|
61
65
|
"dev": "astro-scripts dev \"src/**/*.ts\"",
|
|
62
|
-
"build": "astro-scripts build \"src/**/*.ts\" --clean-dts && tsc",
|
|
66
|
+
"build": "astro-scripts build \"src/**/*.ts\" --clean-dts && tsc -b",
|
|
63
67
|
"build:ci": "astro-scripts build \"src/**/*.ts\"",
|
|
64
|
-
"test": "astro-scripts test --force-exit \"test/**/*.test.ts\""
|
|
65
|
-
"typecheck:tests": "tsc --build tsconfig.test.json"
|
|
68
|
+
"test": "astro-scripts test --force-exit \"test/**/*.test.ts\""
|
|
66
69
|
}
|
|
67
70
|
}
|