@electroplix/adapter 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/bin/cli.mjs +116 -0
- package/dist/README.md +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +397 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/lib/adapter.d.ts +1 -0
- package/dist/src/lib/adapter.d.ts.map +1 -0
- package/dist/src/lib/configs.d.ts +16 -0
- package/dist/src/lib/configs.d.ts.map +1 -0
- package/dist/src/lib/detect.d.ts +33 -0
- package/dist/src/lib/detect.d.ts.map +1 -0
- package/dist/src/lib/github-actions.d.ts +11 -0
- package/dist/src/lib/github-actions.d.ts.map +1 -0
- package/dist/src/lib/logger.d.ts +10 -0
- package/dist/src/lib/logger.d.ts.map +1 -0
- package/dist/src/lib/packages.d.ts +10 -0
- package/dist/src/lib/packages.d.ts.map +1 -0
- package/dist/src/lib/prompts.d.ts +9 -0
- package/dist/src/lib/prompts.d.ts.map +1 -0
- package/dist/src/lib/scripts.d.ts +18 -0
- package/dist/src/lib/scripts.d.ts.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/* ------------------------------------------------------------------ */
|
|
4
|
+
/* @electroplix/adapter CLI */
|
|
5
|
+
/* Configure a Next.js project for Cloudflare Workers via OpenNext */
|
|
6
|
+
/* ------------------------------------------------------------------ */
|
|
7
|
+
|
|
8
|
+
import { resolve } from 'node:path';
|
|
9
|
+
import { createInterface } from 'node:readline';
|
|
10
|
+
import {
|
|
11
|
+
detectProject,
|
|
12
|
+
installPackages,
|
|
13
|
+
injectScripts,
|
|
14
|
+
writeConfigFiles,
|
|
15
|
+
writeGitHubActions,
|
|
16
|
+
logger,
|
|
17
|
+
} from '../dist/index.esm.js';
|
|
18
|
+
|
|
19
|
+
/* ── tiny prompt helpers ──────────────────────────────────────────── */
|
|
20
|
+
|
|
21
|
+
function rl() {
|
|
22
|
+
return createInterface({ input: process.stdin, output: process.stdout });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function confirm(question, defaultYes = true) {
|
|
26
|
+
const hint = defaultYes ? '[Y/n]' : '[y/N]';
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
const r = rl();
|
|
29
|
+
r.question(`${question} ${hint} `, (answer) => {
|
|
30
|
+
r.close();
|
|
31
|
+
const a = answer.trim().toLowerCase();
|
|
32
|
+
if (a === '') return resolve(defaultYes);
|
|
33
|
+
resolve(a === 'y' || a === 'yes');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function input(question, defaultValue) {
|
|
39
|
+
const hint = defaultValue ? ` (${defaultValue})` : '';
|
|
40
|
+
return new Promise((res) => {
|
|
41
|
+
const r = rl();
|
|
42
|
+
r.question(`${question}${hint}: `, (answer) => {
|
|
43
|
+
r.close();
|
|
44
|
+
res(answer.trim() || defaultValue || '');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ── main ─────────────────────────────────────────────────────────── */
|
|
50
|
+
|
|
51
|
+
async function run() {
|
|
52
|
+
logger.banner();
|
|
53
|
+
|
|
54
|
+
const root = resolve(process.cwd());
|
|
55
|
+
const info = detectProject(root);
|
|
56
|
+
|
|
57
|
+
logger.info(`Project root: ${root}`);
|
|
58
|
+
logger.info(`Package manager: ${info.packageManager}`);
|
|
59
|
+
|
|
60
|
+
if (!info.hasPackageJson) {
|
|
61
|
+
logger.error('No package.json found. Run this inside a Next.js project.');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
if (!info.hasNext) {
|
|
65
|
+
logger.error('Next.js is not installed. Run this inside a Next.js project.');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
logger.success(`Detected Next.js ${info.nextVersion ?? '(unknown version)'}`);
|
|
70
|
+
|
|
71
|
+
const defaultName = root.split(/[\\/]/).pop() ?? 'my-nextjs-app';
|
|
72
|
+
const projectName = await input('Worker name for Cloudflare', defaultName);
|
|
73
|
+
|
|
74
|
+
const hasExisting = info.hasOpenNext || info.hasWrangler || info.hasOpenNextConfig;
|
|
75
|
+
let overwrite = false;
|
|
76
|
+
if (hasExisting) {
|
|
77
|
+
overwrite = await confirm('Some config files already exist. Overwrite them?', false);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* 1. install packages */
|
|
81
|
+
if (!info.hasOpenNext) {
|
|
82
|
+
installPackages(info.packageManager, root);
|
|
83
|
+
} else {
|
|
84
|
+
logger.success('@opennextjs/cloudflare is already installed.');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* 2. inject scripts */
|
|
88
|
+
logger.step('Configuring package.json scripts…');
|
|
89
|
+
injectScripts(root, overwrite);
|
|
90
|
+
|
|
91
|
+
/* 3. write config files */
|
|
92
|
+
logger.step('Writing configuration files…');
|
|
93
|
+
writeConfigFiles(root, projectName, overwrite);
|
|
94
|
+
|
|
95
|
+
/* 4. optional GitHub Actions */
|
|
96
|
+
const addGHA = await confirm('Add GitHub Actions CI/CD deploy pipeline?', false);
|
|
97
|
+
if (addGHA) {
|
|
98
|
+
logger.step('Generating GitHub Actions workflow…');
|
|
99
|
+
writeGitHubActions(root, info.packageManager, overwrite);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log('');
|
|
103
|
+
logger.success('Setup complete! Your Next.js project is now configured for Cloudflare Workers.');
|
|
104
|
+
console.log('');
|
|
105
|
+
logger.info('Next steps:');
|
|
106
|
+
const run = info.packageManager === 'npm' ? 'npm run' : info.packageManager;
|
|
107
|
+
logger.dim(`${run} dev → Start local dev server`);
|
|
108
|
+
logger.dim(`${run} preview → Build & preview on Workers locally`);
|
|
109
|
+
logger.dim(`${run} deploy → Build & deploy to Cloudflare Workers`);
|
|
110
|
+
console.log('');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
run().catch((err) => {
|
|
114
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
package/dist/README.md
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./src\\index";
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
function _extends() {
|
|
6
|
+
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
7
|
+
for (var e = 1; e < arguments.length; e++) {
|
|
8
|
+
var t = arguments[e];
|
|
9
|
+
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
10
|
+
}
|
|
11
|
+
return n;
|
|
12
|
+
}, _extends.apply(null, arguments);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Detect the package manager used by the project.
|
|
17
|
+
*/
|
|
18
|
+
function detectPackageManager(root) {
|
|
19
|
+
if (existsSync(join(root, 'bun.lockb')) || existsSync(join(root, 'bun.lock'))) return 'bun';
|
|
20
|
+
if (existsSync(join(root, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
21
|
+
if (existsSync(join(root, 'yarn.lock'))) return 'yarn';
|
|
22
|
+
return 'npm';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Read and parse the project's package.json.
|
|
27
|
+
* Returns null if not found.
|
|
28
|
+
*/
|
|
29
|
+
function readPackageJson(root) {
|
|
30
|
+
const pkgPath = join(root, 'package.json');
|
|
31
|
+
if (!existsSync(pkgPath)) return null;
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
34
|
+
} catch (_unused) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Gather information about the target project.
|
|
41
|
+
*/
|
|
42
|
+
function detectProject(root) {
|
|
43
|
+
var _pkg$dependencies, _pkg$devDependencies, _allDeps$next;
|
|
44
|
+
const pm = detectPackageManager(root);
|
|
45
|
+
const pkg = readPackageJson(root);
|
|
46
|
+
const allDeps = _extends({}, (_pkg$dependencies = pkg == null ? void 0 : pkg.dependencies) != null ? _pkg$dependencies : {}, (_pkg$devDependencies = pkg == null ? void 0 : pkg.devDependencies) != null ? _pkg$devDependencies : {});
|
|
47
|
+
return {
|
|
48
|
+
root,
|
|
49
|
+
packageManager: pm,
|
|
50
|
+
hasPackageJson: pkg !== null,
|
|
51
|
+
hasNext: 'next' in allDeps,
|
|
52
|
+
nextVersion: (_allDeps$next = allDeps['next']) != null ? _allDeps$next : null,
|
|
53
|
+
hasOpenNext: '@opennextjs/cloudflare' in allDeps,
|
|
54
|
+
hasWrangler: existsSync(join(root, 'wrangler.jsonc')) || existsSync(join(root, 'wrangler.toml')),
|
|
55
|
+
hasOpenNextConfig: existsSync(join(root, 'open-next.config.ts'))
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ------------------------------------------------------------------ */
|
|
60
|
+
/* Simple coloured logger – zero dependencies */
|
|
61
|
+
/* ------------------------------------------------------------------ */
|
|
62
|
+
|
|
63
|
+
const RESET = '\x1b[0m';
|
|
64
|
+
const BOLD = '\x1b[1m';
|
|
65
|
+
const GREEN = '\x1b[32m';
|
|
66
|
+
const YELLOW = '\x1b[33m';
|
|
67
|
+
const RED = '\x1b[31m';
|
|
68
|
+
const CYAN = '\x1b[36m';
|
|
69
|
+
const DIM = '\x1b[2m';
|
|
70
|
+
const logger = {
|
|
71
|
+
info(msg) {
|
|
72
|
+
console.log(`${CYAN}ℹ${RESET} ${msg}`);
|
|
73
|
+
},
|
|
74
|
+
success(msg) {
|
|
75
|
+
console.log(`${GREEN}✔${RESET} ${msg}`);
|
|
76
|
+
},
|
|
77
|
+
warn(msg) {
|
|
78
|
+
console.log(`${YELLOW}⚠${RESET} ${msg}`);
|
|
79
|
+
},
|
|
80
|
+
error(msg) {
|
|
81
|
+
console.error(`${RED}✖${RESET} ${msg}`);
|
|
82
|
+
},
|
|
83
|
+
step(msg) {
|
|
84
|
+
console.log(`\n${BOLD}${CYAN}▸ ${msg}${RESET}`);
|
|
85
|
+
},
|
|
86
|
+
dim(msg) {
|
|
87
|
+
console.log(`${DIM} ${msg}${RESET}`);
|
|
88
|
+
},
|
|
89
|
+
banner() {
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log(`${BOLD}${CYAN} ╔══════════════════════════════════════════════════╗${RESET}`);
|
|
92
|
+
console.log(`${BOLD}${CYAN} ║ ║${RESET}`);
|
|
93
|
+
console.log(`${BOLD}${CYAN} ║ ${YELLOW} ⚡⚡${CYAN} ║${RESET}`);
|
|
94
|
+
console.log(`${BOLD}${CYAN} ║ ${YELLOW} ⚡⚡${CYAN} ║${RESET}`);
|
|
95
|
+
console.log(`${BOLD}${CYAN} ║ ${YELLOW} ⚡⚡⚡⚡${CYAN} ║${RESET}`);
|
|
96
|
+
console.log(`${BOLD}${CYAN} ║ ${YELLOW} ⚡⚡${CYAN} ║${RESET}`);
|
|
97
|
+
console.log(`${BOLD}${CYAN} ║ ${YELLOW} ⚡⚡${CYAN} ║${RESET}`);
|
|
98
|
+
console.log(`${BOLD}${CYAN} ║ ${YELLOW} ⚡${CYAN} ║${RESET}`);
|
|
99
|
+
console.log(`${BOLD}${CYAN} ║ ║${RESET}`);
|
|
100
|
+
console.log(`${BOLD}${CYAN} ║ ${BOLD}${YELLOW}⚡ Electroplix Deploy Adapter${CYAN} ║${RESET}`);
|
|
101
|
+
console.log(`${BOLD}${CYAN} ║ ${DIM}Cloudflare Workers deploy for Next.js${RESET}${BOLD}${CYAN} ║${RESET}`);
|
|
102
|
+
console.log(`${BOLD}${CYAN} ║ ║${RESET}`);
|
|
103
|
+
console.log(`${BOLD}${CYAN} ╚══════════════════════════════════════════════════╝${RESET}`);
|
|
104
|
+
console.log('');
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/* ------------------------------------------------------------------ */
|
|
109
|
+
/* Install/uninstall packages via detected package manager */
|
|
110
|
+
/* ------------------------------------------------------------------ */
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
/** Packages that will be added as dependencies */
|
|
114
|
+
const REQUIRED_DEPS = ['@opennextjs/cloudflare'];
|
|
115
|
+
|
|
116
|
+
/** Packages that will be added as devDependencies */
|
|
117
|
+
const REQUIRED_DEV_DEPS = ['wrangler'];
|
|
118
|
+
function runCmd(cmd, cwd) {
|
|
119
|
+
logger.dim(cmd);
|
|
120
|
+
execSync(cmd, {
|
|
121
|
+
cwd,
|
|
122
|
+
stdio: 'inherit'
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function installCmd(pm, pkgs, dev) {
|
|
126
|
+
const list = pkgs.join(' ');
|
|
127
|
+
switch (pm) {
|
|
128
|
+
case 'pnpm':
|
|
129
|
+
return `pnpm add ${dev ? '-D ' : ''}${list}`;
|
|
130
|
+
case 'yarn':
|
|
131
|
+
return `yarn add ${dev ? '--dev ' : ''}${list}`;
|
|
132
|
+
case 'bun':
|
|
133
|
+
return `bun add ${dev ? '--dev ' : ''}${list}`;
|
|
134
|
+
default:
|
|
135
|
+
return `npm install ${dev ? '--save-dev ' : ''}${list}`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Install the required opennext + wrangler packages.
|
|
141
|
+
*/
|
|
142
|
+
function installPackages(pm, cwd) {
|
|
143
|
+
logger.step('Installing dependencies…');
|
|
144
|
+
runCmd(installCmd(pm, REQUIRED_DEPS, false), cwd);
|
|
145
|
+
logger.step('Installing dev dependencies…');
|
|
146
|
+
runCmd(installCmd(pm, REQUIRED_DEV_DEPS, true), cwd);
|
|
147
|
+
logger.success('Packages installed.');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ------------------------------------------------------------------ */
|
|
151
|
+
/* Inject / merge npm scripts into the project's package.json */
|
|
152
|
+
/* ------------------------------------------------------------------ */
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Scripts that will be injected into the target project's package.json.
|
|
157
|
+
* Existing scripts with the same key will NOT be overwritten unless the
|
|
158
|
+
* user explicitly opts-in (handled in the CLI prompt layer).
|
|
159
|
+
*/
|
|
160
|
+
const ADAPTER_SCRIPTS = {
|
|
161
|
+
build: 'next build',
|
|
162
|
+
dev: 'next dev',
|
|
163
|
+
start: 'next start',
|
|
164
|
+
preview: 'opennextjs-cloudflare build && opennextjs-cloudflare preview',
|
|
165
|
+
deploy: 'opennextjs-cloudflare build && opennextjs-cloudflare deploy',
|
|
166
|
+
'cf-typegen': 'wrangler types --env-interface CloudflareEnv env.d.ts'
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Merge adapter scripts into the project's package.json.
|
|
170
|
+
*
|
|
171
|
+
* @param root Project root directory
|
|
172
|
+
* @param overwrite When true existing scripts with the same name are replaced
|
|
173
|
+
*/
|
|
174
|
+
function injectScripts(root, overwrite = false) {
|
|
175
|
+
const pkgPath = join(root, 'package.json');
|
|
176
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
177
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
178
|
+
const added = [];
|
|
179
|
+
const skipped = [];
|
|
180
|
+
for (const [name, cmd] of Object.entries(ADAPTER_SCRIPTS)) {
|
|
181
|
+
if (pkg.scripts[name] && !overwrite) {
|
|
182
|
+
skipped.push(name);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
pkg.scripts[name] = cmd;
|
|
186
|
+
added.push(name);
|
|
187
|
+
}
|
|
188
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
189
|
+
if (added.length) logger.success(`Added scripts: ${added.join(', ')}`);
|
|
190
|
+
if (skipped.length) logger.warn(`Skipped existing scripts: ${skipped.join(', ')}`);
|
|
191
|
+
return {
|
|
192
|
+
added,
|
|
193
|
+
skipped
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* ------------------------------------------------------------------ */
|
|
198
|
+
/* Generate configuration files for OpenNext + Cloudflare Workers */
|
|
199
|
+
/* ------------------------------------------------------------------ */
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
/* ── open-next.config.ts ──────────────────────────────────────────── */
|
|
203
|
+
|
|
204
|
+
function openNextConfigContent() {
|
|
205
|
+
return `\
|
|
206
|
+
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
|
|
207
|
+
|
|
208
|
+
export default defineCloudflareConfig({
|
|
209
|
+
// Uncomment to enable R2 cache:
|
|
210
|
+
// import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
|
|
211
|
+
// See https://opennext.js.org/cloudflare/caching for more details
|
|
212
|
+
// incrementalCache: r2IncrementalCache,
|
|
213
|
+
});
|
|
214
|
+
`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* ── wrangler.jsonc ───────────────────────────────────────────────── */
|
|
218
|
+
|
|
219
|
+
function wranglerConfigContent(projectName) {
|
|
220
|
+
return `\
|
|
221
|
+
{
|
|
222
|
+
"$schema": "node_modules/wrangler/config-schema.json",
|
|
223
|
+
"name": "${projectName}",
|
|
224
|
+
"main": ".open-next/worker.js",
|
|
225
|
+
"compatibility_date": "${new Date().toISOString().slice(0, 10)}",
|
|
226
|
+
"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
|
|
227
|
+
"assets": {
|
|
228
|
+
"binding": "ASSETS",
|
|
229
|
+
"directory": ".open-next/assets"
|
|
230
|
+
},
|
|
231
|
+
"observability": {
|
|
232
|
+
"enabled": true
|
|
233
|
+
},
|
|
234
|
+
"upload_source_maps": true
|
|
235
|
+
}
|
|
236
|
+
`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* ── env.d.ts (Cloudflare env types) ──────────────────────────────── */
|
|
240
|
+
|
|
241
|
+
function envDtsContent() {
|
|
242
|
+
return `\
|
|
243
|
+
// Generated by @electroplix/adapter
|
|
244
|
+
// Run \`wrangler types --env-interface CloudflareEnv env.d.ts\` to regenerate.
|
|
245
|
+
|
|
246
|
+
interface CloudflareEnv {}
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* ── Writer helpers ───────────────────────────────────────────────── */
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Write all configuration files to the project root.
|
|
254
|
+
*
|
|
255
|
+
* @param root Project root directory
|
|
256
|
+
* @param projectName Name used for the wrangler worker
|
|
257
|
+
* @param overwrite Overwrite existing files
|
|
258
|
+
*/
|
|
259
|
+
function writeConfigFiles(root, projectName, overwrite = false) {
|
|
260
|
+
const created = [];
|
|
261
|
+
const skipped = [];
|
|
262
|
+
const files = [{
|
|
263
|
+
name: 'open-next.config.ts',
|
|
264
|
+
content: openNextConfigContent()
|
|
265
|
+
}, {
|
|
266
|
+
name: 'wrangler.jsonc',
|
|
267
|
+
content: wranglerConfigContent(projectName)
|
|
268
|
+
}, {
|
|
269
|
+
name: 'env.d.ts',
|
|
270
|
+
content: envDtsContent()
|
|
271
|
+
}];
|
|
272
|
+
for (const {
|
|
273
|
+
name,
|
|
274
|
+
content
|
|
275
|
+
} of files) {
|
|
276
|
+
const target = join(root, name);
|
|
277
|
+
if (existsSync(target) && !overwrite) {
|
|
278
|
+
skipped.push(name);
|
|
279
|
+
logger.warn(`Skipped ${name} (already exists)`);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
writeFileSync(target, content, 'utf-8');
|
|
283
|
+
created.push(name);
|
|
284
|
+
logger.success(`Created ${name}`);
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
created,
|
|
288
|
+
skipped
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* ------------------------------------------------------------------ */
|
|
293
|
+
/* Generate GitHub Actions CI/CD deploy pipeline */
|
|
294
|
+
/* ------------------------------------------------------------------ */
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Generate the GitHub Actions workflow YAML for deploying a Next.js
|
|
299
|
+
* app to Cloudflare Workers via OpenNext.
|
|
300
|
+
*/
|
|
301
|
+
function deployWorkflowContent(pm) {
|
|
302
|
+
const installCmd = (() => {
|
|
303
|
+
switch (pm) {
|
|
304
|
+
case 'pnpm':
|
|
305
|
+
return 'pnpm install --frozen-lockfile';
|
|
306
|
+
case 'yarn':
|
|
307
|
+
return 'yarn install --frozen-lockfile';
|
|
308
|
+
case 'bun':
|
|
309
|
+
return 'bun install --frozen-lockfile';
|
|
310
|
+
default:
|
|
311
|
+
return 'npm ci';
|
|
312
|
+
}
|
|
313
|
+
})();
|
|
314
|
+
const pmSetup = pm === 'pnpm' ? `
|
|
315
|
+
- name: Setup pnpm
|
|
316
|
+
uses: pnpm/action-setup@v4
|
|
317
|
+
with:
|
|
318
|
+
run_install: false` : '';
|
|
319
|
+
const deployCmd = (() => {
|
|
320
|
+
switch (pm) {
|
|
321
|
+
case 'pnpm':
|
|
322
|
+
return 'pnpm exec opennextjs-cloudflare build && pnpm exec wrangler deploy';
|
|
323
|
+
case 'yarn':
|
|
324
|
+
return 'yarn opennextjs-cloudflare build && yarn wrangler deploy';
|
|
325
|
+
case 'bun':
|
|
326
|
+
return 'bunx opennextjs-cloudflare build && bunx wrangler deploy';
|
|
327
|
+
default:
|
|
328
|
+
return 'npx opennextjs-cloudflare build && npx wrangler deploy';
|
|
329
|
+
}
|
|
330
|
+
})();
|
|
331
|
+
return `\
|
|
332
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
333
|
+
# Deploy Next.js to Cloudflare Workers via OpenNext
|
|
334
|
+
# Generated by @electroplix/adapter
|
|
335
|
+
# ──────────────────────────────────────────────────────────────────────
|
|
336
|
+
|
|
337
|
+
name: Deploy to Cloudflare Workers
|
|
338
|
+
|
|
339
|
+
on:
|
|
340
|
+
push:
|
|
341
|
+
branches: [main]
|
|
342
|
+
workflow_dispatch:
|
|
343
|
+
|
|
344
|
+
concurrency:
|
|
345
|
+
group: deploy-\${{ github.ref }}
|
|
346
|
+
cancel-in-progress: true
|
|
347
|
+
|
|
348
|
+
jobs:
|
|
349
|
+
deploy:
|
|
350
|
+
name: Build & Deploy
|
|
351
|
+
runs-on: ubuntu-latest
|
|
352
|
+
timeout-minutes: 15
|
|
353
|
+
|
|
354
|
+
permissions:
|
|
355
|
+
contents: read
|
|
356
|
+
deployments: write
|
|
357
|
+
|
|
358
|
+
steps:
|
|
359
|
+
- name: Checkout
|
|
360
|
+
uses: actions/checkout@v4
|
|
361
|
+
${pmSetup}
|
|
362
|
+
- name: Setup Node.js
|
|
363
|
+
uses: actions/setup-node@v4
|
|
364
|
+
with:
|
|
365
|
+
node-version: 22
|
|
366
|
+
cache: '${pm === 'bun' ? 'bun' : pm}'
|
|
367
|
+
|
|
368
|
+
- name: Install dependencies
|
|
369
|
+
run: ${installCmd}
|
|
370
|
+
|
|
371
|
+
- name: Build & Deploy
|
|
372
|
+
run: ${deployCmd}
|
|
373
|
+
env:
|
|
374
|
+
CLOUDFLARE_API_TOKEN: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
375
|
+
CLOUDFLARE_ACCOUNT_ID: \${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
376
|
+
`;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Write the deploy workflow file.
|
|
381
|
+
*/
|
|
382
|
+
function writeGitHubActions(root, pm, overwrite = false) {
|
|
383
|
+
const workflowDir = join(root, '.github', 'workflows');
|
|
384
|
+
const target = join(workflowDir, 'deploy.yml');
|
|
385
|
+
if (existsSync(target) && !overwrite) {
|
|
386
|
+
logger.warn('Skipped .github/workflows/deploy.yml (already exists)');
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
mkdirSync(workflowDir, {
|
|
390
|
+
recursive: true
|
|
391
|
+
});
|
|
392
|
+
writeFileSync(target, deployWorkflowContent(pm), 'utf-8');
|
|
393
|
+
logger.success('Created .github/workflows/deploy.yml');
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export { ADAPTER_SCRIPTS, REQUIRED_DEPS, REQUIRED_DEV_DEPS, deployWorkflowContent, detectPackageManager, detectProject, envDtsContent, injectScripts, installPackages, logger, openNextConfigContent, wranglerConfigContent, writeConfigFiles, writeGitHubActions };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { detectProject, detectPackageManager, type ProjectInfo, type PackageManager } from './lib/detect.js';
|
|
2
|
+
export { installPackages, REQUIRED_DEPS, REQUIRED_DEV_DEPS } from './lib/packages.js';
|
|
3
|
+
export { injectScripts, ADAPTER_SCRIPTS, type ScriptsResult } from './lib/scripts.js';
|
|
4
|
+
export { writeConfigFiles, openNextConfigContent, wranglerConfigContent, envDtsContent, type ConfigResult, } from './lib/configs.js';
|
|
5
|
+
export { writeGitHubActions, deployWorkflowContent } from './lib/github-actions.js';
|
|
6
|
+
export { logger } from './lib/logger.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/adapter/src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC7G,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,EACb,KAAK,YAAY,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../../packages/adapter/src/lib/adapter.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function openNextConfigContent(): string;
|
|
2
|
+
export declare function wranglerConfigContent(projectName: string): string;
|
|
3
|
+
export declare function envDtsContent(): string;
|
|
4
|
+
export interface ConfigResult {
|
|
5
|
+
created: string[];
|
|
6
|
+
skipped: string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Write all configuration files to the project root.
|
|
10
|
+
*
|
|
11
|
+
* @param root Project root directory
|
|
12
|
+
* @param projectName Name used for the wrangler worker
|
|
13
|
+
* @param overwrite Overwrite existing files
|
|
14
|
+
*/
|
|
15
|
+
export declare function writeConfigFiles(root: string, projectName: string, overwrite?: boolean): ConfigResult;
|
|
16
|
+
//# sourceMappingURL=configs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configs.d.ts","sourceRoot":"","sources":["../../../../../packages/adapter/src/lib/configs.ts"],"names":[],"mappings":"AAUA,wBAAgB,qBAAqB,IAAI,MAAM,CAW9C;AAID,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAkBjE;AAID,wBAAgB,aAAa,IAAI,MAAM,CAOtC;AAID,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,SAAS,UAAQ,GAChB,YAAY,CAuBd"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
|
|
2
|
+
export interface ProjectInfo {
|
|
3
|
+
/** Absolute path to the project root */
|
|
4
|
+
root: string;
|
|
5
|
+
/** Detected package manager */
|
|
6
|
+
packageManager: PackageManager;
|
|
7
|
+
/** Whether a package.json was found */
|
|
8
|
+
hasPackageJson: boolean;
|
|
9
|
+
/** Whether next is already a dependency */
|
|
10
|
+
hasNext: boolean;
|
|
11
|
+
/** Current Next.js version (if found) */
|
|
12
|
+
nextVersion: string | null;
|
|
13
|
+
/** Whether @opennextjs/cloudflare is already installed */
|
|
14
|
+
hasOpenNext: boolean;
|
|
15
|
+
/** Whether wrangler.jsonc already exists */
|
|
16
|
+
hasWrangler: boolean;
|
|
17
|
+
/** Whether open-next.config.ts already exists */
|
|
18
|
+
hasOpenNextConfig: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Detect the package manager used by the project.
|
|
22
|
+
*/
|
|
23
|
+
export declare function detectPackageManager(root: string): PackageManager;
|
|
24
|
+
/**
|
|
25
|
+
* Read and parse the project's package.json.
|
|
26
|
+
* Returns null if not found.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readPackageJson(root: string): Record<string, any> | null;
|
|
29
|
+
/**
|
|
30
|
+
* Gather information about the target project.
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectProject(root: string): ProjectInfo;
|
|
33
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../../../packages/adapter/src/lib/detect.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,cAAc,EAAE,OAAO,CAAC;IACxB,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0DAA0D;IAC1D,WAAW,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,WAAW,EAAE,OAAO,CAAC;IACrB,iDAAiD;IACjD,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAKjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAQxE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAmBvD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PackageManager } from './detect.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate the GitHub Actions workflow YAML for deploying a Next.js
|
|
4
|
+
* app to Cloudflare Workers via OpenNext.
|
|
5
|
+
*/
|
|
6
|
+
export declare function deployWorkflowContent(pm: PackageManager): string;
|
|
7
|
+
/**
|
|
8
|
+
* Write the deploy workflow file.
|
|
9
|
+
*/
|
|
10
|
+
export declare function writeGitHubActions(root: string, pm: PackageManager, overwrite?: boolean): boolean;
|
|
11
|
+
//# sourceMappingURL=github-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-actions.d.ts","sourceRoot":"","sources":["../../../../../packages/adapter/src/lib/github-actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM,CAiFhE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,SAAS,UAAQ,GAAG,OAAO,CAa/F"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../../../packages/adapter/src/lib/logger.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,MAAM;cACP,MAAM;iBAGH,MAAM;cAGT,MAAM;eAGL,MAAM;cAGP,MAAM;aAGP,MAAM;;CAoBhB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PackageManager } from './detect.js';
|
|
2
|
+
/** Packages that will be added as dependencies */
|
|
3
|
+
export declare const REQUIRED_DEPS: readonly ["@opennextjs/cloudflare"];
|
|
4
|
+
/** Packages that will be added as devDependencies */
|
|
5
|
+
export declare const REQUIRED_DEV_DEPS: readonly ["wrangler"];
|
|
6
|
+
/**
|
|
7
|
+
* Install the required opennext + wrangler packages.
|
|
8
|
+
*/
|
|
9
|
+
export declare function installPackages(pm: PackageManager, cwd: string): void;
|
|
10
|
+
//# sourceMappingURL=packages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packages.d.ts","sourceRoot":"","sources":["../../../../../packages/adapter/src/lib/packages.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,kDAAkD;AAClD,eAAO,MAAM,aAAa,qCAAsC,CAAC;AAEjE,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,uBAAwB,CAAC;AAqBvD;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAQrE"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask a yes/no question. Returns true for yes.
|
|
3
|
+
*/
|
|
4
|
+
export declare function confirm(question: string, defaultYes?: boolean): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* Ask for free-text input with an optional default.
|
|
7
|
+
*/
|
|
8
|
+
export declare function input(question: string, defaultValue?: string): Promise<string>;
|
|
9
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../../../packages/adapter/src/lib/prompts.ts"],"names":[],"mappings":"AAUA;;GAEG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAW7E;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS9E"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scripts that will be injected into the target project's package.json.
|
|
3
|
+
* Existing scripts with the same key will NOT be overwritten unless the
|
|
4
|
+
* user explicitly opts-in (handled in the CLI prompt layer).
|
|
5
|
+
*/
|
|
6
|
+
export declare const ADAPTER_SCRIPTS: Record<string, string>;
|
|
7
|
+
export interface ScriptsResult {
|
|
8
|
+
added: string[];
|
|
9
|
+
skipped: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Merge adapter scripts into the project's package.json.
|
|
13
|
+
*
|
|
14
|
+
* @param root Project root directory
|
|
15
|
+
* @param overwrite When true existing scripts with the same name are replaced
|
|
16
|
+
*/
|
|
17
|
+
export declare function injectScripts(root: string, overwrite?: boolean): ScriptsResult;
|
|
18
|
+
//# sourceMappingURL=scripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scripts.d.ts","sourceRoot":"","sources":["../../../../../packages/adapter/src/lib/scripts.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOlD,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,aAAa,CAwB5E"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@electroplix/adapter",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI for setting up Cloudflare Workers deploy pipelines in Next.js projects via OpenNext.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.esm.js",
|
|
8
|
+
"module": "./dist/index.esm.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"electroplix-adapter": "./bin/cli.mjs"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
"./package.json": "./package.json",
|
|
15
|
+
".": {
|
|
16
|
+
"@electroplix-workspace/source": "./src/index.ts",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.esm.js",
|
|
19
|
+
"default": "./dist/index.esm.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"bin",
|
|
25
|
+
"!**/*.tsbuildinfo"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"nextjs",
|
|
29
|
+
"cloudflare",
|
|
30
|
+
"workers",
|
|
31
|
+
"opennext",
|
|
32
|
+
"deploy",
|
|
33
|
+
"adapter",
|
|
34
|
+
"cli",
|
|
35
|
+
"ci-cd",
|
|
36
|
+
"electroplix"
|
|
37
|
+
],
|
|
38
|
+
"homepage": "https://electroplix.com",
|
|
39
|
+
"author": "Electroplix",
|
|
40
|
+
"dependencies": {}
|
|
41
|
+
}
|