|
1 | | -import { join } from "path"; |
2 | | -import { updateStatus, warn } from "@cloudflare/cli"; |
3 | | -import { brandColor, dim } from "@cloudflare/cli/colors"; |
4 | | -import { inputPrompt, spinner } from "@cloudflare/cli/interactive"; |
5 | | -import { runFrameworkGenerator } from "frameworks/index"; |
6 | | -import { |
7 | | - copyFile, |
8 | | - probePaths, |
9 | | - readFile, |
10 | | - readJSON, |
11 | | - usesEslint, |
12 | | - usesTypescript, |
13 | | - writeFile, |
14 | | - writeJSON, |
15 | | -} from "helpers/files"; |
16 | | -import { detectPackageManager } from "helpers/packageManagers"; |
17 | | -import { installPackages } from "helpers/packages"; |
18 | | -import { getTemplatePath } from "../../src/templates"; |
19 | | -import type { TemplateConfig } from "../../src/templates"; |
20 | | -import type { C3Context } from "types"; |
| 1 | +import pages from "./pages/c3"; |
| 2 | +import workers from "./workers/c3"; |
| 3 | +import type { MultiPlatformTemplateConfig } from "../../src/templates"; |
21 | 4 |
|
22 | | -const { npm, npx } = detectPackageManager(); |
23 | | - |
24 | | -const generate = async (ctx: C3Context) => { |
25 | | - const projectName = ctx.project.name; |
26 | | - |
27 | | - await runFrameworkGenerator(ctx, [projectName]); |
28 | | - |
29 | | - const wranglerConfig = readFile(join(getTemplatePath(ctx), "wrangler.jsonc")); |
30 | | - writeFile(join(ctx.project.path, "wrangler.jsonc"), wranglerConfig); |
31 | | - updateStatus("Created wrangler.jsonc file"); |
32 | | -}; |
33 | | - |
34 | | -const updateNextConfig = (usesTs: boolean) => { |
35 | | - const s = spinner(); |
36 | | - |
37 | | - const configFile = `next.config.${usesTs ? "ts" : "mjs"}`; |
38 | | - s.start(`Updating \`${configFile}\``); |
39 | | - |
40 | | - const configContent = readFile(configFile); |
41 | | - |
42 | | - const updatedConfigFile = |
43 | | - `import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev'; |
44 | | -
|
45 | | - // Here we use the @cloudflare/next-on-pages next-dev module to allow us to |
46 | | - // use bindings during local development (when running the application with |
47 | | - // \`next dev\`). This function is only necessary during development and |
48 | | - // has no impact outside of that. For more information see: |
49 | | - // https://github.com/cloudflare/next-on-pages/blob/main/internal-packages/next-dev/README.md |
50 | | - setupDevPlatform().catch(console.error); |
51 | | -
|
52 | | - `.replace(/\n\t*/g, "\n") + configContent; |
53 | | - |
54 | | - writeFile(configFile, updatedConfigFile); |
55 | | - |
56 | | - s.stop(`${brandColor(`updated`)} ${dim(`\`${configFile}\``)}`); |
57 | | -}; |
58 | | - |
59 | | -const configure = async (ctx: C3Context) => { |
60 | | - const projectPath = ctx.project.path; |
61 | | - |
62 | | - // Add a compatible function handler example |
63 | | - const path = probePaths([ |
64 | | - `${projectPath}/pages/api`, |
65 | | - `${projectPath}/src/pages/api`, |
66 | | - `${projectPath}/src/app/api`, |
67 | | - `${projectPath}/app/api`, |
68 | | - `${projectPath}/src/app`, |
69 | | - `${projectPath}/app`, |
70 | | - ]); |
71 | | - |
72 | | - if (!path) { |
73 | | - throw new Error("Could not find the `/api` or `/app` directory"); |
74 | | - } |
75 | | - |
76 | | - const usesTs = usesTypescript(ctx); |
77 | | - |
78 | | - if (usesTs) { |
79 | | - copyFile( |
80 | | - join(getTemplatePath(ctx), "env.d.ts"), |
81 | | - join(projectPath, "env.d.ts"), |
82 | | - ); |
83 | | - updateStatus("Created an env.d.ts file"); |
84 | | - } |
85 | | - |
86 | | - const installEslintPlugin = await shouldInstallNextOnPagesEslintPlugin(ctx); |
87 | | - |
88 | | - if (installEslintPlugin) { |
89 | | - await writeEslintrc(ctx); |
90 | | - } |
91 | | - |
92 | | - updateNextConfig(usesTs); |
93 | | - |
94 | | - copyFile( |
95 | | - join(getTemplatePath(ctx), "README.md"), |
96 | | - join(projectPath, "README.md"), |
97 | | - ); |
98 | | - updateStatus("Updated the README file"); |
99 | | - |
100 | | - await addDevDependencies(installEslintPlugin); |
101 | | -}; |
102 | | - |
103 | | -export const shouldInstallNextOnPagesEslintPlugin = async ( |
104 | | - ctx: C3Context, |
105 | | -): Promise<boolean> => { |
106 | | - const eslintUsage = usesEslint(ctx); |
107 | | - |
108 | | - if (!eslintUsage.used) { |
109 | | - return false; |
110 | | - } |
111 | | - |
112 | | - if (eslintUsage.configType !== ".eslintrc.json") { |
113 | | - warn( |
114 | | - `Expected .eslintrc.json from Next.js scaffolding but found ${eslintUsage.configType} instead`, |
115 | | - ); |
116 | | - return false; |
117 | | - } |
118 | | - |
119 | | - return await inputPrompt({ |
120 | | - type: "confirm", |
121 | | - question: "Do you want to use the next-on-pages eslint-plugin?", |
122 | | - label: "eslint-plugin", |
123 | | - defaultValue: true, |
124 | | - }); |
125 | | -}; |
126 | | - |
127 | | -export const writeEslintrc = async (ctx: C3Context): Promise<void> => { |
128 | | - const eslintConfig = readJSON(`${ctx.project.path}/.eslintrc.json`) as { |
129 | | - plugins: string[]; |
130 | | - extends: string | string[]; |
131 | | - }; |
132 | | - |
133 | | - eslintConfig.plugins ??= []; |
134 | | - eslintConfig.plugins.push("eslint-plugin-next-on-pages"); |
135 | | - |
136 | | - if (typeof eslintConfig.extends === "string") { |
137 | | - eslintConfig.extends = [eslintConfig.extends]; |
138 | | - } |
139 | | - eslintConfig.extends ??= []; |
140 | | - eslintConfig.extends.push("plugin:eslint-plugin-next-on-pages/recommended"); |
141 | | - |
142 | | - writeJSON(`${ctx.project.path}/.eslintrc.json`, eslintConfig); |
143 | | -}; |
144 | | - |
145 | | -const addDevDependencies = async (installEslintPlugin: boolean) => { |
146 | | - const packages = [ |
147 | | - "@cloudflare/next-on-pages@1", |
148 | | - "@cloudflare/workers-types", |
149 | | - "vercel", |
150 | | - ...(installEslintPlugin ? ["eslint-plugin-next-on-pages"] : []), |
151 | | - ]; |
152 | | - await installPackages(packages, { |
153 | | - dev: true, |
154 | | - startText: "Adding the Cloudflare Pages adapter", |
155 | | - doneText: `${brandColor(`installed`)} ${dim(packages.join(", "))}`, |
156 | | - }); |
157 | | -}; |
158 | | - |
159 | | -export default { |
160 | | - configVersion: 1, |
161 | | - id: "next", |
162 | | - frameworkCli: "create-next-app", |
163 | | - platform: "pages", |
| 5 | +const config: MultiPlatformTemplateConfig = { |
164 | 6 | displayName: "Next.js", |
165 | | - generate, |
166 | | - configure, |
167 | | - copyFiles: { |
168 | | - async selectVariant(ctx) { |
169 | | - const isApp = probePaths([ |
170 | | - `${ctx.project.path}/src/app`, |
171 | | - `${ctx.project.path}/app`, |
172 | | - ]); |
173 | | - |
174 | | - const isTypescript = usesTypescript(ctx); |
175 | | - |
176 | | - const dir = isApp ? "app" : "pages"; |
177 | | - return `${dir}/${isTypescript ? "ts" : "js"}`; |
178 | | - }, |
179 | | - destinationDir(ctx) { |
180 | | - const srcPath = probePaths([`${ctx.project.path}/src`]); |
181 | | - return srcPath ? "./src" : "./"; |
182 | | - }, |
183 | | - variants: { |
184 | | - "app/ts": { |
185 | | - path: "./app/ts", |
186 | | - }, |
187 | | - "app/js": { |
188 | | - path: "./app/js", |
189 | | - }, |
190 | | - "pages/ts": { |
191 | | - path: "./pages/ts", |
192 | | - }, |
193 | | - "pages/js": { |
194 | | - path: "./pages/js", |
195 | | - }, |
196 | | - }, |
197 | | - }, |
198 | | - transformPackageJson: async (_, ctx) => { |
199 | | - const isNpm = npm === "npm"; |
200 | | - const isBun = npm === "bun"; |
201 | | - const isNpmOrBun = isNpm || isBun; |
202 | | - const nextOnPagesScope = isNpmOrBun ? "@cloudflare/" : ""; |
203 | | - const nextOnPagesCommand = `${nextOnPagesScope}next-on-pages`; |
204 | | - const pmCommand = isNpmOrBun ? npx : npm; |
205 | | - const pagesBuildRunCommand = `${ |
206 | | - isNpm ? "npm run" : isBun ? "bun" : pmCommand |
207 | | - } pages:build`; |
208 | | - return { |
209 | | - scripts: { |
210 | | - "pages:build": `${pmCommand} ${nextOnPagesCommand}`, |
211 | | - preview: `${pagesBuildRunCommand} && wrangler pages dev`, |
212 | | - deploy: `${pagesBuildRunCommand} && wrangler pages deploy`, |
213 | | - ...(usesTypescript(ctx) && { |
214 | | - "cf-typegen": `wrangler types --env-interface CloudflareEnv env.d.ts`, |
215 | | - }), |
216 | | - }, |
217 | | - }; |
218 | | - }, |
219 | | - devScript: "dev", |
220 | | - previewScript: "preview", |
221 | | - deployScript: "deploy", |
222 | | - compatibilityFlags: ["nodejs_compat"], |
223 | | -} as TemplateConfig; |
| 7 | + platformVariants: { pages, workers }, |
| 8 | +}; |
| 9 | +export default config; |
0 commit comments