Skip to content

Commit

Permalink
fix(create-cloudflare): update next.js templates + support, use next-…
Browse files Browse the repository at this point in the history
…on-pages v1 (#3245)

* feat(create-cloudflare): update next.js templates + support, use next-on-pages v1

* chore: changeset

* update message+naming for api path retrieval

* fix: set `nodejs_compat` compatibility flag when creating the pages project
  • Loading branch information
james-elicx committed May 23, 2023
1 parent 3dae258 commit 4082cfc
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/big-forks-poke.md
@@ -0,0 +1,5 @@
---
"create-cloudflare": patch
---

Support for setting compatibility flags for each framework when creating a new pages project.
5 changes: 5 additions & 0 deletions .changeset/shy-lobsters-occur.md
@@ -0,0 +1,5 @@
---
"create-cloudflare": patch
---

Fix support for creating API route handlers in the Next.js template when using the app directory.
63 changes: 46 additions & 17 deletions packages/create-cloudflare/src/frameworks/next/index.ts
@@ -1,13 +1,19 @@
import { mkdirSync } from "fs";
import { updateStatus } from "helpers/cli";
import { blue, brandColor, dim } from "helpers/colors";
import { brandColor, dim } from "helpers/colors";
import {
detectPackageManager,
installPackages,
runFrameworkGenerator,
} from "helpers/command";
import { probePaths, usesTypescript, writeFile } from "helpers/files";
import { getFrameworkVersion } from "../index";
import { apiHelloJs, apiHelloTs, nextConfigJs } from "./templates";
import {
apiAppDirHelloJs,
apiAppDirHelloTs,
apiPagesDirHelloJs,
apiPagesDirHelloTs,
} from "./templates";
import type { PagesGeneratorContext, FrameworkConfig } from "types";

const { npm, npx } = detectPackageManager();
Expand All @@ -22,34 +28,56 @@ const generate = async (ctx: PagesGeneratorContext) => {
);
};

const getApiTemplate = (
apiPath: string,
isTypescript: boolean
): [string, string] => {
const isAppDir = /\/app\/api$/.test(apiPath);

if (isAppDir) {
// App directory uses route handlers that are defined in a subdirectory (`/api/hello/route.ts`).
const routeHandlerPath = `${apiPath}/hello`;
mkdirSync(routeHandlerPath, { recursive: true });

return isTypescript
? [`${routeHandlerPath}/route.ts`, apiAppDirHelloTs]
: [`${routeHandlerPath}/route.js`, apiAppDirHelloJs];
}

return isTypescript
? [`${apiPath}/hello.ts`, apiPagesDirHelloTs]
: [`${apiPath}/hello.js`, apiPagesDirHelloJs];
};

const configure = async (ctx: PagesGeneratorContext) => {
const projectName = ctx.project.name;

// Add a compatible function handler example
const apiPath = probePaths(
const path = probePaths(
[
`${projectName}/pages/api`,
`${projectName}/src/pages/api`,
`${projectName}/src/app/api`,
`${projectName}/app/api`,
`${projectName}/src/app`,
`${projectName}/app`,
],
"Could not find the `/api` directory"
"Could not find the `/api` or `/app` directory"
);
const [handlerPath, handlerFile] = usesTypescript(projectName)
? [`${apiPath}/hello.ts`, apiHelloTs]
: [`${apiPath}/hello.js`, apiHelloJs];
writeFile(handlerPath, handlerFile);
updateStatus("Created an example API route handler");

// Add a next config that opts into edge runtime
writeFile(`./${projectName}/next.config.js`, nextConfigJs);
updateStatus(
`Added experimental edge runtime flag to ${blue("next.config.js")}`
// App directory template may not generate an API route handler, so we update the path to add an `api` directory.
const apiPath = path.replace(/\/app$/, "/app/api");

const [handlerPath, handlerFile] = getApiTemplate(
apiPath,
usesTypescript(projectName)
);
writeFile(handlerPath, handlerFile);
updateStatus("Created an example API route handler");

// Add some dev dependencies
process.chdir(projectName);
const packages = ["@cloudflare/next-on-pages", "vercel"];
const packages = ["@cloudflare/next-on-pages@1", "vercel"];
await installPackages(packages, {
dev: true,
startText: "Adding the Cloudflare Pages adapter",
Expand All @@ -62,9 +90,9 @@ const config: FrameworkConfig = {
configure,
displayName: "Next",
packageScripts: {
"pages:build": `${npx} @cloudflare/next-on-pages --experimental-minify`,
"pages:build": `${npx} @cloudflare/next-on-pages@1`,
"pages:deploy": `${npm} run pages:build && wrangler pages publish .vercel/output/static`,
"pages:watch": `${npx} @cloudflare/next-on-pages --watch`,
"pages:watch": `${npx} @cloudflare/next-on-pages@1 --watch`,
"pages:dev": `${npx} wrangler pages dev .vercel/output/static --compatibility-flag=nodejs_compat`,
},
testFlags: [
Expand All @@ -73,9 +101,10 @@ const config: FrameworkConfig = {
"--eslint",
"--tailwind",
"--src-dir",
"--experimental-app",
"--app",
"--import-alias",
'"@/*"',
],
compatibilityFlags: ["nodejs_compat"],
};
export default config;
32 changes: 22 additions & 10 deletions packages/create-cloudflare/src/frameworks/next/templates.ts
@@ -1,5 +1,5 @@
export const apiHelloTs = `
// Next.js Edge API Routes: https://nextjs.org/docs/api-routes/edge-api-routes
export const apiPagesDirHelloTs = `
// Next.js Edge API Routes: https://nextjs.org/docs/pages/building-your-application/routing/api-routes#edge-api-routes
import type { NextRequest } from 'next/server'
Expand All @@ -12,8 +12,8 @@ export default async function handler(req: NextRequest) {
}
`;

export const apiHelloJs = `
// Next.js Edge API Routes: https://nextjs.org/docs/api-routes/edge-api-routes
export const apiPagesDirHelloJs = `
// Next.js Edge API Routes: https://nextjs.org/docs/pages/building-your-application/routing/api-routes#edge-api-routes
export const config = {
runtime: 'edge',
Expand All @@ -24,12 +24,24 @@ export default async function handler(req) {
}
`;

export const nextConfigJs = `
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
runtime: 'edge',
export const apiAppDirHelloTs = `
// Next.js Edge API Route Handlers: https://nextjs.org/docs/app/building-your-application/routing/router-handlers#edge-and-nodejs-runtimes
import type { NextRequest } from 'next/server'
export const runtime = 'edge'
export async function GET(request: NextRequest) {
return new Response(JSON.stringify({ name: 'John Doe' }))
}
`;

export const apiAppDirHelloJs = `
// Next.js Edge API Route Handlers: https://nextjs.org/docs/app/building-your-application/routing/router-handlers#edge-and-nodejs-runtimes
export const runtime = 'edge'
module.exports = nextConfig
export async function GET(request) {
return new Response(JSON.stringify({ name: 'John Doe' }))
}
`;
2 changes: 1 addition & 1 deletion packages/create-cloudflare/src/frameworks/versionMap.json
Expand Up @@ -4,7 +4,7 @@
"docusaurus": "2.4.1",
"gatsby": "5.10.0",
"hono": "0.2.0",
"next": "13.3.4",
"next": "13.4.2",
"nuxt": "3.4.2",
"qwik": "1.1.x",
"react": "5.0.1",
Expand Down
8 changes: 7 additions & 1 deletion packages/create-cloudflare/src/pages.ts
Expand Up @@ -131,7 +131,13 @@ const createProject = async (ctx: PagesGeneratorContext) => {
return;
}
const CLOUDFLARE_ACCOUNT_ID = ctx.account.id;
const cmd = `${npx} wrangler pages project create ${ctx.project.name} --production-branch main`;

const compatFlags = ctx.framework?.config.compatibilityFlags?.join(" ");
const compatFlagsArg = compatFlags
? `--compatibility-flags ${compatFlags}`
: "";

const cmd = `${npx} wrangler pages project create ${ctx.project.name} --production-branch main ${compatFlagsArg}`;

try {
await retry(CREATE_PROJECT_RETRIES, async () =>
Expand Down
1 change: 1 addition & 0 deletions packages/create-cloudflare/src/types.ts
Expand Up @@ -39,4 +39,5 @@ export type FrameworkConfig = {
deployCommand?: string;
devCommand?: string;
testFlags?: string[];
compatibilityFlags?: string[];
};

0 comments on commit 4082cfc

Please sign in to comment.