Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/stale-snakes-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"create-cloudflare": patch
---

Graduate Next.js + Workers Assets template from experimental

You no longer need the `--experimental` flag to access this template.
2 changes: 1 addition & 1 deletion packages/create-cloudflare/e2e-tests/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ describe.skipIf(frameworkToTest || isQuarantineMode())("help text", () => {
npm create cloudflare -- --framework next -- --ts
pnpm create cloudflare --framework next -- --ts
Allowed Values:
next, solid
solid
--platform=<value>
Whether the application should be deployed to Pages or Workers. This is only applicable for Frameworks templates that support both Pages and Workers.
Allowed Values:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,6 @@ import { keys, LONG_TIMEOUT } from "../helpers";

export default function getFrameworkTestConfigExperimental() {
return {
next: {
testCommitMessage: true,
flags: [
"--ts",
"--tailwind",
"--eslint",
"--app",
"--import-alias",
"@/*",
"--src-dir",
],
verifyBuildCfTypes: {
outputFile: "cloudflare-env.d.ts",
envInterfaceName: "CloudflareEnv",
},
verifyPreview: {
route: "/test",
expectedText: "Create Next App",
},
verifyDeploy: {
route: "/",
expectedText: "Create Next App",
},
// see https://github.com/cloudflare/next-on-pages/blob/main/packages/next-on-pages/docs/supported.md#operating-systems
unsupportedOSs: ["win32"],
unsupportedPms: [
// bun and yarn are failing in CI
"bun",
"yarn",
],
},
solid: {
promptHandlers: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,8 @@ export default function getFrameworkTestConfig(pm: string) {
},
flags: ["--typescript", "--no-install", "--no-git-init"],
},
next: {
"next:pages": {
argv: ["--platform", "pages"],
promptHandlers: [
{
matcher: /Do you want to use the next-on-pages eslint-plugin\?/,
Expand Down Expand Up @@ -394,6 +395,38 @@ export default function getFrameworkTestConfig(pm: string) {
"@/*",
],
},
"next:workers": {
argv: ["--platform", "workers"],
testCommitMessage: true,
flags: [
"--ts",
"--tailwind",
"--eslint",
"--app",
"--import-alias",
"@/*",
"--src-dir",
],
verifyBuildCfTypes: {
outputFile: "cloudflare-env.d.ts",
envInterfaceName: "CloudflareEnv",
},
verifyPreview: {
route: "/test",
expectedText: "Create Next App",
},
verifyDeploy: {
route: "/",
expectedText: "Create Next App",
},
// see https://github.com/cloudflare/next-on-pages/blob/main/packages/next-on-pages/docs/supported.md#operating-systems
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment (and the Windows exclusion) actually valid for OpenNext?
Not for this PR but perhaps a quick follow-up? cc @vicb

unsupportedOSs: ["win32"],
unsupportedPms: [
// bun and yarn are failing in CI
"bun",
"yarn",
],
},
"nuxt:pages": {
argv: ["--platform", "pages"],
testCommitMessage: true,
Expand Down
2 changes: 0 additions & 2 deletions packages/create-cloudflare/src/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
writeFile,
writeJSON,
} from "helpers/files";
import nextTemplateExperimental from "templates-experimental/next/c3";
import solidTemplateExperimental from "templates-experimental/solid/c3";
import analogTemplate from "templates/analog/c3";
import angularTemplate from "templates/angular/c3";
Expand Down Expand Up @@ -175,7 +174,6 @@ export type TemplateMap = Record<
export function getFrameworkMap({ experimental = false }): TemplateMap {
if (experimental) {
return {
next: nextTemplateExperimental,
solid: solidTemplateExperimental,
};
} else {
Expand Down
228 changes: 7 additions & 221 deletions packages/create-cloudflare/templates/next/c3.ts
Original file line number Diff line number Diff line change
@@ -1,223 +1,9 @@
import { join } from "path";
import { updateStatus, warn } from "@cloudflare/cli";
import { brandColor, dim } from "@cloudflare/cli/colors";
import { inputPrompt, spinner } from "@cloudflare/cli/interactive";
import { runFrameworkGenerator } from "frameworks/index";
import {
copyFile,
probePaths,
readFile,
readJSON,
usesEslint,
usesTypescript,
writeFile,
writeJSON,
} from "helpers/files";
import { detectPackageManager } from "helpers/packageManagers";
import { installPackages } from "helpers/packages";
import { getTemplatePath } from "../../src/templates";
import type { TemplateConfig } from "../../src/templates";
import type { C3Context } from "types";
import pages from "./pages/c3";
import workers from "./workers/c3";
import type { MultiPlatformTemplateConfig } from "../../src/templates";

const { npm, npx } = detectPackageManager();

const generate = async (ctx: C3Context) => {
const projectName = ctx.project.name;

await runFrameworkGenerator(ctx, [projectName]);

const wranglerConfig = readFile(join(getTemplatePath(ctx), "wrangler.jsonc"));
writeFile(join(ctx.project.path, "wrangler.jsonc"), wranglerConfig);
updateStatus("Created wrangler.jsonc file");
};

const updateNextConfig = (usesTs: boolean) => {
const s = spinner();

const configFile = `next.config.${usesTs ? "ts" : "mjs"}`;
s.start(`Updating \`${configFile}\``);

const configContent = readFile(configFile);

const updatedConfigFile =
`import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev';

// Here we use the @cloudflare/next-on-pages next-dev module to allow us to
// use bindings during local development (when running the application with
// \`next dev\`). This function is only necessary during development and
// has no impact outside of that. For more information see:
// https://github.com/cloudflare/next-on-pages/blob/main/internal-packages/next-dev/README.md
setupDevPlatform().catch(console.error);

`.replace(/\n\t*/g, "\n") + configContent;

writeFile(configFile, updatedConfigFile);

s.stop(`${brandColor(`updated`)} ${dim(`\`${configFile}\``)}`);
};

const configure = async (ctx: C3Context) => {
const projectPath = ctx.project.path;

// Add a compatible function handler example
const path = probePaths([
`${projectPath}/pages/api`,
`${projectPath}/src/pages/api`,
`${projectPath}/src/app/api`,
`${projectPath}/app/api`,
`${projectPath}/src/app`,
`${projectPath}/app`,
]);

if (!path) {
throw new Error("Could not find the `/api` or `/app` directory");
}

const usesTs = usesTypescript(ctx);

if (usesTs) {
copyFile(
join(getTemplatePath(ctx), "env.d.ts"),
join(projectPath, "env.d.ts"),
);
updateStatus("Created an env.d.ts file");
}

const installEslintPlugin = await shouldInstallNextOnPagesEslintPlugin(ctx);

if (installEslintPlugin) {
await writeEslintrc(ctx);
}

updateNextConfig(usesTs);

copyFile(
join(getTemplatePath(ctx), "README.md"),
join(projectPath, "README.md"),
);
updateStatus("Updated the README file");

await addDevDependencies(installEslintPlugin);
};

export const shouldInstallNextOnPagesEslintPlugin = async (
ctx: C3Context,
): Promise<boolean> => {
const eslintUsage = usesEslint(ctx);

if (!eslintUsage.used) {
return false;
}

if (eslintUsage.configType !== ".eslintrc.json") {
warn(
`Expected .eslintrc.json from Next.js scaffolding but found ${eslintUsage.configType} instead`,
);
return false;
}

return await inputPrompt({
type: "confirm",
question: "Do you want to use the next-on-pages eslint-plugin?",
label: "eslint-plugin",
defaultValue: true,
});
};

export const writeEslintrc = async (ctx: C3Context): Promise<void> => {
const eslintConfig = readJSON(`${ctx.project.path}/.eslintrc.json`) as {
plugins: string[];
extends: string | string[];
};

eslintConfig.plugins ??= [];
eslintConfig.plugins.push("eslint-plugin-next-on-pages");

if (typeof eslintConfig.extends === "string") {
eslintConfig.extends = [eslintConfig.extends];
}
eslintConfig.extends ??= [];
eslintConfig.extends.push("plugin:eslint-plugin-next-on-pages/recommended");

writeJSON(`${ctx.project.path}/.eslintrc.json`, eslintConfig);
};

const addDevDependencies = async (installEslintPlugin: boolean) => {
const packages = [
"@cloudflare/next-on-pages@1",
"@cloudflare/workers-types",
"vercel",
...(installEslintPlugin ? ["eslint-plugin-next-on-pages"] : []),
];
await installPackages(packages, {
dev: true,
startText: "Adding the Cloudflare Pages adapter",
doneText: `${brandColor(`installed`)} ${dim(packages.join(", "))}`,
});
};

export default {
configVersion: 1,
id: "next",
frameworkCli: "create-next-app",
platform: "pages",
const config: MultiPlatformTemplateConfig = {
displayName: "Next.js",
generate,
configure,
copyFiles: {
async selectVariant(ctx) {
const isApp = probePaths([
`${ctx.project.path}/src/app`,
`${ctx.project.path}/app`,
]);

const isTypescript = usesTypescript(ctx);

const dir = isApp ? "app" : "pages";
return `${dir}/${isTypescript ? "ts" : "js"}`;
},
destinationDir(ctx) {
const srcPath = probePaths([`${ctx.project.path}/src`]);
return srcPath ? "./src" : "./";
},
variants: {
"app/ts": {
path: "./app/ts",
},
"app/js": {
path: "./app/js",
},
"pages/ts": {
path: "./pages/ts",
},
"pages/js": {
path: "./pages/js",
},
},
},
transformPackageJson: async (_, ctx) => {
const isNpm = npm === "npm";
const isBun = npm === "bun";
const isNpmOrBun = isNpm || isBun;
const nextOnPagesScope = isNpmOrBun ? "@cloudflare/" : "";
const nextOnPagesCommand = `${nextOnPagesScope}next-on-pages`;
const pmCommand = isNpmOrBun ? npx : npm;
const pagesBuildRunCommand = `${
isNpm ? "npm run" : isBun ? "bun" : pmCommand
} pages:build`;
return {
scripts: {
"pages:build": `${pmCommand} ${nextOnPagesCommand}`,
preview: `${pagesBuildRunCommand} && wrangler pages dev`,
deploy: `${pagesBuildRunCommand} && wrangler pages deploy`,
...(usesTypescript(ctx) && {
"cf-typegen": `wrangler types --env-interface CloudflareEnv env.d.ts`,
}),
},
};
},
devScript: "dev",
previewScript: "preview",
deployScript: "deploy",
compatibilityFlags: ["nodejs_compat"],
} as TemplateConfig;
platformVariants: { pages, workers },
};
export default config;
Loading
Loading