import { resolve } from "node:path"; import { logRaw } from "@cloudflare/cli-shared-helpers"; import { brandColor, dim } from "@cloudflare/cli-shared-helpers/colors"; import { spinner } from "@cloudflare/cli-shared-helpers/interactive"; import { runFrameworkGenerator } from "frameworks/index"; import { readJSON, removeFile, writeJSON } from "helpers/files"; import { detectPackageManager } from "helpers/packageManagers"; import { installPackages } from "helpers/packages"; import type { TemplateConfig } from "../../src/templates"; import type { C3Context, PackageJson } from "types"; const { npm } = detectPackageManager(); const generate = async (ctx: C3Context) => { // We use the upstream `create-react-router` default template and overlay // our Cloudflare-specific files via `copyFiles`. This avoids depending on // a third-party Cloudflare template that has been deleted upstream in the past. await runFrameworkGenerator(ctx, [ ctx.project.name, // to prevent asking about git twice, just let c3 do it "--no-git-init", "--no-install", ]); logRaw(""); // newline }; const configure = async (ctx: C3Context) => { // `npmInstall` has already run by this point with the upstream default // template's `package.json`, which doesn't include `@cloudflare/vite-plugin`. // Our overlaid `vite.config.ts` imports it, so install it now (this also // adds it to `package.json`). `wrangler` is installed separately by // `installWrangler()` in the main configure flow before this runs. await installPackages(["@cloudflare/vite-plugin"], { dev: true, startText: "Installing the Cloudflare Vite plugin", doneText: `${brandColor("installed")} ${dim("@cloudflare/vite-plugin")}`, }); // The upstream default template targets a generic Node.js/Docker deployment. // Remove artifacts that don't apply to a Cloudflare Workers project. const s = spinner(); s.start("Removing non-Cloudflare artifacts from template"); removeFile(resolve(ctx.project.path, "Dockerfile")); removeFile(resolve(ctx.project.path, ".dockerignore")); // `transformPackageJson` is deep-merge only and cannot remove keys, so strip // the Node-server deps and `start` script that the default template ships. const pkgJsonPath = resolve(ctx.project.path, "package.json"); const pkgJson = readJSON(pkgJsonPath) as PackageJson; delete pkgJson.dependencies?.["@react-router/node"]; delete pkgJson.dependencies?.["@react-router/serve"]; delete pkgJson.scripts?.start; writeJSON(pkgJsonPath, pkgJson); s.stop(`${brandColor("removed")} ${dim("Node-server template artifacts")}`); }; const config: TemplateConfig = { configVersion: 1, id: "react-router", platform: "workers", frameworkCli: "create-react-router", displayName: "React Router (formerly Remix)", copyFiles: { path: "./ts", }, generate, configure, transformPackageJson: async () => ({ scripts: { deploy: `${npm} run build && wrangler deploy`, preview: `${npm} run build && vite preview`, "cf-typegen": `wrangler types`, typecheck: `wrangler types && react-router typegen && tsc -b`, postinstall: `wrangler types`, }, }), devScript: "dev", deployScript: "deploy", previewScript: "preview", }; export default config;