create-cloudflare
Version:
A CLI for creating and deploying new applications to Cloudflare.
154 lines (127 loc) • 4.34 kB
text/typescript
import { endSection } from "@cloudflare/cli";
import { brandColor } from "@cloudflare/cli/colors";
import { spinner } from "@cloudflare/cli/interactive";
import { runFrameworkGenerator } from "frameworks/index";
import { loadTemplateSnippets, transformFile } from "helpers/codemod";
import { quoteShellArgs, runCommand } from "helpers/command";
import { removeFile, usesTypescript } from "helpers/files";
import { detectPackageManager } from "helpers/packageManagers";
import * as recast from "recast";
import type { TemplateConfig } from "../../../src/templates";
import type { C3Context } from "types";
const { npm, npx, name } = detectPackageManager();
const generate = async (ctx: C3Context) => {
await runFrameworkGenerator(ctx, ["playground", ctx.project.name]);
};
const configure = async (ctx: C3Context) => {
// Add the pages integration
// For some reason `pnpx qwik add` fails for qwik so we use `pnpm qwik add` instead.
const cmd = [name === "pnpm" ? npm : npx, "qwik", "add", "cloudflare-pages"];
endSection(`Running ${quoteShellArgs(cmd)}`);
await runCommand(cmd);
// Remove the extraneous Pages files
removeFile("./public/_routes.json");
addBindingsProxy(ctx);
populateCloudflareEnv();
};
const addBindingsProxy = (ctx: C3Context) => {
// Qwik only has a typescript template atm.
// This check is an extra precaution
if (!usesTypescript(ctx)) {
return;
}
const s = spinner();
s.start("Updating `vite.config.ts`");
const snippets = loadTemplateSnippets(ctx);
const b = recast.types.builders;
transformFile("vite.config.ts", {
// Insert the env declaration after the last import (but before the rest of the body)
visitProgram: function (n) {
const lastImportIndex = n.node.body.findLastIndex(
(t) => t.type === "ImportDeclaration",
);
const lastImport = n.get("body", lastImportIndex);
lastImport.insertAfter(...snippets.getPlatformProxyTs);
return this.traverse(n);
},
// Pass the `platform` object from the declaration to the `qwikCity` plugin
visitCallExpression: function (n) {
const callee = n.node.callee as recast.types.namedTypes.Identifier;
if (callee.name !== "qwikCity") {
return this.traverse(n);
}
// The config object passed to `qwikCity`
const configArgument = n.node.arguments[0] as
| recast.types.namedTypes.ObjectExpression
| undefined;
const platformPropery = b.objectProperty.from({
key: b.identifier("platform"),
value: b.identifier("platform"),
shorthand: true,
});
if (!configArgument) {
n.node.arguments = [b.objectExpression([platformPropery])];
return false;
}
if (configArgument.type !== "ObjectExpression") {
throw new Error("Failed to update `vite.config.ts`");
}
// Add the `platform` object to the object
configArgument.properties.push(platformPropery);
return false;
},
});
s.stop(`${brandColor("updated")} \`vite.config.ts\``);
};
const populateCloudflareEnv = () => {
const entrypointPath = "src/entry.cloudflare-pages.tsx";
const s = spinner();
s.start(`Updating \`${entrypointPath}\``);
transformFile(entrypointPath, {
visitTSInterfaceDeclaration: function (n) {
const b = recast.types.builders;
const id = n.node.id as recast.types.namedTypes.Identifier;
if (id.name !== "QwikCityPlatform") {
this.traverse(n);
}
const newBody = [
["env", "Env"],
// Qwik doesn't supply `cf` to the platform object. Should they do so, uncomment this
// ["cf", "CfProperties"],
].map(([varName, type]) =>
b.tsPropertySignature(
b.identifier(varName),
b.tsTypeAnnotation(b.tsTypeReference(b.identifier(type))),
),
);
n.node.body.body = newBody;
return false;
},
});
s.stop(`${brandColor("updated")} \`${entrypointPath}\``);
};
const config: TemplateConfig = {
configVersion: 1,
id: "qwik",
frameworkCli: "create-qwik",
displayName: "Qwik",
platform: "workers",
copyFiles: {
path: "./templates",
},
path: "templates/qwik/workers",
generate,
configure,
transformPackageJson: async () => ({
scripts: {
deploy: `${npm} run build && wrangler deploy`,
preview: `${npm} run build && wrangler dev`,
"cf-typegen": `wrangler types`,
},
}),
devScript: "dev",
deployScript: "deploy",
previewScript: "preview",
workersTypes: "installed",
};
export default config;