@ebay-dt/bonfire-cli 0.1.0-alpha.3 → 0.1.0
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 +42 -0
- package/dist/command.d.ts +21 -2
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +235 -1
- package/dist/command.js.map +1 -1
- package/dist/convert.d.ts +47 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +509 -0
- package/dist/convert.js.map +1 -0
- package/package.json +5 -3
- package/templates/instance/context/PROTOTYPE.md +108 -0
- package/templates/instance/context/ROOT.md +23 -0
- package/templates/instance/context/hooks/block-env-reads.mjs +144 -0
- package/templates/instance/context/mcp.json +8 -0
- package/templates/instance/context/settings.json +19 -0
- package/templates/instance/context/skills/color-scheme/SKILL.md +98 -0
- package/templates/instance/context/skills/item-data/SKILL.md +145 -0
- package/templates/instance/context/skills/setup-evomcp/SKILL.md +73 -0
- package/templates/instance/next.config.ts +10 -0
- package/templates/instance/src/actions/agents.ts +15 -0
- package/templates/instance/src/actions/prototypes.ts +37 -0
- package/templates/instance/src/app/(prototypes)/layout.tsx +40 -0
- package/templates/instance/src/app/(system)/api/prototype-dirty/route.ts +4 -0
- package/templates/instance/src/app/(system)/auth/login/route.ts +4 -0
- package/templates/instance/src/app/(system)/auth/logout/route.ts +4 -0
- package/templates/instance/src/app/(system)/auth/status/route.ts +4 -0
- package/templates/instance/src/app/(system)/layout.tsx +20 -0
- package/templates/instance/src/app/(system)/login/page.tsx +14 -0
- package/templates/instance/src/app/(system)/page.tsx +33 -0
- package/templates/instance/src/app/api/items/[itemId]/route.ts +21 -0
- package/templates/instance/src/app/api/items/route.ts +11 -0
- package/templates/instance/src/app/layout.tsx +56 -0
- package/templates/instance/src/bonfire.ts +25 -0
- package/templates/instance/src/lib/items/gateway-schemas.ts +100 -0
- package/templates/instance/src/lib/items/server.ts +220 -0
- package/templates/instance/src/proxy.ts +8 -0
package/dist/convert.js
ADDED
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { chmod, lstat, mkdir, readFile, readdir, rm, writeFile, } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { syncContext } from "@ebay-dt/bonfire-context";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
const TEMPLATE_ROOT = fileURLToPath(new URL("../templates/instance/", import.meta.url));
|
|
9
|
+
const BONFIRE_VERSION = "0.1.0";
|
|
10
|
+
const PRESET_VERSION = "0.1.0";
|
|
11
|
+
const DEFAULT_HOSTNAME = "github.corp.ebay.com";
|
|
12
|
+
const DEFAULT_DEPLOYMENT_ENDPOINT = "https://dtsvc.vip.qa.ebay.com/dt/v1/bonfire-deploy";
|
|
13
|
+
const DEFAULT_PORT = 1995;
|
|
14
|
+
export function humanizeAppId(appId) {
|
|
15
|
+
return appId
|
|
16
|
+
.replace(/([a-z0-9])([A-Z])/gu, "$1 $2")
|
|
17
|
+
.replace(/[._-]+/gu, " ")
|
|
18
|
+
.trim()
|
|
19
|
+
.split(/\s+/u)
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.map((word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`)
|
|
22
|
+
.join(" ");
|
|
23
|
+
}
|
|
24
|
+
export class ConvertError extends Error {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "ConvertError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function displayPath(root, path) {
|
|
31
|
+
return relative(root, path) || ".";
|
|
32
|
+
}
|
|
33
|
+
function parseVersion(value, label) {
|
|
34
|
+
if (typeof value !== "string") {
|
|
35
|
+
throw new ConvertError(`${label} must be declared in package.json`);
|
|
36
|
+
}
|
|
37
|
+
const match = value.match(/(\d+)\.(\d+)\.(\d+)/u);
|
|
38
|
+
if (!match) {
|
|
39
|
+
throw new ConvertError(`${label} has an unsupported version: ${value}`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
major: Number(match[1]),
|
|
43
|
+
minor: Number(match[2]),
|
|
44
|
+
patch: Number(match[3]),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function dependencyVersion(packageJson, name) {
|
|
48
|
+
return (packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name]);
|
|
49
|
+
}
|
|
50
|
+
function validateStartingScaffold(packageJson) {
|
|
51
|
+
const next = parseVersion(dependencyVersion(packageJson, "next"), "next");
|
|
52
|
+
if (next.major !== 16 || next.minor < 2) {
|
|
53
|
+
throw new ConvertError(`next must be 16.2 or newer within the 16.x line (found ${next.major}.${next.minor}.${next.patch})`);
|
|
54
|
+
}
|
|
55
|
+
const react = parseVersion(dependencyVersion(packageJson, "react"), "react");
|
|
56
|
+
if (react.major !== 19 || react.minor < 2) {
|
|
57
|
+
throw new ConvertError(`react must be 19.2 or newer within the 19.x line (found ${react.major}.${react.minor}.${react.patch})`);
|
|
58
|
+
}
|
|
59
|
+
if (!dependencyVersion(packageJson, "typescript")) {
|
|
60
|
+
throw new ConvertError("typescript must be declared in package.json");
|
|
61
|
+
}
|
|
62
|
+
if (Number(process.versions.node.split(".")[0]) < 24) {
|
|
63
|
+
throw new ConvertError("Bonfire instance conversion requires Node.js 24 or newer");
|
|
64
|
+
}
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
async function readJson(path) {
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof SyntaxError) {
|
|
73
|
+
throw new ConvertError(`${displayPath(dirname(path), path)} is not valid JSON`);
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function exists(path) {
|
|
79
|
+
try {
|
|
80
|
+
await lstat(path);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error !== null &&
|
|
85
|
+
typeof error === "object" &&
|
|
86
|
+
"code" in error &&
|
|
87
|
+
error.code === "ENOENT") {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function fileContent(path) {
|
|
94
|
+
try {
|
|
95
|
+
return await readFile(path, "utf8");
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error !== null &&
|
|
99
|
+
typeof error === "object" &&
|
|
100
|
+
"code" in error &&
|
|
101
|
+
error.code === "ENOENT") {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function addChange(changes, root, path, kind, reason) {
|
|
108
|
+
changes.push({ kind, path: displayPath(root, path), reason });
|
|
109
|
+
}
|
|
110
|
+
async function planFile(root, path, content, reason, writes, changes) {
|
|
111
|
+
const existing = await fileContent(path);
|
|
112
|
+
if (existing === undefined) {
|
|
113
|
+
writes.set(path, content);
|
|
114
|
+
addChange(changes, root, path, "add", reason);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (existing === content)
|
|
118
|
+
return;
|
|
119
|
+
throw new ConvertError(`${displayPath(root, path)} already exists and differs from the Bonfire scaffold; resolve it before converting`);
|
|
120
|
+
}
|
|
121
|
+
function looksLikeNextStarter(path, content) {
|
|
122
|
+
if (path.endsWith("/page.tsx")) {
|
|
123
|
+
return /Create Next App|[Gg]et started|Looking for a starting point|create-next-app/u.test(content);
|
|
124
|
+
}
|
|
125
|
+
if (path.endsWith("/layout.tsx")) {
|
|
126
|
+
return /next\/font|Geist|Create Next App/u.test(content);
|
|
127
|
+
}
|
|
128
|
+
if (path.endsWith("/page.module.css")) {
|
|
129
|
+
return /\.page|\.main|\.footer|\.grid/u.test(content);
|
|
130
|
+
}
|
|
131
|
+
if (path.endsWith("/globals.css")) {
|
|
132
|
+
return /--background|--foreground|@import ["']tailwindcss/u.test(content);
|
|
133
|
+
}
|
|
134
|
+
if (path.endsWith("/README.md")) {
|
|
135
|
+
return /bootstrapped with[\s\S]*create-next-app/u.test(content);
|
|
136
|
+
}
|
|
137
|
+
if (path.endsWith("/next.config.ts")) {
|
|
138
|
+
return /config options here/u.test(content);
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
function looksLikeStarterAsset(path, content) {
|
|
143
|
+
const name = path.slice(path.lastIndexOf("/") + 1);
|
|
144
|
+
if (!["file.svg", "globe.svg", "next.svg", "vercel.svg", "window.svg"].includes(name)) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return /<svg[\s\S]*viewBox=/u.test(content);
|
|
148
|
+
}
|
|
149
|
+
async function planRemoveStarter(root, path, reason, removals, changes) {
|
|
150
|
+
const content = await fileContent(path);
|
|
151
|
+
if (content === undefined)
|
|
152
|
+
return;
|
|
153
|
+
if (content.includes("@ebay-dt/bonfire-next") ||
|
|
154
|
+
content.includes("A Bonfire instance for creating") ||
|
|
155
|
+
content.includes("outputFileTracingIncludes")) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (!looksLikeNextStarter(path, content) &&
|
|
159
|
+
!looksLikeStarterAsset(path, content)) {
|
|
160
|
+
throw new ConvertError(`${displayPath(root, path)} is customized; it will not be removed automatically`);
|
|
161
|
+
}
|
|
162
|
+
removals.push(path);
|
|
163
|
+
addChange(changes, root, path, "remove", reason);
|
|
164
|
+
}
|
|
165
|
+
function replaceIdentity(content, options) {
|
|
166
|
+
return content
|
|
167
|
+
.replaceAll("design-sandbox-next", () => options.appId)
|
|
168
|
+
.replaceAll("Design Sandbox", () => options.name)
|
|
169
|
+
.replaceAll("github.corp.ebay.com", () => options.hostname)
|
|
170
|
+
.replaceAll(DEFAULT_DEPLOYMENT_ENDPOINT, () => options.deploymentEndpoint);
|
|
171
|
+
}
|
|
172
|
+
function withoutItemGuidance(content) {
|
|
173
|
+
const start = content.indexOf("### Data fetching");
|
|
174
|
+
const end = content.indexOf("### Viewport sizing");
|
|
175
|
+
if (start < 0 || end < 0 || end <= start)
|
|
176
|
+
return content;
|
|
177
|
+
return `${content.slice(0, start)}${content.slice(end)}`;
|
|
178
|
+
}
|
|
179
|
+
async function templateFiles(options) {
|
|
180
|
+
const files = [];
|
|
181
|
+
async function visit(directory) {
|
|
182
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
183
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
184
|
+
const path = join(directory, entry.name);
|
|
185
|
+
const relativePath = relative(TEMPLATE_ROOT, path);
|
|
186
|
+
if (!options.withItemData &&
|
|
187
|
+
(relativePath.startsWith("src/app/api/items/") ||
|
|
188
|
+
relativePath.startsWith("src/lib/items/") ||
|
|
189
|
+
relativePath === "context/skills/item-data/SKILL.md")) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (entry.isDirectory()) {
|
|
193
|
+
await visit(path);
|
|
194
|
+
}
|
|
195
|
+
else if (entry.isFile()) {
|
|
196
|
+
let content = await readFile(path, "utf8");
|
|
197
|
+
content = replaceIdentity(content, options);
|
|
198
|
+
if (!options.withItemData && relativePath === "context/PROTOTYPE.md") {
|
|
199
|
+
content = withoutItemGuidance(content);
|
|
200
|
+
}
|
|
201
|
+
files.push({ relativePath, content });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
await visit(TEMPLATE_ROOT);
|
|
206
|
+
return files;
|
|
207
|
+
}
|
|
208
|
+
function starterReadme(options) {
|
|
209
|
+
return `# ${options.name}\n\nA Bonfire instance for creating, editing, and sharing prototypes.\n\n## Development\n\nUse Node.js 24+, install dependencies with pnpm, then run:\n\n~~~sh\npnpm dev\n~~~\n\nThe development command prepares the Bonfire session secret and the local identity branch before starting Next.js on port ${options.port}.\n\nRun pnpm context:install once on a machine where agent tooling is desired.\n`;
|
|
210
|
+
}
|
|
211
|
+
function addDependency(packageJson, section, name, version) {
|
|
212
|
+
const target = (packageJson[section] ??= {});
|
|
213
|
+
const existing = target[name];
|
|
214
|
+
if (existing !== undefined) {
|
|
215
|
+
if (existing !== version && name.startsWith("@ebay-dt/bonfire-")) {
|
|
216
|
+
throw new ConvertError(`${name} is already declared as ${existing}; expected ${version}. Update it explicitly before converting`);
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
target[name] = version;
|
|
221
|
+
}
|
|
222
|
+
function addScript(packageJson, name, value, allowedExisting = []) {
|
|
223
|
+
const scripts = (packageJson.scripts ??= {});
|
|
224
|
+
const existing = scripts[name];
|
|
225
|
+
if (existing !== undefined) {
|
|
226
|
+
if (existing !== value && !allowedExisting.includes(String(existing))) {
|
|
227
|
+
throw new ConvertError(`package.json script ${name} is customized (${String(existing)}); resolve it before converting`);
|
|
228
|
+
}
|
|
229
|
+
scripts[name] = value;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
scripts[name] = value;
|
|
233
|
+
}
|
|
234
|
+
function mergeLines(existing, required) {
|
|
235
|
+
let result = existing ?? "";
|
|
236
|
+
for (const line of required) {
|
|
237
|
+
if (result.split(/\r?\n/u).some((entry) => entry.trim() === line))
|
|
238
|
+
continue;
|
|
239
|
+
if (result.length > 0 && !result.endsWith("\n"))
|
|
240
|
+
result += "\n";
|
|
241
|
+
result += `${line}\n`;
|
|
242
|
+
}
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
function mergeWorkspace(existing) {
|
|
246
|
+
let result = existing ?? "";
|
|
247
|
+
if (!/^minimumReleaseAge:/mu.test(result)) {
|
|
248
|
+
result = `# Bonfire publishes lockstep alpha packages that must be installable immediately.\nminimumReleaseAge: 0\n${result}`;
|
|
249
|
+
}
|
|
250
|
+
else if (!/^minimumReleaseAge:\s*0\s*$/mu.test(result)) {
|
|
251
|
+
throw new ConvertError("pnpm-workspace.yaml has a conflicting minimumReleaseAge");
|
|
252
|
+
}
|
|
253
|
+
const required = [
|
|
254
|
+
" esbuild: true",
|
|
255
|
+
" sharp: true",
|
|
256
|
+
" unrs-resolver: true",
|
|
257
|
+
];
|
|
258
|
+
for (const line of required) {
|
|
259
|
+
const key = line.trim().split(":")[0] ?? "";
|
|
260
|
+
const match = result.match(new RegExp(`^\\s+${key}:\\s*(\\S+)\\s*$`, "mu"));
|
|
261
|
+
if (match && match[1] !== "true") {
|
|
262
|
+
// create-next-app currently writes these two false entries. They are
|
|
263
|
+
// starter policy, not an instance decision, so normalize them to the
|
|
264
|
+
// Bonfire installation policy.
|
|
265
|
+
if (match[1] !== "false" || !["sharp", "unrs-resolver"].includes(key)) {
|
|
266
|
+
throw new ConvertError(`pnpm-workspace.yaml has a conflicting allowBuilds.${key} setting`);
|
|
267
|
+
}
|
|
268
|
+
result = result.replace(new RegExp(`(^\\s+${key}:\\s*)false(\\s*$)`, "mu"), "$1true$2");
|
|
269
|
+
}
|
|
270
|
+
if (!match) {
|
|
271
|
+
if (!result.endsWith("\n"))
|
|
272
|
+
result += "\n";
|
|
273
|
+
result += `${line}\n`;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
async function planPackageJson(root, options, packageJson, nextVersion, writes, changes) {
|
|
279
|
+
const packageName = options.packageName ??
|
|
280
|
+
(typeof packageJson.name === "string" ? packageJson.name : options.appId);
|
|
281
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/u.test(packageName)) {
|
|
282
|
+
throw new ConvertError(`package name is invalid: ${packageName}`);
|
|
283
|
+
}
|
|
284
|
+
packageJson.name = packageName;
|
|
285
|
+
const engines = (packageJson.engines ??= {});
|
|
286
|
+
if (engines.node !== undefined &&
|
|
287
|
+
!/^(?:\^|~|>=)?\s*2[4-9]|[3-9]\d/u.test(engines.node)) {
|
|
288
|
+
throw new ConvertError("package.json engines.node must allow Node.js 24 or newer");
|
|
289
|
+
}
|
|
290
|
+
engines.node = ">=24";
|
|
291
|
+
packageJson.packageManager ??= "pnpm@11.25.0";
|
|
292
|
+
const dev = `bonfire prepare --root . && bonfire branch prepare --root . --hostname ${options.hostname} && next dev -p ${options.port}`;
|
|
293
|
+
addScript(packageJson, "dev", dev, ["next dev"]);
|
|
294
|
+
addScript(packageJson, "context:sync", "bonfire context sync");
|
|
295
|
+
addScript(packageJson, "context:install", `bonfire context install --app-id ${options.appId}`);
|
|
296
|
+
addScript(packageJson, "check", "tsc --noEmit --project tsconfig.check.json && pnpm context:sync --check");
|
|
297
|
+
addScript(packageJson, "format", "prettier --write .");
|
|
298
|
+
addScript(packageJson, "build", "next build", ["next build"]);
|
|
299
|
+
addScript(packageJson, "start", "next start", ["next start"]);
|
|
300
|
+
addScript(packageJson, "lint", "eslint", ["eslint", "eslint ."]);
|
|
301
|
+
const nextExact = `${nextVersion.major}.${nextVersion.minor}.${nextVersion.patch}`;
|
|
302
|
+
addDependency(packageJson, "dependencies", "@ebay-dt/bonfire-context", BONFIRE_VERSION);
|
|
303
|
+
addDependency(packageJson, "dependencies", "@ebay-dt/bonfire-core", BONFIRE_VERSION);
|
|
304
|
+
addDependency(packageJson, "dependencies", "@ebay-dt/bonfire-next", BONFIRE_VERSION);
|
|
305
|
+
addDependency(packageJson, "dependencies", "@ebay-dt/bonfire-preset-ebay", PRESET_VERSION);
|
|
306
|
+
addDependency(packageJson, "dependencies", "@ebay-dt/bonfire-react", BONFIRE_VERSION);
|
|
307
|
+
addDependency(packageJson, "dependencies", "@ebay/ui-core-react", "9.13.0");
|
|
308
|
+
addDependency(packageJson, "dependencies", "@evo-web/react", "0.4.4");
|
|
309
|
+
addDependency(packageJson, "dependencies", "react-dom", dependencyVersion(packageJson, "react-dom") ?? "19.2.8");
|
|
310
|
+
addDependency(packageJson, "dependencies", "server-only", "^0.0.1");
|
|
311
|
+
addDependency(packageJson, "devDependencies", "@ebay-dt/bonfire-cli", BONFIRE_VERSION);
|
|
312
|
+
addDependency(packageJson, "devDependencies", "@types/node", "^20");
|
|
313
|
+
addDependency(packageJson, "devDependencies", "@types/react", "^19");
|
|
314
|
+
addDependency(packageJson, "devDependencies", "@types/react-dom", "^19");
|
|
315
|
+
addDependency(packageJson, "devDependencies", "eslint", "^9");
|
|
316
|
+
addDependency(packageJson, "devDependencies", "eslint-config-next", nextExact);
|
|
317
|
+
addDependency(packageJson, "devDependencies", "prettier", "^3.9.5");
|
|
318
|
+
if (options.withItemData) {
|
|
319
|
+
addDependency(packageJson, "dependencies", "@tanstack/react-query", "^5.104.0");
|
|
320
|
+
addDependency(packageJson, "dependencies", "zod", "^4.4.3");
|
|
321
|
+
}
|
|
322
|
+
const path = join(root, "package.json");
|
|
323
|
+
const content = `${JSON.stringify(packageJson, null, 2)}\n`;
|
|
324
|
+
const existing = await readFile(path, "utf8");
|
|
325
|
+
if (JSON.stringify(JSON.parse(existing)) !== JSON.stringify(packageJson)) {
|
|
326
|
+
writes.set(path, content);
|
|
327
|
+
addChange(changes, root, path, "update", "merge Bonfire scripts and dependencies");
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
export async function planConversion(options) {
|
|
331
|
+
const root = resolve(options.root);
|
|
332
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(options.appId)) {
|
|
333
|
+
throw new ConvertError("app id must contain only letters, numbers, dots, underscores, and hyphens");
|
|
334
|
+
}
|
|
335
|
+
if (!options.name.trim())
|
|
336
|
+
throw new ConvertError("display name must not be empty");
|
|
337
|
+
const packagePath = join(root, "package.json");
|
|
338
|
+
const packageJson = await readJson(packagePath);
|
|
339
|
+
const nextVersion = validateStartingScaffold(packageJson);
|
|
340
|
+
if (!(await exists(join(root, "src", "app")))) {
|
|
341
|
+
throw new ConvertError("the starting scaffold must use the src/app App Router");
|
|
342
|
+
}
|
|
343
|
+
const writes = new Map();
|
|
344
|
+
const changes = [];
|
|
345
|
+
const removals = [];
|
|
346
|
+
const starterPaths = [
|
|
347
|
+
join(root, "src/app/layout.tsx"),
|
|
348
|
+
join(root, "src/app/page.tsx"),
|
|
349
|
+
join(root, "src/app/page.module.css"),
|
|
350
|
+
join(root, "src/app/globals.css"),
|
|
351
|
+
join(root, "next.config.ts"),
|
|
352
|
+
join(root, "README.md"),
|
|
353
|
+
...["file.svg", "globe.svg", "next.svg", "vercel.svg", "window.svg"].map((name) => join(root, "public", name)),
|
|
354
|
+
];
|
|
355
|
+
for (const path of starterPaths) {
|
|
356
|
+
await planRemoveStarter(root, path, "remove unmodified create-next-app content", removals, changes);
|
|
357
|
+
}
|
|
358
|
+
const nextBuildDirectory = join(root, ".next");
|
|
359
|
+
if (await exists(nextBuildDirectory)) {
|
|
360
|
+
removals.push(nextBuildDirectory);
|
|
361
|
+
addChange(changes, root, nextBuildDirectory, "remove", "clear generated Next.js output");
|
|
362
|
+
}
|
|
363
|
+
const resolvedOptions = { ...options, root };
|
|
364
|
+
const templates = await templateFiles(resolvedOptions);
|
|
365
|
+
for (const file of templates) {
|
|
366
|
+
const path = join(root, file.relativePath);
|
|
367
|
+
if (removals.includes(path)) {
|
|
368
|
+
writes.set(path, file.content);
|
|
369
|
+
addChange(changes, root, path, "update", "replace unmodified create-next-app content");
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
await planFile(root, path, file.content, "add Bonfire instance scaffold", writes, changes);
|
|
373
|
+
}
|
|
374
|
+
await planPackageJson(root, options, packageJson, nextVersion, writes, changes);
|
|
375
|
+
const tsconfigPath = join(root, "tsconfig.json");
|
|
376
|
+
const existingTsconfig = await readFile(tsconfigPath, "utf8");
|
|
377
|
+
const tsconfig = await readJson(tsconfigPath);
|
|
378
|
+
const compilerOptions = (tsconfig.compilerOptions ?? {});
|
|
379
|
+
tsconfig.compilerOptions = compilerOptions;
|
|
380
|
+
const paths = compilerOptions["paths"];
|
|
381
|
+
if (paths && JSON.stringify(paths["@/*"]) !== JSON.stringify(["./src/*"])) {
|
|
382
|
+
throw new ConvertError("tsconfig.json has a conflicting @/* path alias");
|
|
383
|
+
}
|
|
384
|
+
compilerOptions["paths"] = { ...(paths ?? {}), "@/*": ["./src/*"] };
|
|
385
|
+
const nextTsconfig = `${JSON.stringify(tsconfig, null, 2)}\n`;
|
|
386
|
+
if (JSON.stringify(JSON.parse(existingTsconfig)) !== JSON.stringify(tsconfig)) {
|
|
387
|
+
writes.set(tsconfigPath, nextTsconfig);
|
|
388
|
+
addChange(changes, root, tsconfigPath, "update", "retain the Bonfire @/* source alias");
|
|
389
|
+
}
|
|
390
|
+
await planFile(root, join(root, "tsconfig.check.json"), '{\n "extends": "./tsconfig.json",\n "exclude": ["node_modules", ".next"]\n}\n', "add the clean TypeScript check project", writes, changes);
|
|
391
|
+
await planFile(root, join(root, ".prettierrc"), '{\n "semi": false,\n "singleQuote": true,\n "trailingComma": "es5",\n "printWidth": 80\n}\n', "add Bonfire formatting conventions", writes, changes);
|
|
392
|
+
await planFile(root, join(root, ".prettierignore"), "# Generated and external files\n.next/\nnode_modules/\npnpm-lock.yaml\nsrc/app/(prototypes)/**/.claude/settings.json\n", "add formatter exclusions", writes, changes);
|
|
393
|
+
await planFile(root, join(root, ".env.local.example"), "# Bonfire generates this secret during `pnpm dev`.\n# Do NOT manually modify or remove this variable.\nBF_SESSION_SECRET=\n\n# Set exactly to lowercase `true` to skip automatic identity-branch preparation.\nBF_DISABLE_AUTO_BRANCH_SWITCH=\n", "document Bonfire environment setup", writes, changes);
|
|
394
|
+
await planFile(root, join(root, "public/.gitkeep"), "", "retain an explicit public directory", writes, changes);
|
|
395
|
+
const gitignorePath = join(root, ".gitignore");
|
|
396
|
+
const gitignore = await fileContent(gitignorePath);
|
|
397
|
+
const mergedGitignore = mergeLines(gitignore, [
|
|
398
|
+
"# Bonfire local secrets",
|
|
399
|
+
".env.local",
|
|
400
|
+
]);
|
|
401
|
+
if (mergedGitignore !== (gitignore ?? "")) {
|
|
402
|
+
writes.set(gitignorePath, mergedGitignore);
|
|
403
|
+
addChange(changes, root, gitignorePath, "update", "ignore the generated local session secret");
|
|
404
|
+
}
|
|
405
|
+
const workspacePath = join(root, "pnpm-workspace.yaml");
|
|
406
|
+
const workspace = await fileContent(workspacePath);
|
|
407
|
+
const mergedWorkspace = mergeWorkspace(workspace);
|
|
408
|
+
if (mergedWorkspace !== (workspace ?? "")) {
|
|
409
|
+
writes.set(workspacePath, mergedWorkspace);
|
|
410
|
+
addChange(changes, root, workspacePath, "update", "retain the Bonfire pnpm installation policy");
|
|
411
|
+
}
|
|
412
|
+
const npmrcPath = join(root, ".npmrc");
|
|
413
|
+
const npmrc = await fileContent(npmrcPath);
|
|
414
|
+
if (npmrc &&
|
|
415
|
+
/@ebay-dt:\s*registry=/u.test(npmrc) &&
|
|
416
|
+
!npmrc.includes("@ebay-dt:registry=https://registry.npmjs.org/")) {
|
|
417
|
+
throw new ConvertError(".npmrc has a conflicting @ebay-dt registry");
|
|
418
|
+
}
|
|
419
|
+
const mergedNpmrc = mergeLines(npmrc, [
|
|
420
|
+
"@ebay-dt:registry=https://registry.npmjs.org/",
|
|
421
|
+
]);
|
|
422
|
+
if (mergedNpmrc !== (npmrc ?? "")) {
|
|
423
|
+
writes.set(npmrcPath, mergedNpmrc);
|
|
424
|
+
addChange(changes, root, npmrcPath, "update", "resolve Bonfire packages from public npm");
|
|
425
|
+
}
|
|
426
|
+
const nvmrcPath = join(root, ".nvmrc");
|
|
427
|
+
const nvmrc = await fileContent(nvmrcPath);
|
|
428
|
+
if (nvmrc === undefined) {
|
|
429
|
+
await planFile(root, nvmrcPath, "24\n", "select Node.js 24 or newer", writes, changes);
|
|
430
|
+
}
|
|
431
|
+
const readmePath = join(root, "README.md");
|
|
432
|
+
if (removals.includes(readmePath)) {
|
|
433
|
+
writes.set(readmePath, starterReadme(options));
|
|
434
|
+
addChange(changes, root, readmePath, "update", "replace unmodified create-next-app documentation");
|
|
435
|
+
}
|
|
436
|
+
else if (!writes.has(readmePath) &&
|
|
437
|
+
(await fileContent(readmePath)) === undefined) {
|
|
438
|
+
await planFile(root, readmePath, starterReadme(options), "add instance setup documentation", writes, changes);
|
|
439
|
+
}
|
|
440
|
+
return { root, changes, writes, removals, packageJson };
|
|
441
|
+
}
|
|
442
|
+
export async function convert(options) {
|
|
443
|
+
const plan = await planConversion(options);
|
|
444
|
+
for (const change of plan.changes) {
|
|
445
|
+
console.log(`${options.dryRun ? "Would " : ""}${change.kind} ${change.path} (${change.reason})`);
|
|
446
|
+
}
|
|
447
|
+
if (options.dryRun)
|
|
448
|
+
return;
|
|
449
|
+
for (const path of plan.removals) {
|
|
450
|
+
await rm(path, {
|
|
451
|
+
force: true,
|
|
452
|
+
recursive: path === join(plan.root, ".next"),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
for (const [path, content] of plan.writes) {
|
|
456
|
+
await mkdir(dirname(path), { recursive: true });
|
|
457
|
+
await writeFile(path, content);
|
|
458
|
+
if (path.endsWith("context/hooks/block-env-reads.mjs")) {
|
|
459
|
+
await chmod(path, 0o755);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (!options.skipInstall) {
|
|
463
|
+
console.log("Installing dependencies and updating pnpm-lock.yaml...");
|
|
464
|
+
await execFileAsync("pnpm", ["install"], {
|
|
465
|
+
cwd: plan.root,
|
|
466
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
if (!options.skipInstall) {
|
|
470
|
+
const formatPaths = [...plan.writes.keys()]
|
|
471
|
+
.map((path) => displayPath(plan.root, path))
|
|
472
|
+
.filter((path) => /\.(?:json|mjs|md|ts|tsx|yaml|yml)$/u.test(path));
|
|
473
|
+
if (formatPaths.length > 0) {
|
|
474
|
+
await execFileAsync("pnpm", ["exec", "prettier", "--write", ...formatPaths], {
|
|
475
|
+
cwd: plan.root,
|
|
476
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
await execFileAsync("pnpm", ["exec", "next", "typegen"], {
|
|
480
|
+
cwd: plan.root,
|
|
481
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
const contextResult = await syncContext({ repositoryRoot: plan.root });
|
|
485
|
+
if (contextResult.written.length > 0 || contextResult.removed.length > 0) {
|
|
486
|
+
console.log(`Context synchronized (${contextResult.written.length} written, ${contextResult.removed.length} removed).`);
|
|
487
|
+
}
|
|
488
|
+
console.log("Bonfire instance conversion complete.");
|
|
489
|
+
}
|
|
490
|
+
export function defaultConvertOptions(root = process.cwd()) {
|
|
491
|
+
const appId = "bonfire-instance";
|
|
492
|
+
return {
|
|
493
|
+
root,
|
|
494
|
+
appId,
|
|
495
|
+
name: "Bonfire Instance",
|
|
496
|
+
hostname: DEFAULT_HOSTNAME,
|
|
497
|
+
port: DEFAULT_PORT,
|
|
498
|
+
deploymentEndpoint: DEFAULT_DEPLOYMENT_ENDPOINT,
|
|
499
|
+
withItemData: true,
|
|
500
|
+
dryRun: false,
|
|
501
|
+
skipInstall: false,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
export const convertDefaults = {
|
|
505
|
+
hostname: DEFAULT_HOSTNAME,
|
|
506
|
+
deploymentEndpoint: DEFAULT_DEPLOYMENT_ENDPOINT,
|
|
507
|
+
port: DEFAULT_PORT,
|
|
508
|
+
};
|
|
509
|
+
//# sourceMappingURL=convert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"node:child_process\";\nimport {\n chmod,\n lstat,\n mkdir,\n readFile,\n readdir,\n rm,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { fileURLToPath } from \"node:url\";\nimport { syncContext } from \"@ebay-dt/bonfire-context\";\n\nconst execFileAsync = promisify(execFile);\nconst TEMPLATE_ROOT = fileURLToPath(\n new URL(\"../templates/instance/\", import.meta.url),\n);\nconst BONFIRE_VERSION = \"0.1.0\";\nconst PRESET_VERSION = \"0.1.0\";\nconst DEFAULT_HOSTNAME = \"github.corp.ebay.com\";\nconst DEFAULT_DEPLOYMENT_ENDPOINT =\n \"https://dtsvc.vip.qa.ebay.com/dt/v1/bonfire-deploy\";\nconst DEFAULT_PORT = 1995;\n\nexport type ConvertOptions = Readonly<{\n root: string;\n appId: string;\n name: string;\n packageName?: string;\n hostname: string;\n port: number;\n deploymentEndpoint: string;\n withItemData: boolean;\n dryRun: boolean;\n skipInstall: boolean;\n}>;\n\ntype PackageJson = {\n name?: unknown;\n scripts?: Record<string, unknown>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n engines?: Record<string, string>;\n packageManager?: unknown;\n [key: string]: unknown;\n};\n\ntype PlannedChange = Readonly<{\n kind: \"add\" | \"update\" | \"remove\";\n path: string;\n reason: string;\n}>;\n\ntype ConversionPlan = Readonly<{\n root: string;\n changes: readonly PlannedChange[];\n writes: ReadonlyMap<string, string>;\n removals: readonly string[];\n packageJson: PackageJson;\n}>;\n\nexport function humanizeAppId(appId: string) {\n return appId\n .replace(/([a-z0-9])([A-Z])/gu, \"$1 $2\")\n .replace(/[._-]+/gu, \" \")\n .trim()\n .split(/\\s+/u)\n .filter(Boolean)\n .map((word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`)\n .join(\" \");\n}\n\nexport class ConvertError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConvertError\";\n }\n}\n\nfunction displayPath(root: string, path: string) {\n return relative(root, path) || \".\";\n}\n\nfunction parseVersion(value: unknown, label: string) {\n if (typeof value !== \"string\") {\n throw new ConvertError(`${label} must be declared in package.json`);\n }\n const match = value.match(/(\\d+)\\.(\\d+)\\.(\\d+)/u);\n if (!match) {\n throw new ConvertError(`${label} has an unsupported version: ${value}`);\n }\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n };\n}\n\nfunction dependencyVersion(packageJson: PackageJson, name: string) {\n return (\n packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name]\n );\n}\n\nfunction validateStartingScaffold(packageJson: PackageJson) {\n const next = parseVersion(dependencyVersion(packageJson, \"next\"), \"next\");\n if (next.major !== 16 || next.minor < 2) {\n throw new ConvertError(\n `next must be 16.2 or newer within the 16.x line (found ${next.major}.${next.minor}.${next.patch})`,\n );\n }\n const react = parseVersion(dependencyVersion(packageJson, \"react\"), \"react\");\n if (react.major !== 19 || react.minor < 2) {\n throw new ConvertError(\n `react must be 19.2 or newer within the 19.x line (found ${react.major}.${react.minor}.${react.patch})`,\n );\n }\n if (!dependencyVersion(packageJson, \"typescript\")) {\n throw new ConvertError(\"typescript must be declared in package.json\");\n }\n\n if (Number(process.versions.node.split(\".\")[0]) < 24) {\n throw new ConvertError(\n \"Bonfire instance conversion requires Node.js 24 or newer\",\n );\n }\n return next;\n}\n\nasync function readJson(path: string) {\n try {\n return JSON.parse(await readFile(path, \"utf8\")) as PackageJson;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new ConvertError(\n `${displayPath(dirname(path), path)} is not valid JSON`,\n );\n }\n throw error;\n }\n}\n\nasync function exists(path: string) {\n try {\n await lstat(path);\n return true;\n } catch (error) {\n if (\n error !== null &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ) {\n return false;\n }\n throw error;\n }\n}\n\nasync function fileContent(path: string) {\n try {\n return await readFile(path, \"utf8\");\n } catch (error) {\n if (\n error !== null &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ) {\n return undefined;\n }\n throw error;\n }\n}\n\nfunction addChange(\n changes: PlannedChange[],\n root: string,\n path: string,\n kind: PlannedChange[\"kind\"],\n reason: string,\n) {\n changes.push({ kind, path: displayPath(root, path), reason });\n}\n\nasync function planFile(\n root: string,\n path: string,\n content: string,\n reason: string,\n writes: Map<string, string>,\n changes: PlannedChange[],\n) {\n const existing = await fileContent(path);\n if (existing === undefined) {\n writes.set(path, content);\n addChange(changes, root, path, \"add\", reason);\n return;\n }\n if (existing === content) return;\n throw new ConvertError(\n `${displayPath(root, path)} already exists and differs from the Bonfire scaffold; resolve it before converting`,\n );\n}\n\nfunction looksLikeNextStarter(path: string, content: string) {\n if (path.endsWith(\"/page.tsx\")) {\n return /Create Next App|[Gg]et started|Looking for a starting point|create-next-app/u.test(\n content,\n );\n }\n if (path.endsWith(\"/layout.tsx\")) {\n return /next\\/font|Geist|Create Next App/u.test(content);\n }\n if (path.endsWith(\"/page.module.css\")) {\n return /\\.page|\\.main|\\.footer|\\.grid/u.test(content);\n }\n if (path.endsWith(\"/globals.css\")) {\n return /--background|--foreground|@import [\"']tailwindcss/u.test(content);\n }\n if (path.endsWith(\"/README.md\")) {\n return /bootstrapped with[\\s\\S]*create-next-app/u.test(content);\n }\n if (path.endsWith(\"/next.config.ts\")) {\n return /config options here/u.test(content);\n }\n return false;\n}\n\nfunction looksLikeStarterAsset(path: string, content: string) {\n const name = path.slice(path.lastIndexOf(\"/\") + 1);\n if (\n ![\"file.svg\", \"globe.svg\", \"next.svg\", \"vercel.svg\", \"window.svg\"].includes(\n name,\n )\n ) {\n return false;\n }\n return /<svg[\\s\\S]*viewBox=/u.test(content);\n}\n\nasync function planRemoveStarter(\n root: string,\n path: string,\n reason: string,\n removals: string[],\n changes: PlannedChange[],\n) {\n const content = await fileContent(path);\n if (content === undefined) return;\n if (\n content.includes(\"@ebay-dt/bonfire-next\") ||\n content.includes(\"A Bonfire instance for creating\") ||\n content.includes(\"outputFileTracingIncludes\")\n ) {\n return;\n }\n if (\n !looksLikeNextStarter(path, content) &&\n !looksLikeStarterAsset(path, content)\n ) {\n throw new ConvertError(\n `${displayPath(root, path)} is customized; it will not be removed automatically`,\n );\n }\n removals.push(path);\n addChange(changes, root, path, \"remove\", reason);\n}\n\nfunction replaceIdentity(content: string, options: ConvertOptions) {\n return content\n .replaceAll(\"design-sandbox-next\", () => options.appId)\n .replaceAll(\"Design Sandbox\", () => options.name)\n .replaceAll(\"github.corp.ebay.com\", () => options.hostname)\n .replaceAll(DEFAULT_DEPLOYMENT_ENDPOINT, () => options.deploymentEndpoint);\n}\n\nfunction withoutItemGuidance(content: string) {\n const start = content.indexOf(\"### Data fetching\");\n const end = content.indexOf(\"### Viewport sizing\");\n if (start < 0 || end < 0 || end <= start) return content;\n return `${content.slice(0, start)}${content.slice(end)}`;\n}\n\nasync function templateFiles(options: ConvertOptions) {\n const files: Array<{ relativePath: string; content: string }> = [];\n async function visit(directory: string) {\n const entries = await readdir(directory, { withFileTypes: true });\n for (const entry of entries.sort((left, right) =>\n left.name.localeCompare(right.name),\n )) {\n const path = join(directory, entry.name);\n const relativePath = relative(TEMPLATE_ROOT, path);\n if (\n !options.withItemData &&\n (relativePath.startsWith(\"src/app/api/items/\") ||\n relativePath.startsWith(\"src/lib/items/\") ||\n relativePath === \"context/skills/item-data/SKILL.md\")\n ) {\n continue;\n }\n if (entry.isDirectory()) {\n await visit(path);\n } else if (entry.isFile()) {\n let content = await readFile(path, \"utf8\");\n content = replaceIdentity(content, options);\n if (!options.withItemData && relativePath === \"context/PROTOTYPE.md\") {\n content = withoutItemGuidance(content);\n }\n files.push({ relativePath, content });\n }\n }\n }\n await visit(TEMPLATE_ROOT);\n return files;\n}\n\nfunction starterReadme(options: ConvertOptions) {\n return `# ${options.name}\\n\\nA Bonfire instance for creating, editing, and sharing prototypes.\\n\\n## Development\\n\\nUse Node.js 24+, install dependencies with pnpm, then run:\\n\\n~~~sh\\npnpm dev\\n~~~\\n\\nThe development command prepares the Bonfire session secret and the local identity branch before starting Next.js on port ${options.port}.\\n\\nRun pnpm context:install once on a machine where agent tooling is desired.\\n`;\n}\n\nfunction addDependency(\n packageJson: PackageJson,\n section: \"dependencies\" | \"devDependencies\",\n name: string,\n version: string,\n) {\n const target = (packageJson[section] ??= {});\n const existing = target[name];\n if (existing !== undefined) {\n if (existing !== version && name.startsWith(\"@ebay-dt/bonfire-\")) {\n throw new ConvertError(\n `${name} is already declared as ${existing}; expected ${version}. Update it explicitly before converting`,\n );\n }\n return;\n }\n target[name] = version;\n}\n\nfunction addScript(\n packageJson: PackageJson,\n name: string,\n value: string,\n allowedExisting: readonly string[] = [],\n) {\n const scripts = (packageJson.scripts ??= {});\n const existing = scripts[name];\n if (existing !== undefined) {\n if (existing !== value && !allowedExisting.includes(String(existing))) {\n throw new ConvertError(\n `package.json script ${name} is customized (${String(existing)}); resolve it before converting`,\n );\n }\n scripts[name] = value;\n return;\n }\n scripts[name] = value;\n}\n\nfunction mergeLines(existing: string | undefined, required: readonly string[]) {\n let result = existing ?? \"\";\n for (const line of required) {\n if (result.split(/\\r?\\n/u).some((entry) => entry.trim() === line)) continue;\n if (result.length > 0 && !result.endsWith(\"\\n\")) result += \"\\n\";\n result += `${line}\\n`;\n }\n return result;\n}\n\nfunction mergeWorkspace(existing: string | undefined) {\n let result = existing ?? \"\";\n if (!/^minimumReleaseAge:/mu.test(result)) {\n result = `# Bonfire publishes lockstep alpha packages that must be installable immediately.\\nminimumReleaseAge: 0\\n${result}`;\n } else if (!/^minimumReleaseAge:\\s*0\\s*$/mu.test(result)) {\n throw new ConvertError(\n \"pnpm-workspace.yaml has a conflicting minimumReleaseAge\",\n );\n }\n const required = [\n \" esbuild: true\",\n \" sharp: true\",\n \" unrs-resolver: true\",\n ];\n for (const line of required) {\n const key = line.trim().split(\":\")[0] ?? \"\";\n const match = result.match(new RegExp(`^\\\\s+${key}:\\\\s*(\\\\S+)\\\\s*$`, \"mu\"));\n if (match && match[1] !== \"true\") {\n // create-next-app currently writes these two false entries. They are\n // starter policy, not an instance decision, so normalize them to the\n // Bonfire installation policy.\n if (match[1] !== \"false\" || ![\"sharp\", \"unrs-resolver\"].includes(key)) {\n throw new ConvertError(\n `pnpm-workspace.yaml has a conflicting allowBuilds.${key} setting`,\n );\n }\n result = result.replace(\n new RegExp(`(^\\\\s+${key}:\\\\s*)false(\\\\s*$)`, \"mu\"),\n \"$1true$2\",\n );\n }\n if (!match) {\n if (!result.endsWith(\"\\n\")) result += \"\\n\";\n result += `${line}\\n`;\n }\n }\n return result;\n}\n\nasync function planPackageJson(\n root: string,\n options: ConvertOptions,\n packageJson: PackageJson,\n nextVersion: { major: number; minor: number; patch: number },\n writes: Map<string, string>,\n changes: PlannedChange[],\n) {\n const packageName =\n options.packageName ??\n (typeof packageJson.name === \"string\" ? packageJson.name : options.appId);\n if (!/^[a-z0-9][a-z0-9._-]*$/u.test(packageName)) {\n throw new ConvertError(`package name is invalid: ${packageName}`);\n }\n packageJson.name = packageName;\n const engines = (packageJson.engines ??= {});\n if (\n engines.node !== undefined &&\n !/^(?:\\^|~|>=)?\\s*2[4-9]|[3-9]\\d/u.test(engines.node)\n ) {\n throw new ConvertError(\n \"package.json engines.node must allow Node.js 24 or newer\",\n );\n }\n engines.node = \">=24\";\n packageJson.packageManager ??= \"pnpm@11.25.0\";\n\n const dev = `bonfire prepare --root . && bonfire branch prepare --root . --hostname ${options.hostname} && next dev -p ${options.port}`;\n addScript(packageJson, \"dev\", dev, [\"next dev\"]);\n addScript(packageJson, \"context:sync\", \"bonfire context sync\");\n addScript(\n packageJson,\n \"context:install\",\n `bonfire context install --app-id ${options.appId}`,\n );\n addScript(\n packageJson,\n \"check\",\n \"tsc --noEmit --project tsconfig.check.json && pnpm context:sync --check\",\n );\n addScript(packageJson, \"format\", \"prettier --write .\");\n addScript(packageJson, \"build\", \"next build\", [\"next build\"]);\n addScript(packageJson, \"start\", \"next start\", [\"next start\"]);\n addScript(packageJson, \"lint\", \"eslint\", [\"eslint\", \"eslint .\"]);\n\n const nextExact = `${nextVersion.major}.${nextVersion.minor}.${nextVersion.patch}`;\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-context\",\n BONFIRE_VERSION,\n );\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-core\",\n BONFIRE_VERSION,\n );\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-next\",\n BONFIRE_VERSION,\n );\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-preset-ebay\",\n PRESET_VERSION,\n );\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-react\",\n BONFIRE_VERSION,\n );\n addDependency(packageJson, \"dependencies\", \"@ebay/ui-core-react\", \"9.13.0\");\n addDependency(packageJson, \"dependencies\", \"@evo-web/react\", \"0.4.4\");\n addDependency(\n packageJson,\n \"dependencies\",\n \"react-dom\",\n dependencyVersion(packageJson, \"react-dom\") ?? \"19.2.8\",\n );\n addDependency(packageJson, \"dependencies\", \"server-only\", \"^0.0.1\");\n addDependency(\n packageJson,\n \"devDependencies\",\n \"@ebay-dt/bonfire-cli\",\n BONFIRE_VERSION,\n );\n addDependency(packageJson, \"devDependencies\", \"@types/node\", \"^20\");\n addDependency(packageJson, \"devDependencies\", \"@types/react\", \"^19\");\n addDependency(packageJson, \"devDependencies\", \"@types/react-dom\", \"^19\");\n addDependency(packageJson, \"devDependencies\", \"eslint\", \"^9\");\n addDependency(\n packageJson,\n \"devDependencies\",\n \"eslint-config-next\",\n nextExact,\n );\n addDependency(packageJson, \"devDependencies\", \"prettier\", \"^3.9.5\");\n if (options.withItemData) {\n addDependency(\n packageJson,\n \"dependencies\",\n \"@tanstack/react-query\",\n \"^5.104.0\",\n );\n addDependency(packageJson, \"dependencies\", \"zod\", \"^4.4.3\");\n }\n\n const path = join(root, \"package.json\");\n const content = `${JSON.stringify(packageJson, null, 2)}\\n`;\n const existing = await readFile(path, \"utf8\");\n if (JSON.stringify(JSON.parse(existing)) !== JSON.stringify(packageJson)) {\n writes.set(path, content);\n addChange(\n changes,\n root,\n path,\n \"update\",\n \"merge Bonfire scripts and dependencies\",\n );\n }\n}\n\nexport async function planConversion(\n options: ConvertOptions,\n): Promise<ConversionPlan> {\n const root = resolve(options.root);\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(options.appId)) {\n throw new ConvertError(\n \"app id must contain only letters, numbers, dots, underscores, and hyphens\",\n );\n }\n if (!options.name.trim())\n throw new ConvertError(\"display name must not be empty\");\n const packagePath = join(root, \"package.json\");\n const packageJson = await readJson(packagePath);\n const nextVersion = validateStartingScaffold(packageJson);\n if (!(await exists(join(root, \"src\", \"app\")))) {\n throw new ConvertError(\n \"the starting scaffold must use the src/app App Router\",\n );\n }\n\n const writes = new Map<string, string>();\n const changes: PlannedChange[] = [];\n const removals: string[] = [];\n\n const starterPaths = [\n join(root, \"src/app/layout.tsx\"),\n join(root, \"src/app/page.tsx\"),\n join(root, \"src/app/page.module.css\"),\n join(root, \"src/app/globals.css\"),\n join(root, \"next.config.ts\"),\n join(root, \"README.md\"),\n ...[\"file.svg\", \"globe.svg\", \"next.svg\", \"vercel.svg\", \"window.svg\"].map(\n (name) => join(root, \"public\", name),\n ),\n ];\n for (const path of starterPaths) {\n await planRemoveStarter(\n root,\n path,\n \"remove unmodified create-next-app content\",\n removals,\n changes,\n );\n }\n const nextBuildDirectory = join(root, \".next\");\n if (await exists(nextBuildDirectory)) {\n removals.push(nextBuildDirectory);\n addChange(\n changes,\n root,\n nextBuildDirectory,\n \"remove\",\n \"clear generated Next.js output\",\n );\n }\n\n const resolvedOptions = { ...options, root };\n const templates = await templateFiles(resolvedOptions);\n for (const file of templates) {\n const path = join(root, file.relativePath);\n if (removals.includes(path)) {\n writes.set(path, file.content);\n addChange(\n changes,\n root,\n path,\n \"update\",\n \"replace unmodified create-next-app content\",\n );\n continue;\n }\n await planFile(\n root,\n path,\n file.content,\n \"add Bonfire instance scaffold\",\n writes,\n changes,\n );\n }\n\n await planPackageJson(\n root,\n options,\n packageJson,\n nextVersion,\n writes,\n changes,\n );\n\n const tsconfigPath = join(root, \"tsconfig.json\");\n const existingTsconfig = await readFile(tsconfigPath, \"utf8\");\n const tsconfig = await readJson(tsconfigPath);\n const compilerOptions = (tsconfig.compilerOptions ?? {}) as Record<\n string,\n unknown\n >;\n tsconfig.compilerOptions = compilerOptions;\n const paths = compilerOptions[\"paths\"] as Record<string, unknown> | undefined;\n if (paths && JSON.stringify(paths[\"@/*\"]) !== JSON.stringify([\"./src/*\"])) {\n throw new ConvertError(\"tsconfig.json has a conflicting @/* path alias\");\n }\n compilerOptions[\"paths\"] = { ...(paths ?? {}), \"@/*\": [\"./src/*\"] };\n const nextTsconfig = `${JSON.stringify(tsconfig, null, 2)}\\n`;\n if (\n JSON.stringify(JSON.parse(existingTsconfig)) !== JSON.stringify(tsconfig)\n ) {\n writes.set(tsconfigPath, nextTsconfig);\n addChange(\n changes,\n root,\n tsconfigPath,\n \"update\",\n \"retain the Bonfire @/* source alias\",\n );\n }\n\n await planFile(\n root,\n join(root, \"tsconfig.check.json\"),\n '{\\n \"extends\": \"./tsconfig.json\",\\n \"exclude\": [\"node_modules\", \".next\"]\\n}\\n',\n \"add the clean TypeScript check project\",\n writes,\n changes,\n );\n await planFile(\n root,\n join(root, \".prettierrc\"),\n '{\\n \"semi\": false,\\n \"singleQuote\": true,\\n \"trailingComma\": \"es5\",\\n \"printWidth\": 80\\n}\\n',\n \"add Bonfire formatting conventions\",\n writes,\n changes,\n );\n await planFile(\n root,\n join(root, \".prettierignore\"),\n \"# Generated and external files\\n.next/\\nnode_modules/\\npnpm-lock.yaml\\nsrc/app/(prototypes)/**/.claude/settings.json\\n\",\n \"add formatter exclusions\",\n writes,\n changes,\n );\n await planFile(\n root,\n join(root, \".env.local.example\"),\n \"# Bonfire generates this secret during `pnpm dev`.\\n# Do NOT manually modify or remove this variable.\\nBF_SESSION_SECRET=\\n\\n# Set exactly to lowercase `true` to skip automatic identity-branch preparation.\\nBF_DISABLE_AUTO_BRANCH_SWITCH=\\n\",\n \"document Bonfire environment setup\",\n writes,\n changes,\n );\n await planFile(\n root,\n join(root, \"public/.gitkeep\"),\n \"\",\n \"retain an explicit public directory\",\n writes,\n changes,\n );\n\n const gitignorePath = join(root, \".gitignore\");\n const gitignore = await fileContent(gitignorePath);\n const mergedGitignore = mergeLines(gitignore, [\n \"# Bonfire local secrets\",\n \".env.local\",\n ]);\n if (mergedGitignore !== (gitignore ?? \"\")) {\n writes.set(gitignorePath, mergedGitignore);\n addChange(\n changes,\n root,\n gitignorePath,\n \"update\",\n \"ignore the generated local session secret\",\n );\n }\n\n const workspacePath = join(root, \"pnpm-workspace.yaml\");\n const workspace = await fileContent(workspacePath);\n const mergedWorkspace = mergeWorkspace(workspace);\n if (mergedWorkspace !== (workspace ?? \"\")) {\n writes.set(workspacePath, mergedWorkspace);\n addChange(\n changes,\n root,\n workspacePath,\n \"update\",\n \"retain the Bonfire pnpm installation policy\",\n );\n }\n const npmrcPath = join(root, \".npmrc\");\n const npmrc = await fileContent(npmrcPath);\n if (\n npmrc &&\n /@ebay-dt:\\s*registry=/u.test(npmrc) &&\n !npmrc.includes(\"@ebay-dt:registry=https://registry.npmjs.org/\")\n ) {\n throw new ConvertError(\".npmrc has a conflicting @ebay-dt registry\");\n }\n const mergedNpmrc = mergeLines(npmrc, [\n \"@ebay-dt:registry=https://registry.npmjs.org/\",\n ]);\n if (mergedNpmrc !== (npmrc ?? \"\")) {\n writes.set(npmrcPath, mergedNpmrc);\n addChange(\n changes,\n root,\n npmrcPath,\n \"update\",\n \"resolve Bonfire packages from public npm\",\n );\n }\n const nvmrcPath = join(root, \".nvmrc\");\n const nvmrc = await fileContent(nvmrcPath);\n if (nvmrc === undefined) {\n await planFile(\n root,\n nvmrcPath,\n \"24\\n\",\n \"select Node.js 24 or newer\",\n writes,\n changes,\n );\n }\n\n const readmePath = join(root, \"README.md\");\n if (removals.includes(readmePath)) {\n writes.set(readmePath, starterReadme(options));\n addChange(\n changes,\n root,\n readmePath,\n \"update\",\n \"replace unmodified create-next-app documentation\",\n );\n } else if (\n !writes.has(readmePath) &&\n (await fileContent(readmePath)) === undefined\n ) {\n await planFile(\n root,\n readmePath,\n starterReadme(options),\n \"add instance setup documentation\",\n writes,\n changes,\n );\n }\n\n return { root, changes, writes, removals, packageJson };\n}\n\nexport async function convert(options: ConvertOptions) {\n const plan = await planConversion(options);\n for (const change of plan.changes) {\n console.log(\n `${options.dryRun ? \"Would \" : \"\"}${change.kind} ${change.path} (${change.reason})`,\n );\n }\n if (options.dryRun) return;\n\n for (const path of plan.removals) {\n await rm(path, {\n force: true,\n recursive: path === join(plan.root, \".next\"),\n });\n }\n for (const [path, content] of plan.writes) {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content);\n if (path.endsWith(\"context/hooks/block-env-reads.mjs\")) {\n await chmod(path, 0o755);\n }\n }\n\n if (!options.skipInstall) {\n console.log(\"Installing dependencies and updating pnpm-lock.yaml...\");\n await execFileAsync(\"pnpm\", [\"install\"], {\n cwd: plan.root,\n maxBuffer: 10 * 1024 * 1024,\n });\n }\n\n if (!options.skipInstall) {\n const formatPaths = [...plan.writes.keys()]\n .map((path) => displayPath(plan.root, path))\n .filter((path) => /\\.(?:json|mjs|md|ts|tsx|yaml|yml)$/u.test(path));\n if (formatPaths.length > 0) {\n await execFileAsync(\n \"pnpm\",\n [\"exec\", \"prettier\", \"--write\", ...formatPaths],\n {\n cwd: plan.root,\n maxBuffer: 10 * 1024 * 1024,\n },\n );\n }\n await execFileAsync(\"pnpm\", [\"exec\", \"next\", \"typegen\"], {\n cwd: plan.root,\n maxBuffer: 10 * 1024 * 1024,\n });\n }\n\n const contextResult = await syncContext({ repositoryRoot: plan.root });\n if (contextResult.written.length > 0 || contextResult.removed.length > 0) {\n console.log(\n `Context synchronized (${contextResult.written.length} written, ${contextResult.removed.length} removed).`,\n );\n }\n console.log(\"Bonfire instance conversion complete.\");\n}\n\nexport function defaultConvertOptions(root = process.cwd()): ConvertOptions {\n const appId = \"bonfire-instance\";\n return {\n root,\n appId,\n name: \"Bonfire Instance\",\n hostname: DEFAULT_HOSTNAME,\n port: DEFAULT_PORT,\n deploymentEndpoint: DEFAULT_DEPLOYMENT_ENDPOINT,\n withItemData: true,\n dryRun: false,\n skipInstall: false,\n };\n}\n\nexport const convertDefaults = {\n hostname: DEFAULT_HOSTNAME,\n deploymentEndpoint: DEFAULT_DEPLOYMENT_ENDPOINT,\n port: DEFAULT_PORT,\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ebay-dt/bonfire-cli",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "The Bonfire command-line tool.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
|
+
"templates",
|
|
12
13
|
"README.md",
|
|
13
14
|
"LICENSE"
|
|
14
15
|
],
|
|
@@ -30,9 +31,10 @@
|
|
|
30
31
|
"access": "public"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
34
|
+
"@ebay-dt/bonfire-context": "0.1.0",
|
|
33
35
|
"@next/env": "16.3.4",
|
|
34
|
-
"@ebay-dt/bonfire-core": "0.1.0
|
|
35
|
-
"@ebay-dt/bonfire-node": "0.1.0
|
|
36
|
+
"@ebay-dt/bonfire-core": "0.1.0",
|
|
37
|
+
"@ebay-dt/bonfire-node": "0.1.0"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
38
40
|
"@repo/eslint-config": "0.0.0",
|