@astrojs/cloudflare 14.0.0-alpha.1 → 14.0.0-beta.3
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/entrypoints/preview.js +1 -1
- package/dist/index.js +55 -8
- package/dist/utils/headers.d.ts +26 -0
- package/dist/utils/headers.js +63 -0
- package/package.json +2 -2
|
@@ -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.3";
|
|
92
92
|
const localPrefix = `${colors.dim("\u2503")} Local `;
|
|
93
93
|
const networkPrefix = `${colors.dim("\u2503")} Network `;
|
|
94
94
|
const emptyPrefix = " ".repeat(11);
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { createReadStream, existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { appendFile, readFile, rename, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { appendFile, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { relative } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { normalizePath } from "vite";
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
removeLeadingForwardSlash,
|
|
9
|
+
removeTrailingForwardSlash
|
|
10
|
+
} from "@astrojs/internal-helpers/path";
|
|
8
11
|
import { createRedirectsFromAstroRoutes, printAsRedirects } from "@astrojs/underscore-redirects";
|
|
9
12
|
import { cloudflare as cfVitePlugin } from "@cloudflare/vite-plugin";
|
|
10
13
|
import { astroFrontmatterScanPlugin } from "./esbuild-plugin-astro-frontmatter.js";
|
|
11
14
|
import { getParts } from "./utils/generate-routes-json.js";
|
|
15
|
+
import { buildAssetsHeadersContent } from "./utils/headers.js";
|
|
12
16
|
import {
|
|
13
17
|
normalizeImageServiceConfig,
|
|
14
18
|
setImageConfig
|
|
@@ -101,6 +105,7 @@ function createIntegration({
|
|
|
101
105
|
const needsImagesBindingForDev = isCompile && command === "dev";
|
|
102
106
|
const usesContentCollections = hasContentCollectionsConfig(config.srcDir);
|
|
103
107
|
const prebundleContentRuntime = command === "dev" && usesContentCollections;
|
|
108
|
+
const isTypeGenPhase = command === "build" || command === "sync";
|
|
104
109
|
const adapterPluginConfig = {
|
|
105
110
|
config: cloudflareConfigCustomizer({
|
|
106
111
|
needsSessionKVBinding,
|
|
@@ -138,6 +143,16 @@ function createIntegration({
|
|
|
138
143
|
];
|
|
139
144
|
const isAstroPrismPackageInstalled = await getIsAstroPrismInstalled(config.root);
|
|
140
145
|
const userOptimizeDeps = config.vite?.optimizeDeps;
|
|
146
|
+
const cloudflareVitePlugins = cfVitePlugin({
|
|
147
|
+
...cfPluginConfig,
|
|
148
|
+
viteEnvironment: { name: "ssr" },
|
|
149
|
+
assetsOnly: () => _buildOutput === "static"
|
|
150
|
+
});
|
|
151
|
+
if (isTypeGenPhase) {
|
|
152
|
+
for (const plugin of cloudflareVitePlugins) {
|
|
153
|
+
plugin.configureServer = void 0;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
141
156
|
updateConfig({
|
|
142
157
|
build: {
|
|
143
158
|
redirects: false
|
|
@@ -146,11 +161,7 @@ function createIntegration({
|
|
|
146
161
|
vite: {
|
|
147
162
|
plugins: [
|
|
148
163
|
...prerenderEnvironment === "node" && command === "dev" ? [createNodePrerenderPlugin()] : [],
|
|
149
|
-
|
|
150
|
-
...cfPluginConfig,
|
|
151
|
-
viteEnvironment: { name: "ssr" },
|
|
152
|
-
assetsOnly: () => _buildOutput === "static"
|
|
153
|
-
}),
|
|
164
|
+
cloudflareVitePlugins,
|
|
154
165
|
{
|
|
155
166
|
name: "@astrojs/cloudflare:cf-imports",
|
|
156
167
|
enforce: "pre",
|
|
@@ -166,6 +177,9 @@ function createIntegration({
|
|
|
166
177
|
{
|
|
167
178
|
name: "@astrojs/cloudflare:environment",
|
|
168
179
|
configEnvironment(environmentName, _options) {
|
|
180
|
+
if (isTypeGenPhase) {
|
|
181
|
+
return { optimizeDeps: { noDiscovery: true, include: [] } };
|
|
182
|
+
}
|
|
169
183
|
const isServerEnvironment = ["astro", "ssr", "prerender"].includes(
|
|
170
184
|
environmentName
|
|
171
185
|
);
|
|
@@ -356,7 +370,7 @@ function createIntegration({
|
|
|
356
370
|
},
|
|
357
371
|
"astro:build:done": async ({ dir, logger, assets }) => {
|
|
358
372
|
if (_config.base !== "/") {
|
|
359
|
-
for (const file of [".assetsignore", "_headers"]) {
|
|
373
|
+
for (const file of [".assetsignore", "_headers", "_redirects"]) {
|
|
360
374
|
try {
|
|
361
375
|
await rename(
|
|
362
376
|
new URL(`./${file}`, _config.build.client),
|
|
@@ -378,6 +392,39 @@ function createIntegration({
|
|
|
378
392
|
} catch {
|
|
379
393
|
}
|
|
380
394
|
}
|
|
395
|
+
if (_config.build.assetsPrefix) {
|
|
396
|
+
logger.debug(
|
|
397
|
+
"Skipping Cache-Control injection for assets \u2014 `build.assetsPrefix` is set, so assets are served from a different origin."
|
|
398
|
+
);
|
|
399
|
+
} else {
|
|
400
|
+
const headersPath = new URL("./_headers", _originalClientDir);
|
|
401
|
+
const result = await buildAssetsHeadersContent(
|
|
402
|
+
{
|
|
403
|
+
assetsDir: _config.build.assets,
|
|
404
|
+
basePrefix: removeTrailingForwardSlash(_config.base),
|
|
405
|
+
headersPath
|
|
406
|
+
},
|
|
407
|
+
(path) => readFile(path, "utf-8")
|
|
408
|
+
);
|
|
409
|
+
if (result === null) {
|
|
410
|
+
logger.debug(
|
|
411
|
+
`Skipping Cache-Control injection \u2014 _headers already sets Cache-Control on a matching rule.`
|
|
412
|
+
);
|
|
413
|
+
} else {
|
|
414
|
+
const tempPath = new URL("./_headers.tmp", _originalClientDir);
|
|
415
|
+
try {
|
|
416
|
+
await writeFile(tempPath, result.content);
|
|
417
|
+
await rename(tempPath, headersPath);
|
|
418
|
+
} catch (err) {
|
|
419
|
+
await unlink(tempPath).catch(() => {
|
|
420
|
+
});
|
|
421
|
+
throw err;
|
|
422
|
+
}
|
|
423
|
+
logger.info(
|
|
424
|
+
`Injected immutable Cache-Control for ${result.assetsPattern} into _headers.`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
381
428
|
let redirectsExists = false;
|
|
382
429
|
try {
|
|
383
430
|
const redirectsStat = await stat(new URL("./_redirects", _originalClientDir));
|
|
@@ -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
|
+
};
|
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.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"author": "withastro",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"cheerio": "1.2.0",
|
|
56
56
|
"devalue": "^5.8.1",
|
|
57
57
|
"prismjs": "^1.30.0",
|
|
58
|
-
"astro": "7.0.0-
|
|
58
|
+
"astro": "7.0.0-beta.6",
|
|
59
59
|
"astro-scripts": "0.0.14"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|