Skip to content

Commit cab7e1c

Browse files
authored
[C3] Astro template dev bindings improvements (#5072)
* C3: Improve bindings support in Astro template * Remove build-cf-types target when js is used * Changeset * Addressing PR feedback * Remove unwanted lockfile changes
1 parent c1ed773 commit cab7e1c

File tree

12 files changed

+181
-12
lines changed

12 files changed

+181
-12
lines changed

.changeset/tricky-beers-push.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"create-cloudflare": minor
3+
---
4+
5+
feature: Improve bindings support in Astro template.
6+
7+
C3 will now create Astro projects configured to use miniflare in dev automatically. This is done by adding a configuration for the adapter of `{ runtime: 'local'}` (see [Astro docs](https://docs.astro.build/en/guides/integrations-guide/cloudflare/#runtime) for more details). A `wrangler.toml` file will also be added where bindings can be added to be used in dev.
8+
9+
Along with this change, projects will now use the default vite-based `astro dev` command instead of using `wrangler pages dev` on build output.
10+
11+
When Typescript is used, the `src/env.d.ts` file will be updated to add type definitions `runtime.env` which can be re-generated with a newly added `build-cf-types` script.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { APIRoute } from "astro";
2+
3+
export const GET: APIRoute = async ({ locals }) => {
4+
const { TEST } = locals.runtime.env;
5+
6+
return new Response(JSON.stringify({ test: TEST }), {
7+
status: 200,
8+
});
9+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[vars]
2+
TEST = "C3_TEST"

packages/create-cloudflare/e2e-tests/frameworks.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import type { Suite } from "vitest";
3434

3535
const TEST_TIMEOUT = 1000 * 60 * 5;
3636
const LONG_TIMEOUT = 1000 * 60 * 10;
37+
const TEST_RETRIES = 1;
3738

3839
type FrameworkTestConfig = RunnerConfig & {
3940
testCommitMessage: boolean;
@@ -60,6 +61,16 @@ const frameworkTests: Record<string, FrameworkTestConfig> = {
6061
route: "/",
6162
expectedText: "Hello, Astronaut!",
6263
},
64+
verifyDev: {
65+
route: "/test",
66+
expectedText: "C3_TEST",
67+
},
68+
verifyBuild: {
69+
outputDir: "./dist",
70+
script: "build",
71+
route: "/test",
72+
expectedText: "C3_TEST",
73+
},
6374
},
6475
docusaurus: {
6576
unsupportedPms: ["bun"],
@@ -271,6 +282,10 @@ describe.concurrent(`E2E: Web frameworks`, () => {
271282

272283
const quarantineModeMatch = isQuarantineMode() == (quarantine ?? false);
273284

285+
const retries = process.env.E2E_RETRIES
286+
? parseInt(process.env.E2E_RETRIES)
287+
: TEST_RETRIES;
288+
274289
// If the framework in question is being run in isolation, always run it.
275290
// Otherwise, only run the test if it's configured `quarantine` value matches
276291
// what is set in E2E_QUARANTINE
@@ -353,7 +368,10 @@ describe.concurrent(`E2E: Web frameworks`, () => {
353368
}
354369
}
355370
},
356-
{ retry: 1, timeout: timeout || TEST_TIMEOUT }
371+
{
372+
retry: retries,
373+
timeout: timeout || TEST_TIMEOUT,
374+
}
357375
);
358376
});
359377
});

packages/create-cloudflare/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"deepmerge": "^4.3.1",
7070
"degit": "^2.8.4",
7171
"dns2": "^2.1.0",
72+
"dotenv": "^16.0.0",
7273
"esbuild": "^0.17.12",
7374
"execa": "^7.1.1",
7475
"glob": "^10.3.3",

packages/create-cloudflare/templates/astro/c3.ts

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { logRaw } from "@cloudflare/cli";
2-
import { brandColor, dim } from "@cloudflare/cli/colors";
1+
import { logRaw, updateStatus } from "@cloudflare/cli";
2+
import { blue, brandColor, dim } from "@cloudflare/cli/colors";
3+
import { loadTemplateSnippets, transformFile } from "helpers/codemod";
34
import { runCommand, runFrameworkGenerator } from "helpers/command";
4-
import { compatDateFlag } from "helpers/files";
5+
import { usesTypescript } from "helpers/files";
56
import { detectPackageManager } from "helpers/packages";
7+
import * as recast from "recast";
68
import type { TemplateConfig } from "../../src/templates";
7-
import type { C3Context } from "types";
9+
import type { C3Context, PackageJson } from "types";
810

911
const { npx } = detectPackageManager();
1012

@@ -14,27 +16,95 @@ const generate = async (ctx: C3Context) => {
1416
logRaw(""); // newline
1517
};
1618

17-
const configure = async () => {
19+
const configure = async (ctx: C3Context) => {
1820
await runCommand([npx, "astro", "add", "cloudflare", "-y"], {
1921
silent: true,
2022
startText: "Installing adapter",
2123
doneText: `${brandColor("installed")} ${dim(
2224
`via \`${npx} astro add cloudflare\``
2325
)}`,
2426
});
27+
28+
updateAstroConfig();
29+
updateEnvDeclaration(ctx);
30+
};
31+
32+
const updateAstroConfig = () => {
33+
const filePath = "astro.config.mjs";
34+
35+
updateStatus(`Updating configuration in ${blue(filePath)}`);
36+
37+
transformFile(filePath, {
38+
visitCallExpression: function (n) {
39+
const callee = n.node.callee as recast.types.namedTypes.Identifier;
40+
if (callee.name !== "cloudflare") {
41+
return this.traverse(n);
42+
}
43+
44+
const b = recast.types.builders;
45+
n.node.arguments = [
46+
b.objectExpression([
47+
b.objectProperty(
48+
b.identifier("runtime"),
49+
b.objectExpression([
50+
b.objectProperty(b.identifier("mode"), b.stringLiteral("local")),
51+
])
52+
),
53+
]),
54+
];
55+
56+
return false;
57+
},
58+
});
59+
};
60+
61+
const updateEnvDeclaration = (ctx: C3Context) => {
62+
if (!usesTypescript(ctx)) {
63+
return;
64+
}
65+
66+
const filePath = "src/env.d.ts";
67+
68+
updateStatus(`Adding type declarations in ${blue(filePath)}`);
69+
70+
transformFile(filePath, {
71+
visitProgram: function (n) {
72+
const snippets = loadTemplateSnippets(ctx);
73+
const patch = snippets.runtimeDeclarationTs;
74+
const b = recast.types.builders;
75+
76+
// Preserve comments with the new body
77+
const comments = n.get("comments").value;
78+
n.node.comments = comments.map((c: recast.types.namedTypes.CommentLine) =>
79+
b.commentLine(c.value)
80+
);
81+
82+
// Add the patch
83+
n.get("body").push(...patch);
84+
85+
return false;
86+
},
87+
});
2588
};
2689

2790
const config: TemplateConfig = {
2891
configVersion: 1,
2992
id: "astro",
3093
platform: "pages",
3194
displayName: "Astro",
95+
copyFiles: {
96+
path: "./templates",
97+
},
98+
devScript: "dev",
99+
deployScript: "deploy",
100+
previewScript: "preview",
32101
generate,
33102
configure,
34-
transformPackageJson: async () => ({
103+
transformPackageJson: async (pkgJson: PackageJson, ctx: C3Context) => ({
35104
scripts: {
36-
"pages:dev": `wrangler pages dev ${await compatDateFlag()} -- astro dev`,
37-
"pages:deploy": `astro build && wrangler pages deploy ./dist`,
105+
deploy: `astro build && wrangler pages deploy ./dist`,
106+
preview: `astro build && wrangler pages dev ./dist`,
107+
...(usesTypescript(ctx) && { "build-cf-types": `wrangler types` }),
38108
},
39109
}),
40110
testFlags: [
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
type Runtime = import("@astrojs/cloudflare").DirectoryRuntime<Env>;
2+
declare namespace App {
3+
interface Locals extends Runtime {}
4+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name = "<TBD>"
2+
compatibility_date = "<TBD>"
3+
4+
# Variable bindings. These are arbitrary, plaintext strings (similar to environment variables)
5+
# Note: Use secrets to store sensitive data.
6+
# Docs: https://developers.cloudflare.com/workers/platform/environment-variables
7+
# [vars]
8+
# MY_VARIABLE = "production_value"
9+
10+
# Bind a KV Namespace. Use KV as persistent storage for small key-value pairs.
11+
# Docs: https://developers.cloudflare.com/workers/runtime-apis/kv
12+
# [[kv_namespaces]]
13+
# binding = "MY_KV_NAMESPACE"
14+
# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
15+
16+
# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files.
17+
# Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/
18+
# [[r2_buckets]]
19+
# binding = "MY_BUCKET"
20+
# bucket_name = "my-bucket"
21+
22+
# Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer.
23+
# Docs: https://developers.cloudflare.com/queues/get-started
24+
# [[queues.producers]]
25+
# binding = "MY_QUEUE"
26+
# queue = "my-queue"
27+
28+
# Bind a Queue consumer. Queue Consumers can retrieve tasks scheduled by Producers to act on them.
29+
# Docs: https://developers.cloudflare.com/queues/get-started
30+
# [[queues.consumers]]
31+
# queue = "my-queue"
32+
33+
# Bind another Worker service. Use this binding to call another Worker without network overhead.
34+
# Docs: https://developers.cloudflare.com/workers/platform/services
35+
# [[services]]
36+
# binding = "MY_SERVICE"
37+
# service = "my-service"
38+
39+
# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model.
40+
# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps.
41+
# Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects
42+
# [[durable_objects.bindings]]
43+
# name = "MY_DURABLE_OBJECT"
44+
# class_name = "MyDurableObject"
45+
46+
# Durable Object migrations.
47+
# Docs: https://developers.cloudflare.com/workers/learning/using-durable-objects#configure-durable-object-classes-with-migrations
48+
# [[migrations]]
49+
# tag = "v1"
50+
# new_classes = ["MyDurableObject"]

packages/create-cloudflare/templates/nuxt/c3.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ const config: TemplateConfig = {
8181
configVersion: 1,
8282
id: "nuxt",
8383
platform: "pages",
84+
displayName: "Nuxt",
8485
copyFiles: {
8586
path: "./templates",
8687
},
87-
displayName: "Nuxt",
8888
devScript: "dev",
8989
deployScript: "deploy",
9090
generate,

packages/create-cloudflare/turbo.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"CLOUDFLARE_API_TOKEN",
1414
"FRAMEWORK_CLI_TO_TEST",
1515
"E2E_QUARANTINE",
16-
"E2E_PROJECT_PATH"
16+
"E2E_PROJECT_PATH",
17+
"E2E_RETRIES"
1718
]
1819
}
1920
}

0 commit comments

Comments
 (0)