betterstart-cli 0.0.79 → 0.0.80
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/dist/cli.js +237 -238
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ import * as p from "@clack/prompts";
|
|
|
31
31
|
import fs from "fs";
|
|
32
32
|
import path from "path";
|
|
33
33
|
import { fileURLToPath } from "url";
|
|
34
|
-
var CONFIG_FILE_NAME = "
|
|
34
|
+
var CONFIG_FILE_NAME = "betterstart.config.ts";
|
|
35
35
|
function findConfigFile(cwd) {
|
|
36
36
|
const filePath = path.join(cwd, CONFIG_FILE_NAME);
|
|
37
37
|
return fs.existsSync(filePath) ? filePath : void 0;
|
|
@@ -159,8 +159,180 @@ import path27 from "path";
|
|
|
159
159
|
import * as p4 from "@clack/prompts";
|
|
160
160
|
|
|
161
161
|
// core-engine/config/serialize.ts
|
|
162
|
+
import fs3 from "fs";
|
|
163
|
+
import path4 from "path";
|
|
164
|
+
|
|
165
|
+
// core-engine/snapshots/store.ts
|
|
166
|
+
import crypto from "crypto";
|
|
162
167
|
import fs2 from "fs";
|
|
163
168
|
import path3 from "path";
|
|
169
|
+
|
|
170
|
+
// core-engine/utils/json-format.ts
|
|
171
|
+
function stringifyProjectJson(value) {
|
|
172
|
+
return `${compactShortStringArrays(JSON.stringify(value, null, 2))}
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
function compactShortStringArrays(json) {
|
|
176
|
+
return json.replace(
|
|
177
|
+
/\[\n((?:[ \t]+"(?:[^"\\]|\\.)*",?\n)+)([ \t]*)\]/g,
|
|
178
|
+
(match, entries) => {
|
|
179
|
+
const values = entries.trim().split("\n").map((line) => line.trim());
|
|
180
|
+
const inline = `[${values.map((entry) => entry.replace(/,$/, "")).join(", ")}]`;
|
|
181
|
+
return inline.length <= 70 ? inline : match;
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// core-engine/snapshots/store.ts
|
|
187
|
+
var BARREL_SCOPE = "_barrels";
|
|
188
|
+
var REMOVED_SCOPE = "_removed";
|
|
189
|
+
function normalizeLineEndings(content) {
|
|
190
|
+
return content.replace(/\r\n/g, "\n");
|
|
191
|
+
}
|
|
192
|
+
function hashContent(content) {
|
|
193
|
+
return `sha256-${crypto.createHash("sha256").update(normalizeLineEndings(content)).digest("hex")}`;
|
|
194
|
+
}
|
|
195
|
+
function snapshotRoot(cwd) {
|
|
196
|
+
return path3.join(cwd, ".betterstart", "snapshots");
|
|
197
|
+
}
|
|
198
|
+
function scopeDir(cwd, scope) {
|
|
199
|
+
return path3.join(snapshotRoot(cwd), scope);
|
|
200
|
+
}
|
|
201
|
+
function manifestPath(cwd, scope) {
|
|
202
|
+
return path3.join(scopeDir(cwd, scope), "manifest.json");
|
|
203
|
+
}
|
|
204
|
+
function filesDir(cwd, scope) {
|
|
205
|
+
return path3.join(scopeDir(cwd, scope), "files");
|
|
206
|
+
}
|
|
207
|
+
function snapshotFilePath(cwd, scope, filePath) {
|
|
208
|
+
return path3.join(filesDir(cwd, scope), ...filePath.split("/"));
|
|
209
|
+
}
|
|
210
|
+
function removedDir(cwd) {
|
|
211
|
+
return path3.join(snapshotRoot(cwd), REMOVED_SCOPE);
|
|
212
|
+
}
|
|
213
|
+
function tombstonePath(cwd, scope) {
|
|
214
|
+
return path3.join(removedDir(cwd), scope);
|
|
215
|
+
}
|
|
216
|
+
function loadManifest(cwd, scope) {
|
|
217
|
+
const filePath = manifestPath(cwd, scope);
|
|
218
|
+
if (!fs2.existsSync(filePath)) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
222
|
+
}
|
|
223
|
+
function saveManifest(cwd, scope, manifest) {
|
|
224
|
+
fs2.mkdirSync(scopeDir(cwd, scope), { recursive: true });
|
|
225
|
+
fs2.writeFileSync(manifestPath(cwd, scope), stringifyProjectJson(manifest), "utf-8");
|
|
226
|
+
}
|
|
227
|
+
function writeSnapshotFiles(cwd, scope, files) {
|
|
228
|
+
const dir = filesDir(cwd, scope);
|
|
229
|
+
fs2.rmSync(dir, { recursive: true, force: true });
|
|
230
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
231
|
+
return files.map((file) => {
|
|
232
|
+
const outputPath = snapshotFilePath(cwd, scope, file.path);
|
|
233
|
+
fs2.mkdirSync(path3.dirname(outputPath), { recursive: true });
|
|
234
|
+
fs2.writeFileSync(outputPath, file.content, "utf-8");
|
|
235
|
+
return {
|
|
236
|
+
path: file.path,
|
|
237
|
+
hash: hashContent(file.content)
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function readSnapshotFiles(cwd, scope, manifest) {
|
|
242
|
+
return manifest.files.filter((entry) => fs2.existsSync(snapshotFilePath(cwd, scope, entry.path))).map((entry) => ({
|
|
243
|
+
path: entry.path,
|
|
244
|
+
content: fs2.readFileSync(snapshotFilePath(cwd, scope, entry.path), "utf-8")
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
function deleteSnapshot(cwd, scope) {
|
|
248
|
+
fs2.rmSync(scopeDir(cwd, scope), { recursive: true, force: true });
|
|
249
|
+
}
|
|
250
|
+
function hasSnapshotFiles(cwd, scope) {
|
|
251
|
+
return fs2.existsSync(filesDir(cwd, scope));
|
|
252
|
+
}
|
|
253
|
+
function writeTombstone(cwd, scope) {
|
|
254
|
+
fs2.mkdirSync(removedDir(cwd), { recursive: true });
|
|
255
|
+
fs2.writeFileSync(tombstonePath(cwd, scope), "", "utf-8");
|
|
256
|
+
}
|
|
257
|
+
function hasTombstone(cwd, scope) {
|
|
258
|
+
return fs2.existsSync(tombstonePath(cwd, scope));
|
|
259
|
+
}
|
|
260
|
+
function clearTombstone(cwd, scope) {
|
|
261
|
+
fs2.rmSync(tombstonePath(cwd, scope), { force: true });
|
|
262
|
+
}
|
|
263
|
+
function listSnapshotScopes(cwd) {
|
|
264
|
+
const dir = snapshotRoot(cwd);
|
|
265
|
+
if (!fs2.existsSync(dir)) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
return fs2.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => name !== REMOVED_SCOPE).sort();
|
|
269
|
+
}
|
|
270
|
+
function createManifest(scope, schemaJson, cliVersion, files, skipped) {
|
|
271
|
+
return {
|
|
272
|
+
schemaName: scope,
|
|
273
|
+
schemaHash: hashContent(JSON.stringify(schemaJson)),
|
|
274
|
+
schemaJson,
|
|
275
|
+
cliVersion,
|
|
276
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
277
|
+
files: files.map((file) => ({
|
|
278
|
+
path: file.path,
|
|
279
|
+
hash: hashContent(file.content)
|
|
280
|
+
})),
|
|
281
|
+
skipped: [...skipped].sort()
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function snapshotRootExists(cwd) {
|
|
285
|
+
return fs2.existsSync(snapshotRoot(cwd));
|
|
286
|
+
}
|
|
287
|
+
function manifestFilesByPath(manifest) {
|
|
288
|
+
return new Map(manifest.files.map((file) => [file.path, file]));
|
|
289
|
+
}
|
|
290
|
+
function loadAllManifests(cwd) {
|
|
291
|
+
return listSnapshotScopes(cwd).filter((scope) => scope !== BARREL_SCOPE).map((scope) => ({ scope, manifest: loadManifest(cwd, scope) })).filter(
|
|
292
|
+
(entry) => entry.manifest !== null
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
function snapshotTargetPath(cwd, filePath) {
|
|
296
|
+
return path3.join(cwd, ...filePath.split("/"));
|
|
297
|
+
}
|
|
298
|
+
function getCurrentCliVersion() {
|
|
299
|
+
let currentDir = path3.dirname(new URL(import.meta.url).pathname);
|
|
300
|
+
for (let depth = 0; depth < 6; depth++) {
|
|
301
|
+
const packagePath = path3.join(currentDir, "package.json");
|
|
302
|
+
if (fs2.existsSync(packagePath)) {
|
|
303
|
+
try {
|
|
304
|
+
const pkg = JSON.parse(fs2.readFileSync(packagePath, "utf-8"));
|
|
305
|
+
if (pkg.name === "betterstart-cli" && pkg.version) {
|
|
306
|
+
return pkg.version;
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const parentDir = path3.dirname(currentDir);
|
|
312
|
+
if (parentDir === currentDir) {
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
currentDir = parentDir;
|
|
316
|
+
}
|
|
317
|
+
return "0.0.0";
|
|
318
|
+
}
|
|
319
|
+
function getAllTrackedFilePaths(cwd) {
|
|
320
|
+
const paths = /* @__PURE__ */ new Set();
|
|
321
|
+
for (const { manifest } of loadAllManifests(cwd)) {
|
|
322
|
+
for (const file of manifest.files) {
|
|
323
|
+
paths.add(file.path);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const barrelManifest = loadManifest(cwd, BARREL_SCOPE);
|
|
327
|
+
if (barrelManifest) {
|
|
328
|
+
for (const file of barrelManifest.files) {
|
|
329
|
+
paths.add(file.path);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return Array.from(paths).sort();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// core-engine/config/serialize.ts
|
|
164
336
|
function quote(value) {
|
|
165
337
|
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
166
338
|
}
|
|
@@ -176,6 +348,7 @@ function serializeConfig(config) {
|
|
|
176
348
|
|
|
177
349
|
export default defineConfig({
|
|
178
350
|
framework: 'next',
|
|
351
|
+
cliVersion: ${quote(getCurrentCliVersion())},
|
|
179
352
|
|
|
180
353
|
frameworkConfig: {
|
|
181
354
|
next: {
|
|
@@ -224,13 +397,13 @@ export default defineConfig({
|
|
|
224
397
|
`;
|
|
225
398
|
}
|
|
226
399
|
function writeConfigFile(cwd, config) {
|
|
227
|
-
|
|
400
|
+
fs3.writeFileSync(path4.join(cwd, CONFIG_FILE_NAME), serializeConfig(config), "utf-8");
|
|
228
401
|
}
|
|
229
402
|
|
|
230
403
|
// core-engine/utils/package-manager.ts
|
|
231
404
|
import { execFileSync } from "child_process";
|
|
232
|
-
import
|
|
233
|
-
import
|
|
405
|
+
import fs4 from "fs";
|
|
406
|
+
import path5 from "path";
|
|
234
407
|
var LOCKFILE_MAP = {
|
|
235
408
|
"pnpm-lock.yaml": "pnpm",
|
|
236
409
|
"package-lock.json": "npm",
|
|
@@ -239,18 +412,18 @@ var LOCKFILE_MAP = {
|
|
|
239
412
|
"bun.lock": "bun"
|
|
240
413
|
};
|
|
241
414
|
function detectPackageManager(cwd) {
|
|
242
|
-
let dir =
|
|
243
|
-
const root =
|
|
415
|
+
let dir = path5.resolve(cwd);
|
|
416
|
+
const root = path5.parse(dir).root;
|
|
244
417
|
while (dir !== root) {
|
|
245
418
|
for (const [lockfile, pm] of Object.entries(LOCKFILE_MAP)) {
|
|
246
|
-
if (
|
|
419
|
+
if (fs4.existsSync(path5.join(dir, lockfile))) {
|
|
247
420
|
return pm;
|
|
248
421
|
}
|
|
249
422
|
}
|
|
250
|
-
const pkgPath =
|
|
251
|
-
if (
|
|
423
|
+
const pkgPath = path5.join(dir, "package.json");
|
|
424
|
+
if (fs4.existsSync(pkgPath)) {
|
|
252
425
|
try {
|
|
253
|
-
const pkg = JSON.parse(
|
|
426
|
+
const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
|
|
254
427
|
if (typeof pkg.packageManager === "string") {
|
|
255
428
|
const name = pkg.packageManager.split("@")[0];
|
|
256
429
|
if (name === "pnpm" || name === "npm" || name === "yarn" || name === "bun") {
|
|
@@ -260,7 +433,7 @@ function detectPackageManager(cwd) {
|
|
|
260
433
|
} catch {
|
|
261
434
|
}
|
|
262
435
|
}
|
|
263
|
-
dir =
|
|
436
|
+
dir = path5.dirname(dir);
|
|
264
437
|
}
|
|
265
438
|
return "npm";
|
|
266
439
|
}
|
|
@@ -284,25 +457,25 @@ function createNextAppCommand() {
|
|
|
284
457
|
}
|
|
285
458
|
|
|
286
459
|
// adapters/next/config.ts
|
|
287
|
-
import
|
|
288
|
-
import
|
|
460
|
+
import fs6 from "fs";
|
|
461
|
+
import path7 from "path";
|
|
289
462
|
|
|
290
463
|
// adapters/next/config/detect.ts
|
|
291
|
-
import
|
|
292
|
-
import
|
|
464
|
+
import fs5 from "fs";
|
|
465
|
+
import path6 from "path";
|
|
293
466
|
var NEXT_CONFIG_FILES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
294
467
|
function detectProjectName(cwd) {
|
|
295
|
-
const pkgPath =
|
|
296
|
-
if (
|
|
468
|
+
const pkgPath = path6.join(cwd, "package.json");
|
|
469
|
+
if (fs5.existsSync(pkgPath)) {
|
|
297
470
|
try {
|
|
298
|
-
const pkg = JSON.parse(
|
|
471
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
299
472
|
if (typeof pkg.name === "string" && pkg.name.length > 0) {
|
|
300
473
|
return formatProjectName(pkg.name);
|
|
301
474
|
}
|
|
302
475
|
} catch {
|
|
303
476
|
}
|
|
304
477
|
}
|
|
305
|
-
return formatProjectName(
|
|
478
|
+
return formatProjectName(path6.basename(cwd));
|
|
306
479
|
}
|
|
307
480
|
function formatProjectName(name) {
|
|
308
481
|
const base = name.includes("/") ? name.split("/").pop() : name;
|
|
@@ -310,21 +483,21 @@ function formatProjectName(name) {
|
|
|
310
483
|
}
|
|
311
484
|
function detectProject(cwd, namespaceValue = DEFAULT_ADMIN_NAMESPACE) {
|
|
312
485
|
const namespace = validateAdminNamespace(namespaceValue);
|
|
313
|
-
const isExisting = NEXT_CONFIG_FILES.some((f) =>
|
|
314
|
-
const hasSrcDir =
|
|
315
|
-
const hasTypeScript =
|
|
486
|
+
const isExisting = NEXT_CONFIG_FILES.some((f) => fs5.existsSync(path6.join(cwd, f)));
|
|
487
|
+
const hasSrcDir = fs5.existsSync(path6.join(cwd, "src"));
|
|
488
|
+
const hasTypeScript = fs5.existsSync(path6.join(cwd, "tsconfig.json")) || fs5.existsSync(path6.join(cwd, "tsconfig.app.json"));
|
|
316
489
|
const hasTailwind = detectTailwind(cwd);
|
|
317
490
|
const linter = detectLinter(cwd);
|
|
318
491
|
const conflicts = [];
|
|
319
492
|
if (isExisting) {
|
|
320
|
-
if (
|
|
493
|
+
if (fs5.existsSync(path6.join(cwd, namespace))) {
|
|
321
494
|
conflicts.push(`${namespace}/ directory already exists`);
|
|
322
495
|
}
|
|
323
|
-
if (
|
|
324
|
-
conflicts.push(
|
|
496
|
+
if (fs5.existsSync(path6.join(cwd, CONFIG_FILE_NAME))) {
|
|
497
|
+
conflicts.push(`${CONFIG_FILE_NAME} already exists`);
|
|
325
498
|
}
|
|
326
499
|
const appBase = hasSrcDir ? "src/app" : "app";
|
|
327
|
-
if (
|
|
500
|
+
if (fs5.existsSync(path6.join(cwd, appBase, `(${namespace})`))) {
|
|
328
501
|
conflicts.push(`${appBase}/(${namespace})/ route group already exists`);
|
|
329
502
|
}
|
|
330
503
|
if (hasTsconfigAdminAliases(cwd, namespace)) {
|
|
@@ -348,19 +521,19 @@ var ESLINT_CONFIG_FILES = [
|
|
|
348
521
|
];
|
|
349
522
|
function detectLinter(cwd) {
|
|
350
523
|
for (const f of BIOME_CONFIG_FILES) {
|
|
351
|
-
if (
|
|
524
|
+
if (fs5.existsSync(path6.join(cwd, f))) {
|
|
352
525
|
return { type: "biome", configFile: f };
|
|
353
526
|
}
|
|
354
527
|
}
|
|
355
528
|
for (const f of ESLINT_CONFIG_FILES) {
|
|
356
|
-
if (
|
|
529
|
+
if (fs5.existsSync(path6.join(cwd, f))) {
|
|
357
530
|
return { type: "eslint", configFile: f };
|
|
358
531
|
}
|
|
359
532
|
}
|
|
360
|
-
const pkgPath =
|
|
361
|
-
if (
|
|
533
|
+
const pkgPath = path6.join(cwd, "package.json");
|
|
534
|
+
if (fs5.existsSync(pkgPath)) {
|
|
362
535
|
try {
|
|
363
|
-
const pkg = JSON.parse(
|
|
536
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
364
537
|
if (pkg.eslintConfig) {
|
|
365
538
|
return { type: "eslint", configFile: "package.json (eslintConfig)" };
|
|
366
539
|
}
|
|
@@ -371,14 +544,14 @@ function detectLinter(cwd) {
|
|
|
371
544
|
}
|
|
372
545
|
function detectTailwind(cwd) {
|
|
373
546
|
const cssFiles = ["globals.css", "app.css", "index.css"].flatMap((f) => [
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
547
|
+
path6.join(cwd, "src", "app", f),
|
|
548
|
+
path6.join(cwd, "app", f),
|
|
549
|
+
path6.join(cwd, "src", f),
|
|
550
|
+
path6.join(cwd, f)
|
|
378
551
|
]);
|
|
379
552
|
for (const cssFile of cssFiles) {
|
|
380
|
-
if (
|
|
381
|
-
const content =
|
|
553
|
+
if (fs5.existsSync(cssFile)) {
|
|
554
|
+
const content = fs5.readFileSync(cssFile, "utf-8");
|
|
382
555
|
if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'") || content.includes("@theme")) {
|
|
383
556
|
return true;
|
|
384
557
|
}
|
|
@@ -386,8 +559,8 @@ function detectTailwind(cwd) {
|
|
|
386
559
|
}
|
|
387
560
|
const postcssFiles = ["postcss.config.js", "postcss.config.mjs", "postcss.config.cjs"];
|
|
388
561
|
for (const f of postcssFiles) {
|
|
389
|
-
if (
|
|
390
|
-
const content =
|
|
562
|
+
if (fs5.existsSync(path6.join(cwd, f))) {
|
|
563
|
+
const content = fs5.readFileSync(path6.join(cwd, f), "utf-8");
|
|
391
564
|
if (content.includes("tailwindcss") || content.includes("@tailwindcss")) {
|
|
392
565
|
return true;
|
|
393
566
|
}
|
|
@@ -396,20 +569,20 @@ function detectTailwind(cwd) {
|
|
|
396
569
|
return false;
|
|
397
570
|
}
|
|
398
571
|
function hasTsconfigAdminAliases(cwd, namespace) {
|
|
399
|
-
const tsconfigPath =
|
|
400
|
-
if (!
|
|
572
|
+
const tsconfigPath = path6.join(cwd, "tsconfig.json");
|
|
573
|
+
if (!fs5.existsSync(tsconfigPath)) return false;
|
|
401
574
|
try {
|
|
402
|
-
const content =
|
|
575
|
+
const content = fs5.readFileSync(tsconfigPath, "utf-8");
|
|
403
576
|
return content.includes(`@${namespace}/`);
|
|
404
577
|
} catch {
|
|
405
578
|
return false;
|
|
406
579
|
}
|
|
407
580
|
}
|
|
408
581
|
function detectNextMajorVersion(cwd) {
|
|
409
|
-
const pkgPath =
|
|
410
|
-
if (!
|
|
582
|
+
const pkgPath = path6.join(cwd, "package.json");
|
|
583
|
+
if (!fs5.existsSync(pkgPath)) return 16;
|
|
411
584
|
try {
|
|
412
|
-
const pkg = JSON.parse(
|
|
585
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
413
586
|
const version2 = pkg.dependencies?.next ?? pkg.devDependencies?.next;
|
|
414
587
|
if (typeof version2 !== "string") return 16;
|
|
415
588
|
const match = version2.match(/(\d+)/);
|
|
@@ -421,7 +594,7 @@ function detectNextMajorVersion(cwd) {
|
|
|
421
594
|
}
|
|
422
595
|
|
|
423
596
|
// adapters/next/config.ts
|
|
424
|
-
var unsupportedStateMessage = '
|
|
597
|
+
var unsupportedStateMessage = 'betterstart.config.ts must use the current BetterStart config shape with framework: "next" and frameworkConfig.next. Old top-level srcDir, nextMajorVersion, and paths config is no longer supported.';
|
|
425
598
|
function getDefaultConfig(srcDir, nextMajorVersion = 16) {
|
|
426
599
|
const namespace = DEFAULT_ADMIN_NAMESPACE;
|
|
427
600
|
const paths = deriveNamespacePaths(namespace, srcDir);
|
|
@@ -472,7 +645,7 @@ async function resolveConfig(cwd) {
|
|
|
472
645
|
const workingDir = cwd ?? process.cwd();
|
|
473
646
|
const configPath = findConfigFile(workingDir);
|
|
474
647
|
const nextMajorVersion = detectNextMajorVersion(workingDir);
|
|
475
|
-
const hasSrc =
|
|
648
|
+
const hasSrc = fs6.existsSync(path7.join(workingDir, "src"));
|
|
476
649
|
const defaultConfig = getDefaultConfig(hasSrc, nextMajorVersion);
|
|
477
650
|
if (!configPath) {
|
|
478
651
|
return defaultConfig;
|
|
@@ -524,176 +697,6 @@ import { execFileSync as execFileSync2 } from "child_process";
|
|
|
524
697
|
import fs8 from "fs";
|
|
525
698
|
import path10 from "path";
|
|
526
699
|
|
|
527
|
-
// core-engine/snapshots/store.ts
|
|
528
|
-
import crypto from "crypto";
|
|
529
|
-
import fs6 from "fs";
|
|
530
|
-
import path7 from "path";
|
|
531
|
-
|
|
532
|
-
// core-engine/utils/json-format.ts
|
|
533
|
-
function stringifyProjectJson(value) {
|
|
534
|
-
return `${compactShortStringArrays(JSON.stringify(value, null, 2))}
|
|
535
|
-
`;
|
|
536
|
-
}
|
|
537
|
-
function compactShortStringArrays(json) {
|
|
538
|
-
return json.replace(
|
|
539
|
-
/\[\n((?:[ \t]+"(?:[^"\\]|\\.)*",?\n)+)([ \t]*)\]/g,
|
|
540
|
-
(match, entries) => {
|
|
541
|
-
const values = entries.trim().split("\n").map((line) => line.trim());
|
|
542
|
-
const inline = `[${values.map((entry) => entry.replace(/,$/, "")).join(", ")}]`;
|
|
543
|
-
return inline.length <= 70 ? inline : match;
|
|
544
|
-
}
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// core-engine/snapshots/store.ts
|
|
549
|
-
var BARREL_SCOPE = "_barrels";
|
|
550
|
-
var REMOVED_SCOPE = "_removed";
|
|
551
|
-
function normalizeLineEndings(content) {
|
|
552
|
-
return content.replace(/\r\n/g, "\n");
|
|
553
|
-
}
|
|
554
|
-
function hashContent(content) {
|
|
555
|
-
return `sha256-${crypto.createHash("sha256").update(normalizeLineEndings(content)).digest("hex")}`;
|
|
556
|
-
}
|
|
557
|
-
function snapshotRoot(cwd) {
|
|
558
|
-
return path7.join(cwd, ".betterstart", "snapshots");
|
|
559
|
-
}
|
|
560
|
-
function scopeDir(cwd, scope) {
|
|
561
|
-
return path7.join(snapshotRoot(cwd), scope);
|
|
562
|
-
}
|
|
563
|
-
function manifestPath(cwd, scope) {
|
|
564
|
-
return path7.join(scopeDir(cwd, scope), "manifest.json");
|
|
565
|
-
}
|
|
566
|
-
function filesDir(cwd, scope) {
|
|
567
|
-
return path7.join(scopeDir(cwd, scope), "files");
|
|
568
|
-
}
|
|
569
|
-
function snapshotFilePath(cwd, scope, filePath) {
|
|
570
|
-
return path7.join(filesDir(cwd, scope), ...filePath.split("/"));
|
|
571
|
-
}
|
|
572
|
-
function removedDir(cwd) {
|
|
573
|
-
return path7.join(snapshotRoot(cwd), REMOVED_SCOPE);
|
|
574
|
-
}
|
|
575
|
-
function tombstonePath(cwd, scope) {
|
|
576
|
-
return path7.join(removedDir(cwd), scope);
|
|
577
|
-
}
|
|
578
|
-
function loadManifest(cwd, scope) {
|
|
579
|
-
const filePath = manifestPath(cwd, scope);
|
|
580
|
-
if (!fs6.existsSync(filePath)) {
|
|
581
|
-
return null;
|
|
582
|
-
}
|
|
583
|
-
return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
|
|
584
|
-
}
|
|
585
|
-
function saveManifest(cwd, scope, manifest) {
|
|
586
|
-
fs6.mkdirSync(scopeDir(cwd, scope), { recursive: true });
|
|
587
|
-
fs6.writeFileSync(manifestPath(cwd, scope), stringifyProjectJson(manifest), "utf-8");
|
|
588
|
-
}
|
|
589
|
-
function writeSnapshotFiles(cwd, scope, files) {
|
|
590
|
-
const dir = filesDir(cwd, scope);
|
|
591
|
-
fs6.rmSync(dir, { recursive: true, force: true });
|
|
592
|
-
fs6.mkdirSync(dir, { recursive: true });
|
|
593
|
-
return files.map((file) => {
|
|
594
|
-
const outputPath = snapshotFilePath(cwd, scope, file.path);
|
|
595
|
-
fs6.mkdirSync(path7.dirname(outputPath), { recursive: true });
|
|
596
|
-
fs6.writeFileSync(outputPath, file.content, "utf-8");
|
|
597
|
-
return {
|
|
598
|
-
path: file.path,
|
|
599
|
-
hash: hashContent(file.content)
|
|
600
|
-
};
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
function readSnapshotFiles(cwd, scope, manifest) {
|
|
604
|
-
return manifest.files.filter((entry) => fs6.existsSync(snapshotFilePath(cwd, scope, entry.path))).map((entry) => ({
|
|
605
|
-
path: entry.path,
|
|
606
|
-
content: fs6.readFileSync(snapshotFilePath(cwd, scope, entry.path), "utf-8")
|
|
607
|
-
}));
|
|
608
|
-
}
|
|
609
|
-
function deleteSnapshot(cwd, scope) {
|
|
610
|
-
fs6.rmSync(scopeDir(cwd, scope), { recursive: true, force: true });
|
|
611
|
-
}
|
|
612
|
-
function hasSnapshotFiles(cwd, scope) {
|
|
613
|
-
return fs6.existsSync(filesDir(cwd, scope));
|
|
614
|
-
}
|
|
615
|
-
function writeTombstone(cwd, scope) {
|
|
616
|
-
fs6.mkdirSync(removedDir(cwd), { recursive: true });
|
|
617
|
-
fs6.writeFileSync(tombstonePath(cwd, scope), "", "utf-8");
|
|
618
|
-
}
|
|
619
|
-
function hasTombstone(cwd, scope) {
|
|
620
|
-
return fs6.existsSync(tombstonePath(cwd, scope));
|
|
621
|
-
}
|
|
622
|
-
function clearTombstone(cwd, scope) {
|
|
623
|
-
fs6.rmSync(tombstonePath(cwd, scope), { force: true });
|
|
624
|
-
}
|
|
625
|
-
function listSnapshotScopes(cwd) {
|
|
626
|
-
const dir = snapshotRoot(cwd);
|
|
627
|
-
if (!fs6.existsSync(dir)) {
|
|
628
|
-
return [];
|
|
629
|
-
}
|
|
630
|
-
return fs6.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => name !== REMOVED_SCOPE).sort();
|
|
631
|
-
}
|
|
632
|
-
function createManifest(scope, schemaJson, cliVersion, files, skipped) {
|
|
633
|
-
return {
|
|
634
|
-
schemaName: scope,
|
|
635
|
-
schemaHash: hashContent(JSON.stringify(schemaJson)),
|
|
636
|
-
schemaJson,
|
|
637
|
-
cliVersion,
|
|
638
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
639
|
-
files: files.map((file) => ({
|
|
640
|
-
path: file.path,
|
|
641
|
-
hash: hashContent(file.content)
|
|
642
|
-
})),
|
|
643
|
-
skipped: [...skipped].sort()
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
function snapshotRootExists(cwd) {
|
|
647
|
-
return fs6.existsSync(snapshotRoot(cwd));
|
|
648
|
-
}
|
|
649
|
-
function manifestFilesByPath(manifest) {
|
|
650
|
-
return new Map(manifest.files.map((file) => [file.path, file]));
|
|
651
|
-
}
|
|
652
|
-
function loadAllManifests(cwd) {
|
|
653
|
-
return listSnapshotScopes(cwd).filter((scope) => scope !== BARREL_SCOPE).map((scope) => ({ scope, manifest: loadManifest(cwd, scope) })).filter(
|
|
654
|
-
(entry) => entry.manifest !== null
|
|
655
|
-
);
|
|
656
|
-
}
|
|
657
|
-
function snapshotTargetPath(cwd, filePath) {
|
|
658
|
-
return path7.join(cwd, ...filePath.split("/"));
|
|
659
|
-
}
|
|
660
|
-
function getCurrentCliVersion() {
|
|
661
|
-
let currentDir = path7.dirname(new URL(import.meta.url).pathname);
|
|
662
|
-
for (let depth = 0; depth < 6; depth++) {
|
|
663
|
-
const packagePath = path7.join(currentDir, "package.json");
|
|
664
|
-
if (fs6.existsSync(packagePath)) {
|
|
665
|
-
try {
|
|
666
|
-
const pkg = JSON.parse(fs6.readFileSync(packagePath, "utf-8"));
|
|
667
|
-
if (pkg.name === "betterstart-cli" && pkg.version) {
|
|
668
|
-
return pkg.version;
|
|
669
|
-
}
|
|
670
|
-
} catch {
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
const parentDir = path7.dirname(currentDir);
|
|
674
|
-
if (parentDir === currentDir) {
|
|
675
|
-
break;
|
|
676
|
-
}
|
|
677
|
-
currentDir = parentDir;
|
|
678
|
-
}
|
|
679
|
-
return "0.0.0";
|
|
680
|
-
}
|
|
681
|
-
function getAllTrackedFilePaths(cwd) {
|
|
682
|
-
const paths = /* @__PURE__ */ new Set();
|
|
683
|
-
for (const { manifest } of loadAllManifests(cwd)) {
|
|
684
|
-
for (const file of manifest.files) {
|
|
685
|
-
paths.add(file.path);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
const barrelManifest = loadManifest(cwd, BARREL_SCOPE);
|
|
689
|
-
if (barrelManifest) {
|
|
690
|
-
for (const file of barrelManifest.files) {
|
|
691
|
-
paths.add(file.path);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
return Array.from(paths).sort();
|
|
695
|
-
}
|
|
696
|
-
|
|
697
700
|
// adapters/next/init/scaffolders/dependencies.ts
|
|
698
701
|
import { spawn } from "child_process";
|
|
699
702
|
import fs7 from "fs";
|
|
@@ -22696,13 +22699,13 @@ import { spawn as spawn3 } from "child_process";
|
|
|
22696
22699
|
import fs27 from "fs";
|
|
22697
22700
|
import path33 from "path";
|
|
22698
22701
|
var LOCAL_PROTOCOL_PATTERN = /^(workspace|link|file|portal):/;
|
|
22699
|
-
var
|
|
22700
|
-
var
|
|
22702
|
+
var CONFIG_IMPORT_PATTERN = /^import\s+\{[^}]*\bdefineConfig\b[^}]*\}\s+from\s+['"]betterstart-cli['"];?[^\S\n]*$/m;
|
|
22703
|
+
var CONFIG_IMPORT_SHIM = "const defineConfig = <T>(config: T): T => config";
|
|
22701
22704
|
var LOCKFILE_REFRESH_TIMEOUT_MS = 12e4;
|
|
22702
22705
|
async function guardProjectForDeploy(cwd, refreshLockfile = runPnpmLockfileOnly) {
|
|
22703
22706
|
const restores = [];
|
|
22704
22707
|
const { localSpecDeps, removedCliDep } = guardPackageJson(cwd, restores);
|
|
22705
|
-
|
|
22708
|
+
guardBetterstartConfig(cwd, restores);
|
|
22706
22709
|
const lockfile = removedCliDep ? await guardPnpmLockfile(cwd, restores, refreshLockfile) : "not-needed";
|
|
22707
22710
|
return {
|
|
22708
22711
|
localSpecDeps,
|
|
@@ -22776,17 +22779,13 @@ function runPnpmLockfileOnly(cwd) {
|
|
|
22776
22779
|
});
|
|
22777
22780
|
});
|
|
22778
22781
|
}
|
|
22779
|
-
function
|
|
22780
|
-
const
|
|
22781
|
-
if (!fs27.existsSync(
|
|
22782
|
-
const original = fs27.readFileSync(
|
|
22783
|
-
if (!
|
|
22784
|
-
fs27.writeFileSync(
|
|
22785
|
-
|
|
22786
|
-
original.replace(ADMIN_CONFIG_IMPORT_PATTERN, ADMIN_CONFIG_IMPORT_SHIM),
|
|
22787
|
-
"utf-8"
|
|
22788
|
-
);
|
|
22789
|
-
restores.push(() => fs27.writeFileSync(adminConfigPath, original, "utf-8"));
|
|
22782
|
+
function guardBetterstartConfig(cwd, restores) {
|
|
22783
|
+
const configPath = path33.join(cwd, CONFIG_FILE_NAME);
|
|
22784
|
+
if (!fs27.existsSync(configPath)) return;
|
|
22785
|
+
const original = fs27.readFileSync(configPath, "utf-8");
|
|
22786
|
+
if (!CONFIG_IMPORT_PATTERN.test(original)) return;
|
|
22787
|
+
fs27.writeFileSync(configPath, original.replace(CONFIG_IMPORT_PATTERN, CONFIG_IMPORT_SHIM), "utf-8");
|
|
22788
|
+
restores.push(() => fs27.writeFileSync(configPath, original, "utf-8"));
|
|
22790
22789
|
}
|
|
22791
22790
|
|
|
22792
22791
|
// adapters/next/init/railway/project.ts
|
|
@@ -23908,8 +23907,8 @@ function scaffoldBase({
|
|
|
23908
23907
|
ensureDir(path38.resolve(cwd, dir));
|
|
23909
23908
|
}
|
|
23910
23909
|
const configContent = serializeConfig(config);
|
|
23911
|
-
if (safeWriteFile(path38.resolve(cwd,
|
|
23912
|
-
created.push(
|
|
23910
|
+
if (safeWriteFile(path38.resolve(cwd, CONFIG_FILE_NAME), configContent)) {
|
|
23911
|
+
created.push(CONFIG_FILE_NAME);
|
|
23913
23912
|
}
|
|
23914
23913
|
return created;
|
|
23915
23914
|
}
|
|
@@ -26976,7 +26975,7 @@ function resolveNonInteractiveProject(name) {
|
|
|
26976
26975
|
return { projectName };
|
|
26977
26976
|
}
|
|
26978
26977
|
async function readExistingConfigNamespace(cwd) {
|
|
26979
|
-
const configPath = path52.resolve(cwd,
|
|
26978
|
+
const configPath = path52.resolve(cwd, CONFIG_FILE_NAME);
|
|
26980
26979
|
if (!fs41.existsSync(configPath)) {
|
|
26981
26980
|
return;
|
|
26982
26981
|
}
|
|
@@ -26993,7 +26992,7 @@ async function readExistingConfigNamespace(cwd) {
|
|
|
26993
26992
|
}
|
|
26994
26993
|
async function resolveForceInitNamespaces(cwd, targetNamespace) {
|
|
26995
26994
|
const namespaces = /* @__PURE__ */ new Set([validateAdminNamespace(targetNamespace)]);
|
|
26996
|
-
const hasConfig = fs41.existsSync(path52.resolve(cwd,
|
|
26995
|
+
const hasConfig = fs41.existsSync(path52.resolve(cwd, CONFIG_FILE_NAME));
|
|
26997
26996
|
const existingNamespace = await readExistingConfigNamespace(cwd);
|
|
26998
26997
|
if (existingNamespace) {
|
|
26999
26998
|
namespaces.add(existingNamespace);
|
|
@@ -27015,7 +27014,7 @@ function removeExistingAdminPaths(cwd, namespaces) {
|
|
|
27015
27014
|
...segments.flatMap((segment) => [segment, `app/(${segment})`, `src/app/(${segment})`]),
|
|
27016
27015
|
".betterstart"
|
|
27017
27016
|
];
|
|
27018
|
-
const nukeFiles = [
|
|
27017
|
+
const nukeFiles = [CONFIG_FILE_NAME, "ADMIN.md", "drizzle.config.ts"];
|
|
27019
27018
|
let removed = 0;
|
|
27020
27019
|
for (const dir of nukeDirs) {
|
|
27021
27020
|
const fullPath = path52.resolve(cwd, dir);
|
|
@@ -27772,7 +27771,7 @@ async function runInitCommand(name, options) {
|
|
|
27772
27771
|
}
|
|
27773
27772
|
}
|
|
27774
27773
|
if (seedResult.success) {
|
|
27775
|
-
const seedSuccessMessage = seedOverwriteMode === "admin" ? `Admin user replaced` : `Admin user ${pc9.green(seedEmail)}
|
|
27774
|
+
const seedSuccessMessage = seedOverwriteMode === "admin" ? `Admin user replaced` : `Admin user ${pc9.green(seedEmail)} created`;
|
|
27776
27775
|
s.stop(seedSuccessMessage);
|
|
27777
27776
|
rowsBelowCredentialPrompts += clackLogRows(seedSuccessMessage);
|
|
27778
27777
|
eraseRowsAbove(credentialPromptRows, rowsBelowCredentialPrompts);
|
|
@@ -28837,7 +28836,7 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
28837
28836
|
const configFiles = [];
|
|
28838
28837
|
const configPaths = [];
|
|
28839
28838
|
const candidates = [
|
|
28840
|
-
[
|
|
28839
|
+
[CONFIG_FILE_NAME, path57.join(cwd, CONFIG_FILE_NAME)],
|
|
28841
28840
|
["drizzle.config.ts", path57.join(cwd, "drizzle.config.ts")],
|
|
28842
28841
|
["ADMIN.md", path57.join(cwd, "ADMIN.md")]
|
|
28843
28842
|
];
|