betterstart-cli 0.0.26 → 0.0.28
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
CHANGED
|
@@ -112,7 +112,7 @@ function createInitCommand(runtime) {
|
|
|
112
112
|
return new Command("init").description("Scaffold Admin into a new or existing Next.js app").argument("[name]", "Project name (creates new directory if fresh project)").option("--plugins <plugins>", "Comma-separated content plugin IDs (e.g. blog)").option("--integrations <integrations>", "Comma-separated integration IDs (e.g. r2,resend)").option("--namespace <name>", "Generated admin namespace (e.g. admin, dashboard)").option("-y, --yes", "Skip all prompts (accept defaults)").option("--skip-migration", "Skip running drizzle-kit push after scaffolding").option("--skip-admin-creation", "Skip prompting to create the initial admin user").option("--skip-dev-server-start", "Skip prompting to start the development server").option(
|
|
113
113
|
"--database-url <url>",
|
|
114
114
|
"PostgreSQL database connection string (postgres:// or postgresql://)"
|
|
115
|
-
).option("--no-vercel", "Disable the Vercel (Neon) provisioning flow (manual connection only)").option("--vercel-plan <id>", "Neon plan id passed to `vercel integration add neon`").option("--force", "Overwrite all existing Admin files (nuclear option)").action(
|
|
115
|
+
).option("--no-vercel", "Disable the Vercel (Neon) provisioning flow (manual connection only)").option("--vercel-plan <id>", "Neon plan id passed to `vercel integration add neon`").option("--skip-deploy", "Skip the post-scaffold deploy to the linked Vercel project").option("--force", "Overwrite all existing Admin files (nuclear option)").action(
|
|
116
116
|
(name, options) => runtime.runInit(name, options)
|
|
117
117
|
);
|
|
118
118
|
}
|
|
@@ -836,11 +836,48 @@ var resendIntegration = {
|
|
|
836
836
|
configure: "resend"
|
|
837
837
|
};
|
|
838
838
|
|
|
839
|
+
// adapters/next/integrations/vercel-blob/integration.ts
|
|
840
|
+
var vercelBlobIntegration = {
|
|
841
|
+
id: "vercel-blob",
|
|
842
|
+
title: "Vercel Blob",
|
|
843
|
+
description: "Remote media storage on Vercel Blob",
|
|
844
|
+
kind: "storage",
|
|
845
|
+
version: "1.0.0",
|
|
846
|
+
dependencies: [],
|
|
847
|
+
conflicts: [],
|
|
848
|
+
files: [
|
|
849
|
+
{
|
|
850
|
+
outputPath: "lib/actions/vercel-blob/provider.ts",
|
|
851
|
+
templatePath: "actions/vercel-blob.ts"
|
|
852
|
+
}
|
|
853
|
+
],
|
|
854
|
+
packageDependencies: ["@vercel/blob"],
|
|
855
|
+
devDependencies: [],
|
|
856
|
+
capabilities: {
|
|
857
|
+
storageProvider: {
|
|
858
|
+
provider: "vercel-blob",
|
|
859
|
+
importPath: "@admin/actions/vercel-blob/provider",
|
|
860
|
+
exportName: "vercelBlobStorageProvider"
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
envSections: [
|
|
864
|
+
{
|
|
865
|
+
// BLOB_READ_WRITE_TOKEN is deliberately unprefixed: it is the name the
|
|
866
|
+
// @vercel/blob SDK reads by default and the one Vercel injects into
|
|
867
|
+
// deployments when a Blob store is connected to the project.
|
|
868
|
+
header: "Storage (Vercel Blob)",
|
|
869
|
+
vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: "" }]
|
|
870
|
+
}
|
|
871
|
+
],
|
|
872
|
+
configure: "vercel-blob"
|
|
873
|
+
};
|
|
874
|
+
|
|
839
875
|
// adapters/next/integration-registry.ts
|
|
840
876
|
var integrationRegistry = {
|
|
841
877
|
mailchimp: mailchimpIntegration,
|
|
842
878
|
resend: resendIntegration,
|
|
843
|
-
r2: r2Integration
|
|
879
|
+
r2: r2Integration,
|
|
880
|
+
"vercel-blob": vercelBlobIntegration
|
|
844
881
|
};
|
|
845
882
|
function listIntegrationDefinitions() {
|
|
846
883
|
return Object.values(integrationRegistry);
|
|
@@ -2551,15 +2588,15 @@ function collectSchemaSlotLayoutErrors(schema, errors) {
|
|
|
2551
2588
|
}
|
|
2552
2589
|
collectSlotLayoutErrors(input.slot, "slot", errors);
|
|
2553
2590
|
}
|
|
2554
|
-
function collectSlotLayoutErrors(value,
|
|
2591
|
+
function collectSlotLayoutErrors(value, path59, errors) {
|
|
2555
2592
|
if (!isRecord(value)) {
|
|
2556
|
-
errors.push(`${
|
|
2593
|
+
errors.push(`${path59} must be an object with "main" and/or "sidebar" field groups.`);
|
|
2557
2594
|
return;
|
|
2558
2595
|
}
|
|
2559
2596
|
const validKeys = /* @__PURE__ */ new Set(["main", "sidebar"]);
|
|
2560
2597
|
for (const key of Object.keys(value)) {
|
|
2561
2598
|
if (!validKeys.has(key)) {
|
|
2562
|
-
errors.push(`${
|
|
2599
|
+
errors.push(`${path59} has unsupported key "${key}". Expected "main" or "sidebar".`);
|
|
2563
2600
|
}
|
|
2564
2601
|
}
|
|
2565
2602
|
const areas = [
|
|
@@ -2570,24 +2607,24 @@ function collectSlotLayoutErrors(value, path57, errors) {
|
|
|
2570
2607
|
for (const [name, area] of areas) {
|
|
2571
2608
|
if (area === void 0) continue;
|
|
2572
2609
|
hasArea = true;
|
|
2573
|
-
collectSlotAreaErrors(area, `${
|
|
2610
|
+
collectSlotAreaErrors(area, `${path59}.${name}`, errors);
|
|
2574
2611
|
}
|
|
2575
2612
|
if (!hasArea) {
|
|
2576
|
-
errors.push(`${
|
|
2613
|
+
errors.push(`${path59} must define at least one of "main" or "sidebar".`);
|
|
2577
2614
|
}
|
|
2578
2615
|
}
|
|
2579
|
-
function collectSlotAreaErrors(value,
|
|
2616
|
+
function collectSlotAreaErrors(value, path59, errors) {
|
|
2580
2617
|
if (!isRecord(value)) {
|
|
2581
|
-
errors.push(`${
|
|
2618
|
+
errors.push(`${path59} must be an object with a "fields" array.`);
|
|
2582
2619
|
return;
|
|
2583
2620
|
}
|
|
2584
2621
|
const fields = value.fields;
|
|
2585
2622
|
if (!Array.isArray(fields)) {
|
|
2586
|
-
errors.push(`${
|
|
2623
|
+
errors.push(`${path59}.fields must be an array.`);
|
|
2587
2624
|
return;
|
|
2588
2625
|
}
|
|
2589
2626
|
for (const field of fields) {
|
|
2590
|
-
walkSlot(field, `${
|
|
2627
|
+
walkSlot(field, `${path59}.fields.${field.name ?? "unnamed"}`, errors);
|
|
2591
2628
|
}
|
|
2592
2629
|
}
|
|
2593
2630
|
function collectInvalidHeightErrors(topLevelFields, rootPath, errors) {
|
|
@@ -3197,12 +3234,45 @@ async function promptR2Config(cwd) {
|
|
|
3197
3234
|
updatedEnvKeys: envResult.updated
|
|
3198
3235
|
};
|
|
3199
3236
|
}
|
|
3237
|
+
async function collectVercelBlobConfig(cwd) {
|
|
3238
|
+
const overwriteEnvKeys = /* @__PURE__ */ new Set();
|
|
3239
|
+
const token = await resolvePasswordEnvValue({
|
|
3240
|
+
cwd,
|
|
3241
|
+
key: "BLOB_READ_WRITE_TOKEN",
|
|
3242
|
+
message: "Vercel Blob read-write token (from the store on vercel.com/dashboard/stores)",
|
|
3243
|
+
cancelMessage: "Vercel Blob configuration cancelled.",
|
|
3244
|
+
overwriteEnvKeys,
|
|
3245
|
+
validate(value) {
|
|
3246
|
+
if (!value?.trim()) {
|
|
3247
|
+
return "Enter a Vercel Blob read-write token.";
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
});
|
|
3251
|
+
return {
|
|
3252
|
+
sections: [
|
|
3253
|
+
{
|
|
3254
|
+
header: "Storage (Vercel Blob)",
|
|
3255
|
+
vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: token.trim() }]
|
|
3256
|
+
}
|
|
3257
|
+
],
|
|
3258
|
+
overwriteKeys: overwriteEnvKeys
|
|
3259
|
+
};
|
|
3260
|
+
}
|
|
3261
|
+
async function promptVercelBlobConfig(cwd) {
|
|
3262
|
+
const collected = await collectVercelBlobConfig(cwd);
|
|
3263
|
+
const envResult = appendEnvVars(cwd, collected.sections, collected.overwriteKeys);
|
|
3264
|
+
return {
|
|
3265
|
+
configured: true,
|
|
3266
|
+
addedEnvKeys: envResult.added,
|
|
3267
|
+
updatedEnvKeys: envResult.updated
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3200
3270
|
async function collectIntegrationConfig(cwd, integrationIds) {
|
|
3201
3271
|
const sections = [];
|
|
3202
3272
|
const overwriteKeys = /* @__PURE__ */ new Set();
|
|
3203
3273
|
for (const integrationId of resolveIntegrationInstallOrder(integrationIds)) {
|
|
3204
3274
|
const definition = getIntegrationDefinition(integrationId);
|
|
3205
|
-
const collected = definition.configure === "resend" ? await collectResendConfig(cwd) : definition.configure === "r2" ? await collectR2Config(cwd) : void 0;
|
|
3275
|
+
const collected = definition.configure === "resend" ? await collectResendConfig(cwd) : definition.configure === "r2" ? await collectR2Config(cwd) : definition.configure === "vercel-blob" ? await collectVercelBlobConfig(cwd) : void 0;
|
|
3206
3276
|
if (!collected) {
|
|
3207
3277
|
continue;
|
|
3208
3278
|
}
|
|
@@ -3236,6 +3306,17 @@ async function configureIntegration(definition, cwd, interactive) {
|
|
|
3236
3306
|
updatedEnvKeys: envResult2.updated
|
|
3237
3307
|
};
|
|
3238
3308
|
}
|
|
3309
|
+
if (definition.configure === "vercel-blob") {
|
|
3310
|
+
if (interactive) {
|
|
3311
|
+
return promptVercelBlobConfig(cwd);
|
|
3312
|
+
}
|
|
3313
|
+
const envResult2 = appendEnvVars(cwd, definition.envSections);
|
|
3314
|
+
return {
|
|
3315
|
+
configured: hasRequiredIntegrationEnv(definition, cwd),
|
|
3316
|
+
addedEnvKeys: envResult2.added,
|
|
3317
|
+
updatedEnvKeys: envResult2.updated
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3239
3320
|
const envResult = appendEnvVars(cwd, definition.envSections);
|
|
3240
3321
|
return {
|
|
3241
3322
|
configured: true,
|
|
@@ -4234,9 +4315,9 @@ function getReadFieldType(field) {
|
|
|
4234
4315
|
}
|
|
4235
4316
|
return getFieldType(field, "output");
|
|
4236
4317
|
}
|
|
4237
|
-
function collectReadSelectFields(fields,
|
|
4318
|
+
function collectReadSelectFields(fields, path59 = []) {
|
|
4238
4319
|
const matches = fields.flatMap((field) => {
|
|
4239
|
-
const currentPath = [...
|
|
4320
|
+
const currentPath = [...path59, field.name];
|
|
4240
4321
|
const matches2 = field.type === "select" ? [{ field, path: currentPath }] : [];
|
|
4241
4322
|
if (field.fields) {
|
|
4242
4323
|
matches2.push(...collectReadSelectFields(field.fields, currentPath));
|
|
@@ -5283,14 +5364,14 @@ function buildStepsConstant(steps) {
|
|
|
5283
5364
|
${entries.join(",\n")}
|
|
5284
5365
|
]`;
|
|
5285
5366
|
}
|
|
5286
|
-
function buildComponentSource(
|
|
5367
|
+
function buildComponentSource(p22) {
|
|
5287
5368
|
const providerOpen = ` <NuqsAdapter>
|
|
5288
5369
|
<React.Suspense fallback={null}>
|
|
5289
|
-
<${
|
|
5370
|
+
<${p22.pascal}FormInner />
|
|
5290
5371
|
</React.Suspense>
|
|
5291
5372
|
</NuqsAdapter>`;
|
|
5292
5373
|
const exportWrapper = `
|
|
5293
|
-
export function ${
|
|
5374
|
+
export function ${p22.pascal}Form() {
|
|
5294
5375
|
return (
|
|
5295
5376
|
${providerOpen}
|
|
5296
5377
|
)
|
|
@@ -5299,22 +5380,22 @@ ${providerOpen}
|
|
|
5299
5380
|
return `'use client'
|
|
5300
5381
|
|
|
5301
5382
|
import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
|
|
5302
|
-
import { ChevronLeft, ChevronRight${
|
|
5383
|
+
import { ChevronLeft, ChevronRight${p22.hasListFields ? ", Trash2" : ""} } from 'lucide-react'
|
|
5303
5384
|
import { createParser, useQueryState } from 'nuqs'
|
|
5304
5385
|
import { NuqsAdapter } from 'nuqs/adapters/next/app'
|
|
5305
5386
|
import * as React from 'react'
|
|
5306
|
-
${
|
|
5387
|
+
${p22.rhfImport}
|
|
5307
5388
|
import { z } from 'zod/v3'
|
|
5308
|
-
import { create${
|
|
5389
|
+
import { create${p22.pascal}Submission } from '@admin/actions/${p22.actionImportPath}'
|
|
5309
5390
|
|
|
5310
5391
|
const formSchema = z.object({
|
|
5311
|
-
${
|
|
5392
|
+
${p22.zodFields}
|
|
5312
5393
|
})
|
|
5313
5394
|
|
|
5314
5395
|
type FormValues = z.infer<typeof formSchema>
|
|
5315
5396
|
${buildFieldErrorHelper()}
|
|
5316
5397
|
|
|
5317
|
-
${
|
|
5398
|
+
${p22.stepsConst}
|
|
5318
5399
|
|
|
5319
5400
|
const stepParser = createParser({
|
|
5320
5401
|
parse(value) {
|
|
@@ -5332,7 +5413,7 @@ const stepParser = createParser({
|
|
|
5332
5413
|
}
|
|
5333
5414
|
}).withDefault(0)
|
|
5334
5415
|
|
|
5335
|
-
function ${
|
|
5416
|
+
function ${p22.pascal}FormInner() {
|
|
5336
5417
|
const [currentStep, setCurrentStep] = useQueryState('step', stepParser)
|
|
5337
5418
|
const [submitted, setSubmitted] = React.useState(false)
|
|
5338
5419
|
const [submitting, startSubmitTransition] = React.useTransition()
|
|
@@ -5340,11 +5421,11 @@ function ${p20.pascal}FormInner() {
|
|
|
5340
5421
|
const form = useForm<FormValues>({
|
|
5341
5422
|
resolver: standardSchemaResolver(formSchema),
|
|
5342
5423
|
defaultValues: {
|
|
5343
|
-
${
|
|
5424
|
+
${p22.defaults}
|
|
5344
5425
|
},
|
|
5345
5426
|
})
|
|
5346
5427
|
|
|
5347
|
-
${
|
|
5428
|
+
${p22.fieldArraySetup}${p22.watchSetup}
|
|
5348
5429
|
async function handleNext() {
|
|
5349
5430
|
const stepFields = STEPS[currentStep].fields as (keyof FormValues)[]
|
|
5350
5431
|
const isValid = await form.trigger(stepFields, { shouldFocus: true })
|
|
@@ -5360,9 +5441,9 @@ ${p20.fieldArraySetup}${p20.watchSetup}
|
|
|
5360
5441
|
function onSubmit(values: FormValues) {
|
|
5361
5442
|
startSubmitTransition(async () => {
|
|
5362
5443
|
try {
|
|
5363
|
-
const result = await create${
|
|
5444
|
+
const result = await create${p22.pascal}Submission(values)
|
|
5364
5445
|
if (result.success) {
|
|
5365
|
-
${
|
|
5446
|
+
${p22.successHandler}
|
|
5366
5447
|
} else {
|
|
5367
5448
|
form.setError('root', { message: result.error || 'Something went wrong' })
|
|
5368
5449
|
}
|
|
@@ -5376,7 +5457,7 @@ ${p20.fieldArraySetup}${p20.watchSetup}
|
|
|
5376
5457
|
return (
|
|
5377
5458
|
<div className="rounded-sm border p-6 text-center">
|
|
5378
5459
|
<h3 className="text-lg font-semibold">Thank you!</h3>
|
|
5379
|
-
<p className="mt-2 text-muted-foreground">${
|
|
5460
|
+
<p className="mt-2 text-muted-foreground">${p22.successMessage}</p>
|
|
5380
5461
|
</div>
|
|
5381
5462
|
)
|
|
5382
5463
|
}
|
|
@@ -5393,7 +5474,7 @@ ${p20.fieldArraySetup}${p20.watchSetup}
|
|
|
5393
5474
|
|
|
5394
5475
|
{/* Step content */}
|
|
5395
5476
|
<div key={currentStep} className="animate-in fade-in duration-300 space-y-6">
|
|
5396
|
-
${
|
|
5477
|
+
${p22.stepContentBlocks}
|
|
5397
5478
|
</div>
|
|
5398
5479
|
|
|
5399
5480
|
{form.formState.errors.root && (
|
|
@@ -5427,7 +5508,7 @@ ${p20.stepContentBlocks}
|
|
|
5427
5508
|
onClick={() => form.handleSubmit(onSubmit)()}
|
|
5428
5509
|
className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-xs transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50"
|
|
5429
5510
|
>
|
|
5430
|
-
{submitting ? 'Submitting...' : ${
|
|
5511
|
+
{submitting ? 'Submitting...' : ${p22.submitText}}
|
|
5431
5512
|
</button>
|
|
5432
5513
|
)}
|
|
5433
5514
|
</div>
|
|
@@ -13531,14 +13612,14 @@ function safeIdentifier(value, fallback) {
|
|
|
13531
13612
|
const base = pascal ? `${pascal.charAt(0).toLowerCase()}${pascal.slice(1)}` : fallback;
|
|
13532
13613
|
return /^[A-Za-z_$]/.test(base) ? base : fallback;
|
|
13533
13614
|
}
|
|
13534
|
-
function pathExpression(
|
|
13535
|
-
return `\`${
|
|
13615
|
+
function pathExpression(path59) {
|
|
13616
|
+
return `\`${path59.map((part) => typeof part === "string" ? part : `\${${part.expression}}`).join(".")}\``;
|
|
13536
13617
|
}
|
|
13537
|
-
function pathNameProp(
|
|
13538
|
-
if (
|
|
13539
|
-
return `"${
|
|
13618
|
+
function pathNameProp(path59) {
|
|
13619
|
+
if (path59.every((part) => typeof part === "string")) {
|
|
13620
|
+
return `"${path59.map((part) => part).join(".")}"`;
|
|
13540
13621
|
}
|
|
13541
|
-
return `{${pathExpression(
|
|
13622
|
+
return `{${pathExpression(path59)}}`;
|
|
13542
13623
|
}
|
|
13543
13624
|
function findTitlePath(fields) {
|
|
13544
13625
|
const stringTypes = ["string", "varchar", "text"];
|
|
@@ -13560,23 +13641,23 @@ function findTitlePath(fields) {
|
|
|
13560
13641
|
function requiredLeafPaths(fields, prefix = []) {
|
|
13561
13642
|
return fields.flatMap((field) => {
|
|
13562
13643
|
if (field.hidden) return [];
|
|
13563
|
-
const
|
|
13644
|
+
const path59 = [...prefix, field.name];
|
|
13564
13645
|
if ((field.type === "group" || field.type === "section") && field.fields?.length) {
|
|
13565
|
-
return requiredLeafPaths(field.fields,
|
|
13646
|
+
return requiredLeafPaths(field.fields, path59);
|
|
13566
13647
|
}
|
|
13567
|
-
if (field.required) return [
|
|
13648
|
+
if (field.required) return [path59.join(".")];
|
|
13568
13649
|
return [];
|
|
13569
13650
|
});
|
|
13570
13651
|
}
|
|
13571
13652
|
function validationTriggerExpression(basePath, requiredPaths) {
|
|
13572
13653
|
if (requiredPaths.length === 0) return `\`${basePath}.\${expandedIndex}\` as never`;
|
|
13573
|
-
const paths = requiredPaths.map((
|
|
13654
|
+
const paths = requiredPaths.map((path59) => `\`${basePath}.\${expandedIndex}.${path59}\``).join(", ");
|
|
13574
13655
|
return `[${paths}] as never`;
|
|
13575
13656
|
}
|
|
13576
|
-
function renderTextInputField(field, indent, label, nestedHint,
|
|
13657
|
+
function renderTextInputField(field, indent, label, nestedHint, path59) {
|
|
13577
13658
|
return `${indent}<FormField
|
|
13578
13659
|
${indent} control={form.control}
|
|
13579
|
-
${indent} name=${pathNameProp(
|
|
13660
|
+
${indent} name=${pathNameProp(path59)}
|
|
13580
13661
|
${indent} render={({ field: formField }) => (
|
|
13581
13662
|
${indent} <FormItem${formItemProps(field)}>
|
|
13582
13663
|
${indent} ${labelWithDescription(formLabel(field, label), nestedHint, `${indent} `)}
|
|
@@ -13588,11 +13669,11 @@ ${indent} </FormItem>
|
|
|
13588
13669
|
${indent} )}
|
|
13589
13670
|
${indent}/>`;
|
|
13590
13671
|
}
|
|
13591
|
-
function renderNestedField(field, indent,
|
|
13672
|
+
function renderNestedField(field, indent, path59, depth) {
|
|
13592
13673
|
const nestedLabel = field.label || field.name;
|
|
13593
13674
|
const nestedHint = field.hint ? `<FormDescription>${field.hint}</FormDescription>` : "";
|
|
13594
13675
|
if ((field.type === "group" || field.type === "section") && field.fields?.length) {
|
|
13595
|
-
const groupFields = field.fields.map((child) => renderNestedField(child, `${indent} `, [...
|
|
13676
|
+
const groupFields = field.fields.map((child) => renderNestedField(child, `${indent} `, [...path59, child.name], depth)).filter(Boolean).join("\n");
|
|
13596
13677
|
if (!groupFields) return "";
|
|
13597
13678
|
const heading = nestedLabel && nestedLabel !== field.name ? `${indent}<h3 className="text-base font-medium">${nestedLabel}</h3>
|
|
13598
13679
|
` : "";
|
|
@@ -13602,7 +13683,7 @@ ${groupFields}
|
|
|
13602
13683
|
${indent}</div>`;
|
|
13603
13684
|
}
|
|
13604
13685
|
if (field.type === "list" && field.fields?.length) {
|
|
13605
|
-
return renderNestedObjectListField(field, indent, nestedLabel,
|
|
13686
|
+
return renderNestedObjectListField(field, indent, nestedLabel, path59, depth);
|
|
13606
13687
|
}
|
|
13607
13688
|
if (field.type === "list") {
|
|
13608
13689
|
const hideLabelProp = field.label ? "" : `
|
|
@@ -13610,7 +13691,7 @@ ${indent} hideLabel`;
|
|
|
13610
13691
|
const descriptionProp = field.hint ? `
|
|
13611
13692
|
${indent} description={${JSON.stringify(field.hint)}}` : "";
|
|
13612
13693
|
return `${indent}<DynamicListField
|
|
13613
|
-
${indent} name={${pathExpression(
|
|
13694
|
+
${indent} name={${pathExpression(path59)}}
|
|
13614
13695
|
${indent} label="${nestedLabel}"${hideLabelProp}${descriptionProp}
|
|
13615
13696
|
${indent} disabled={isPending}${field.maxItems ? `
|
|
13616
13697
|
${indent} maxItems={${field.maxItems}}` : ""}
|
|
@@ -13620,7 +13701,7 @@ ${indent}/>`;
|
|
|
13620
13701
|
if (field.type === "boolean") {
|
|
13621
13702
|
return `${indent}<FormField
|
|
13622
13703
|
${indent} control={form.control}
|
|
13623
|
-
${indent} name=${pathNameProp(
|
|
13704
|
+
${indent} name=${pathNameProp(path59)}
|
|
13624
13705
|
${indent} render={({ field: formField }) => (
|
|
13625
13706
|
${indent} <FormItem${formItemProps(field, "flex flex-row items-start space-x-3 space-y-0")}>
|
|
13626
13707
|
${indent} <FormControl>
|
|
@@ -13635,7 +13716,7 @@ ${indent}/>`;
|
|
|
13635
13716
|
const acceptProp = field.type === "image" ? ' accept="image/*"' : field.type === "video" ? ' accept="video/*"' : "";
|
|
13636
13717
|
return `${indent}<FormField
|
|
13637
13718
|
${indent} control={form.control}
|
|
13638
|
-
${indent} name=${pathNameProp(
|
|
13719
|
+
${indent} name=${pathNameProp(path59)}
|
|
13639
13720
|
${indent} render={({ field: formField }) => (
|
|
13640
13721
|
${indent} <FormItem${formItemProps(field)}>
|
|
13641
13722
|
${indent} ${labelWithDescription(formLabel(field, nestedLabel), nestedHint, `${indent} `)}
|
|
@@ -13650,7 +13731,7 @@ ${indent}/>`;
|
|
|
13650
13731
|
if (field.type === "icon") {
|
|
13651
13732
|
return `${indent}<FormField
|
|
13652
13733
|
${indent} control={form.control}
|
|
13653
|
-
${indent} name=${pathNameProp(
|
|
13734
|
+
${indent} name=${pathNameProp(path59)}
|
|
13654
13735
|
${indent} render={({ field: formField }) => (
|
|
13655
13736
|
${indent} <FormItem${formItemProps(field)}>
|
|
13656
13737
|
${indent} ${labelWithDescription(formLabel(field, nestedLabel), nestedHint, `${indent} `)}
|
|
@@ -13667,7 +13748,7 @@ ${indent}/>`;
|
|
|
13667
13748
|
const emptyValue = field.required ? "undefined" : "null";
|
|
13668
13749
|
return `${indent}<FormField
|
|
13669
13750
|
${indent} control={form.control}
|
|
13670
|
-
${indent} name=${pathNameProp(
|
|
13751
|
+
${indent} name=${pathNameProp(path59)}
|
|
13671
13752
|
${indent} render={({ field: formField }) => (
|
|
13672
13753
|
${indent} <FormItem${formItemProps(field)}>
|
|
13673
13754
|
${indent} ${labelWithDescription(formLabel(field, nestedLabel), nestedHint, `${indent} `)}
|
|
@@ -13692,16 +13773,16 @@ ${indent} </FormItem>
|
|
|
13692
13773
|
${indent} )}
|
|
13693
13774
|
${indent}/>`;
|
|
13694
13775
|
}
|
|
13695
|
-
return renderTextInputField(field, indent, nestedLabel, nestedHint,
|
|
13776
|
+
return renderTextInputField(field, indent, nestedLabel, nestedHint, path59);
|
|
13696
13777
|
}
|
|
13697
|
-
function renderNestedObjectListField(field, indent, label,
|
|
13778
|
+
function renderNestedObjectListField(field, indent, label, path59, depth) {
|
|
13698
13779
|
const singularLabel = singularize(label);
|
|
13699
13780
|
const itemIndexVar = `${safeIdentifier(field.name, "nestedList")}Index${depth}`;
|
|
13700
13781
|
const titlePath = findTitlePath(field.fields ?? []);
|
|
13701
13782
|
const titlePathSuffix = titlePath ? `.${titlePath.join(".")}` : "";
|
|
13702
13783
|
const renderTitleProp = titlePath ? `
|
|
13703
13784
|
${indent} renderTitle={(${itemIndexVar}) =>
|
|
13704
|
-
${indent} String(form.watch(\`${
|
|
13785
|
+
${indent} String(form.watch(\`${path59.map((part) => typeof part === "string" ? part : `\${${part.expression}}`).join(
|
|
13705
13786
|
"."
|
|
13706
13787
|
)}.\${${itemIndexVar}}${titlePathSuffix}\` as never) || \`${singularLabel} \${${itemIndexVar} + 1}\`)
|
|
13707
13788
|
${indent} }` : "";
|
|
@@ -13709,7 +13790,7 @@ ${indent} }` : "";
|
|
|
13709
13790
|
(child) => renderNestedField(
|
|
13710
13791
|
child,
|
|
13711
13792
|
`${indent} `,
|
|
13712
|
-
[...
|
|
13793
|
+
[...path59, { expression: itemIndexVar }, child.name],
|
|
13713
13794
|
depth + 1
|
|
13714
13795
|
)
|
|
13715
13796
|
).filter(Boolean).join("\n");
|
|
@@ -13724,7 +13805,7 @@ ${indent} maxItems={${field.maxItems}}` : "";
|
|
|
13724
13805
|
const validatePathsProp = validatePaths.length > 0 ? `
|
|
13725
13806
|
${indent} validatePaths={${JSON.stringify(validatePaths)}}` : "";
|
|
13726
13807
|
return `${indent}<NestedObjectListField
|
|
13727
|
-
${indent} name={${pathExpression(
|
|
13808
|
+
${indent} name={${pathExpression(path59)}}
|
|
13728
13809
|
${indent} label={${JSON.stringify(label)}}
|
|
13729
13810
|
${indent} singularLabel={${JSON.stringify(singularLabel)}}
|
|
13730
13811
|
${indent} defaultValue={${defaultItem}}
|
|
@@ -17368,7 +17449,7 @@ function applyTextEdits(content, edits) {
|
|
|
17368
17449
|
content
|
|
17369
17450
|
);
|
|
17370
17451
|
}
|
|
17371
|
-
function collectPreviewChanges(
|
|
17452
|
+
function collectPreviewChanges(path59, beforeContent, afterContent) {
|
|
17372
17453
|
const beforeLines = beforeContent.split("\n");
|
|
17373
17454
|
const afterLines = afterContent.split("\n");
|
|
17374
17455
|
const maxLength = Math.max(beforeLines.length, afterLines.length);
|
|
@@ -17380,7 +17461,7 @@ function collectPreviewChanges(path57, beforeContent, afterContent) {
|
|
|
17380
17461
|
continue;
|
|
17381
17462
|
}
|
|
17382
17463
|
changes.push({
|
|
17383
|
-
path:
|
|
17464
|
+
path: path59,
|
|
17384
17465
|
line: index + 1,
|
|
17385
17466
|
before: before.trim(),
|
|
17386
17467
|
after: after.trim()
|
|
@@ -18991,7 +19072,7 @@ async function runAddCommand(items, options) {
|
|
|
18991
19072
|
|
|
18992
19073
|
// adapters/next/commands/add-field.ts
|
|
18993
19074
|
import path30 from "path";
|
|
18994
|
-
import * as
|
|
19075
|
+
import * as p8 from "@clack/prompts";
|
|
18995
19076
|
|
|
18996
19077
|
// core-engine/schema/add-field.ts
|
|
18997
19078
|
import fs23 from "fs";
|
|
@@ -19643,7 +19724,7 @@ function getTabFields(tab) {
|
|
|
19643
19724
|
// adapters/next/commands/generate.ts
|
|
19644
19725
|
import fs24 from "fs";
|
|
19645
19726
|
import path29 from "path";
|
|
19646
|
-
import * as
|
|
19727
|
+
import * as p6 from "@clack/prompts";
|
|
19647
19728
|
|
|
19648
19729
|
// core-engine/snapshots/rename-detector.ts
|
|
19649
19730
|
function levenshtein(a, b) {
|
|
@@ -19735,6 +19816,43 @@ function diffSchemas(before, after) {
|
|
|
19735
19816
|
};
|
|
19736
19817
|
}
|
|
19737
19818
|
|
|
19819
|
+
// core-engine/utils/spinner.ts
|
|
19820
|
+
import { stripVTControlCharacters } from "util";
|
|
19821
|
+
import * as p5 from "@clack/prompts";
|
|
19822
|
+
var RENDER_OVERHEAD = 7;
|
|
19823
|
+
var MIN_MESSAGE_WIDTH = 8;
|
|
19824
|
+
function fitSpinnerMessage(message) {
|
|
19825
|
+
const columns = process.stdout.columns ?? 80;
|
|
19826
|
+
const max = Math.max(columns - RENDER_OVERHEAD, MIN_MESSAGE_WIDTH);
|
|
19827
|
+
const visible = stripVTControlCharacters(message);
|
|
19828
|
+
if (visible.length <= max) return message;
|
|
19829
|
+
return `${visible.slice(0, max - 1)}\u2026`;
|
|
19830
|
+
}
|
|
19831
|
+
function spinner2(options) {
|
|
19832
|
+
const inner = p5.spinner(options);
|
|
19833
|
+
const guided = options?.withGuide !== false;
|
|
19834
|
+
let started = false;
|
|
19835
|
+
return {
|
|
19836
|
+
start: (message = "") => {
|
|
19837
|
+
started = true;
|
|
19838
|
+
inner.start(fitSpinnerMessage(message));
|
|
19839
|
+
},
|
|
19840
|
+
message: (message = "") => inner.message(fitSpinnerMessage(message)),
|
|
19841
|
+
stop: (message = "") => {
|
|
19842
|
+
started = false;
|
|
19843
|
+
inner.stop(message);
|
|
19844
|
+
},
|
|
19845
|
+
clear: () => {
|
|
19846
|
+
if (!started) return;
|
|
19847
|
+
started = false;
|
|
19848
|
+
inner.clear();
|
|
19849
|
+
if (guided && process.stdout.isTTY && process.env.CI !== "true") {
|
|
19850
|
+
process.stdout.write("\x1B[1A\x1B[2K");
|
|
19851
|
+
}
|
|
19852
|
+
}
|
|
19853
|
+
};
|
|
19854
|
+
}
|
|
19855
|
+
|
|
19738
19856
|
// adapters/next/commands/generate.ts
|
|
19739
19857
|
function isInteractiveSession2() {
|
|
19740
19858
|
return Boolean(process.stdin.isTTY) && process.env.CI !== "true";
|
|
@@ -19801,7 +19919,7 @@ async function ensureMarkdownRendererDependencies(cwd, required) {
|
|
|
19801
19919
|
);
|
|
19802
19920
|
if (dependencies.length === 0 && devDependencies.length === 0) return;
|
|
19803
19921
|
const pm = detectPackageManager(cwd);
|
|
19804
|
-
const s =
|
|
19922
|
+
const s = spinner2();
|
|
19805
19923
|
s.start("Installing markdown renderer dependencies");
|
|
19806
19924
|
const result = await installDependenciesAsync({
|
|
19807
19925
|
cwd,
|
|
@@ -19860,11 +19978,11 @@ function printSchemaDiff(scope, loaded, cwd) {
|
|
|
19860
19978
|
}
|
|
19861
19979
|
}
|
|
19862
19980
|
async function promptConfirm(message) {
|
|
19863
|
-
const result = await
|
|
19981
|
+
const result = await p6.confirm({
|
|
19864
19982
|
message,
|
|
19865
19983
|
initialValue: true
|
|
19866
19984
|
});
|
|
19867
|
-
if (
|
|
19985
|
+
if (p6.isCancel(result)) {
|
|
19868
19986
|
throw new Error("Generation cancelled.");
|
|
19869
19987
|
}
|
|
19870
19988
|
return Boolean(result);
|
|
@@ -20277,34 +20395,34 @@ async function runGenerateCommand(schemaName, options) {
|
|
|
20277
20395
|
}
|
|
20278
20396
|
|
|
20279
20397
|
// adapters/next/commands/schema-prompts.ts
|
|
20280
|
-
import * as
|
|
20398
|
+
import * as p7 from "@clack/prompts";
|
|
20281
20399
|
function isInteractiveSession3() {
|
|
20282
20400
|
return Boolean(process.stdin.isTTY) && process.env.CI !== "true";
|
|
20283
20401
|
}
|
|
20284
20402
|
function fail(message) {
|
|
20285
|
-
|
|
20403
|
+
p7.log.error(message);
|
|
20286
20404
|
process.exit(1);
|
|
20287
20405
|
}
|
|
20288
20406
|
function cancel2(context) {
|
|
20289
|
-
|
|
20407
|
+
p7.cancel(context.cancelMessage);
|
|
20290
20408
|
process.exit(0);
|
|
20291
20409
|
}
|
|
20292
20410
|
function cancelIfNeeded(context, value) {
|
|
20293
|
-
if (
|
|
20411
|
+
if (p7.isCancel(value)) {
|
|
20294
20412
|
cancel2(context);
|
|
20295
20413
|
}
|
|
20296
20414
|
return value;
|
|
20297
20415
|
}
|
|
20298
20416
|
async function promptConfirm2(context, message, initialValue = true) {
|
|
20299
|
-
const confirmed = await
|
|
20300
|
-
if (
|
|
20417
|
+
const confirmed = await p7.confirm({ message, initialValue });
|
|
20418
|
+
if (p7.isCancel(confirmed)) {
|
|
20301
20419
|
cancel2(context);
|
|
20302
20420
|
}
|
|
20303
20421
|
return Boolean(confirmed);
|
|
20304
20422
|
}
|
|
20305
20423
|
async function promptText(context, message, optionsOrDefaultValue) {
|
|
20306
20424
|
const options = typeof optionsOrDefaultValue === "string" ? { defaultValue: optionsOrDefaultValue } : optionsOrDefaultValue ?? {};
|
|
20307
|
-
const value = await
|
|
20425
|
+
const value = await p7.text({
|
|
20308
20426
|
message,
|
|
20309
20427
|
defaultValue: options.defaultValue,
|
|
20310
20428
|
placeholder: options.placeholder,
|
|
@@ -20321,7 +20439,7 @@ async function promptText(context, message, optionsOrDefaultValue) {
|
|
|
20321
20439
|
return String(cancelIfNeeded(context, value)).trim();
|
|
20322
20440
|
}
|
|
20323
20441
|
async function promptOptionalText(context, message) {
|
|
20324
|
-
const value = await
|
|
20442
|
+
const value = await p7.text({
|
|
20325
20443
|
message,
|
|
20326
20444
|
placeholder: "Leave blank to skip"
|
|
20327
20445
|
});
|
|
@@ -20329,7 +20447,7 @@ async function promptOptionalText(context, message) {
|
|
|
20329
20447
|
return result || void 0;
|
|
20330
20448
|
}
|
|
20331
20449
|
async function promptSelectValue(context, message, options, initialValue) {
|
|
20332
|
-
const value = await
|
|
20450
|
+
const value = await p7.select({
|
|
20333
20451
|
message,
|
|
20334
20452
|
options,
|
|
20335
20453
|
initialValue
|
|
@@ -20548,7 +20666,7 @@ async function shouldPromptRequired(type) {
|
|
|
20548
20666
|
return !["group", "section", "tabs", "separator"].includes(type);
|
|
20549
20667
|
}
|
|
20550
20668
|
async function promptFormFileOptions(context) {
|
|
20551
|
-
const selected = await
|
|
20669
|
+
const selected = await p7.multiselect({
|
|
20552
20670
|
message: "Field options",
|
|
20553
20671
|
options: [
|
|
20554
20672
|
{ value: "required", label: "Required field?" },
|
|
@@ -20587,7 +20705,7 @@ async function promptFieldOptions(context, kind, type, creatable) {
|
|
|
20587
20705
|
try {
|
|
20588
20706
|
return parseInteractiveOptionsList(value);
|
|
20589
20707
|
} catch (error) {
|
|
20590
|
-
|
|
20708
|
+
p7.log.error(error instanceof Error ? error.message : String(error));
|
|
20591
20709
|
}
|
|
20592
20710
|
}
|
|
20593
20711
|
}
|
|
@@ -20705,7 +20823,7 @@ async function promptChildFields(context, kind, schemasDir, scope, fields, depth
|
|
|
20705
20823
|
const addChild = fields.length === 0 ? await promptConfirm2(context, "Add a child field?", true) : await promptConfirm2(context, "Add another child field?", false);
|
|
20706
20824
|
if (!addChild) {
|
|
20707
20825
|
if (fields.length === 0) {
|
|
20708
|
-
|
|
20826
|
+
p7.log.warn("Containers need at least one child field.");
|
|
20709
20827
|
continue;
|
|
20710
20828
|
}
|
|
20711
20829
|
return;
|
|
@@ -20730,7 +20848,7 @@ async function promptTabsField(context, kind, schemasDir, scope, name, label, de
|
|
|
20730
20848
|
const addTab = field.tabs.length === 0 ? await promptConfirm2(context, "Add a tab?", true) : await promptConfirm2(context, "Add another tab?", false);
|
|
20731
20849
|
if (!addTab) {
|
|
20732
20850
|
if (field.tabs.length === 0) {
|
|
20733
|
-
|
|
20851
|
+
p7.log.warn("Tabs need at least one tab.");
|
|
20734
20852
|
continue;
|
|
20735
20853
|
}
|
|
20736
20854
|
return field;
|
|
@@ -20755,7 +20873,7 @@ async function promptTabsField(context, kind, schemasDir, scope, name, label, de
|
|
|
20755
20873
|
const addChild = mainFields.length + sidebarFields.length === 0 ? await promptConfirm2(context, "Add a tab child field?", true) : await promptConfirm2(context, "Add another tab child field?", false);
|
|
20756
20874
|
if (!addChild) {
|
|
20757
20875
|
if (mainFields.length + sidebarFields.length === 0) {
|
|
20758
|
-
|
|
20876
|
+
p7.log.warn("Tabs need at least one child field.");
|
|
20759
20877
|
continue;
|
|
20760
20878
|
}
|
|
20761
20879
|
break;
|
|
@@ -20826,7 +20944,7 @@ async function applyAdvancedOptions(context, field, kind, type, options) {
|
|
|
20826
20944
|
if (Number.isInteger(parsed) && parsed > 0) {
|
|
20827
20945
|
record.length = parsed;
|
|
20828
20946
|
} else {
|
|
20829
|
-
|
|
20947
|
+
p7.log.warn("Skipped invalid length.");
|
|
20830
20948
|
}
|
|
20831
20949
|
}
|
|
20832
20950
|
}
|
|
@@ -20984,11 +21102,11 @@ function hasNonInteractiveFieldOptions(options) {
|
|
|
20984
21102
|
);
|
|
20985
21103
|
}
|
|
20986
21104
|
function fail2(message) {
|
|
20987
|
-
|
|
21105
|
+
p8.log.error(message);
|
|
20988
21106
|
process.exit(1);
|
|
20989
21107
|
}
|
|
20990
21108
|
function cancel4(message = "Add field cancelled.") {
|
|
20991
|
-
|
|
21109
|
+
p8.cancel(message);
|
|
20992
21110
|
process.exit(0);
|
|
20993
21111
|
}
|
|
20994
21112
|
function schemaNameFromLoaded(loaded) {
|
|
@@ -21093,7 +21211,7 @@ function printPreview(loaded, owner, firstTimeGeneration, insertion, options) {
|
|
|
21093
21211
|
} else {
|
|
21094
21212
|
lines.push("schema integration: none");
|
|
21095
21213
|
}
|
|
21096
|
-
|
|
21214
|
+
p8.note(
|
|
21097
21215
|
`${lines.join("\n")}
|
|
21098
21216
|
|
|
21099
21217
|
${stringifyProjectJson(insertion.field).trim()}`,
|
|
@@ -21126,7 +21244,7 @@ Re-run with --yes to confirm these warning cases.`);
|
|
|
21126
21244
|
}
|
|
21127
21245
|
return;
|
|
21128
21246
|
}
|
|
21129
|
-
|
|
21247
|
+
p8.note(warnings.join("\n"), "Warnings");
|
|
21130
21248
|
const confirmed = await promptConfirm3("Continue with these warnings?", true);
|
|
21131
21249
|
if (!confirmed) {
|
|
21132
21250
|
cancel4();
|
|
@@ -21245,7 +21363,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21245
21363
|
}
|
|
21246
21364
|
}
|
|
21247
21365
|
writeAuthoredGeneratedSchema(loaded);
|
|
21248
|
-
|
|
21366
|
+
p8.log.success(`Updated ${path30.relative(cwd, loaded.filePath)}`);
|
|
21249
21367
|
try {
|
|
21250
21368
|
await runGenerateCommand(selectedSchemaName, {
|
|
21251
21369
|
force: false,
|
|
@@ -21256,8 +21374,8 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21256
21374
|
cwd
|
|
21257
21375
|
});
|
|
21258
21376
|
} catch (error) {
|
|
21259
|
-
|
|
21260
|
-
|
|
21377
|
+
p8.log.error(error instanceof Error ? error.message : String(error));
|
|
21378
|
+
p8.log.info(
|
|
21261
21379
|
`Schema JSON was kept. Re-run \`betterstart generate ${selectedSchemaName}${options.skipMigration ? " --skip-migration" : ""}\` after resolving the issue.`
|
|
21262
21380
|
);
|
|
21263
21381
|
process.exit(1);
|
|
@@ -21267,7 +21385,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21267
21385
|
// adapters/next/commands/create.ts
|
|
21268
21386
|
import fs25 from "fs";
|
|
21269
21387
|
import path31 from "path";
|
|
21270
|
-
import * as
|
|
21388
|
+
import * as p9 from "@clack/prompts";
|
|
21271
21389
|
var CREATE_PROMPT_CONTEXT = {
|
|
21272
21390
|
cancelMessage: "Create schema cancelled."
|
|
21273
21391
|
};
|
|
@@ -21458,7 +21576,7 @@ async function promptTabSlotFields(kind, schemasDir, titleField) {
|
|
|
21458
21576
|
);
|
|
21459
21577
|
if (!addField) {
|
|
21460
21578
|
if (!hasFields) {
|
|
21461
|
-
|
|
21579
|
+
p9.log.warn("Tabs need at least one child field.");
|
|
21462
21580
|
continue;
|
|
21463
21581
|
}
|
|
21464
21582
|
return { main, sidebar };
|
|
@@ -21506,7 +21624,7 @@ async function promptSchemaTab(kind, schemasDir, titleField, existingTabs) {
|
|
|
21506
21624
|
while (fields.length === 0) {
|
|
21507
21625
|
await promptAdditionalSchemaFields(kind, schemasDir, fields);
|
|
21508
21626
|
if (fields.length === 0) {
|
|
21509
|
-
|
|
21627
|
+
p9.log.warn("Tabs need at least one child field.");
|
|
21510
21628
|
}
|
|
21511
21629
|
}
|
|
21512
21630
|
}
|
|
@@ -21596,7 +21714,7 @@ async function promptFormSubmissionColumns(schema) {
|
|
|
21596
21714
|
}
|
|
21597
21715
|
const existingColumnNames = schema.columns ? new Set(schema.columns.map((column) => column.accessorKey)) : void 0;
|
|
21598
21716
|
const initialValues = existingColumnNames ? fields.filter((field) => existingColumnNames.has(field.name)).map((field) => field.name) : fields.map((field) => field.name);
|
|
21599
|
-
const selected = await
|
|
21717
|
+
const selected = await p9.multiselect({
|
|
21600
21718
|
message: "Submission table columns",
|
|
21601
21719
|
options: fields.map((field) => ({
|
|
21602
21720
|
value: field.name,
|
|
@@ -21605,7 +21723,7 @@ async function promptFormSubmissionColumns(schema) {
|
|
|
21605
21723
|
initialValues,
|
|
21606
21724
|
required: false
|
|
21607
21725
|
});
|
|
21608
|
-
if (
|
|
21726
|
+
if (p9.isCancel(selected)) {
|
|
21609
21727
|
cancel2(CREATE_PROMPT_CONTEXT);
|
|
21610
21728
|
}
|
|
21611
21729
|
const selectedNames = new Set(selected);
|
|
@@ -21665,7 +21783,7 @@ function schemaFilePath(kind, schemasDir, schemaName) {
|
|
|
21665
21783
|
return kind === "form" ? path31.join(schemasDir, "forms", `${schemaName}.json`) : path31.join(schemasDir, `${schemaName}.json`);
|
|
21666
21784
|
}
|
|
21667
21785
|
function printPreview2(loaded, cwd) {
|
|
21668
|
-
|
|
21786
|
+
p9.note(
|
|
21669
21787
|
`schema: ${loaded.name} (${loaded.kind})
|
|
21670
21788
|
path: ${path31.relative(cwd, loaded.filePath)}
|
|
21671
21789
|
owner: user
|
|
@@ -21720,7 +21838,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21720
21838
|
}
|
|
21721
21839
|
fs25.mkdirSync(path31.dirname(filePath), { recursive: true });
|
|
21722
21840
|
writeAuthoredGeneratedSchema(loaded);
|
|
21723
|
-
|
|
21841
|
+
p9.log.success(`Created ${path31.relative(cwd, filePath)}`);
|
|
21724
21842
|
try {
|
|
21725
21843
|
await runGenerateCommand(metadata.name, {
|
|
21726
21844
|
force: false,
|
|
@@ -21731,8 +21849,8 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21731
21849
|
cwd
|
|
21732
21850
|
});
|
|
21733
21851
|
} catch (error) {
|
|
21734
|
-
|
|
21735
|
-
|
|
21852
|
+
p9.log.error(error instanceof Error ? error.message : String(error));
|
|
21853
|
+
p9.log.info(
|
|
21736
21854
|
`Schema JSON was kept. Re-run \`betterstart generate ${metadata.name}${options.skipMigration ? " --skip-migration" : ""}\` after resolving the issue.`
|
|
21737
21855
|
);
|
|
21738
21856
|
process.exit(1);
|
|
@@ -21741,40 +21859,41 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21741
21859
|
|
|
21742
21860
|
// adapters/next/commands/init.ts
|
|
21743
21861
|
import { execFileSync as execFileSync5, spawn as spawn4 } from "child_process";
|
|
21744
|
-
import
|
|
21745
|
-
import
|
|
21746
|
-
import * as
|
|
21862
|
+
import fs39 from "fs";
|
|
21863
|
+
import path50 from "path";
|
|
21864
|
+
import * as p17 from "@clack/prompts";
|
|
21747
21865
|
|
|
21748
21866
|
// adapters/next/init/prompts/database.ts
|
|
21749
21867
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
21750
|
-
import * as
|
|
21868
|
+
import * as p10 from "@clack/prompts";
|
|
21751
21869
|
import pc from "picocolors";
|
|
21752
21870
|
var VERCEL_NEON_URL = "https://vercel.com/dashboard/integrations/checkout/neon";
|
|
21753
|
-
async function
|
|
21871
|
+
async function promptServices(options) {
|
|
21754
21872
|
if (!options.allowVercel) {
|
|
21755
21873
|
const url2 = await promptConnectionString();
|
|
21756
21874
|
return { provider: "manual", url: url2 };
|
|
21757
21875
|
}
|
|
21758
|
-
const choice = await
|
|
21759
|
-
message: "
|
|
21876
|
+
const choice = await p10.select({
|
|
21877
|
+
message: "Connect a PostgreSQL Database",
|
|
21760
21878
|
options: [
|
|
21761
21879
|
{
|
|
21762
|
-
value: "vercel
|
|
21763
|
-
label: "Vercel
|
|
21764
|
-
hint: "
|
|
21880
|
+
value: "vercel",
|
|
21881
|
+
label: "Vercel",
|
|
21882
|
+
hint: "Automatically provision a Neon Postgres database"
|
|
21765
21883
|
},
|
|
21766
21884
|
{
|
|
21767
21885
|
value: "manual",
|
|
21768
|
-
label: "
|
|
21886
|
+
label: "Manual",
|
|
21887
|
+
hint: "Manually enter connection details"
|
|
21769
21888
|
}
|
|
21770
21889
|
],
|
|
21771
|
-
initialValue: "vercel
|
|
21890
|
+
initialValue: "vercel"
|
|
21772
21891
|
});
|
|
21773
|
-
if (
|
|
21774
|
-
|
|
21892
|
+
if (p10.isCancel(choice)) {
|
|
21893
|
+
p10.cancel("Setup cancelled.");
|
|
21775
21894
|
process.exit(0);
|
|
21776
21895
|
}
|
|
21777
|
-
if (choice === "vercel
|
|
21896
|
+
if (choice === "vercel") {
|
|
21778
21897
|
return { provider: "vercel-cli" };
|
|
21779
21898
|
}
|
|
21780
21899
|
const url = await promptConnectionString();
|
|
@@ -21782,12 +21901,12 @@ async function promptDatabase(options) {
|
|
|
21782
21901
|
}
|
|
21783
21902
|
function openBrowserVercelNeon() {
|
|
21784
21903
|
openBrowser(VERCEL_NEON_URL);
|
|
21785
|
-
|
|
21904
|
+
p10.log.info(
|
|
21786
21905
|
`Opening Vercel... Create a Neon Postgres database, then copy the ${pc.cyan("DATABASE_URL")} from the dashboard.`
|
|
21787
21906
|
);
|
|
21788
21907
|
}
|
|
21789
21908
|
async function promptConnectionString() {
|
|
21790
|
-
const input = await
|
|
21909
|
+
const input = await p10.text({
|
|
21791
21910
|
message: "Paste your PostgreSQL connection string",
|
|
21792
21911
|
placeholder: "postgres://user:pass@host/db",
|
|
21793
21912
|
validate(val) {
|
|
@@ -21801,8 +21920,8 @@ async function promptConnectionString() {
|
|
|
21801
21920
|
}
|
|
21802
21921
|
}
|
|
21803
21922
|
});
|
|
21804
|
-
if (
|
|
21805
|
-
|
|
21923
|
+
if (p10.isCancel(input)) {
|
|
21924
|
+
p10.cancel("Setup cancelled.");
|
|
21806
21925
|
process.exit(0);
|
|
21807
21926
|
}
|
|
21808
21927
|
return input.replace(/^['"]|['"]$/g, "").trim();
|
|
@@ -21823,8 +21942,8 @@ function openBrowser(url) {
|
|
|
21823
21942
|
|
|
21824
21943
|
// adapters/next/init/prompts/plugins.ts
|
|
21825
21944
|
import { styleText } from "util";
|
|
21826
|
-
import * as
|
|
21827
|
-
async function promptPlugins(cwd) {
|
|
21945
|
+
import * as p11 from "@clack/prompts";
|
|
21946
|
+
async function promptPlugins(cwd, options = {}) {
|
|
21828
21947
|
const sections = [];
|
|
21829
21948
|
const overwriteKeys = /* @__PURE__ */ new Set();
|
|
21830
21949
|
const mergeIntegrationConfig = (collected) => {
|
|
@@ -21833,80 +21952,94 @@ async function promptPlugins(cwd) {
|
|
|
21833
21952
|
overwriteKeys.add(key);
|
|
21834
21953
|
}
|
|
21835
21954
|
};
|
|
21836
|
-
const
|
|
21837
|
-
message:
|
|
21838
|
-
${styleText("dim", "Press [Spacebar] to select/unselect")}`,
|
|
21955
|
+
const storage = await p11.select({
|
|
21956
|
+
message: "Choose a file storage",
|
|
21839
21957
|
options: [
|
|
21840
21958
|
{
|
|
21841
|
-
value: "
|
|
21842
|
-
label: "
|
|
21843
|
-
|
|
21844
|
-
}
|
|
21845
|
-
],
|
|
21846
|
-
required: false,
|
|
21847
|
-
initialValues: ["blog"]
|
|
21848
|
-
});
|
|
21849
|
-
if (p10.isCancel(selectedPlugins)) {
|
|
21850
|
-
p10.cancel("Setup cancelled.");
|
|
21851
|
-
process.exit(0);
|
|
21852
|
-
}
|
|
21853
|
-
const storage = await p10.select({
|
|
21854
|
-
message: "Choose a file storage mode",
|
|
21855
|
-
options: [
|
|
21959
|
+
value: "vercel-blob",
|
|
21960
|
+
label: "Vercel Blob"
|
|
21961
|
+
},
|
|
21856
21962
|
{
|
|
21857
21963
|
value: "r2",
|
|
21858
|
-
label: "Cloudflare R2"
|
|
21859
|
-
hint: "recommended for hosted/serverless production"
|
|
21964
|
+
label: "Cloudflare R2"
|
|
21860
21965
|
},
|
|
21861
21966
|
{
|
|
21862
21967
|
value: "local",
|
|
21863
21968
|
label: "Local filesystem (public/media)",
|
|
21864
|
-
hint: "
|
|
21969
|
+
hint: "\u26A0 For testing only"
|
|
21865
21970
|
}
|
|
21866
21971
|
],
|
|
21867
|
-
initialValue: "
|
|
21972
|
+
initialValue: "vercel-blob"
|
|
21868
21973
|
});
|
|
21869
|
-
if (
|
|
21870
|
-
|
|
21974
|
+
if (p11.isCancel(storage)) {
|
|
21975
|
+
p11.cancel("Setup cancelled.");
|
|
21871
21976
|
process.exit(0);
|
|
21872
21977
|
}
|
|
21873
21978
|
if (storage === "r2") {
|
|
21874
21979
|
mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["r2"]));
|
|
21980
|
+
} else if (storage === "vercel-blob") {
|
|
21981
|
+
const existingToken = readEnvVar(cwd, "BLOB_READ_WRITE_TOKEN")?.trim();
|
|
21982
|
+
const flow = !existingToken && options.provisionVercelBlob ? await options.provisionVercelBlob() : void 0;
|
|
21983
|
+
if (flow?.ok && flow.token) {
|
|
21984
|
+
sections.push({
|
|
21985
|
+
header: "Storage (Vercel Blob)",
|
|
21986
|
+
vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: flow.token }]
|
|
21987
|
+
});
|
|
21988
|
+
overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
|
|
21989
|
+
} else {
|
|
21990
|
+
if (existingToken) {
|
|
21991
|
+
p11.log.info("Using the existing Vercel Blob token from .env.local");
|
|
21992
|
+
} else if (options.provisionVercelBlob) {
|
|
21993
|
+
p11.log.info("Falling back to a manual Vercel Blob token.");
|
|
21994
|
+
}
|
|
21995
|
+
mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["vercel-blob"]));
|
|
21996
|
+
}
|
|
21875
21997
|
}
|
|
21876
|
-
const
|
|
21877
|
-
message:
|
|
21878
|
-
${styleText(
|
|
21879
|
-
"dim",
|
|
21880
|
-
"Auth + Forms email delivery"
|
|
21881
|
-
)}`,
|
|
21998
|
+
const selectedPlugins = await p11.multiselect({
|
|
21999
|
+
message: "Select presets",
|
|
21882
22000
|
options: [
|
|
21883
22001
|
{
|
|
21884
|
-
value: "
|
|
21885
|
-
label: "
|
|
21886
|
-
|
|
21887
|
-
{
|
|
21888
|
-
value: "skip",
|
|
21889
|
-
label: "Skip"
|
|
21890
|
-
},
|
|
21891
|
-
{
|
|
21892
|
-
value: "cloudflare",
|
|
21893
|
-
label: `Cloudflare ${styleText("dim", "(coming soon)")}`,
|
|
21894
|
-
disabled: true
|
|
22002
|
+
value: "blog",
|
|
22003
|
+
label: "Blog",
|
|
22004
|
+
hint: "Posts"
|
|
21895
22005
|
}
|
|
21896
22006
|
],
|
|
21897
|
-
|
|
22007
|
+
required: false,
|
|
22008
|
+
initialValues: ["blog"]
|
|
21898
22009
|
});
|
|
21899
|
-
if (
|
|
21900
|
-
|
|
22010
|
+
if (p11.isCancel(selectedPlugins)) {
|
|
22011
|
+
p11.cancel("Setup cancelled.");
|
|
21901
22012
|
process.exit(0);
|
|
21902
22013
|
}
|
|
21903
22014
|
const integrations = [];
|
|
21904
|
-
|
|
22015
|
+
const setupResend = await p11.confirm({
|
|
22016
|
+
message: `Do you want to setup resend.com emails?
|
|
22017
|
+
${styleText(
|
|
22018
|
+
"dim",
|
|
22019
|
+
"This will be used for form submission confirmations, password resets etc."
|
|
22020
|
+
)}`,
|
|
22021
|
+
initialValue: true
|
|
22022
|
+
});
|
|
22023
|
+
if (p11.isCancel(setupResend)) {
|
|
22024
|
+
p11.cancel("Setup cancelled.");
|
|
22025
|
+
process.exit(0);
|
|
22026
|
+
}
|
|
22027
|
+
if (setupResend) {
|
|
21905
22028
|
integrations.push("resend");
|
|
21906
22029
|
mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["resend"]));
|
|
22030
|
+
} else {
|
|
22031
|
+
sections.push({
|
|
22032
|
+
header: "Email (Resend)",
|
|
22033
|
+
vars: [
|
|
22034
|
+
{ key: "BETTERSTART_RESEND_API_KEY", value: "" },
|
|
22035
|
+
{ key: "BETTERSTART_EMAIL_FROM", value: "" }
|
|
22036
|
+
]
|
|
22037
|
+
});
|
|
21907
22038
|
}
|
|
21908
22039
|
if (storage === "r2") {
|
|
21909
22040
|
integrations.push("r2");
|
|
22041
|
+
} else if (storage === "vercel-blob") {
|
|
22042
|
+
integrations.push("vercel-blob");
|
|
21910
22043
|
}
|
|
21911
22044
|
return {
|
|
21912
22045
|
plugins: selectedPlugins,
|
|
@@ -21917,10 +22050,10 @@ ${styleText(
|
|
|
21917
22050
|
}
|
|
21918
22051
|
|
|
21919
22052
|
// adapters/next/init/prompts/project.ts
|
|
21920
|
-
import * as
|
|
22053
|
+
import * as p12 from "@clack/prompts";
|
|
21921
22054
|
import pc2 from "picocolors";
|
|
21922
22055
|
async function promptProject(defaultName) {
|
|
21923
|
-
const projectName = await
|
|
22056
|
+
const projectName = await p12.text({
|
|
21924
22057
|
message: [
|
|
21925
22058
|
"What is your project name?",
|
|
21926
22059
|
`${pc2.cyan("\u2502")} ${pc2.dim("If you don't want a directory created with the project name, just press `")}${pc2.bold(
|
|
@@ -21939,16 +22072,16 @@ async function promptProject(defaultName) {
|
|
|
21939
22072
|
return void 0;
|
|
21940
22073
|
}
|
|
21941
22074
|
});
|
|
21942
|
-
if (
|
|
21943
|
-
|
|
22075
|
+
if (p12.isCancel(projectName)) {
|
|
22076
|
+
p12.cancel("Setup cancelled.");
|
|
21944
22077
|
process.exit(0);
|
|
21945
22078
|
}
|
|
21946
|
-
const useSrcDir = await
|
|
22079
|
+
const useSrcDir = await p12.confirm({
|
|
21947
22080
|
message: "Use src/ directory?",
|
|
21948
22081
|
initialValue: false
|
|
21949
22082
|
});
|
|
21950
|
-
if (
|
|
21951
|
-
|
|
22083
|
+
if (p12.isCancel(useSrcDir)) {
|
|
22084
|
+
p12.cancel("Setup cancelled.");
|
|
21952
22085
|
process.exit(0);
|
|
21953
22086
|
}
|
|
21954
22087
|
return { projectName: projectName.trim(), useSrcDir };
|
|
@@ -22945,6 +23078,18 @@ function shouldOverwriteAuthUrl(cwd, devPort) {
|
|
|
22945
23078
|
if (!existingAuthUrl) return false;
|
|
22946
23079
|
return existingAuthUrl === LEGACY_DEFAULT_AUTH_URL && existingAuthUrl !== `http://localhost:${devPort}`;
|
|
22947
23080
|
}
|
|
23081
|
+
function persistDatabaseUrl(cwd, databaseUrl) {
|
|
23082
|
+
appendEnvVars(
|
|
23083
|
+
cwd,
|
|
23084
|
+
[
|
|
23085
|
+
{
|
|
23086
|
+
header: "Database (PostgreSQL)",
|
|
23087
|
+
vars: [{ key: "DATABASE_URL", value: databaseUrl }]
|
|
23088
|
+
}
|
|
23089
|
+
],
|
|
23090
|
+
/* @__PURE__ */ new Set(["DATABASE_URL"])
|
|
23091
|
+
);
|
|
23092
|
+
}
|
|
22948
23093
|
function scaffoldEnv(cwd, options) {
|
|
22949
23094
|
const devPort = detectDevPort(cwd);
|
|
22950
23095
|
const sections = getCoreEnvSections(options.databaseUrl, devPort, options.namespace ?? "admin");
|
|
@@ -23827,15 +23972,16 @@ function scaffoldTsconfig(cwd, config) {
|
|
|
23827
23972
|
}
|
|
23828
23973
|
|
|
23829
23974
|
// adapters/next/init/vercel/flow.ts
|
|
23830
|
-
import * as
|
|
23831
|
-
import
|
|
23975
|
+
import * as p16 from "@clack/prompts";
|
|
23976
|
+
import pc6 from "picocolors";
|
|
23832
23977
|
|
|
23833
23978
|
// adapters/next/init/vercel/auth.ts
|
|
23834
|
-
import * as
|
|
23979
|
+
import * as p13 from "@clack/prompts";
|
|
23835
23980
|
import pc3 from "picocolors";
|
|
23836
23981
|
|
|
23837
23982
|
// adapters/next/init/vercel/runner.ts
|
|
23838
23983
|
import { spawn as spawn3 } from "child_process";
|
|
23984
|
+
import { stripVTControlCharacters as stripVTControlCharacters2 } from "util";
|
|
23839
23985
|
var VERCEL_CLI_VERSION = "latest";
|
|
23840
23986
|
var DEFAULT_CAPTURE_TIMEOUT_MS = 12e4;
|
|
23841
23987
|
function pathRunner() {
|
|
@@ -23852,18 +23998,43 @@ async function resolveVercelRunner() {
|
|
|
23852
23998
|
});
|
|
23853
23999
|
return probe.success ? pathRunner() : npxRunner();
|
|
23854
24000
|
}
|
|
24001
|
+
function createFilteredLineForwarder(shouldShow, write) {
|
|
24002
|
+
let pending = "";
|
|
24003
|
+
const emit = (line) => {
|
|
24004
|
+
if (shouldShow(stripVTControlCharacters2(line).trim())) write(`${line}
|
|
24005
|
+
`);
|
|
24006
|
+
};
|
|
24007
|
+
return {
|
|
24008
|
+
push(chunk) {
|
|
24009
|
+
pending += chunk;
|
|
24010
|
+
const lines = pending.split(/\r\n|\n|\r/);
|
|
24011
|
+
pending = lines.pop() ?? "";
|
|
24012
|
+
for (const line of lines) emit(line);
|
|
24013
|
+
},
|
|
24014
|
+
flush() {
|
|
24015
|
+
if (pending !== "") emit(pending);
|
|
24016
|
+
pending = "";
|
|
24017
|
+
}
|
|
24018
|
+
};
|
|
24019
|
+
}
|
|
23855
24020
|
function runVercel(runner, args, options) {
|
|
23856
24021
|
const timeoutMs = options.timeoutMs ?? DEFAULT_CAPTURE_TIMEOUT_MS;
|
|
23857
24022
|
const argv = [...runner.prefix, ...args];
|
|
23858
24023
|
return new Promise((resolve) => {
|
|
23859
24024
|
const child = spawn3(runner.bin, argv, {
|
|
23860
24025
|
cwd: options.cwd,
|
|
23861
|
-
stdio: options.mode === "inherit" ? "inherit" : "pipe",
|
|
23862
|
-
|
|
24026
|
+
stdio: options.mode === "inherit" ? "inherit" : options.mode === "stream" ? ["inherit", "pipe", "pipe"] : "pipe",
|
|
24027
|
+
// npm_config_loglevel silences npx's "npm warn deprecated" install noise
|
|
24028
|
+
// on the npx runner; harmless for a PATH-installed vercel.
|
|
24029
|
+
env: { ...process.env, npm_config_loglevel: "error", ...options.env }
|
|
23863
24030
|
});
|
|
23864
24031
|
let stdout = "";
|
|
23865
24032
|
let stderr = "";
|
|
23866
24033
|
let timedOut = false;
|
|
24034
|
+
if (options.mode === "capture" && options.stdinInput !== void 0) {
|
|
24035
|
+
child.stdin?.write(options.stdinInput);
|
|
24036
|
+
child.stdin?.end();
|
|
24037
|
+
}
|
|
23867
24038
|
if (options.mode === "capture") {
|
|
23868
24039
|
child.stdout?.on("data", (chunk) => {
|
|
23869
24040
|
stdout += chunk.toString();
|
|
@@ -23871,6 +24042,30 @@ function runVercel(runner, args, options) {
|
|
|
23871
24042
|
child.stderr?.on("data", (chunk) => {
|
|
23872
24043
|
stderr += chunk.toString();
|
|
23873
24044
|
});
|
|
24045
|
+
} else if (options.mode === "stream") {
|
|
24046
|
+
const shouldShow = options.streamLineFilter ?? (() => true);
|
|
24047
|
+
const outForwarder = createFilteredLineForwarder(
|
|
24048
|
+
shouldShow,
|
|
24049
|
+
(text7) => process.stdout.write(text7)
|
|
24050
|
+
);
|
|
24051
|
+
const errForwarder = createFilteredLineForwarder(
|
|
24052
|
+
shouldShow,
|
|
24053
|
+
(text7) => process.stderr.write(text7)
|
|
24054
|
+
);
|
|
24055
|
+
child.stdout?.on("data", (chunk) => {
|
|
24056
|
+
const text7 = chunk.toString();
|
|
24057
|
+
stdout += text7;
|
|
24058
|
+
outForwarder.push(text7);
|
|
24059
|
+
});
|
|
24060
|
+
child.stderr?.on("data", (chunk) => {
|
|
24061
|
+
const text7 = chunk.toString();
|
|
24062
|
+
stderr += text7;
|
|
24063
|
+
errForwarder.push(text7);
|
|
24064
|
+
});
|
|
24065
|
+
child.on("close", () => {
|
|
24066
|
+
outForwarder.flush();
|
|
24067
|
+
errForwarder.flush();
|
|
24068
|
+
});
|
|
23874
24069
|
}
|
|
23875
24070
|
const timeout = setTimeout(() => {
|
|
23876
24071
|
timedOut = true;
|
|
@@ -23941,6 +24136,19 @@ function extractOutermostJson(text7) {
|
|
|
23941
24136
|
// adapters/next/init/vercel/auth.ts
|
|
23942
24137
|
var WHOAMI_TIMEOUT_MS = 3e4;
|
|
23943
24138
|
var LOGIN_TIMEOUT_MS = 3e5;
|
|
24139
|
+
var LOGIN_NOISE_PATTERNS = [
|
|
24140
|
+
/^vercel cli \d/i,
|
|
24141
|
+
/^>?$/,
|
|
24142
|
+
/^npm (warn|notice)/i,
|
|
24143
|
+
/congratulations! you are now signed in/i,
|
|
24144
|
+
/^to deploy something/i,
|
|
24145
|
+
/to deploy every commit automatically/i,
|
|
24146
|
+
/connect a git repository/i,
|
|
24147
|
+
/vercel\.link\/git/i
|
|
24148
|
+
];
|
|
24149
|
+
function showVercelLoginLine(line) {
|
|
24150
|
+
return !LOGIN_NOISE_PATTERNS.some((pattern) => pattern.test(line));
|
|
24151
|
+
}
|
|
23944
24152
|
async function checkVercelAuth(runner, cwd, env) {
|
|
23945
24153
|
const result = await runVercel(runner, ["whoami"], {
|
|
23946
24154
|
cwd,
|
|
@@ -23956,35 +24164,38 @@ async function checkVercelAuth(runner, cwd, env) {
|
|
|
23956
24164
|
}
|
|
23957
24165
|
async function ensureVercelAuth(runner, cwd, options) {
|
|
23958
24166
|
const env = options.env ?? process.env;
|
|
23959
|
-
const
|
|
23960
|
-
|
|
23961
|
-
runner.source === "npx" ? "Checking Vercel
|
|
24167
|
+
const checkSpinner = spinner2();
|
|
24168
|
+
checkSpinner.start(
|
|
24169
|
+
runner.source === "npx" ? "Checking Vercel, This may take a moment" : "Checking Vercel"
|
|
23962
24170
|
);
|
|
23963
24171
|
const existing = await checkVercelAuth(runner, cwd, env);
|
|
23964
24172
|
if (existing.authed) {
|
|
23965
|
-
|
|
24173
|
+
if (options.quietIfAuthed) {
|
|
24174
|
+
checkSpinner.clear();
|
|
24175
|
+
} else {
|
|
24176
|
+
checkSpinner.stop(signedInMessage(existing.username));
|
|
24177
|
+
}
|
|
23966
24178
|
return existing;
|
|
23967
24179
|
}
|
|
23968
24180
|
if (env.VERCEL_TOKEN) {
|
|
23969
|
-
|
|
24181
|
+
checkSpinner.stop("Could not verify VERCEL_TOKEN with Vercel");
|
|
23970
24182
|
return { authed: false, reason: "login-failed" };
|
|
23971
24183
|
}
|
|
23972
|
-
|
|
23973
|
-
|
|
23974
|
-
`Sign in to Vercel to continue
|
|
23975
|
-
"(or press Ctrl-C to enter a connection string manually)"
|
|
23976
|
-
)}`
|
|
24184
|
+
checkSpinner.clear();
|
|
24185
|
+
p13.log.info(
|
|
24186
|
+
`Sign in to Vercel to continue ${pc3.dim("(or press Ctrl-C to enter a connection string manually)")}`
|
|
23977
24187
|
);
|
|
23978
24188
|
const login = await runVercel(runner, ["login"], {
|
|
23979
24189
|
cwd,
|
|
23980
|
-
mode: "
|
|
24190
|
+
mode: "stream",
|
|
24191
|
+
streamLineFilter: showVercelLoginLine,
|
|
23981
24192
|
timeoutMs: LOGIN_TIMEOUT_MS,
|
|
23982
24193
|
env
|
|
23983
24194
|
});
|
|
23984
24195
|
if (!login.success) {
|
|
23985
24196
|
return { authed: false, reason: login.timedOut ? "timeout" : "login-cancelled" };
|
|
23986
24197
|
}
|
|
23987
|
-
const verifySpinner =
|
|
24198
|
+
const verifySpinner = spinner2();
|
|
23988
24199
|
verifySpinner.start("Verifying Vercel sign-in");
|
|
23989
24200
|
const after = await checkVercelAuth(runner, cwd, env);
|
|
23990
24201
|
if (after.authed) {
|
|
@@ -23998,78 +24209,26 @@ function signedInMessage(username) {
|
|
|
23998
24209
|
return username ? `Signed in to Vercel as ${pc3.cyan(username)}` : "Signed in to Vercel";
|
|
23999
24210
|
}
|
|
24000
24211
|
|
|
24001
|
-
// adapters/next/init/vercel/
|
|
24212
|
+
// adapters/next/init/vercel/blob.ts
|
|
24213
|
+
import * as p14 from "@clack/prompts";
|
|
24214
|
+
import pc4 from "picocolors";
|
|
24215
|
+
|
|
24216
|
+
// adapters/next/init/vercel/env-pull.ts
|
|
24002
24217
|
import fs34 from "fs";
|
|
24003
24218
|
import os from "os";
|
|
24004
24219
|
import path45 from "path";
|
|
24005
|
-
import * as p13 from "@clack/prompts";
|
|
24006
|
-
var PROVISION_TIMEOUT_MS = 18e4;
|
|
24007
|
-
var INTERACTIVE_PROVISION_TIMEOUT_MS = 6e5;
|
|
24008
24220
|
var ENV_PULL_TIMEOUT_MS = 6e4;
|
|
24009
|
-
async function
|
|
24010
|
-
const addArgs = ["integration", "add", "neon", "--no-env-pull"];
|
|
24011
|
-
if (options.plan) addArgs.push("--plan", options.plan);
|
|
24012
|
-
const add = await runVercel(runner, addArgs, {
|
|
24013
|
-
cwd,
|
|
24014
|
-
mode: "inherit",
|
|
24015
|
-
timeoutMs: INTERACTIVE_PROVISION_TIMEOUT_MS,
|
|
24016
|
-
env: options.env
|
|
24017
|
-
});
|
|
24018
|
-
if (!add.success) {
|
|
24019
|
-
return { failure: add.timedOut ? "timeout" : "provision-failed" };
|
|
24020
|
-
}
|
|
24021
|
-
const pullSpinner = p13.spinner();
|
|
24022
|
-
pullSpinner.start("Retrieving the database connection string");
|
|
24023
|
-
const databaseUrl = await pullDatabaseUrl(runner, cwd, options.env);
|
|
24024
|
-
if (!databaseUrl) {
|
|
24025
|
-
pullSpinner.stop("No DATABASE_URL came back from Vercel");
|
|
24026
|
-
return { failure: "env-pull-empty" };
|
|
24027
|
-
}
|
|
24028
|
-
pullSpinner.stop("Captured the Neon DATABASE_URL");
|
|
24029
|
-
return { databaseUrl };
|
|
24030
|
-
}
|
|
24031
|
-
async function provisionNeon(runner, cwd, options) {
|
|
24032
|
-
const addArgs = ["integration", "add", "neon", "--no-env-pull", "--format", "json"];
|
|
24033
|
-
if (options.plan) addArgs.push("--plan", options.plan);
|
|
24034
|
-
const spinner9 = p13.spinner();
|
|
24035
|
-
spinner9.start("Provisioning Neon Postgres via Vercel");
|
|
24036
|
-
const add = await runVercel(runner, addArgs, {
|
|
24037
|
-
cwd,
|
|
24038
|
-
mode: "capture",
|
|
24039
|
-
timeoutMs: PROVISION_TIMEOUT_MS,
|
|
24040
|
-
env: options.env
|
|
24041
|
-
});
|
|
24042
|
-
if (!add.success) {
|
|
24043
|
-
spinner9.stop("Neon provisioning did not complete");
|
|
24044
|
-
const combined = `${add.stdout}
|
|
24045
|
-
${add.stderr}`.trim();
|
|
24046
|
-
const detail = combined || void 0;
|
|
24047
|
-
if (/terms/i.test(combined)) return { failure: "terms-required", detail };
|
|
24048
|
-
if (/claim/i.test(combined)) return { failure: "claim-required", detail };
|
|
24049
|
-
return { failure: add.timedOut ? "timeout" : "provision-failed", detail };
|
|
24050
|
-
}
|
|
24051
|
-
const meta = parseVercelJson(add.stdout);
|
|
24052
|
-
const resourceName = meta?.resourceName ?? meta?.name;
|
|
24053
|
-
spinner9.message("Retrieving the database connection string");
|
|
24054
|
-
const databaseUrl = await pullDatabaseUrl(runner, cwd, options.env);
|
|
24055
|
-
if (!databaseUrl) {
|
|
24056
|
-
spinner9.stop("No DATABASE_URL came back from Vercel");
|
|
24057
|
-
return { resourceName, failure: "env-pull-empty" };
|
|
24058
|
-
}
|
|
24059
|
-
spinner9.stop("Captured the Neon DATABASE_URL");
|
|
24060
|
-
return { databaseUrl, resourceName };
|
|
24061
|
-
}
|
|
24062
|
-
async function pullDatabaseUrl(runner, cwd, env) {
|
|
24221
|
+
async function pullVercelEnvValue(runner, cwd, read, env, environment = "development") {
|
|
24063
24222
|
const tmpDir = fs34.mkdtempSync(path45.join(os.tmpdir(), "betterstart-vercel-"));
|
|
24064
24223
|
const tmpEnv = path45.join(tmpDir, ".env.pull");
|
|
24065
24224
|
try {
|
|
24066
24225
|
const pull = await runVercel(
|
|
24067
24226
|
runner,
|
|
24068
|
-
["env", "pull", tmpEnv, "--environment",
|
|
24227
|
+
["env", "pull", tmpEnv, "--environment", environment, "--yes"],
|
|
24069
24228
|
{ cwd, mode: "capture", timeoutMs: ENV_PULL_TIMEOUT_MS, env }
|
|
24070
24229
|
);
|
|
24071
24230
|
if (!pull.success) return void 0;
|
|
24072
|
-
return
|
|
24231
|
+
return read(tmpEnv);
|
|
24073
24232
|
} finally {
|
|
24074
24233
|
try {
|
|
24075
24234
|
fs34.rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -24077,9 +24236,9 @@ async function pullDatabaseUrl(runner, cwd, env) {
|
|
|
24077
24236
|
}
|
|
24078
24237
|
}
|
|
24079
24238
|
}
|
|
24080
|
-
function
|
|
24081
|
-
if (!fs34.existsSync(filePath)) return void 0;
|
|
24239
|
+
function parseDotenvFile(filePath) {
|
|
24082
24240
|
const vars = /* @__PURE__ */ new Map();
|
|
24241
|
+
if (!fs34.existsSync(filePath)) return vars;
|
|
24083
24242
|
for (const line of fs34.readFileSync(filePath, "utf-8").split("\n")) {
|
|
24084
24243
|
const trimmed = line.trim();
|
|
24085
24244
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -24088,16 +24247,7 @@ function readDbUrlFromDotenv(filePath) {
|
|
|
24088
24247
|
const value = trimmed.slice(eq + 1).replace(/^['"]|['"]$/g, "").trim();
|
|
24089
24248
|
if (key) vars.set(key, value);
|
|
24090
24249
|
}
|
|
24091
|
-
|
|
24092
|
-
if (isPg(vars.get("DATABASE_URL"))) return vars.get("DATABASE_URL");
|
|
24093
|
-
if (isPg(vars.get("POSTGRES_URL"))) return vars.get("POSTGRES_URL");
|
|
24094
|
-
for (const [key, value] of vars) {
|
|
24095
|
-
if (/DATABASE_URL$/.test(key) && isPg(value)) return value;
|
|
24096
|
-
}
|
|
24097
|
-
for (const value of vars.values()) {
|
|
24098
|
-
if (isPg(value)) return value;
|
|
24099
|
-
}
|
|
24100
|
-
return void 0;
|
|
24250
|
+
return vars;
|
|
24101
24251
|
}
|
|
24102
24252
|
|
|
24103
24253
|
// adapters/next/init/vercel/project.ts
|
|
@@ -24153,44 +24303,442 @@ ${add.stderr}`)) continue;
|
|
|
24153
24303
|
};
|
|
24154
24304
|
}
|
|
24155
24305
|
function readLinkedProjectId(cwd) {
|
|
24306
|
+
return readLinkedProjectJson(cwd)?.projectId;
|
|
24307
|
+
}
|
|
24308
|
+
function readLinkedProjectName(cwd) {
|
|
24309
|
+
return readLinkedProjectJson(cwd)?.projectName;
|
|
24310
|
+
}
|
|
24311
|
+
function readLinkedProjectJson(cwd) {
|
|
24156
24312
|
try {
|
|
24157
24313
|
const projectJsonPath = path46.join(cwd, ".vercel", "project.json");
|
|
24158
24314
|
if (!fs35.existsSync(projectJsonPath)) return void 0;
|
|
24159
|
-
|
|
24160
|
-
return parsed.projectId;
|
|
24315
|
+
return JSON.parse(fs35.readFileSync(projectJsonPath, "utf-8"));
|
|
24161
24316
|
} catch {
|
|
24162
24317
|
return void 0;
|
|
24163
24318
|
}
|
|
24164
24319
|
}
|
|
24165
24320
|
|
|
24166
|
-
// adapters/next/init/vercel/
|
|
24167
|
-
|
|
24168
|
-
|
|
24169
|
-
|
|
24170
|
-
|
|
24171
|
-
|
|
24172
|
-
|
|
24173
|
-
|
|
24174
|
-
|
|
24175
|
-
|
|
24176
|
-
|
|
24177
|
-
|
|
24178
|
-
|
|
24179
|
-
|
|
24180
|
-
|
|
24181
|
-
|
|
24182
|
-
|
|
24183
|
-
|
|
24184
|
-
|
|
24185
|
-
|
|
24321
|
+
// adapters/next/init/vercel/blob.ts
|
|
24322
|
+
var PROVISION_TIMEOUT_MS = 18e4;
|
|
24323
|
+
var INTERACTIVE_PROVISION_TIMEOUT_MS = 6e5;
|
|
24324
|
+
var MAX_STORE_NAME_ATTEMPTS = 5;
|
|
24325
|
+
function blobStoreName(projectName) {
|
|
24326
|
+
return sanitizeVercelProjectName(`${projectName}-blob`);
|
|
24327
|
+
}
|
|
24328
|
+
function isNameTaken(result) {
|
|
24329
|
+
return /already exists|already taken|already in use|name.*taken/i.test(
|
|
24330
|
+
`${result.stdout}
|
|
24331
|
+
${result.stderr}`
|
|
24332
|
+
);
|
|
24333
|
+
}
|
|
24334
|
+
async function createStoreQuiet(runner, cwd, baseName, env) {
|
|
24335
|
+
let result;
|
|
24336
|
+
let storeName = baseName;
|
|
24337
|
+
for (let attempt = 1; attempt <= MAX_STORE_NAME_ATTEMPTS; attempt++) {
|
|
24338
|
+
storeName = versionedVercelProjectName(baseName, attempt);
|
|
24339
|
+
result = await runVercel(
|
|
24340
|
+
runner,
|
|
24341
|
+
["blob", "create-store", storeName, "--access", "public", "--yes"],
|
|
24342
|
+
{ cwd, mode: "capture", timeoutMs: PROVISION_TIMEOUT_MS, env }
|
|
24186
24343
|
);
|
|
24187
|
-
|
|
24188
|
-
|
|
24189
|
-
|
|
24190
|
-
|
|
24191
|
-
|
|
24192
|
-
|
|
24193
|
-
|
|
24344
|
+
if (result.success || !isNameTaken(result)) break;
|
|
24345
|
+
}
|
|
24346
|
+
return { result, storeName };
|
|
24347
|
+
}
|
|
24348
|
+
async function provisionBlobStoreInteractive(runner, cwd, options) {
|
|
24349
|
+
const quietSpinner = spinner2();
|
|
24350
|
+
quietSpinner.start("Creating your Vercel Blob store");
|
|
24351
|
+
const quiet = await createStoreQuiet(runner, cwd, options.name, options.env);
|
|
24352
|
+
if (quiet.result.success) {
|
|
24353
|
+
quietSpinner.message("Retrieving the Blob read-write token");
|
|
24354
|
+
const token2 = await pullBlobToken(runner, cwd, options.env);
|
|
24355
|
+
if (!token2) {
|
|
24356
|
+
quietSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
|
|
24357
|
+
return { storeName: quiet.storeName, failure: "env-pull-empty" };
|
|
24358
|
+
}
|
|
24359
|
+
quietSpinner.clear();
|
|
24360
|
+
return { token: token2, storeName: quiet.storeName };
|
|
24361
|
+
}
|
|
24362
|
+
quietSpinner.clear();
|
|
24363
|
+
p14.log.info(
|
|
24364
|
+
`Create your Blob store in the Vercel prompts below ${pc4.dim(
|
|
24365
|
+
"(connect it to all environments)."
|
|
24366
|
+
)}`
|
|
24367
|
+
);
|
|
24368
|
+
const add = await runVercel(
|
|
24369
|
+
runner,
|
|
24370
|
+
["blob", "create-store", quiet.storeName, "--access", "public"],
|
|
24371
|
+
{ cwd, mode: "inherit", timeoutMs: INTERACTIVE_PROVISION_TIMEOUT_MS, env: options.env }
|
|
24372
|
+
);
|
|
24373
|
+
if (!add.success) {
|
|
24374
|
+
return { failure: add.timedOut ? "timeout" : "provision-failed" };
|
|
24375
|
+
}
|
|
24376
|
+
const pullSpinner = spinner2();
|
|
24377
|
+
pullSpinner.start("Retrieving the Blob read-write token");
|
|
24378
|
+
const token = await pullBlobToken(runner, cwd, options.env);
|
|
24379
|
+
if (!token) {
|
|
24380
|
+
pullSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
|
|
24381
|
+
return { storeName: quiet.storeName, failure: "env-pull-empty" };
|
|
24382
|
+
}
|
|
24383
|
+
pullSpinner.clear();
|
|
24384
|
+
return { token, storeName: quiet.storeName };
|
|
24385
|
+
}
|
|
24386
|
+
async function provisionBlobStore(runner, cwd, options) {
|
|
24387
|
+
const provisionSpinner = spinner2();
|
|
24388
|
+
provisionSpinner.start("Creating your Vercel Blob store");
|
|
24389
|
+
const { result, storeName } = await createStoreQuiet(runner, cwd, options.name, options.env);
|
|
24390
|
+
if (!result.success) {
|
|
24391
|
+
provisionSpinner.stop("Vercel Blob provisioning did not complete");
|
|
24392
|
+
const combined = `${result.stdout}
|
|
24393
|
+
${result.stderr}`.trim();
|
|
24394
|
+
return {
|
|
24395
|
+
failure: result.timedOut ? "timeout" : "provision-failed",
|
|
24396
|
+
detail: combined || void 0
|
|
24397
|
+
};
|
|
24398
|
+
}
|
|
24399
|
+
provisionSpinner.message("Retrieving the Blob read-write token");
|
|
24400
|
+
const token = await pullBlobToken(runner, cwd, options.env);
|
|
24401
|
+
if (!token) {
|
|
24402
|
+
provisionSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
|
|
24403
|
+
return { storeName, failure: "env-pull-empty" };
|
|
24404
|
+
}
|
|
24405
|
+
provisionSpinner.clear();
|
|
24406
|
+
return { token, storeName };
|
|
24407
|
+
}
|
|
24408
|
+
function pullBlobToken(runner, cwd, env) {
|
|
24409
|
+
return pullVercelEnvValue(runner, cwd, readBlobTokenFromDotenv, env);
|
|
24410
|
+
}
|
|
24411
|
+
function readBlobTokenFromDotenv(filePath) {
|
|
24412
|
+
const vars = parseDotenvFile(filePath);
|
|
24413
|
+
const isBlobToken = (v) => !!v && v.startsWith("vercel_blob_rw_");
|
|
24414
|
+
if (isBlobToken(vars.get("BLOB_READ_WRITE_TOKEN"))) return vars.get("BLOB_READ_WRITE_TOKEN");
|
|
24415
|
+
for (const [key, value] of vars) {
|
|
24416
|
+
if (/_READ_WRITE_TOKEN$/.test(key) && isBlobToken(value)) return value;
|
|
24417
|
+
}
|
|
24418
|
+
for (const value of vars.values()) {
|
|
24419
|
+
if (isBlobToken(value)) return value;
|
|
24420
|
+
}
|
|
24421
|
+
return void 0;
|
|
24422
|
+
}
|
|
24423
|
+
|
|
24424
|
+
// adapters/next/init/vercel/deploy.ts
|
|
24425
|
+
import fs36 from "fs";
|
|
24426
|
+
import path47 from "path";
|
|
24427
|
+
import { stripVTControlCharacters as stripVTControlCharacters3 } from "util";
|
|
24428
|
+
var DEPLOY_TIMEOUT_MS = 9e5;
|
|
24429
|
+
var ENV_ADD_TIMEOUT_MS = 3e4;
|
|
24430
|
+
var DETAIL_MAX_LINES = 15;
|
|
24431
|
+
var ENV_SYNC_SKIP_KEYS = /* @__PURE__ */ new Set([
|
|
24432
|
+
"VERCEL_OIDC_TOKEN",
|
|
24433
|
+
"BETTERSTART_AUTH_URL",
|
|
24434
|
+
"NEXT_PUBLIC_BETTERSTART_AUTH_URL"
|
|
24435
|
+
]);
|
|
24436
|
+
function isPlaceholderValue(value) {
|
|
24437
|
+
return value === "postgresql://..." || value.startsWith("your_");
|
|
24438
|
+
}
|
|
24439
|
+
function collectDeployEnvVars(cwd, existingProductionKeys) {
|
|
24440
|
+
const local = parseDotenvFile(path47.join(cwd, ".env.local"));
|
|
24441
|
+
const vars = [];
|
|
24442
|
+
for (const [key, value] of local) {
|
|
24443
|
+
if (ENV_SYNC_SKIP_KEYS.has(key)) continue;
|
|
24444
|
+
if (!value || isPlaceholderValue(value)) continue;
|
|
24445
|
+
if (existingProductionKeys.has(key)) continue;
|
|
24446
|
+
vars.push({ key, value });
|
|
24447
|
+
}
|
|
24448
|
+
return vars;
|
|
24449
|
+
}
|
|
24450
|
+
async function syncVercelProductionEnv(runner, cwd, env) {
|
|
24451
|
+
const existingKeys = await pullVercelEnvValue(
|
|
24452
|
+
runner,
|
|
24453
|
+
cwd,
|
|
24454
|
+
(filePath) => new Set(parseDotenvFile(filePath).keys()),
|
|
24455
|
+
env,
|
|
24456
|
+
"production"
|
|
24457
|
+
) ?? /* @__PURE__ */ new Set();
|
|
24458
|
+
const synced = [];
|
|
24459
|
+
const failed = [];
|
|
24460
|
+
for (const { key, value } of collectDeployEnvVars(cwd, existingKeys)) {
|
|
24461
|
+
const add = await runVercel(runner, ["env", "add", key, "production"], {
|
|
24462
|
+
cwd,
|
|
24463
|
+
mode: "capture",
|
|
24464
|
+
timeoutMs: ENV_ADD_TIMEOUT_MS,
|
|
24465
|
+
env,
|
|
24466
|
+
stdinInput: `${value}
|
|
24467
|
+
`
|
|
24468
|
+
});
|
|
24469
|
+
if (add.success) synced.push(key);
|
|
24470
|
+
else failed.push(key);
|
|
24471
|
+
}
|
|
24472
|
+
return { synced, failed };
|
|
24473
|
+
}
|
|
24474
|
+
var LOCAL_PROTOCOL_PATTERN = /^(workspace|link|file|portal):/;
|
|
24475
|
+
var ADMIN_CONFIG_IMPORT_PATTERN = /^import\s+\{[^}]*\bdefineConfig\b[^}]*\}\s+from\s+['"]betterstart-cli['"];?[^\S\n]*$/m;
|
|
24476
|
+
var ADMIN_CONFIG_IMPORT_SHIM = "const defineConfig = <T>(config: T): T => config";
|
|
24477
|
+
function guardProjectForDeploy(cwd) {
|
|
24478
|
+
const restores = [];
|
|
24479
|
+
const localSpecDeps = guardPackageJson(cwd, restores);
|
|
24480
|
+
guardAdminConfig(cwd, restores);
|
|
24481
|
+
return {
|
|
24482
|
+
localSpecDeps,
|
|
24483
|
+
restore() {
|
|
24484
|
+
for (const restoreFile2 of restores) {
|
|
24485
|
+
try {
|
|
24486
|
+
restoreFile2();
|
|
24487
|
+
} catch {
|
|
24488
|
+
}
|
|
24489
|
+
}
|
|
24490
|
+
}
|
|
24491
|
+
};
|
|
24492
|
+
}
|
|
24493
|
+
function guardPackageJson(cwd, restores) {
|
|
24494
|
+
const packageJsonPath = path47.join(cwd, "package.json");
|
|
24495
|
+
if (!fs36.existsSync(packageJsonPath)) {
|
|
24496
|
+
return [];
|
|
24497
|
+
}
|
|
24498
|
+
const original = fs36.readFileSync(packageJsonPath, "utf-8");
|
|
24499
|
+
let parsed;
|
|
24500
|
+
try {
|
|
24501
|
+
parsed = JSON.parse(original);
|
|
24502
|
+
} catch {
|
|
24503
|
+
return [];
|
|
24504
|
+
}
|
|
24505
|
+
const localSpecDeps = [];
|
|
24506
|
+
let removed = false;
|
|
24507
|
+
for (const section of ["dependencies", "devDependencies"]) {
|
|
24508
|
+
const deps = parsed[section];
|
|
24509
|
+
if (!deps || typeof deps !== "object") continue;
|
|
24510
|
+
const record = deps;
|
|
24511
|
+
if ("betterstart-cli" in record) {
|
|
24512
|
+
Reflect.deleteProperty(record, "betterstart-cli");
|
|
24513
|
+
removed = true;
|
|
24514
|
+
}
|
|
24515
|
+
for (const [name, spec] of Object.entries(record)) {
|
|
24516
|
+
if (typeof spec === "string" && LOCAL_PROTOCOL_PATTERN.test(spec)) {
|
|
24517
|
+
localSpecDeps.push(name);
|
|
24518
|
+
}
|
|
24519
|
+
}
|
|
24520
|
+
}
|
|
24521
|
+
if (removed) {
|
|
24522
|
+
fs36.writeFileSync(packageJsonPath, `${JSON.stringify(parsed, null, 2)}
|
|
24523
|
+
`, "utf-8");
|
|
24524
|
+
restores.push(() => fs36.writeFileSync(packageJsonPath, original, "utf-8"));
|
|
24525
|
+
}
|
|
24526
|
+
return localSpecDeps;
|
|
24527
|
+
}
|
|
24528
|
+
function guardAdminConfig(cwd, restores) {
|
|
24529
|
+
const adminConfigPath = path47.join(cwd, "admin.config.ts");
|
|
24530
|
+
if (!fs36.existsSync(adminConfigPath)) {
|
|
24531
|
+
return;
|
|
24532
|
+
}
|
|
24533
|
+
const original = fs36.readFileSync(adminConfigPath, "utf-8");
|
|
24534
|
+
if (!ADMIN_CONFIG_IMPORT_PATTERN.test(original)) {
|
|
24535
|
+
return;
|
|
24536
|
+
}
|
|
24537
|
+
fs36.writeFileSync(
|
|
24538
|
+
adminConfigPath,
|
|
24539
|
+
original.replace(ADMIN_CONFIG_IMPORT_PATTERN, ADMIN_CONFIG_IMPORT_SHIM),
|
|
24540
|
+
"utf-8"
|
|
24541
|
+
);
|
|
24542
|
+
restores.push(() => fs36.writeFileSync(adminConfigPath, original, "utf-8"));
|
|
24543
|
+
}
|
|
24544
|
+
function ensureVercelJsonFramework(cwd) {
|
|
24545
|
+
const vercelJsonPath = path47.join(cwd, "vercel.json");
|
|
24546
|
+
if (fs36.existsSync(vercelJsonPath)) {
|
|
24547
|
+
return "exists";
|
|
24548
|
+
}
|
|
24549
|
+
fs36.writeFileSync(
|
|
24550
|
+
vercelJsonPath,
|
|
24551
|
+
`${JSON.stringify(
|
|
24552
|
+
{ $schema: "https://openapi.vercel.sh/vercel.json", framework: "nextjs" },
|
|
24553
|
+
null,
|
|
24554
|
+
2
|
|
24555
|
+
)}
|
|
24556
|
+
`,
|
|
24557
|
+
"utf-8"
|
|
24558
|
+
);
|
|
24559
|
+
return "created";
|
|
24560
|
+
}
|
|
24561
|
+
function parseDeployedUrl(stdout, stderr) {
|
|
24562
|
+
const plainErr = stripVTControlCharacters3(stderr);
|
|
24563
|
+
const production = plainErr.match(/Production:\s+(https:\/\/\S+)/i)?.[1];
|
|
24564
|
+
if (production) return production;
|
|
24565
|
+
const fromStdout = stripVTControlCharacters3(stdout).match(/https:\/\/\S+/)?.[0];
|
|
24566
|
+
return fromStdout;
|
|
24567
|
+
}
|
|
24568
|
+
async function deployVercelProject(runner, cwd, env) {
|
|
24569
|
+
const deploy = await runVercel(runner, ["deploy", "--prod", "--yes"], {
|
|
24570
|
+
cwd,
|
|
24571
|
+
mode: "capture",
|
|
24572
|
+
timeoutMs: DEPLOY_TIMEOUT_MS,
|
|
24573
|
+
env
|
|
24574
|
+
});
|
|
24575
|
+
if (deploy.success) {
|
|
24576
|
+
return { url: parseDeployedUrl(deploy.stdout, deploy.stderr) };
|
|
24577
|
+
}
|
|
24578
|
+
return {
|
|
24579
|
+
failure: deploy.timedOut ? "timeout" : "deploy-failed",
|
|
24580
|
+
detail: outputTail(`${deploy.stdout}
|
|
24581
|
+
${deploy.stderr}`)
|
|
24582
|
+
};
|
|
24583
|
+
}
|
|
24584
|
+
function outputTail(output) {
|
|
24585
|
+
const lines = stripVTControlCharacters3(output).split("\n").map((line) => line.trimEnd()).filter((line) => line.trim().length > 0);
|
|
24586
|
+
if (lines.length === 0) return void 0;
|
|
24587
|
+
return lines.slice(-DETAIL_MAX_LINES).join("\n");
|
|
24588
|
+
}
|
|
24589
|
+
|
|
24590
|
+
// adapters/next/init/vercel/env-guard.ts
|
|
24591
|
+
import fs37 from "fs";
|
|
24592
|
+
import path48 from "path";
|
|
24593
|
+
function guardEnvLocal(cwd) {
|
|
24594
|
+
const envPath = path48.join(cwd, ".env.local");
|
|
24595
|
+
let original;
|
|
24596
|
+
try {
|
|
24597
|
+
original = fs37.readFileSync(envPath, "utf-8");
|
|
24598
|
+
} catch {
|
|
24599
|
+
original = void 0;
|
|
24600
|
+
}
|
|
24601
|
+
return {
|
|
24602
|
+
restore() {
|
|
24603
|
+
try {
|
|
24604
|
+
const current = fs37.existsSync(envPath) ? fs37.readFileSync(envPath, "utf-8") : void 0;
|
|
24605
|
+
if (current === original) return;
|
|
24606
|
+
if (original === void 0) {
|
|
24607
|
+
fs37.rmSync(envPath, { force: true });
|
|
24608
|
+
} else {
|
|
24609
|
+
fs37.writeFileSync(envPath, original);
|
|
24610
|
+
}
|
|
24611
|
+
} catch {
|
|
24612
|
+
}
|
|
24613
|
+
}
|
|
24614
|
+
};
|
|
24615
|
+
}
|
|
24616
|
+
|
|
24617
|
+
// adapters/next/init/vercel/neon.ts
|
|
24618
|
+
import * as p15 from "@clack/prompts";
|
|
24619
|
+
import pc5 from "picocolors";
|
|
24620
|
+
var PROVISION_TIMEOUT_MS2 = 18e4;
|
|
24621
|
+
var INTERACTIVE_PROVISION_TIMEOUT_MS2 = 6e5;
|
|
24622
|
+
async function provisionNeonInteractive(runner, cwd, options) {
|
|
24623
|
+
const addArgs = ["integration", "add", "neon", "--no-env-pull"];
|
|
24624
|
+
if (options.plan) addArgs.push("--plan", options.plan);
|
|
24625
|
+
const quietSpinner = spinner2();
|
|
24626
|
+
quietSpinner.start("Creating your Neon database");
|
|
24627
|
+
const quiet = await runVercel(runner, addArgs, {
|
|
24628
|
+
cwd,
|
|
24629
|
+
mode: "capture",
|
|
24630
|
+
timeoutMs: PROVISION_TIMEOUT_MS2,
|
|
24631
|
+
env: options.env
|
|
24632
|
+
});
|
|
24633
|
+
if (quiet.success) {
|
|
24634
|
+
quietSpinner.message("Retrieving the database connection string");
|
|
24635
|
+
const databaseUrl2 = await pullDatabaseUrl(runner, cwd, options.env);
|
|
24636
|
+
if (!databaseUrl2) {
|
|
24637
|
+
quietSpinner.stop("No DATABASE_URL came back from Vercel");
|
|
24638
|
+
return { failure: "env-pull-empty" };
|
|
24639
|
+
}
|
|
24640
|
+
quietSpinner.clear();
|
|
24641
|
+
return { databaseUrl: databaseUrl2 };
|
|
24642
|
+
}
|
|
24643
|
+
quietSpinner.clear();
|
|
24644
|
+
p15.log.info(
|
|
24645
|
+
`Create your Neon database in the Vercel prompts below ${pc5.dim(
|
|
24646
|
+
"(the Free plan is recommended)."
|
|
24647
|
+
)}`
|
|
24648
|
+
);
|
|
24649
|
+
const add = await runVercel(runner, addArgs, {
|
|
24650
|
+
cwd,
|
|
24651
|
+
mode: "inherit",
|
|
24652
|
+
timeoutMs: INTERACTIVE_PROVISION_TIMEOUT_MS2,
|
|
24653
|
+
env: options.env
|
|
24654
|
+
});
|
|
24655
|
+
if (!add.success) {
|
|
24656
|
+
return { failure: add.timedOut ? "timeout" : "provision-failed" };
|
|
24657
|
+
}
|
|
24658
|
+
const pullSpinner = spinner2();
|
|
24659
|
+
pullSpinner.start("Retrieving the database connection string");
|
|
24660
|
+
const databaseUrl = await pullDatabaseUrl(runner, cwd, options.env);
|
|
24661
|
+
if (!databaseUrl) {
|
|
24662
|
+
pullSpinner.stop("No DATABASE_URL came back from Vercel");
|
|
24663
|
+
return { failure: "env-pull-empty" };
|
|
24664
|
+
}
|
|
24665
|
+
pullSpinner.clear();
|
|
24666
|
+
return { databaseUrl };
|
|
24667
|
+
}
|
|
24668
|
+
async function provisionNeon(runner, cwd, options) {
|
|
24669
|
+
const addArgs = ["integration", "add", "neon", "--no-env-pull", "--format", "json"];
|
|
24670
|
+
if (options.plan) addArgs.push("--plan", options.plan);
|
|
24671
|
+
const provisionSpinner = spinner2();
|
|
24672
|
+
provisionSpinner.start("Creating your Neon database");
|
|
24673
|
+
const add = await runVercel(runner, addArgs, {
|
|
24674
|
+
cwd,
|
|
24675
|
+
mode: "capture",
|
|
24676
|
+
timeoutMs: PROVISION_TIMEOUT_MS2,
|
|
24677
|
+
env: options.env
|
|
24678
|
+
});
|
|
24679
|
+
if (!add.success) {
|
|
24680
|
+
provisionSpinner.stop("Neon provisioning did not complete");
|
|
24681
|
+
const combined = `${add.stdout}
|
|
24682
|
+
${add.stderr}`.trim();
|
|
24683
|
+
const detail = combined || void 0;
|
|
24684
|
+
if (/terms/i.test(combined)) return { failure: "terms-required", detail };
|
|
24685
|
+
if (/claim/i.test(combined)) return { failure: "claim-required", detail };
|
|
24686
|
+
return { failure: add.timedOut ? "timeout" : "provision-failed", detail };
|
|
24687
|
+
}
|
|
24688
|
+
const meta = parseVercelJson(add.stdout);
|
|
24689
|
+
const resourceName = meta?.resourceName ?? meta?.name;
|
|
24690
|
+
provisionSpinner.message("Retrieving the database connection string");
|
|
24691
|
+
const databaseUrl = await pullDatabaseUrl(runner, cwd, options.env);
|
|
24692
|
+
if (!databaseUrl) {
|
|
24693
|
+
provisionSpinner.stop("No DATABASE_URL came back from Vercel");
|
|
24694
|
+
return { resourceName, failure: "env-pull-empty" };
|
|
24695
|
+
}
|
|
24696
|
+
provisionSpinner.clear();
|
|
24697
|
+
return { databaseUrl, resourceName };
|
|
24698
|
+
}
|
|
24699
|
+
function pullDatabaseUrl(runner, cwd, env) {
|
|
24700
|
+
return pullVercelEnvValue(runner, cwd, readDbUrlFromDotenv, env);
|
|
24701
|
+
}
|
|
24702
|
+
function readDbUrlFromDotenv(filePath) {
|
|
24703
|
+
const vars = parseDotenvFile(filePath);
|
|
24704
|
+
const isPg = (v) => !!v && (v.startsWith("postgres://") || v.startsWith("postgresql://"));
|
|
24705
|
+
if (isPg(vars.get("DATABASE_URL"))) return vars.get("DATABASE_URL");
|
|
24706
|
+
if (isPg(vars.get("POSTGRES_URL"))) return vars.get("POSTGRES_URL");
|
|
24707
|
+
for (const [key, value] of vars) {
|
|
24708
|
+
if (/DATABASE_URL$/.test(key) && isPg(value)) return value;
|
|
24709
|
+
}
|
|
24710
|
+
for (const value of vars.values()) {
|
|
24711
|
+
if (isPg(value)) return value;
|
|
24712
|
+
}
|
|
24713
|
+
return void 0;
|
|
24714
|
+
}
|
|
24715
|
+
|
|
24716
|
+
// adapters/next/init/vercel/flow.ts
|
|
24717
|
+
async function runVercelNeonFlow(options) {
|
|
24718
|
+
const env = options.env ?? process.env;
|
|
24719
|
+
const envGuard = guardEnvLocal(options.cwd);
|
|
24720
|
+
try {
|
|
24721
|
+
const runnerSpinner = spinner2();
|
|
24722
|
+
runnerSpinner.start("Checking for the Vercel CLI");
|
|
24723
|
+
const runner = await resolveVercelRunner();
|
|
24724
|
+
runnerSpinner.clear();
|
|
24725
|
+
const auth = await ensureVercelAuth(runner, options.cwd, { env });
|
|
24726
|
+
if (!auth.authed) {
|
|
24727
|
+
p16.log.warn(authFailureMessage(auth.reason));
|
|
24728
|
+
return { ok: false };
|
|
24729
|
+
}
|
|
24730
|
+
const projectSpinner = spinner2();
|
|
24731
|
+
projectSpinner.start(`Creating a Vercel project ${pc6.cyan(options.projectName)}`);
|
|
24732
|
+
const project2 = await createVercelProject(runner, options.cwd, options.projectName, env);
|
|
24733
|
+
projectSpinner.stop(
|
|
24734
|
+
project2.linked ? `Linked Vercel project ${pc6.cyan(project2.name)}` : `Using Vercel project ${pc6.cyan(project2.name)}`
|
|
24735
|
+
);
|
|
24736
|
+
const neon = await provisionNeonForMode(runner, options);
|
|
24737
|
+
if (neon.failure || !neon.databaseUrl) {
|
|
24738
|
+
p16.log.warn(neonFailureMessage(neon.failure));
|
|
24739
|
+
if (neon.detail) p16.log.message(pc6.dim(neon.detail));
|
|
24740
|
+
return { ok: false };
|
|
24741
|
+
}
|
|
24194
24742
|
return {
|
|
24195
24743
|
ok: true,
|
|
24196
24744
|
databaseUrl: neon.databaseUrl,
|
|
@@ -24198,21 +24746,140 @@ async function runVercelNeonFlow(options) {
|
|
|
24198
24746
|
neonResourceName: neon.resourceName
|
|
24199
24747
|
};
|
|
24200
24748
|
} catch (error) {
|
|
24201
|
-
|
|
24749
|
+
p16.log.warn(
|
|
24202
24750
|
`Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
|
|
24203
24751
|
);
|
|
24204
24752
|
return { ok: false };
|
|
24753
|
+
} finally {
|
|
24754
|
+
envGuard.restore();
|
|
24205
24755
|
}
|
|
24206
24756
|
}
|
|
24207
|
-
function
|
|
24208
|
-
const
|
|
24209
|
-
|
|
24210
|
-
|
|
24211
|
-
|
|
24757
|
+
async function runVercelBlobFlow(options) {
|
|
24758
|
+
const env = options.env ?? process.env;
|
|
24759
|
+
const envGuard = guardEnvLocal(options.cwd);
|
|
24760
|
+
try {
|
|
24761
|
+
const runnerSpinner = spinner2();
|
|
24762
|
+
runnerSpinner.start("Checking for the Vercel CLI");
|
|
24763
|
+
const runner = await resolveVercelRunner();
|
|
24764
|
+
runnerSpinner.clear();
|
|
24765
|
+
const auth = await ensureVercelAuth(runner, options.cwd, { env, quietIfAuthed: true });
|
|
24766
|
+
if (!auth.authed) {
|
|
24767
|
+
p16.log.warn(authFailureMessage(auth.reason));
|
|
24768
|
+
return { ok: false };
|
|
24769
|
+
}
|
|
24770
|
+
let projectId = readLinkedProjectId(options.cwd);
|
|
24771
|
+
if (!projectId) {
|
|
24772
|
+
const projectSpinner = spinner2();
|
|
24773
|
+
projectSpinner.start(`Creating a Vercel project ${pc6.cyan(options.projectName)}`);
|
|
24774
|
+
const project2 = await createVercelProject(runner, options.cwd, options.projectName, env);
|
|
24775
|
+
projectSpinner.stop(
|
|
24776
|
+
project2.linked ? `Linked Vercel project ${pc6.cyan(project2.name)}` : `Using Vercel project ${pc6.cyan(project2.name)}`
|
|
24777
|
+
);
|
|
24778
|
+
projectId = project2.projectId;
|
|
24779
|
+
}
|
|
24780
|
+
const blob = await provisionBlobForMode(runner, options);
|
|
24781
|
+
if (blob.failure || !blob.token) {
|
|
24782
|
+
p16.log.warn(blobFailureMessage(blob.failure));
|
|
24783
|
+
if (blob.detail) p16.log.message(pc6.dim(blob.detail));
|
|
24784
|
+
return { ok: false };
|
|
24785
|
+
}
|
|
24786
|
+
return {
|
|
24787
|
+
ok: true,
|
|
24788
|
+
token: blob.token,
|
|
24789
|
+
vercelProjectId: projectId,
|
|
24790
|
+
blobStoreName: blob.storeName
|
|
24791
|
+
};
|
|
24792
|
+
} catch (error) {
|
|
24793
|
+
p16.log.warn(
|
|
24794
|
+
`Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
|
|
24212
24795
|
);
|
|
24213
|
-
return
|
|
24796
|
+
return { ok: false };
|
|
24797
|
+
} finally {
|
|
24798
|
+
envGuard.restore();
|
|
24799
|
+
}
|
|
24800
|
+
}
|
|
24801
|
+
async function runVercelDeployFlow(options) {
|
|
24802
|
+
const env = options.env ?? process.env;
|
|
24803
|
+
const envGuard = guardEnvLocal(options.cwd);
|
|
24804
|
+
try {
|
|
24805
|
+
const runnerSpinner = spinner2();
|
|
24806
|
+
runnerSpinner.start("Checking for the Vercel CLI");
|
|
24807
|
+
const runner = await resolveVercelRunner();
|
|
24808
|
+
runnerSpinner.clear();
|
|
24809
|
+
const auth = await ensureVercelAuth(runner, options.cwd, { env, quietIfAuthed: true });
|
|
24810
|
+
if (!auth.authed) {
|
|
24811
|
+
p16.log.warn(authFailureMessage(auth.reason));
|
|
24812
|
+
printManualDeployHint();
|
|
24813
|
+
return { ok: false };
|
|
24814
|
+
}
|
|
24815
|
+
if (!readLinkedProjectId(options.cwd)) {
|
|
24816
|
+
const projectSpinner = spinner2();
|
|
24817
|
+
projectSpinner.start(`Creating a Vercel project ${pc6.cyan(options.projectName)}`);
|
|
24818
|
+
const project2 = await createVercelProject(runner, options.cwd, options.projectName, env);
|
|
24819
|
+
projectSpinner.stop(
|
|
24820
|
+
project2.linked ? `Linked Vercel project ${pc6.cyan(project2.name)}` : `Using Vercel project ${pc6.cyan(project2.name)}`
|
|
24821
|
+
);
|
|
24822
|
+
}
|
|
24823
|
+
const envSpinner = spinner2();
|
|
24824
|
+
envSpinner.start("Syncing environment variables to Vercel");
|
|
24825
|
+
const sync = await syncVercelProductionEnv(runner, options.cwd, env);
|
|
24826
|
+
if (sync.synced.length > 0) {
|
|
24827
|
+
envSpinner.stop(
|
|
24828
|
+
`Synced ${sync.synced.length} environment variable${sync.synced.length === 1 ? "" : "s"} to Vercel`
|
|
24829
|
+
);
|
|
24830
|
+
} else {
|
|
24831
|
+
envSpinner.clear();
|
|
24832
|
+
}
|
|
24833
|
+
for (const key of sync.failed) {
|
|
24834
|
+
p16.log.warn(
|
|
24835
|
+
`Could not set ${pc6.cyan(key)} on Vercel \u2014 add it in the project's environment settings.`
|
|
24836
|
+
);
|
|
24837
|
+
}
|
|
24838
|
+
if (ensureVercelJsonFramework(options.cwd) === "created") {
|
|
24839
|
+
p16.log.success(`Created ${pc6.cyan("vercel.json")} with the Next.js framework preset`);
|
|
24840
|
+
}
|
|
24841
|
+
const packageGuard = guardProjectForDeploy(options.cwd);
|
|
24842
|
+
for (const dep of packageGuard.localSpecDeps) {
|
|
24843
|
+
p16.log.warn(
|
|
24844
|
+
`Dependency ${pc6.cyan(dep)} uses a local spec that cannot install on Vercel \u2014 the remote build may fail.`
|
|
24845
|
+
);
|
|
24846
|
+
}
|
|
24847
|
+
const deploySpinner = spinner2();
|
|
24848
|
+
deploySpinner.start("Deploying to Vercel (this may take a few minutes)");
|
|
24849
|
+
let deploy;
|
|
24850
|
+
try {
|
|
24851
|
+
deploy = await deployVercelProject(runner, options.cwd, env);
|
|
24852
|
+
} finally {
|
|
24853
|
+
packageGuard.restore();
|
|
24854
|
+
}
|
|
24855
|
+
if (deploy.failure) {
|
|
24856
|
+
deploySpinner.stop(`${pc6.yellow("\u25B2")} ${deployFailureMessage(deploy.failure)}`);
|
|
24857
|
+
if (deploy.detail) p16.log.message(pc6.dim(deploy.detail));
|
|
24858
|
+
printManualDeployHint();
|
|
24859
|
+
return { ok: false };
|
|
24860
|
+
}
|
|
24861
|
+
const linkedName = readLinkedProjectName(options.cwd);
|
|
24862
|
+
const url = linkedName ? `https://${linkedName}.vercel.app` : deploy.url;
|
|
24863
|
+
deploySpinner.stop(url ? `Deployed ${pc6.cyan(url)}` : "Deployed to Vercel");
|
|
24864
|
+
return { ok: true, url, syncedEnvKeys: sync.synced };
|
|
24865
|
+
} catch (error) {
|
|
24866
|
+
p16.log.warn(`Vercel deploy failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
24867
|
+
printManualDeployHint();
|
|
24868
|
+
return { ok: false };
|
|
24869
|
+
} finally {
|
|
24870
|
+
envGuard.restore();
|
|
24214
24871
|
}
|
|
24215
|
-
|
|
24872
|
+
}
|
|
24873
|
+
function printManualDeployHint() {
|
|
24874
|
+
p16.log.info(`You can deploy manually: ${pc6.cyan("vercel deploy --prod")}`);
|
|
24875
|
+
}
|
|
24876
|
+
function provisionBlobForMode(runner, options) {
|
|
24877
|
+
const opts = { name: blobStoreName(options.projectName), env: options.env };
|
|
24878
|
+
return options.interactive ? provisionBlobStoreInteractive(runner, options.cwd, opts) : provisionBlobStore(runner, options.cwd, opts);
|
|
24879
|
+
}
|
|
24880
|
+
function provisionNeonForMode(runner, options) {
|
|
24881
|
+
const opts = { plan: options.plan, env: options.env };
|
|
24882
|
+
return options.interactive ? provisionNeonInteractive(runner, options.cwd, opts) : provisionNeon(runner, options.cwd, opts);
|
|
24216
24883
|
}
|
|
24217
24884
|
function authFailureMessage(reason) {
|
|
24218
24885
|
switch (reason) {
|
|
@@ -24228,6 +24895,24 @@ function authFailureMessage(reason) {
|
|
|
24228
24895
|
return "Could not sign in to Vercel.";
|
|
24229
24896
|
}
|
|
24230
24897
|
}
|
|
24898
|
+
function blobFailureMessage(reason) {
|
|
24899
|
+
switch (reason) {
|
|
24900
|
+
case "env-pull-empty":
|
|
24901
|
+
return "Created a Blob store, but no BLOB_READ_WRITE_TOKEN came back \u2014 falling back to manual entry.";
|
|
24902
|
+
case "timeout":
|
|
24903
|
+
return "Vercel Blob provisioning timed out \u2014 falling back to manual entry.";
|
|
24904
|
+
default:
|
|
24905
|
+
return "Vercel Blob provisioning did not complete \u2014 falling back to manual entry.";
|
|
24906
|
+
}
|
|
24907
|
+
}
|
|
24908
|
+
function deployFailureMessage(reason) {
|
|
24909
|
+
switch (reason) {
|
|
24910
|
+
case "timeout":
|
|
24911
|
+
return "Vercel deploy timed out.";
|
|
24912
|
+
default:
|
|
24913
|
+
return "Vercel deploy did not complete.";
|
|
24914
|
+
}
|
|
24915
|
+
}
|
|
24231
24916
|
function neonFailureMessage(reason) {
|
|
24232
24917
|
switch (reason) {
|
|
24233
24918
|
case "terms-required":
|
|
@@ -24244,11 +24929,11 @@ function neonFailureMessage(reason) {
|
|
|
24244
24929
|
}
|
|
24245
24930
|
|
|
24246
24931
|
// adapters/next/commands/init.ts
|
|
24247
|
-
import
|
|
24932
|
+
import pc7 from "picocolors";
|
|
24248
24933
|
|
|
24249
24934
|
// adapters/next/commands/seed.ts
|
|
24250
|
-
import
|
|
24251
|
-
import
|
|
24935
|
+
import fs38 from "fs";
|
|
24936
|
+
import path49 from "path";
|
|
24252
24937
|
import * as clack from "@clack/prompts";
|
|
24253
24938
|
function buildSeedScript(authBasePath = "/api/admin/auth") {
|
|
24254
24939
|
return `/**
|
|
@@ -24416,7 +25101,7 @@ main().catch((err) => {
|
|
|
24416
25101
|
`;
|
|
24417
25102
|
}
|
|
24418
25103
|
async function runSeedCommand(options) {
|
|
24419
|
-
const cwd = options.cwd ?
|
|
25104
|
+
const cwd = options.cwd ? path49.resolve(options.cwd) : process.cwd();
|
|
24420
25105
|
clack.intro("BetterStart Seed");
|
|
24421
25106
|
let config;
|
|
24422
25107
|
try {
|
|
@@ -24456,15 +25141,15 @@ async function runSeedCommand(options) {
|
|
|
24456
25141
|
clack.cancel("Cancelled.");
|
|
24457
25142
|
return;
|
|
24458
25143
|
}
|
|
24459
|
-
const scriptsDir =
|
|
24460
|
-
const seedPath =
|
|
24461
|
-
if (!
|
|
24462
|
-
|
|
25144
|
+
const scriptsDir = path49.join(cwd, adminDir, "scripts");
|
|
25145
|
+
const seedPath = path49.join(scriptsDir, "seed.ts");
|
|
25146
|
+
if (!fs38.existsSync(scriptsDir)) {
|
|
25147
|
+
fs38.mkdirSync(scriptsDir, { recursive: true });
|
|
24463
25148
|
}
|
|
24464
25149
|
const namespace = resolveAdminNamespace(config.frameworkConfig.next.namespace);
|
|
24465
|
-
|
|
25150
|
+
fs38.writeFileSync(seedPath, buildSeedScript(`${namespace.apiPath}/auth`), "utf-8");
|
|
24466
25151
|
const { execFile } = await import("child_process");
|
|
24467
|
-
const tsxBin =
|
|
25152
|
+
const tsxBin = path49.join(cwd, "node_modules", ".bin", "tsx");
|
|
24468
25153
|
const runSeed2 = (overwrite) => new Promise((resolve, reject) => {
|
|
24469
25154
|
execFile(
|
|
24470
25155
|
tsxBin,
|
|
@@ -24490,45 +25175,45 @@ async function runSeedCommand(options) {
|
|
|
24490
25175
|
}
|
|
24491
25176
|
);
|
|
24492
25177
|
});
|
|
24493
|
-
const
|
|
24494
|
-
|
|
25178
|
+
const seedSpinner = spinner2();
|
|
25179
|
+
seedSpinner.start("Creating admin user...");
|
|
24495
25180
|
try {
|
|
24496
25181
|
const result = await runSeed2(false);
|
|
24497
25182
|
if (result.code === 2) {
|
|
24498
25183
|
const existingName = result.stdout.split("\n").find((l) => l.startsWith("EXISTING_USER:"))?.replace("EXISTING_USER:", "")?.trim() || "unknown";
|
|
24499
|
-
|
|
25184
|
+
seedSpinner.stop(`Account already exists for ${email}`);
|
|
24500
25185
|
const overwrite = await clack.confirm({
|
|
24501
25186
|
message: `An admin account (${existingName}) already exists with this email. Replace it?`
|
|
24502
25187
|
});
|
|
24503
25188
|
if (clack.isCancel(overwrite) || !overwrite) {
|
|
24504
25189
|
clack.cancel("Seed cancelled.");
|
|
24505
25190
|
try {
|
|
24506
|
-
|
|
25191
|
+
fs38.unlinkSync(seedPath);
|
|
24507
25192
|
} catch {
|
|
24508
25193
|
}
|
|
24509
25194
|
return;
|
|
24510
25195
|
}
|
|
24511
|
-
|
|
25196
|
+
seedSpinner.start("Replacing admin user...");
|
|
24512
25197
|
await runSeed2(true);
|
|
24513
|
-
|
|
25198
|
+
seedSpinner.stop("Admin user replaced");
|
|
24514
25199
|
} else {
|
|
24515
|
-
|
|
25200
|
+
seedSpinner.stop("Admin user created");
|
|
24516
25201
|
}
|
|
24517
25202
|
} catch (err) {
|
|
24518
|
-
|
|
25203
|
+
seedSpinner.stop("Failed to create admin user");
|
|
24519
25204
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
24520
25205
|
clack.log.error(errMsg);
|
|
24521
25206
|
clack.log.info("You can run the seed script manually:");
|
|
24522
25207
|
clack.log.info(
|
|
24523
|
-
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${
|
|
25208
|
+
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${path49.relative(cwd, seedPath)}`
|
|
24524
25209
|
);
|
|
24525
25210
|
clack.outro("");
|
|
24526
25211
|
process.exit(1);
|
|
24527
25212
|
}
|
|
24528
25213
|
try {
|
|
24529
|
-
|
|
24530
|
-
if (
|
|
24531
|
-
|
|
25214
|
+
fs38.unlinkSync(seedPath);
|
|
25215
|
+
if (fs38.existsSync(scriptsDir) && fs38.readdirSync(scriptsDir).length === 0) {
|
|
25216
|
+
fs38.rmdirSync(scriptsDir);
|
|
24532
25217
|
}
|
|
24533
25218
|
} catch {
|
|
24534
25219
|
}
|
|
@@ -24549,6 +25234,18 @@ function readNamespaceFromConfigSource(source) {
|
|
|
24549
25234
|
const match = source.match(/\bnamespace\s*:\s*(['"`])([^'"`]+)\1/);
|
|
24550
25235
|
return normalizeNamespaceForRemoval(match?.[2]);
|
|
24551
25236
|
}
|
|
25237
|
+
function formatStorageProviderLabel(provider) {
|
|
25238
|
+
switch (provider) {
|
|
25239
|
+
case "vercel-blob":
|
|
25240
|
+
return "Vercel Blob";
|
|
25241
|
+
case "r2":
|
|
25242
|
+
return "Cloudflare R2";
|
|
25243
|
+
case "local":
|
|
25244
|
+
return "local filesystem";
|
|
25245
|
+
default:
|
|
25246
|
+
return provider;
|
|
25247
|
+
}
|
|
25248
|
+
}
|
|
24552
25249
|
function resolveNonInteractiveProject(name) {
|
|
24553
25250
|
const projectName = name?.trim() || "my-app";
|
|
24554
25251
|
if (projectName !== "." && !/^[a-z0-9_-]+$/i.test(projectName)) {
|
|
@@ -24557,11 +25254,11 @@ function resolveNonInteractiveProject(name) {
|
|
|
24557
25254
|
return { projectName, useSrcDir: false };
|
|
24558
25255
|
}
|
|
24559
25256
|
async function readExistingConfigNamespace(cwd) {
|
|
24560
|
-
const configPath =
|
|
24561
|
-
if (!
|
|
25257
|
+
const configPath = path50.resolve(cwd, "admin.config.ts");
|
|
25258
|
+
if (!fs39.existsSync(configPath)) {
|
|
24562
25259
|
return;
|
|
24563
25260
|
}
|
|
24564
|
-
const sourceNamespace = readNamespaceFromConfigSource(
|
|
25261
|
+
const sourceNamespace = readNamespaceFromConfigSource(fs39.readFileSync(configPath, "utf-8"));
|
|
24565
25262
|
if (sourceNamespace) {
|
|
24566
25263
|
return sourceNamespace;
|
|
24567
25264
|
}
|
|
@@ -24574,7 +25271,7 @@ async function readExistingConfigNamespace(cwd) {
|
|
|
24574
25271
|
}
|
|
24575
25272
|
async function resolveForceInitNamespaces(cwd, targetNamespace) {
|
|
24576
25273
|
const namespaces = /* @__PURE__ */ new Set([validateAdminNamespace(targetNamespace)]);
|
|
24577
|
-
const hasConfig =
|
|
25274
|
+
const hasConfig = fs39.existsSync(path50.resolve(cwd, "admin.config.ts"));
|
|
24578
25275
|
const existingNamespace = await readExistingConfigNamespace(cwd);
|
|
24579
25276
|
if (existingNamespace) {
|
|
24580
25277
|
namespaces.add(existingNamespace);
|
|
@@ -24599,39 +25296,51 @@ function removeExistingAdminPaths(cwd, namespaces) {
|
|
|
24599
25296
|
const nukeFiles = ["admin.config.ts", "ADMIN.md", "drizzle.config.ts"];
|
|
24600
25297
|
let removed = 0;
|
|
24601
25298
|
for (const dir of nukeDirs) {
|
|
24602
|
-
const fullPath =
|
|
24603
|
-
if (
|
|
24604
|
-
|
|
25299
|
+
const fullPath = path50.resolve(cwd, dir);
|
|
25300
|
+
if (fs39.existsSync(fullPath)) {
|
|
25301
|
+
fs39.rmSync(fullPath, { recursive: true, force: true });
|
|
24605
25302
|
removed++;
|
|
24606
25303
|
}
|
|
24607
25304
|
}
|
|
24608
25305
|
for (const file of nukeFiles) {
|
|
24609
|
-
const fullPath =
|
|
24610
|
-
if (
|
|
24611
|
-
|
|
25306
|
+
const fullPath = path50.resolve(cwd, file);
|
|
25307
|
+
if (fs39.existsSync(fullPath)) {
|
|
25308
|
+
fs39.unlinkSync(fullPath);
|
|
24612
25309
|
removed++;
|
|
24613
25310
|
}
|
|
24614
25311
|
}
|
|
24615
25312
|
return removed;
|
|
24616
25313
|
}
|
|
24617
25314
|
async function runInitCommand(name, options) {
|
|
24618
|
-
|
|
25315
|
+
p17.box(
|
|
25316
|
+
`
|
|
25317
|
+
\u2584 \u2597 \u2597 \u2584\u2596\u2597 \u2597
|
|
25318
|
+
\u2599\u2598\u2588\u258C\u259C\u2598\u259C\u2598\u2588\u258C\u259B\u2598\u259A \u259C\u2598\u2580\u258C\u259B\u2598\u259C\u2598
|
|
25319
|
+
\u2599\u2598\u2599\u2596\u2590\u2596\u2590\u2596\u2599\u2596\u258C \u2584\u258C\u2590\u2596\u2588\u258C\u258C \u2590\u2596
|
|
25320
|
+
`,
|
|
25321
|
+
"Setup your Next.js Dashboard",
|
|
25322
|
+
{
|
|
25323
|
+
rounded: true,
|
|
25324
|
+
contentAlign: "center",
|
|
25325
|
+
titleAlign: "center"
|
|
25326
|
+
}
|
|
25327
|
+
);
|
|
24619
25328
|
let cwd = process.cwd();
|
|
24620
|
-
let projectName =
|
|
25329
|
+
let projectName = path50.basename(cwd);
|
|
24621
25330
|
let forceMode = Boolean(options.force);
|
|
24622
25331
|
let namespace = DEFAULT_ADMIN_NAMESPACE;
|
|
24623
25332
|
if (options.namespace) {
|
|
24624
25333
|
try {
|
|
24625
25334
|
namespace = validateAdminNamespace(options.namespace);
|
|
24626
25335
|
} catch (error) {
|
|
24627
|
-
|
|
25336
|
+
p17.log.error(error instanceof Error ? error.message : String(error));
|
|
24628
25337
|
process.exit(1);
|
|
24629
25338
|
}
|
|
24630
25339
|
}
|
|
24631
25340
|
if (!options.yes && !options.namespace) {
|
|
24632
25341
|
const defaultDashboardPath = resolveAdminNamespace(DEFAULT_ADMIN_NAMESPACE).routePath;
|
|
24633
|
-
const namespaceInput = await
|
|
24634
|
-
message: "
|
|
25342
|
+
const namespaceInput = await p17.text({
|
|
25343
|
+
message: "Enter the dashboard path",
|
|
24635
25344
|
placeholder: `eg. ${defaultDashboardPath}`,
|
|
24636
25345
|
defaultValue: defaultDashboardPath,
|
|
24637
25346
|
validate(value) {
|
|
@@ -24643,8 +25352,8 @@ async function runInitCommand(name, options) {
|
|
|
24643
25352
|
}
|
|
24644
25353
|
}
|
|
24645
25354
|
});
|
|
24646
|
-
if (
|
|
24647
|
-
|
|
25355
|
+
if (p17.isCancel(namespaceInput)) {
|
|
25356
|
+
p17.cancel("Setup cancelled.");
|
|
24648
25357
|
process.exit(0);
|
|
24649
25358
|
}
|
|
24650
25359
|
namespace = validateAdminDashboardPath(namespaceInput);
|
|
@@ -24654,36 +25363,35 @@ async function runInitCommand(name, options) {
|
|
|
24654
25363
|
let isFreshProject = false;
|
|
24655
25364
|
let srcDir;
|
|
24656
25365
|
if (project2.isExisting) {
|
|
24657
|
-
p15.log.info(`Next.js app detected ${pc5.dim("\xB7")} ${pc5.cyan(pm)}`);
|
|
24658
25366
|
srcDir = project2.hasSrcDir;
|
|
24659
25367
|
if (!project2.hasTypeScript) {
|
|
24660
|
-
|
|
25368
|
+
p17.log.error("TypeScript is required. Please add a tsconfig.json first.");
|
|
24661
25369
|
process.exit(1);
|
|
24662
25370
|
}
|
|
24663
25371
|
if (forceMode) {
|
|
24664
25372
|
const nuked = removeExistingAdminPaths(cwd, await resolveForceInitNamespaces(cwd, namespace));
|
|
24665
25373
|
if (nuked > 0) {
|
|
24666
|
-
|
|
25374
|
+
p17.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
|
|
24667
25375
|
}
|
|
24668
25376
|
project2 = detectProject(cwd, namespace);
|
|
24669
25377
|
} else if (project2.conflicts.length > 0) {
|
|
24670
|
-
const conflictLines = project2.conflicts.map((c) => `${
|
|
25378
|
+
const conflictLines = project2.conflicts.map((c) => `${pc7.yellow("\u25B2")} ${c}`);
|
|
24671
25379
|
conflictLines.push(
|
|
24672
25380
|
"",
|
|
24673
|
-
|
|
25381
|
+
pc7.dim(`Use ${pc7.bold("--force")} to remove existing admin files before scaffolding.`)
|
|
24674
25382
|
);
|
|
24675
|
-
|
|
25383
|
+
p17.note(conflictLines.join("\n"), pc7.yellow("Conflicts"));
|
|
24676
25384
|
if (!options.yes) {
|
|
24677
|
-
const proceed = await
|
|
25385
|
+
const proceed = await p17.confirm({
|
|
24678
25386
|
message: [
|
|
24679
|
-
`Continue with ${
|
|
24680
|
-
`${
|
|
24681
|
-
|
|
25387
|
+
`Continue with ${pc7.bold(pc7.cyan("--force"))}?`,
|
|
25388
|
+
`${pc7.cyan("\u2502")} ${pc7.dim("This will force overwrite the existing admin code.")}`,
|
|
25389
|
+
pc7.cyan("\u2502")
|
|
24682
25390
|
].join("\n"),
|
|
24683
25391
|
initialValue: true
|
|
24684
25392
|
});
|
|
24685
|
-
if (
|
|
24686
|
-
|
|
25393
|
+
if (p17.isCancel(proceed) || !proceed) {
|
|
25394
|
+
p17.cancel("Setup cancelled.");
|
|
24687
25395
|
process.exit(0);
|
|
24688
25396
|
}
|
|
24689
25397
|
forceMode = true;
|
|
@@ -24692,23 +25400,23 @@ async function runInitCommand(name, options) {
|
|
|
24692
25400
|
await resolveForceInitNamespaces(cwd, namespace)
|
|
24693
25401
|
);
|
|
24694
25402
|
if (nuked > 0) {
|
|
24695
|
-
|
|
25403
|
+
p17.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
|
|
24696
25404
|
}
|
|
24697
25405
|
project2 = detectProject(cwd, namespace);
|
|
24698
25406
|
}
|
|
24699
25407
|
}
|
|
24700
25408
|
} else {
|
|
24701
|
-
|
|
25409
|
+
p17.log.info("No Next.js app found \u2014 Running the fresh project mode...");
|
|
24702
25410
|
let projectPrompt;
|
|
24703
25411
|
try {
|
|
24704
25412
|
projectPrompt = options.yes ? resolveNonInteractiveProject(name) : await promptProject(name);
|
|
24705
25413
|
} catch (error) {
|
|
24706
|
-
|
|
25414
|
+
p17.log.error(error instanceof Error ? error.message : String(error));
|
|
24707
25415
|
process.exit(1);
|
|
24708
25416
|
}
|
|
24709
25417
|
srcDir = projectPrompt.useSrcDir;
|
|
24710
25418
|
if (!options.yes) {
|
|
24711
|
-
const pmChoice = await
|
|
25419
|
+
const pmChoice = await p17.select({
|
|
24712
25420
|
message: "Which package manager do you want to use?",
|
|
24713
25421
|
options: [
|
|
24714
25422
|
{ value: "pnpm", label: "pnpm", hint: "recommended" },
|
|
@@ -24717,13 +25425,13 @@ async function runInitCommand(name, options) {
|
|
|
24717
25425
|
{ value: "bun", label: "bun" }
|
|
24718
25426
|
]
|
|
24719
25427
|
});
|
|
24720
|
-
if (
|
|
24721
|
-
|
|
25428
|
+
if (p17.isCancel(pmChoice)) {
|
|
25429
|
+
p17.cancel("Setup cancelled.");
|
|
24722
25430
|
process.exit(0);
|
|
24723
25431
|
}
|
|
24724
25432
|
pm = pmChoice;
|
|
24725
25433
|
}
|
|
24726
|
-
const displayName = projectPrompt.projectName === "." ?
|
|
25434
|
+
const displayName = projectPrompt.projectName === "." ? path50.basename(cwd) : projectPrompt.projectName;
|
|
24727
25435
|
projectName = displayName;
|
|
24728
25436
|
const { bin, prefix } = createNextAppCommand(pm);
|
|
24729
25437
|
const cnaArgs = [
|
|
@@ -24740,7 +25448,7 @@ async function runInitCommand(name, options) {
|
|
|
24740
25448
|
];
|
|
24741
25449
|
if (srcDir) cnaArgs.push("--src-dir");
|
|
24742
25450
|
else cnaArgs.push("--no-src-dir");
|
|
24743
|
-
const createNextAppSpinner =
|
|
25451
|
+
const createNextAppSpinner = spinner2();
|
|
24744
25452
|
createNextAppSpinner.start(`Creating a Next.js app (latest)`);
|
|
24745
25453
|
const createNextAppResult = await runQuietCommand(bin, cnaArgs, {
|
|
24746
25454
|
cwd,
|
|
@@ -24752,79 +25460,45 @@ async function runInitCommand(name, options) {
|
|
|
24752
25460
|
process.stderr.write(`${createNextAppResult.output.trimEnd()}
|
|
24753
25461
|
`);
|
|
24754
25462
|
}
|
|
24755
|
-
|
|
24756
|
-
|
|
25463
|
+
p17.log.error(createNextAppResult.error);
|
|
25464
|
+
p17.log.info(
|
|
24757
25465
|
`You can create the project manually:
|
|
24758
|
-
${
|
|
24759
|
-
Then run ${
|
|
25466
|
+
${pc7.cyan(`npx create-next-app@latest ${projectPrompt.projectName} --typescript --tailwind --app`)}
|
|
25467
|
+
Then run ${pc7.cyan("betterstart init")} inside it.`
|
|
24760
25468
|
);
|
|
24761
25469
|
process.exit(1);
|
|
24762
25470
|
}
|
|
24763
|
-
cwd =
|
|
24764
|
-
const hasPackageJson =
|
|
25471
|
+
cwd = path50.resolve(cwd, projectPrompt.projectName);
|
|
25472
|
+
const hasPackageJson = fs39.existsSync(path50.join(cwd, "package.json"));
|
|
24765
25473
|
const hasNextConfig = ["next.config.ts", "next.config.js", "next.config.mjs"].some(
|
|
24766
|
-
(f) =>
|
|
25474
|
+
(f) => fs39.existsSync(path50.join(cwd, f))
|
|
24767
25475
|
);
|
|
24768
25476
|
if (!hasPackageJson || !hasNextConfig) {
|
|
24769
25477
|
createNextAppSpinner.stop(`Failed to create Next.js app: ${displayName}`);
|
|
24770
|
-
|
|
25478
|
+
p17.log.error(
|
|
24771
25479
|
"create-next-app completed but the project was not created. This can happen with nested npx calls."
|
|
24772
25480
|
);
|
|
24773
25481
|
const manualCmd = `npx create-next-app@latest ${projectPrompt.projectName} --typescript --tailwind --app`;
|
|
24774
|
-
|
|
25482
|
+
p17.log.info(
|
|
24775
25483
|
`Create the project manually:
|
|
24776
|
-
${
|
|
24777
|
-
Then run ${
|
|
25484
|
+
${pc7.cyan(manualCmd)}
|
|
25485
|
+
Then run ${pc7.cyan("betterstart init")} inside it.`
|
|
24778
25486
|
);
|
|
24779
25487
|
process.exit(1);
|
|
24780
25488
|
}
|
|
24781
25489
|
const installedNextVersion = detectInstalledNextVersion(cwd);
|
|
24782
|
-
const nextVersionSuffix = installedNextVersion ? `${
|
|
25490
|
+
const nextVersionSuffix = installedNextVersion ? `${pc7.cyan(`v${installedNextVersion}`)}` : "";
|
|
24783
25491
|
createNextAppSpinner.stop(`Created a Next.js ${nextVersionSuffix} app in ${displayName}`);
|
|
24784
25492
|
project2 = detectProject(cwd, namespace);
|
|
24785
25493
|
isFreshProject = true;
|
|
24786
25494
|
}
|
|
24787
|
-
const selectedPlugins = (() => {
|
|
24788
|
-
if (options.plugins) {
|
|
24789
|
-
return parsePluginList(options.plugins);
|
|
24790
|
-
}
|
|
24791
|
-
return [];
|
|
24792
|
-
})();
|
|
24793
|
-
const selectedIntegrations = (() => {
|
|
24794
|
-
if (options.integrations) {
|
|
24795
|
-
return parseIntegrationList(options.integrations);
|
|
24796
|
-
}
|
|
24797
|
-
return [];
|
|
24798
|
-
})();
|
|
24799
|
-
let collectedIntegrationConfig = {
|
|
24800
|
-
sections: [],
|
|
24801
|
-
overwriteKeys: /* @__PURE__ */ new Set()
|
|
24802
|
-
};
|
|
24803
|
-
let pluginSelection;
|
|
24804
|
-
if (selectedPlugins.length > 0 || selectedIntegrations.length > 0) {
|
|
24805
|
-
pluginSelection = {
|
|
24806
|
-
plugins: selectedPlugins,
|
|
24807
|
-
integrations: selectedIntegrations,
|
|
24808
|
-
storage: selectedIntegrations.includes("r2") ? "r2" : "local"
|
|
24809
|
-
};
|
|
24810
|
-
} else if (options.yes) {
|
|
24811
|
-
pluginSelection = { plugins: [], integrations: [], storage: "local" };
|
|
24812
|
-
} else {
|
|
24813
|
-
const promptResult = await promptPlugins(cwd);
|
|
24814
|
-
collectedIntegrationConfig = promptResult.integrationConfig;
|
|
24815
|
-
pluginSelection = {
|
|
24816
|
-
plugins: promptResult.plugins,
|
|
24817
|
-
integrations: promptResult.integrations,
|
|
24818
|
-
storage: promptResult.storage
|
|
24819
|
-
};
|
|
24820
|
-
}
|
|
24821
25495
|
let databaseUrl;
|
|
24822
25496
|
const existingDbUrl = readExistingDbUrl(cwd);
|
|
24823
25497
|
if (options.yes) {
|
|
24824
25498
|
if (options.databaseUrl) {
|
|
24825
25499
|
if (!isValidDbUrl(options.databaseUrl)) {
|
|
24826
|
-
|
|
24827
|
-
`Invalid database URL. Must start with ${
|
|
25500
|
+
p17.log.error(
|
|
25501
|
+
`Invalid database URL. Must start with ${pc7.cyan("postgres://")} or ${pc7.cyan("postgresql://")}`
|
|
24828
25502
|
);
|
|
24829
25503
|
process.exit(1);
|
|
24830
25504
|
}
|
|
@@ -24841,17 +25515,19 @@ async function runInitCommand(name, options) {
|
|
|
24841
25515
|
});
|
|
24842
25516
|
if (flow.ok && flow.databaseUrl) {
|
|
24843
25517
|
databaseUrl = flow.databaseUrl;
|
|
25518
|
+
persistDatabaseUrl(cwd, databaseUrl);
|
|
25519
|
+
p17.log.success(`Saved DATABASE_URL to ${pc7.cyan(".env.local")}`);
|
|
24844
25520
|
} else {
|
|
24845
|
-
|
|
25521
|
+
p17.log.warn("Vercel provisioning did not complete; continuing without a database URL.");
|
|
24846
25522
|
}
|
|
24847
25523
|
}
|
|
24848
25524
|
} else if (existingDbUrl) {
|
|
24849
25525
|
const masked = maskDbUrl(existingDbUrl);
|
|
24850
|
-
|
|
25526
|
+
p17.log.info(`Using existing database URL from .env.local ${pc7.dim(`(${masked})`)}`);
|
|
24851
25527
|
databaseUrl = existingDbUrl;
|
|
24852
25528
|
} else {
|
|
24853
|
-
const
|
|
24854
|
-
if (
|
|
25529
|
+
const servicesResult = await promptServices({ allowVercel: options.vercel !== false });
|
|
25530
|
+
if (servicesResult.provider === "vercel-cli") {
|
|
24855
25531
|
const flow = await runVercelNeonFlow({
|
|
24856
25532
|
cwd,
|
|
24857
25533
|
projectName,
|
|
@@ -24861,14 +25537,77 @@ async function runInitCommand(name, options) {
|
|
|
24861
25537
|
});
|
|
24862
25538
|
if (flow.ok && flow.databaseUrl) {
|
|
24863
25539
|
databaseUrl = flow.databaseUrl;
|
|
25540
|
+
persistDatabaseUrl(cwd, databaseUrl);
|
|
25541
|
+
p17.log.success(`Saved DATABASE_URL to ${pc7.cyan(".env.local")}`);
|
|
24864
25542
|
} else {
|
|
24865
|
-
|
|
25543
|
+
p17.log.info("Falling back to a manual database connection string.");
|
|
24866
25544
|
openBrowserVercelNeon();
|
|
24867
25545
|
databaseUrl = await promptConnectionString();
|
|
24868
25546
|
}
|
|
24869
25547
|
} else {
|
|
24870
|
-
databaseUrl =
|
|
25548
|
+
databaseUrl = servicesResult.url;
|
|
25549
|
+
}
|
|
25550
|
+
}
|
|
25551
|
+
const selectedPlugins = (() => {
|
|
25552
|
+
if (options.plugins) {
|
|
25553
|
+
return parsePluginList(options.plugins);
|
|
25554
|
+
}
|
|
25555
|
+
return [];
|
|
25556
|
+
})();
|
|
25557
|
+
const selectedIntegrations = (() => {
|
|
25558
|
+
if (options.integrations) {
|
|
25559
|
+
return parseIntegrationList(options.integrations);
|
|
25560
|
+
}
|
|
25561
|
+
return [];
|
|
25562
|
+
})();
|
|
25563
|
+
let collectedIntegrationConfig = {
|
|
25564
|
+
sections: [],
|
|
25565
|
+
overwriteKeys: /* @__PURE__ */ new Set()
|
|
25566
|
+
};
|
|
25567
|
+
let pluginSelection;
|
|
25568
|
+
const vercelBlobAllowed = options.vercel !== false;
|
|
25569
|
+
if (selectedPlugins.length > 0 || selectedIntegrations.length > 0) {
|
|
25570
|
+
pluginSelection = {
|
|
25571
|
+
plugins: selectedPlugins,
|
|
25572
|
+
integrations: selectedIntegrations,
|
|
25573
|
+
storage: selectedIntegrations.includes("vercel-blob") ? "vercel-blob" : selectedIntegrations.includes("r2") ? "r2" : "local"
|
|
25574
|
+
};
|
|
25575
|
+
if (pluginSelection.storage === "vercel-blob" && !readEnvVar(cwd, "BLOB_READ_WRITE_TOKEN")?.trim() && vercelBlobAllowed && (!options.yes || process.env.VERCEL_TOKEN)) {
|
|
25576
|
+
const flow = await runVercelBlobFlow({
|
|
25577
|
+
cwd,
|
|
25578
|
+
projectName,
|
|
25579
|
+
interactive: !options.yes,
|
|
25580
|
+
env: process.env
|
|
25581
|
+
});
|
|
25582
|
+
if (flow.ok && flow.token) {
|
|
25583
|
+
collectedIntegrationConfig.sections.push({
|
|
25584
|
+
header: "Storage (Vercel Blob)",
|
|
25585
|
+
vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: flow.token }]
|
|
25586
|
+
});
|
|
25587
|
+
collectedIntegrationConfig.overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
|
|
25588
|
+
} else if (!options.yes) {
|
|
25589
|
+
p17.log.info("Falling back to a manual Vercel Blob token.");
|
|
25590
|
+
const collected = await collectIntegrationConfig(cwd, ["vercel-blob"]);
|
|
25591
|
+
collectedIntegrationConfig.sections.push(...collected.sections);
|
|
25592
|
+
for (const key of collected.overwriteKeys) {
|
|
25593
|
+
collectedIntegrationConfig.overwriteKeys.add(key);
|
|
25594
|
+
}
|
|
25595
|
+
} else {
|
|
25596
|
+
p17.log.warn("Vercel Blob provisioning did not complete; continuing without a Blob token.");
|
|
25597
|
+
}
|
|
24871
25598
|
}
|
|
25599
|
+
} else if (options.yes) {
|
|
25600
|
+
pluginSelection = { plugins: [], integrations: [], storage: "local" };
|
|
25601
|
+
} else {
|
|
25602
|
+
const promptResult = await promptPlugins(cwd, {
|
|
25603
|
+
provisionVercelBlob: vercelBlobAllowed ? () => runVercelBlobFlow({ cwd, projectName, interactive: true, env: process.env }) : void 0
|
|
25604
|
+
});
|
|
25605
|
+
collectedIntegrationConfig = promptResult.integrationConfig;
|
|
25606
|
+
pluginSelection = {
|
|
25607
|
+
plugins: promptResult.plugins,
|
|
25608
|
+
integrations: promptResult.integrations,
|
|
25609
|
+
storage: promptResult.storage
|
|
25610
|
+
};
|
|
24872
25611
|
}
|
|
24873
25612
|
const nextMajorVersion = detectNextMajorVersion(cwd);
|
|
24874
25613
|
const config = {
|
|
@@ -24879,7 +25618,7 @@ async function runInitCommand(name, options) {
|
|
|
24879
25618
|
config.paths = config.frameworkConfig.next.paths;
|
|
24880
25619
|
config.database.migrationsDir = deriveMigrationsDir(namespace);
|
|
24881
25620
|
const results = [];
|
|
24882
|
-
const s =
|
|
25621
|
+
const s = spinner2();
|
|
24883
25622
|
s.start("Directory structure");
|
|
24884
25623
|
const baseFiles = scaffoldBase({
|
|
24885
25624
|
cwd,
|
|
@@ -24952,34 +25691,34 @@ async function runInitCommand(name, options) {
|
|
|
24952
25691
|
const maxLabel = Math.max(...results.map((r) => r.label.length));
|
|
24953
25692
|
const noteLines = results.map((r) => {
|
|
24954
25693
|
const padded = r.label.padEnd(maxLabel + 3);
|
|
24955
|
-
return `${
|
|
25694
|
+
return `${pc7.green("\u2713")} ${padded}${pc7.dim(r.result)}`;
|
|
24956
25695
|
});
|
|
24957
25696
|
s.stop("");
|
|
24958
25697
|
process.stdout.write("\x1B[2A\x1B[J");
|
|
24959
|
-
|
|
24960
|
-
const drizzleConfigPath =
|
|
24961
|
-
if (!dbFiles.includes("drizzle.config.ts") &&
|
|
25698
|
+
p17.note(noteLines.join("\n"), "Scaffolded admin");
|
|
25699
|
+
const drizzleConfigPath = path50.join(cwd, "drizzle.config.ts");
|
|
25700
|
+
if (!dbFiles.includes("drizzle.config.ts") && fs39.existsSync(drizzleConfigPath)) {
|
|
24962
25701
|
if (forceMode) {
|
|
24963
25702
|
const { readNamespacedTemplate } = await import("./template-reader-X6KFP7B3.js");
|
|
24964
|
-
|
|
25703
|
+
fs39.writeFileSync(
|
|
24965
25704
|
drizzleConfigPath,
|
|
24966
25705
|
readNamespacedTemplate("drizzle.config.ts", namespace),
|
|
24967
25706
|
"utf-8"
|
|
24968
25707
|
);
|
|
24969
|
-
|
|
25708
|
+
p17.log.success("Updated drizzle.config.ts");
|
|
24970
25709
|
} else if (!options.yes) {
|
|
24971
|
-
const overwrite = await
|
|
25710
|
+
const overwrite = await p17.confirm({
|
|
24972
25711
|
message: "drizzle.config.ts already exists. Overwrite with latest version?",
|
|
24973
25712
|
initialValue: true
|
|
24974
25713
|
});
|
|
24975
|
-
if (!
|
|
25714
|
+
if (!p17.isCancel(overwrite) && overwrite) {
|
|
24976
25715
|
const { readNamespacedTemplate } = await import("./template-reader-X6KFP7B3.js");
|
|
24977
|
-
|
|
25716
|
+
fs39.writeFileSync(
|
|
24978
25717
|
drizzleConfigPath,
|
|
24979
25718
|
readNamespacedTemplate("drizzle.config.ts", namespace),
|
|
24980
25719
|
"utf-8"
|
|
24981
25720
|
);
|
|
24982
|
-
|
|
25721
|
+
p17.log.success("Updated drizzle.config.ts");
|
|
24983
25722
|
}
|
|
24984
25723
|
}
|
|
24985
25724
|
}
|
|
@@ -25005,11 +25744,11 @@ async function runInitCommand(name, options) {
|
|
|
25005
25744
|
depsInstalled = true;
|
|
25006
25745
|
} else {
|
|
25007
25746
|
s.stop("Failed to install dependencies");
|
|
25008
|
-
|
|
25009
|
-
|
|
25747
|
+
p17.log.warning(depsResult.error ?? "Unknown error");
|
|
25748
|
+
p17.log.info(
|
|
25010
25749
|
`You can install them manually:
|
|
25011
|
-
${
|
|
25012
|
-
${
|
|
25750
|
+
${pc7.cyan(`${pm} add ${depsResult.dependencies.join(" ")}`)}
|
|
25751
|
+
${pc7.cyan(`${pm} add -D ${depsResult.devDeps.join(" ")}`)}`
|
|
25013
25752
|
);
|
|
25014
25753
|
}
|
|
25015
25754
|
if (depsInstalled) {
|
|
@@ -25059,46 +25798,46 @@ async function runInitCommand(name, options) {
|
|
|
25059
25798
|
process.stdout.write("\x1B[2A\x1B[J");
|
|
25060
25799
|
}
|
|
25061
25800
|
writeConfigFile(cwd, resolvedIntegrationInstallResult.config);
|
|
25062
|
-
const usesLocalStorage = resolvedIntegrationInstallResult.config.storage.provider
|
|
25801
|
+
const usesLocalStorage = resolvedIntegrationInstallResult.config.storage.provider === "local";
|
|
25063
25802
|
const installLines = [];
|
|
25064
25803
|
if (depsInstalled) {
|
|
25065
25804
|
installLines.push(
|
|
25066
|
-
`${
|
|
25805
|
+
`${pc7.green("\u2713")} Dependencies ${pc7.dim(`${depsResult.dependencies.length} deps + ${depsResult.devDeps.length} dev deps`)}`
|
|
25067
25806
|
);
|
|
25068
25807
|
}
|
|
25069
25808
|
if (coreSchemasResult.errors.length > 0) {
|
|
25070
25809
|
installLines.push(
|
|
25071
|
-
`${
|
|
25810
|
+
`${pc7.yellow("\u25B2")} Core schemas ${pc7.dim(`${coreSchemasResult.errors.length} warning(s)`)}`
|
|
25072
25811
|
);
|
|
25073
25812
|
for (const err of coreSchemasResult.errors) {
|
|
25074
|
-
installLines.push(` ${
|
|
25813
|
+
installLines.push(` ${pc7.dim(err)}`);
|
|
25075
25814
|
}
|
|
25076
25815
|
} else {
|
|
25077
25816
|
installLines.push(
|
|
25078
|
-
`${
|
|
25817
|
+
`${pc7.green("\u2713")} Core schemas ${pc7.dim(`${coreSchemasResult.schemas.length} schemas, ${coreSchemasResult.generatedFiles.length} files`)}`
|
|
25079
25818
|
);
|
|
25080
25819
|
}
|
|
25081
25820
|
installLines.push(
|
|
25082
|
-
`${
|
|
25821
|
+
`${pc7.green("\u2713")} Plugins ${pc7.dim(resolvedPluginInstallResult.installed.length > 0 ? resolvedPluginInstallResult.installed.join(", ") : "core only")}`
|
|
25083
25822
|
);
|
|
25084
25823
|
for (const warning of resolvedPluginInstallResult.warnings) {
|
|
25085
|
-
installLines.push(` ${
|
|
25824
|
+
installLines.push(` ${pc7.yellow("\u25B2")} ${warning}`);
|
|
25086
25825
|
}
|
|
25087
25826
|
installLines.push(
|
|
25088
|
-
`${
|
|
25827
|
+
`${pc7.green("\u2713")} Integrations ${pc7.dim(resolvedIntegrationInstallResult.installed.length > 0 ? resolvedIntegrationInstallResult.installed.join(", ") : "none")}`
|
|
25089
25828
|
);
|
|
25090
25829
|
for (const warning of resolvedIntegrationInstallResult.warnings) {
|
|
25091
|
-
installLines.push(` ${
|
|
25830
|
+
installLines.push(` ${pc7.yellow("\u25B2")} ${warning}`);
|
|
25092
25831
|
}
|
|
25093
25832
|
if (usesLocalStorage) {
|
|
25094
25833
|
installLines.push(
|
|
25095
|
-
` ${
|
|
25834
|
+
` ${pc7.yellow("\u25B2")} Local filesystem storage is only for development or self-hosted persistent-disk deployments. Use Vercel Blob or Cloudflare R2 for hosted/serverless production.`
|
|
25096
25835
|
);
|
|
25097
25836
|
}
|
|
25098
|
-
|
|
25837
|
+
p17.note(installLines.join("\n"), "Installed");
|
|
25099
25838
|
let dbPushed = false;
|
|
25100
25839
|
if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
|
|
25101
|
-
|
|
25840
|
+
p17.log.info(`Skipping database schema push ${pc7.dim("(--skip-migration)")}`);
|
|
25102
25841
|
} else if (depsResult.success && hasDbUrl(cwd)) {
|
|
25103
25842
|
let driverReady = hasDrizzleKitPostgresDriverDependency(cwd);
|
|
25104
25843
|
if (!driverReady) {
|
|
@@ -25113,19 +25852,19 @@ async function runInitCommand(name, options) {
|
|
|
25113
25852
|
});
|
|
25114
25853
|
if (!driverResult.success) {
|
|
25115
25854
|
s.stop("Database push failed");
|
|
25116
|
-
|
|
25117
|
-
|
|
25118
|
-
`Install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP} manually, then run: ${
|
|
25855
|
+
p17.log.warning(driverResult.error ?? `Failed to install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP}`);
|
|
25856
|
+
p17.log.info(
|
|
25857
|
+
`Install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP} manually, then run: ${pc7.cyan(drizzlePushCommand(pm))}`
|
|
25119
25858
|
);
|
|
25120
25859
|
} else {
|
|
25121
25860
|
driverReady = hasDrizzleKitPostgresDriverDependency(cwd);
|
|
25122
|
-
s.stop(`${
|
|
25861
|
+
s.stop(`${pc7.green("\u2713")} Drizzle Kit Postgres driver dependency installed`);
|
|
25123
25862
|
}
|
|
25124
25863
|
}
|
|
25125
25864
|
if (driverReady) {
|
|
25126
25865
|
const useTerminalForDbPush = shouldUseTerminalForDbPush(options);
|
|
25127
25866
|
if (useTerminalForDbPush) {
|
|
25128
|
-
|
|
25867
|
+
p17.log.info(`Running ${pc7.cyan("drizzle-kit push --force")}`);
|
|
25129
25868
|
} else {
|
|
25130
25869
|
s.start("Pushing database schema (drizzle-kit push)");
|
|
25131
25870
|
}
|
|
@@ -25134,15 +25873,15 @@ async function runInitCommand(name, options) {
|
|
|
25134
25873
|
if (useTerminalForDbPush) {
|
|
25135
25874
|
const verification = await verifyDatabaseReachable(cwd);
|
|
25136
25875
|
if (!verification.success) {
|
|
25137
|
-
|
|
25138
|
-
|
|
25876
|
+
p17.log.warning(verification.error);
|
|
25877
|
+
p17.log.error("Database was not reachable. Aborting setup.");
|
|
25139
25878
|
process.exit(1);
|
|
25140
25879
|
}
|
|
25141
25880
|
}
|
|
25142
25881
|
if (useTerminalForDbPush) {
|
|
25143
|
-
|
|
25882
|
+
p17.log.success("Database schema pushed");
|
|
25144
25883
|
} else {
|
|
25145
|
-
s.stop(`${
|
|
25884
|
+
s.stop(`${pc7.green("\u2713")} Database schema pushed`);
|
|
25146
25885
|
}
|
|
25147
25886
|
dbPushed = true;
|
|
25148
25887
|
} else {
|
|
@@ -25150,12 +25889,12 @@ async function runInitCommand(name, options) {
|
|
|
25150
25889
|
s.stop("Database push failed");
|
|
25151
25890
|
}
|
|
25152
25891
|
const pushError = pushResult.error ?? "Unknown error";
|
|
25153
|
-
|
|
25892
|
+
p17.log.warning(pushError);
|
|
25154
25893
|
if (isDatabaseReachabilityError(pushError)) {
|
|
25155
|
-
|
|
25894
|
+
p17.log.error("Database was not reachable. Aborting setup.");
|
|
25156
25895
|
process.exit(1);
|
|
25157
25896
|
}
|
|
25158
|
-
|
|
25897
|
+
p17.log.info(`You can run it manually: ${pc7.cyan(drizzlePushCommand(pm))}`);
|
|
25159
25898
|
}
|
|
25160
25899
|
}
|
|
25161
25900
|
}
|
|
@@ -25164,8 +25903,8 @@ async function runInitCommand(name, options) {
|
|
|
25164
25903
|
let seedSuccess = false;
|
|
25165
25904
|
let adminAccountReady = false;
|
|
25166
25905
|
if (dbPushed && options.skipAdminCreation) {
|
|
25167
|
-
|
|
25168
|
-
`Skipping admin user creation ${
|
|
25906
|
+
p17.log.info(
|
|
25907
|
+
`Skipping admin user creation ${pc7.dim(`(use ${betterstartExecCommand(pm, "seed")} later)`)}`
|
|
25169
25908
|
);
|
|
25170
25909
|
}
|
|
25171
25910
|
if (dbPushed && !options.yes && !options.skipAdminCreation) {
|
|
@@ -25174,14 +25913,14 @@ async function runInitCommand(name, options) {
|
|
|
25174
25913
|
let replaceExistingAdmin = false;
|
|
25175
25914
|
const adminCheck = await checkExistingAdmin(cwd, adminDir, authBasePath);
|
|
25176
25915
|
if (adminCheck.error) {
|
|
25177
|
-
|
|
25916
|
+
p17.log.warning(`Could not verify existing admin account ${pc7.dim(`(${adminCheck.error})`)}`);
|
|
25178
25917
|
if (isDatabaseReachabilityError(adminCheck.error)) {
|
|
25179
|
-
|
|
25918
|
+
p17.log.error("Database was not reachable. Aborting setup.");
|
|
25180
25919
|
process.exit(1);
|
|
25181
25920
|
}
|
|
25182
25921
|
} else if (adminCheck.existingAdmin) {
|
|
25183
25922
|
const existingAdminLabel = formatAdminIdentity(adminCheck.existingAdmin);
|
|
25184
|
-
const adminAction = await
|
|
25923
|
+
const adminAction = await p17.select({
|
|
25185
25924
|
message: "Found an already existing admin account. Do you want to replace it or skip?",
|
|
25186
25925
|
options: [
|
|
25187
25926
|
{
|
|
@@ -25195,34 +25934,34 @@ async function runInitCommand(name, options) {
|
|
|
25195
25934
|
}
|
|
25196
25935
|
]
|
|
25197
25936
|
});
|
|
25198
|
-
if (
|
|
25199
|
-
|
|
25937
|
+
if (p17.isCancel(adminAction)) {
|
|
25938
|
+
p17.cancel("Setup cancelled.");
|
|
25200
25939
|
process.exit(0);
|
|
25201
25940
|
}
|
|
25202
25941
|
if (adminAction === "skip") {
|
|
25203
25942
|
adminAccountReady = true;
|
|
25204
|
-
|
|
25943
|
+
p17.log.info(`Keeping existing admin account ${pc7.dim(`(${existingAdminLabel})`)}`);
|
|
25205
25944
|
} else {
|
|
25206
25945
|
replaceExistingAdmin = true;
|
|
25207
25946
|
}
|
|
25208
25947
|
}
|
|
25209
25948
|
if (!adminAccountReady) {
|
|
25210
|
-
|
|
25211
|
-
|
|
25212
|
-
replaceExistingAdmin ? "Replace the existing admin account to access the admin." : "Create your first admin user to access the
|
|
25949
|
+
p17.note(
|
|
25950
|
+
pc7.dim(
|
|
25951
|
+
replaceExistingAdmin ? "Replace the existing admin account to access the admin." : "Create your first admin user to access the dashboard."
|
|
25213
25952
|
),
|
|
25214
25953
|
"Admin account"
|
|
25215
25954
|
);
|
|
25216
|
-
const credentials = await
|
|
25955
|
+
const credentials = await p17.group(
|
|
25217
25956
|
{
|
|
25218
|
-
email: () =>
|
|
25957
|
+
email: () => p17.text({
|
|
25219
25958
|
message: "Admin email",
|
|
25220
25959
|
placeholder: "admin@example.com",
|
|
25221
25960
|
validate: (v) => {
|
|
25222
25961
|
if (!v || !v.includes("@")) return "Please enter a valid email";
|
|
25223
25962
|
}
|
|
25224
25963
|
}),
|
|
25225
|
-
password: () =>
|
|
25964
|
+
password: () => p17.password({
|
|
25226
25965
|
message: "Admin password",
|
|
25227
25966
|
validate: (v) => {
|
|
25228
25967
|
if (!v || v.length < 8) return "Password must be at least 8 characters";
|
|
@@ -25231,7 +25970,7 @@ async function runInitCommand(name, options) {
|
|
|
25231
25970
|
},
|
|
25232
25971
|
{
|
|
25233
25972
|
onCancel: () => {
|
|
25234
|
-
|
|
25973
|
+
p17.cancel("Setup cancelled.");
|
|
25235
25974
|
process.exit(0);
|
|
25236
25975
|
}
|
|
25237
25976
|
}
|
|
@@ -25249,12 +25988,12 @@ async function runInitCommand(name, options) {
|
|
|
25249
25988
|
seedOverwriteMode
|
|
25250
25989
|
);
|
|
25251
25990
|
if (seedResult.existingUser) {
|
|
25252
|
-
s.stop(`${
|
|
25253
|
-
const replace = await
|
|
25991
|
+
s.stop(`${pc7.yellow("\u25B2")} An account already exists for ${credentials.email}`);
|
|
25992
|
+
const replace = await p17.confirm({
|
|
25254
25993
|
message: "Replace the existing account with this email?",
|
|
25255
25994
|
initialValue: false
|
|
25256
25995
|
});
|
|
25257
|
-
if (!
|
|
25996
|
+
if (!p17.isCancel(replace) && replace) {
|
|
25258
25997
|
seedOverwriteMode = "email";
|
|
25259
25998
|
s.start("Replacing admin user");
|
|
25260
25999
|
seedResult = await runSeed(
|
|
@@ -25269,20 +26008,20 @@ async function runInitCommand(name, options) {
|
|
|
25269
26008
|
}
|
|
25270
26009
|
if (seedResult.success) {
|
|
25271
26010
|
s.stop(
|
|
25272
|
-
seedOverwriteMode === "admin" ? `${
|
|
26011
|
+
seedOverwriteMode === "admin" ? `${pc7.green("\u2713")} Admin user replaced` : `${pc7.green("\u2713")} Admin user created`
|
|
25273
26012
|
);
|
|
25274
26013
|
seedSuccess = true;
|
|
25275
26014
|
adminAccountReady = true;
|
|
25276
26015
|
} else if (seedResult.error) {
|
|
25277
|
-
s.stop(`${
|
|
25278
|
-
|
|
25279
|
-
`${
|
|
26016
|
+
s.stop(`${pc7.red("\u2717")} Failed to create admin user`);
|
|
26017
|
+
p17.note(
|
|
26018
|
+
`${pc7.red(seedResult.error)}
|
|
25280
26019
|
|
|
25281
|
-
Run manually: ${
|
|
25282
|
-
|
|
26020
|
+
Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
26021
|
+
pc7.red("Seed failed")
|
|
25283
26022
|
);
|
|
25284
26023
|
if (isDatabaseReachabilityError(seedResult.error)) {
|
|
25285
|
-
|
|
26024
|
+
p17.log.error("Database was not reachable. Aborting setup.");
|
|
25286
26025
|
process.exit(1);
|
|
25287
26026
|
}
|
|
25288
26027
|
}
|
|
@@ -25308,43 +26047,37 @@ Run manually: ${pc5.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
25308
26047
|
const installedPluginList = resolvedPluginInstallResult.installed.length > 0 ? resolvedPluginInstallResult.installed.join(", ") : "core only";
|
|
25309
26048
|
const installedIntegrationList = resolvedIntegrationInstallResult.installed.length > 0 ? resolvedIntegrationInstallResult.installed.join(", ") : "none";
|
|
25310
26049
|
const summaryLines = [
|
|
25311
|
-
`Plugins: ${
|
|
25312
|
-
`Integrations: ${
|
|
25313
|
-
`Storage: ${
|
|
25314
|
-
`Files created: ${
|
|
26050
|
+
`Plugins: ${pc7.cyan(installedPluginList)}`,
|
|
26051
|
+
`Integrations: ${pc7.cyan(installedIntegrationList)}`,
|
|
26052
|
+
`Storage: ${pc7.cyan(formatStorageProviderLabel(resolvedIntegrationInstallResult.config.storage.provider))}`,
|
|
26053
|
+
`Files created: ${pc7.cyan(String(totalFiles))}`,
|
|
25315
26054
|
`Env vars: ${envResult.added.length} added, ${envResult.skipped.length} skipped`
|
|
25316
26055
|
];
|
|
25317
|
-
|
|
25318
|
-
|
|
25319
|
-
|
|
25320
|
-
|
|
25321
|
-
|
|
25322
|
-
|
|
25323
|
-
|
|
25324
|
-
|
|
25325
|
-
|
|
25326
|
-
|
|
25327
|
-
|
|
26056
|
+
p17.note(summaryLines.join("\n"), "Admin scaffolded successfully");
|
|
26057
|
+
if (options.vercel !== false && !options.skipDeploy) {
|
|
26058
|
+
const linkedVercelProject = Boolean(readLinkedProjectId(cwd));
|
|
26059
|
+
if (!options.yes) {
|
|
26060
|
+
const deployNow = await p17.confirm({
|
|
26061
|
+
message: "Deploy to Vercel now?",
|
|
26062
|
+
initialValue: linkedVercelProject
|
|
26063
|
+
});
|
|
26064
|
+
if (!p17.isCancel(deployNow) && deployNow) {
|
|
26065
|
+
await runVercelDeployFlow({ cwd, projectName, env: process.env });
|
|
26066
|
+
}
|
|
26067
|
+
} else if (linkedVercelProject && process.env.VERCEL_TOKEN) {
|
|
26068
|
+
const deployFlow = await runVercelDeployFlow({ cwd, projectName, env: process.env });
|
|
26069
|
+
if (!deployFlow.ok) {
|
|
26070
|
+
p17.log.warn("Vercel deploy did not complete; continuing.");
|
|
26071
|
+
}
|
|
26072
|
+
}
|
|
25328
26073
|
}
|
|
25329
|
-
nextSteps.push(
|
|
25330
|
-
` ${step++}. Run ${pc5.cyan(runCommand(pm, "dev"))} and open ${pc5.cyan(adminLoginUrl)}`
|
|
25331
|
-
);
|
|
25332
|
-
nextSteps.push(
|
|
25333
|
-
` ${step++}. Run ${pc5.cyan(betterstartExecCommand(pm, "generate <schema>"))} to create content types`
|
|
25334
|
-
);
|
|
25335
|
-
nextSteps.push(
|
|
25336
|
-
` ${step++}. Run ${pc5.cyan(betterstartExecCommand(pm, "add --integration <name>"))} to install integrations later`
|
|
25337
|
-
);
|
|
25338
|
-
summaryLines.push("", "Next steps:", ...nextSteps);
|
|
25339
|
-
p15.note(summaryLines.join("\n"), "Admin scaffolded successfully");
|
|
25340
26074
|
if (!options.yes && !options.skipDevServerStart) {
|
|
25341
26075
|
const devCmd = runCommand(pm, "dev");
|
|
25342
|
-
const startDev = await
|
|
26076
|
+
const startDev = await p17.confirm({
|
|
25343
26077
|
message: "Start the development server?",
|
|
25344
26078
|
initialValue: true
|
|
25345
26079
|
});
|
|
25346
|
-
if (!
|
|
25347
|
-
p15.outro(`Starting ${pc5.cyan(devCmd)}...`);
|
|
26080
|
+
if (!p17.isCancel(startDev) && startDev) {
|
|
25348
26081
|
await startManagedDevServer(cwd, devCmd, adminLoginUrl, {
|
|
25349
26082
|
email: seedSuccess && seedEmail ? seedEmail : void 0,
|
|
25350
26083
|
password: seedSuccess && seedPassword ? seedPassword : void 0
|
|
@@ -25352,15 +26085,15 @@ Run manually: ${pc5.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
25352
26085
|
return;
|
|
25353
26086
|
}
|
|
25354
26087
|
}
|
|
25355
|
-
|
|
26088
|
+
p17.outro("Done!");
|
|
25356
26089
|
}
|
|
25357
26090
|
function isValidDbUrl(url) {
|
|
25358
26091
|
return url.startsWith("postgres://") || url.startsWith("postgresql://");
|
|
25359
26092
|
}
|
|
25360
26093
|
function readExistingDbUrl(cwd) {
|
|
25361
|
-
const envPath =
|
|
25362
|
-
if (!
|
|
25363
|
-
const content =
|
|
26094
|
+
const envPath = path50.join(cwd, ".env.local");
|
|
26095
|
+
if (!fs39.existsSync(envPath)) return void 0;
|
|
26096
|
+
const content = fs39.readFileSync(envPath, "utf-8");
|
|
25364
26097
|
for (const line of content.split("\n")) {
|
|
25365
26098
|
const trimmed = line.trim();
|
|
25366
26099
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -25383,9 +26116,9 @@ function maskDbUrl(url) {
|
|
|
25383
26116
|
}
|
|
25384
26117
|
}
|
|
25385
26118
|
function hasDbUrl(cwd) {
|
|
25386
|
-
const envPath =
|
|
25387
|
-
if (!
|
|
25388
|
-
const content =
|
|
26119
|
+
const envPath = path50.join(cwd, ".env.local");
|
|
26120
|
+
if (!fs39.existsSync(envPath)) return false;
|
|
26121
|
+
const content = fs39.readFileSync(envPath, "utf-8");
|
|
25389
26122
|
for (const line of content.split("\n")) {
|
|
25390
26123
|
const trimmed = line.trim();
|
|
25391
26124
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -25399,20 +26132,20 @@ function hasDbUrl(cwd) {
|
|
|
25399
26132
|
return false;
|
|
25400
26133
|
}
|
|
25401
26134
|
function detectInstalledNextVersion(cwd) {
|
|
25402
|
-
const installedNextPackagePath =
|
|
25403
|
-
if (
|
|
26135
|
+
const installedNextPackagePath = path50.join(cwd, "node_modules", "next", "package.json");
|
|
26136
|
+
if (fs39.existsSync(installedNextPackagePath)) {
|
|
25404
26137
|
try {
|
|
25405
|
-
const installedPkg = JSON.parse(
|
|
26138
|
+
const installedPkg = JSON.parse(fs39.readFileSync(installedNextPackagePath, "utf-8"));
|
|
25406
26139
|
if (typeof installedPkg.version === "string" && installedPkg.version.length > 0) {
|
|
25407
26140
|
return installedPkg.version;
|
|
25408
26141
|
}
|
|
25409
26142
|
} catch {
|
|
25410
26143
|
}
|
|
25411
26144
|
}
|
|
25412
|
-
const projectPackagePath =
|
|
25413
|
-
if (!
|
|
26145
|
+
const projectPackagePath = path50.join(cwd, "package.json");
|
|
26146
|
+
if (!fs39.existsSync(projectPackagePath)) return void 0;
|
|
25414
26147
|
try {
|
|
25415
|
-
const projectPkg = JSON.parse(
|
|
26148
|
+
const projectPkg = JSON.parse(fs39.readFileSync(projectPackagePath, "utf-8"));
|
|
25416
26149
|
const nextVersion = projectPkg.dependencies?.next ?? projectPkg.devDependencies?.next;
|
|
25417
26150
|
if (typeof nextVersion !== "string" || nextVersion.length === 0) {
|
|
25418
26151
|
return void 0;
|
|
@@ -25544,23 +26277,23 @@ main().catch((error) => {
|
|
|
25544
26277
|
};
|
|
25545
26278
|
}
|
|
25546
26279
|
function runSeedScript(cwd, adminDir, authBasePath, envOverrides) {
|
|
25547
|
-
const scriptsDir =
|
|
25548
|
-
const seedPath =
|
|
25549
|
-
if (!
|
|
25550
|
-
|
|
26280
|
+
const scriptsDir = path50.join(cwd, adminDir, "scripts");
|
|
26281
|
+
const seedPath = path50.join(scriptsDir, "seed.ts");
|
|
26282
|
+
if (!fs39.existsSync(scriptsDir)) {
|
|
26283
|
+
fs39.mkdirSync(scriptsDir, { recursive: true });
|
|
25551
26284
|
}
|
|
25552
|
-
|
|
26285
|
+
fs39.writeFileSync(seedPath, buildSeedScript(authBasePath), "utf-8");
|
|
25553
26286
|
const cleanup = () => {
|
|
25554
26287
|
try {
|
|
25555
|
-
|
|
25556
|
-
if (
|
|
25557
|
-
|
|
26288
|
+
fs39.unlinkSync(seedPath);
|
|
26289
|
+
if (fs39.existsSync(scriptsDir) && fs39.readdirSync(scriptsDir).length === 0) {
|
|
26290
|
+
fs39.rmdirSync(scriptsDir);
|
|
25558
26291
|
}
|
|
25559
26292
|
} catch {
|
|
25560
26293
|
}
|
|
25561
26294
|
};
|
|
25562
26295
|
return new Promise((resolve) => {
|
|
25563
|
-
const tsxBin =
|
|
26296
|
+
const tsxBin = path50.join(cwd, "node_modules", ".bin", "tsx");
|
|
25564
26297
|
const child = spawn4(tsxBin, [seedPath], {
|
|
25565
26298
|
cwd,
|
|
25566
26299
|
stdio: "pipe",
|
|
@@ -25734,12 +26467,12 @@ function stripAnsi(value) {
|
|
|
25734
26467
|
return value.replace(ANSI_ESCAPE_PATTERN, "");
|
|
25735
26468
|
}
|
|
25736
26469
|
function printAdminReadyNote(state) {
|
|
25737
|
-
const lines = [`Admin: ${
|
|
26470
|
+
const lines = [`Admin: ${pc7.cyan(state.adminLoginUrl)}`];
|
|
25738
26471
|
if (state.adminEmail && state.adminPassword) {
|
|
25739
|
-
lines.unshift(`Password: ${
|
|
25740
|
-
lines.unshift(`Admin user: ${
|
|
26472
|
+
lines.unshift(`Password: ${pc7.cyan(state.adminPassword)}`);
|
|
26473
|
+
lines.unshift(`Admin user: ${pc7.cyan(state.adminEmail)}`);
|
|
25741
26474
|
}
|
|
25742
|
-
|
|
26475
|
+
p17.note(lines.join("\n"), "Admin ready");
|
|
25743
26476
|
}
|
|
25744
26477
|
function shouldSuppressDevServerStartupLine(line) {
|
|
25745
26478
|
const plain = stripAnsi(line).trim();
|
|
@@ -25749,7 +26482,7 @@ function shouldSuppressDevServerStartupLine(line) {
|
|
|
25749
26482
|
if (plain.includes("Ready in ")) {
|
|
25750
26483
|
return { suppress: true, isReadyLine: true };
|
|
25751
26484
|
}
|
|
25752
|
-
if (plain.startsWith("> ") || plain.startsWith("\u25B2 Next.js ") || plain.startsWith("- ") || plain.startsWith("\xB7 ") || plain.startsWith("\u2A2F ")) {
|
|
26485
|
+
if (plain.startsWith("> ") || plain.startsWith("$ ") || plain.startsWith("\u25B2 Next.js ") || plain.startsWith("- ") || plain.startsWith("\xB7 ") || plain.startsWith("\u2A2F ")) {
|
|
25753
26486
|
return { suppress: true, isReadyLine: false };
|
|
25754
26487
|
}
|
|
25755
26488
|
if (plain.startsWith("\u2713 ") && !plain.includes("Ready in ")) {
|
|
@@ -25850,75 +26583,75 @@ function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminCredentials) {
|
|
|
25850
26583
|
}
|
|
25851
26584
|
|
|
25852
26585
|
// adapters/next/commands/list-integrations.ts
|
|
25853
|
-
import
|
|
25854
|
-
import * as
|
|
26586
|
+
import path51 from "path";
|
|
26587
|
+
import * as p18 from "@clack/prompts";
|
|
25855
26588
|
async function runListIntegrationsCommand(options) {
|
|
25856
|
-
const cwd = options.cwd ?
|
|
26589
|
+
const cwd = options.cwd ? path51.resolve(options.cwd) : process.cwd();
|
|
25857
26590
|
const config = await resolveConfig(cwd);
|
|
25858
26591
|
const installedIntegrations = new Set(config.integrations.installed);
|
|
25859
26592
|
const lines = listAvailableIntegrations().map((integration) => {
|
|
25860
26593
|
const status = installedIntegrations.has(integration.id) ? "installed" : "available";
|
|
25861
|
-
return `${integration.id.padEnd(
|
|
26594
|
+
return `${integration.id.padEnd(12)} ${status.padEnd(10)} ${integration.kind.padEnd(8)} ${integration.description}`;
|
|
25862
26595
|
});
|
|
25863
|
-
|
|
25864
|
-
|
|
26596
|
+
p18.note(lines.join("\n"), "BetterStart integrations");
|
|
26597
|
+
p18.outro(
|
|
25865
26598
|
`${installedIntegrations.size} installed, ${lines.length - installedIntegrations.size} available`
|
|
25866
26599
|
);
|
|
25867
26600
|
}
|
|
25868
26601
|
|
|
25869
26602
|
// adapters/next/commands/list-plugins.ts
|
|
25870
|
-
import
|
|
25871
|
-
import * as
|
|
26603
|
+
import path52 from "path";
|
|
26604
|
+
import * as p19 from "@clack/prompts";
|
|
25872
26605
|
async function runListPluginsCommand(options) {
|
|
25873
|
-
const cwd = options.cwd ?
|
|
26606
|
+
const cwd = options.cwd ? path52.resolve(options.cwd) : process.cwd();
|
|
25874
26607
|
const config = await resolveConfig(cwd);
|
|
25875
26608
|
const installedPlugins = new Set(config.plugins.installed);
|
|
25876
26609
|
const lines = listAvailablePlugins().map((plugin) => {
|
|
25877
26610
|
const status = installedPlugins.has(plugin.id) ? "installed" : "available";
|
|
25878
26611
|
return `${plugin.id.padEnd(10)} ${status.padEnd(10)} ${plugin.kind.padEnd(12)} ${plugin.description}`;
|
|
25879
26612
|
});
|
|
25880
|
-
|
|
25881
|
-
|
|
26613
|
+
p19.note(lines.join("\n"), "BetterStart plugins");
|
|
26614
|
+
p19.outro(`${installedPlugins.size} installed, ${lines.length - installedPlugins.size} available`);
|
|
25882
26615
|
}
|
|
25883
26616
|
|
|
25884
26617
|
// adapters/next/commands/remove.ts
|
|
25885
|
-
import
|
|
25886
|
-
import * as
|
|
26618
|
+
import path53 from "path";
|
|
26619
|
+
import * as p20 from "@clack/prompts";
|
|
25887
26620
|
async function runRemoveCommand(items, options) {
|
|
25888
26621
|
const removeIntegrationsMode = Boolean(options.integration);
|
|
25889
26622
|
if (!removeIntegrationsMode && items.includes("core")) {
|
|
25890
|
-
|
|
26623
|
+
p20.log.error("The core Admin cannot be removed.");
|
|
25891
26624
|
process.exit(1);
|
|
25892
26625
|
}
|
|
25893
26626
|
const pluginIds = items.filter(isPluginId);
|
|
25894
26627
|
const integrationIds = items.filter(isIntegrationId);
|
|
25895
26628
|
if (!removeIntegrationsMode && integrationIds.length > 0) {
|
|
25896
|
-
|
|
26629
|
+
p20.log.error(
|
|
25897
26630
|
`Integration IDs require --integration. Run \`betterstart remove --integration ${integrationIds.join(" ")}\`.`
|
|
25898
26631
|
);
|
|
25899
26632
|
process.exit(1);
|
|
25900
26633
|
}
|
|
25901
26634
|
if (removeIntegrationsMode && pluginIds.length > 0) {
|
|
25902
|
-
|
|
26635
|
+
p20.log.error(`Plugin IDs cannot be removed with --integration: ${pluginIds.join(", ")}`);
|
|
25903
26636
|
process.exit(1);
|
|
25904
26637
|
}
|
|
25905
26638
|
const invalidItems = removeIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((pluginId) => !isPluginId(pluginId));
|
|
25906
26639
|
if (invalidItems.length > 0) {
|
|
25907
|
-
|
|
26640
|
+
p20.log.error(
|
|
25908
26641
|
removeIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPluginMessage(invalidItems)
|
|
25909
26642
|
);
|
|
25910
26643
|
process.exit(1);
|
|
25911
26644
|
}
|
|
25912
|
-
const cwd = options.cwd ?
|
|
26645
|
+
const cwd = options.cwd ? path53.resolve(options.cwd) : process.cwd();
|
|
25913
26646
|
const config = await resolveConfig(cwd);
|
|
25914
26647
|
const pm = detectPackageManager(cwd);
|
|
25915
26648
|
if (!options.force) {
|
|
25916
|
-
const confirmed = await
|
|
26649
|
+
const confirmed = await p20.confirm({
|
|
25917
26650
|
message: `Remove ${removeIntegrationsMode ? "integration" : "plugin"}${items.length === 1 ? "" : "s"} ${items.join(", ")}?`,
|
|
25918
26651
|
initialValue: false
|
|
25919
26652
|
});
|
|
25920
|
-
if (
|
|
25921
|
-
|
|
26653
|
+
if (p20.isCancel(confirmed) || !confirmed) {
|
|
26654
|
+
p20.cancel(`${removeIntegrationsMode ? "Integration" : "Plugin"} removal cancelled.`);
|
|
25922
26655
|
process.exit(0);
|
|
25923
26656
|
}
|
|
25924
26657
|
}
|
|
@@ -25931,13 +26664,13 @@ async function runRemoveCommand(items, options) {
|
|
|
25931
26664
|
});
|
|
25932
26665
|
writeConfigFile(cwd, result2.config);
|
|
25933
26666
|
if (result2.removed.length === 0) {
|
|
25934
|
-
|
|
26667
|
+
p20.outro("No integrations were removed.");
|
|
25935
26668
|
return;
|
|
25936
26669
|
}
|
|
25937
26670
|
if (result2.warnings.length > 0) {
|
|
25938
|
-
|
|
26671
|
+
p20.note(result2.warnings.join("\n"), "Warnings");
|
|
25939
26672
|
}
|
|
25940
|
-
|
|
26673
|
+
p20.outro(
|
|
25941
26674
|
`Removed integration${result2.removed.length === 1 ? "" : "s"}: ${result2.removed.join(", ")}`
|
|
25942
26675
|
);
|
|
25943
26676
|
return;
|
|
@@ -25950,18 +26683,18 @@ async function runRemoveCommand(items, options) {
|
|
|
25950
26683
|
});
|
|
25951
26684
|
writeConfigFile(cwd, result.config);
|
|
25952
26685
|
if (result.removed.length === 0) {
|
|
25953
|
-
|
|
26686
|
+
p20.outro("No plugins were removed.");
|
|
25954
26687
|
return;
|
|
25955
26688
|
}
|
|
25956
26689
|
if (result.warnings.length > 0) {
|
|
25957
|
-
|
|
26690
|
+
p20.note(result.warnings.join("\n"), "Warnings");
|
|
25958
26691
|
}
|
|
25959
|
-
|
|
26692
|
+
p20.outro(`Removed plugin${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
|
|
25960
26693
|
}
|
|
25961
26694
|
|
|
25962
26695
|
// adapters/next/commands/remove-schema.ts
|
|
25963
|
-
import
|
|
25964
|
-
import
|
|
26696
|
+
import fs40 from "fs";
|
|
26697
|
+
import path54 from "path";
|
|
25965
26698
|
import readline from "readline";
|
|
25966
26699
|
async function promptConfirm4(message) {
|
|
25967
26700
|
const rl = readline.createInterface({
|
|
@@ -25976,30 +26709,30 @@ async function promptConfirm4(message) {
|
|
|
25976
26709
|
});
|
|
25977
26710
|
}
|
|
25978
26711
|
function removePath2(cwd, filePath) {
|
|
25979
|
-
const fullPath =
|
|
25980
|
-
const existed =
|
|
25981
|
-
|
|
26712
|
+
const fullPath = path54.join(cwd, ...filePath.split("/"));
|
|
26713
|
+
const existed = fs40.existsSync(fullPath);
|
|
26714
|
+
fs40.rmSync(fullPath, { recursive: true, force: true });
|
|
25982
26715
|
return existed;
|
|
25983
26716
|
}
|
|
25984
26717
|
function cleanupEmptyDirs3(cwd, deletedPaths, configPaths) {
|
|
25985
26718
|
const stopRoots = /* @__PURE__ */ new Set([
|
|
25986
|
-
|
|
25987
|
-
|
|
25988
|
-
|
|
26719
|
+
path54.join(cwd, ...configPaths.adminDir.split("/")),
|
|
26720
|
+
path54.join(cwd, ...configPaths.adminNavigationDir.split("/")),
|
|
26721
|
+
path54.join(cwd, ...configPaths.pagesDir.split("/"))
|
|
25989
26722
|
]);
|
|
25990
26723
|
for (const deletedPath of deletedPaths) {
|
|
25991
|
-
let current =
|
|
26724
|
+
let current = path54.dirname(path54.join(cwd, ...deletedPath.split("/")));
|
|
25992
26725
|
while (!stopRoots.has(current)) {
|
|
25993
|
-
if (!
|
|
25994
|
-
current =
|
|
26726
|
+
if (!fs40.existsSync(current)) {
|
|
26727
|
+
current = path54.dirname(current);
|
|
25995
26728
|
continue;
|
|
25996
26729
|
}
|
|
25997
|
-
const entries =
|
|
26730
|
+
const entries = fs40.readdirSync(current);
|
|
25998
26731
|
if (entries.length > 0) {
|
|
25999
26732
|
break;
|
|
26000
26733
|
}
|
|
26001
|
-
|
|
26002
|
-
current =
|
|
26734
|
+
fs40.rmdirSync(current);
|
|
26735
|
+
current = path54.dirname(current);
|
|
26003
26736
|
}
|
|
26004
26737
|
}
|
|
26005
26738
|
}
|
|
@@ -26015,7 +26748,7 @@ function resolveSchemaOwnerForRemoval(cwd, schemaName) {
|
|
|
26015
26748
|
}
|
|
26016
26749
|
async function runRemoveSchemaCommand(schemaName, options) {
|
|
26017
26750
|
const owner = resolveSchemaOwnerForRemoval(
|
|
26018
|
-
options.cwd ?
|
|
26751
|
+
options.cwd ? path54.resolve(options.cwd) : process.cwd(),
|
|
26019
26752
|
schemaName
|
|
26020
26753
|
);
|
|
26021
26754
|
if (owner === "core") {
|
|
@@ -26028,7 +26761,7 @@ async function runRemoveSchemaCommand(schemaName, options) {
|
|
|
26028
26761
|
console.error(` "${schemaName}" is owned by ${owner}. Remove the owning plugin instead.`);
|
|
26029
26762
|
process.exit(1);
|
|
26030
26763
|
}
|
|
26031
|
-
const cwd = options.cwd ?
|
|
26764
|
+
const cwd = options.cwd ? path54.resolve(options.cwd) : process.cwd();
|
|
26032
26765
|
const config = await resolveConfig(cwd);
|
|
26033
26766
|
const paths = resolveProjectPaths(config);
|
|
26034
26767
|
const manifest = loadManifest(cwd, schemaName);
|
|
@@ -26059,7 +26792,7 @@ async function runRemoveSchemaCommand(schemaName, options) {
|
|
|
26059
26792
|
}
|
|
26060
26793
|
const loaded = (() => {
|
|
26061
26794
|
try {
|
|
26062
|
-
return loadSchema(
|
|
26795
|
+
return loadSchema(path54.join(cwd, ...paths.schemasDir.split("/")), schemaName);
|
|
26063
26796
|
} catch {
|
|
26064
26797
|
return null;
|
|
26065
26798
|
}
|
|
@@ -26103,13 +26836,13 @@ async function runRemoveSchemaCommand(schemaName, options) {
|
|
|
26103
26836
|
}
|
|
26104
26837
|
|
|
26105
26838
|
// adapters/next/commands/uninstall.ts
|
|
26106
|
-
import
|
|
26107
|
-
import
|
|
26108
|
-
import * as
|
|
26109
|
-
import
|
|
26839
|
+
import fs42 from "fs";
|
|
26840
|
+
import path55 from "path";
|
|
26841
|
+
import * as p21 from "@clack/prompts";
|
|
26842
|
+
import pc8 from "picocolors";
|
|
26110
26843
|
|
|
26111
26844
|
// adapters/next/commands/uninstall-cleaners.ts
|
|
26112
|
-
import
|
|
26845
|
+
import fs41 from "fs";
|
|
26113
26846
|
function stripJsonComments2(input) {
|
|
26114
26847
|
let result = "";
|
|
26115
26848
|
let i = 0;
|
|
@@ -26143,8 +26876,8 @@ function stripJsonComments2(input) {
|
|
|
26143
26876
|
return result;
|
|
26144
26877
|
}
|
|
26145
26878
|
function cleanTsconfig(tsconfigPath, aliasRoot = "@admin") {
|
|
26146
|
-
if (!
|
|
26147
|
-
const raw =
|
|
26879
|
+
if (!fs41.existsSync(tsconfigPath)) return [];
|
|
26880
|
+
const raw = fs41.readFileSync(tsconfigPath, "utf-8");
|
|
26148
26881
|
const stripped = stripJsonComments2(raw).replace(/,\s*([\]}])/g, "$1");
|
|
26149
26882
|
let tsconfig;
|
|
26150
26883
|
try {
|
|
@@ -26168,13 +26901,13 @@ function cleanTsconfig(tsconfigPath, aliasRoot = "@admin") {
|
|
|
26168
26901
|
compilerOptions.paths = paths;
|
|
26169
26902
|
}
|
|
26170
26903
|
tsconfig.compilerOptions = compilerOptions;
|
|
26171
|
-
|
|
26904
|
+
fs41.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
26172
26905
|
`, "utf-8");
|
|
26173
26906
|
return removed;
|
|
26174
26907
|
}
|
|
26175
26908
|
function cleanCss(cssPath, namespace = "admin") {
|
|
26176
|
-
if (!
|
|
26177
|
-
const content =
|
|
26909
|
+
if (!fs41.existsSync(cssPath)) return [];
|
|
26910
|
+
const content = fs41.readFileSync(cssPath, "utf-8");
|
|
26178
26911
|
const lines = content.split("\n");
|
|
26179
26912
|
const sourcePattern = new RegExp(`^@source\\s+"[^"]*(?:admin|${namespace})[^"]*";\\s*$`);
|
|
26180
26913
|
const removed = [];
|
|
@@ -26188,12 +26921,12 @@ function cleanCss(cssPath, namespace = "admin") {
|
|
|
26188
26921
|
}
|
|
26189
26922
|
if (removed.length === 0) return [];
|
|
26190
26923
|
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
26191
|
-
|
|
26924
|
+
fs41.writeFileSync(cssPath, cleaned, "utf-8");
|
|
26192
26925
|
return removed;
|
|
26193
26926
|
}
|
|
26194
26927
|
function cleanEnvFile(envPath) {
|
|
26195
|
-
if (!
|
|
26196
|
-
const content =
|
|
26928
|
+
if (!fs41.existsSync(envPath)) return [];
|
|
26929
|
+
const content = fs41.readFileSync(envPath, "utf-8");
|
|
26197
26930
|
const lines = content.split("\n");
|
|
26198
26931
|
const removed = [];
|
|
26199
26932
|
const kept = [];
|
|
@@ -26226,9 +26959,9 @@ function cleanEnvFile(envPath) {
|
|
|
26226
26959
|
if (removed.length === 0) return [];
|
|
26227
26960
|
const result = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
26228
26961
|
if (result === "") {
|
|
26229
|
-
|
|
26962
|
+
fs41.unlinkSync(envPath);
|
|
26230
26963
|
} else {
|
|
26231
|
-
|
|
26964
|
+
fs41.writeFileSync(envPath, `${result}
|
|
26232
26965
|
`, "utf-8");
|
|
26233
26966
|
}
|
|
26234
26967
|
return removed;
|
|
@@ -26254,15 +26987,15 @@ function findMainCss2(cwd) {
|
|
|
26254
26987
|
"globals.css"
|
|
26255
26988
|
];
|
|
26256
26989
|
for (const candidate of candidates) {
|
|
26257
|
-
const filePath =
|
|
26258
|
-
if (
|
|
26990
|
+
const filePath = path55.join(cwd, candidate);
|
|
26991
|
+
if (fs42.existsSync(filePath)) return filePath;
|
|
26259
26992
|
}
|
|
26260
26993
|
return void 0;
|
|
26261
26994
|
}
|
|
26262
26995
|
function isCLICreatedBiome(biomePath) {
|
|
26263
|
-
if (!
|
|
26996
|
+
if (!fs42.existsSync(biomePath)) return false;
|
|
26264
26997
|
try {
|
|
26265
|
-
const content = JSON.parse(
|
|
26998
|
+
const content = JSON.parse(fs42.readFileSync(biomePath, "utf-8"));
|
|
26266
26999
|
return content.$schema?.includes("biomejs.dev") && content.formatter?.indentStyle === "space" && content.javascript?.formatter?.quoteStyle === "single" && Array.isArray(content.files?.ignore) && content.files.ignore.includes(".next");
|
|
26267
27000
|
} catch {
|
|
26268
27001
|
return false;
|
|
@@ -26271,17 +27004,17 @@ function isCLICreatedBiome(biomePath) {
|
|
|
26271
27004
|
function buildUninstallPlan(cwd, namespaceValue) {
|
|
26272
27005
|
const steps = [];
|
|
26273
27006
|
const namespace = resolveAdminNamespace(namespaceValue);
|
|
26274
|
-
const hasSrc =
|
|
27007
|
+
const hasSrc = fs42.existsSync(path55.join(cwd, "src"));
|
|
26275
27008
|
const appBase = hasSrc ? "src/app" : "app";
|
|
26276
27009
|
const dirs = [];
|
|
26277
|
-
const adminDir =
|
|
26278
|
-
const legacyAdminDir =
|
|
26279
|
-
const adminRouteGroup =
|
|
26280
|
-
const legacyAdminRouteGroup =
|
|
26281
|
-
if (
|
|
26282
|
-
if (namespace.segment !== "admin" &&
|
|
26283
|
-
if (
|
|
26284
|
-
if (namespace.segment !== "admin" &&
|
|
27010
|
+
const adminDir = path55.join(cwd, namespace.segment);
|
|
27011
|
+
const legacyAdminDir = path55.join(cwd, "admin");
|
|
27012
|
+
const adminRouteGroup = path55.join(cwd, appBase, namespace.routeGroup);
|
|
27013
|
+
const legacyAdminRouteGroup = path55.join(cwd, appBase, "(admin)");
|
|
27014
|
+
if (fs42.existsSync(adminDir)) dirs.push(`${namespace.segment}/`);
|
|
27015
|
+
if (namespace.segment !== "admin" && fs42.existsSync(legacyAdminDir)) dirs.push("admin/");
|
|
27016
|
+
if (fs42.existsSync(adminRouteGroup)) dirs.push(`${appBase}/${namespace.routeGroup}/`);
|
|
27017
|
+
if (namespace.segment !== "admin" && fs42.existsSync(legacyAdminRouteGroup))
|
|
26285
27018
|
dirs.push(`${appBase}/(admin)/`);
|
|
26286
27019
|
if (dirs.length > 0) {
|
|
26287
27020
|
steps.push({
|
|
@@ -26290,14 +27023,14 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
26290
27023
|
count: dirs.length,
|
|
26291
27024
|
unit: dirs.length === 1 ? "directory" : "directories",
|
|
26292
27025
|
execute() {
|
|
26293
|
-
if (
|
|
26294
|
-
if (
|
|
26295
|
-
|
|
26296
|
-
if (
|
|
26297
|
-
|
|
27026
|
+
if (fs42.existsSync(adminDir)) fs42.rmSync(adminDir, { recursive: true, force: true });
|
|
27027
|
+
if (fs42.existsSync(legacyAdminDir))
|
|
27028
|
+
fs42.rmSync(legacyAdminDir, { recursive: true, force: true });
|
|
27029
|
+
if (fs42.existsSync(adminRouteGroup)) {
|
|
27030
|
+
fs42.rmSync(adminRouteGroup, { recursive: true, force: true });
|
|
26298
27031
|
}
|
|
26299
|
-
if (
|
|
26300
|
-
|
|
27032
|
+
if (fs42.existsSync(legacyAdminRouteGroup)) {
|
|
27033
|
+
fs42.rmSync(legacyAdminRouteGroup, { recursive: true, force: true });
|
|
26301
27034
|
}
|
|
26302
27035
|
}
|
|
26303
27036
|
});
|
|
@@ -26305,17 +27038,17 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
26305
27038
|
const configFiles = [];
|
|
26306
27039
|
const configPaths = [];
|
|
26307
27040
|
const candidates = [
|
|
26308
|
-
["admin.config.ts",
|
|
26309
|
-
["drizzle.config.ts",
|
|
26310
|
-
["ADMIN.md",
|
|
27041
|
+
["admin.config.ts", path55.join(cwd, "admin.config.ts")],
|
|
27042
|
+
["drizzle.config.ts", path55.join(cwd, "drizzle.config.ts")],
|
|
27043
|
+
["ADMIN.md", path55.join(cwd, "ADMIN.md")]
|
|
26311
27044
|
];
|
|
26312
27045
|
for (const [label, fullPath] of candidates) {
|
|
26313
|
-
if (
|
|
27046
|
+
if (fs42.existsSync(fullPath)) {
|
|
26314
27047
|
configFiles.push(label);
|
|
26315
27048
|
configPaths.push(fullPath);
|
|
26316
27049
|
}
|
|
26317
27050
|
}
|
|
26318
|
-
const biomePath =
|
|
27051
|
+
const biomePath = path55.join(cwd, "biome.json");
|
|
26319
27052
|
if (isCLICreatedBiome(biomePath)) {
|
|
26320
27053
|
configFiles.push("biome.json (CLI-created)");
|
|
26321
27054
|
configPaths.push(biomePath);
|
|
@@ -26327,15 +27060,15 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
26327
27060
|
count: configFiles.length,
|
|
26328
27061
|
unit: configFiles.length === 1 ? "file" : "files",
|
|
26329
27062
|
execute() {
|
|
26330
|
-
for (const
|
|
26331
|
-
if (
|
|
27063
|
+
for (const p22 of configPaths) {
|
|
27064
|
+
if (fs42.existsSync(p22)) fs42.unlinkSync(p22);
|
|
26332
27065
|
}
|
|
26333
27066
|
}
|
|
26334
27067
|
});
|
|
26335
27068
|
}
|
|
26336
|
-
const tsconfigPath =
|
|
26337
|
-
if (
|
|
26338
|
-
const content =
|
|
27069
|
+
const tsconfigPath = path55.join(cwd, "tsconfig.json");
|
|
27070
|
+
if (fs42.existsSync(tsconfigPath)) {
|
|
27071
|
+
const content = fs42.readFileSync(tsconfigPath, "utf-8");
|
|
26339
27072
|
const aliasMatches = [
|
|
26340
27073
|
...content.match(/"@admin\//g) ?? [],
|
|
26341
27074
|
...content.match(new RegExp(`"${namespace.alias}/`, "g")) ?? []
|
|
@@ -26355,12 +27088,12 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
26355
27088
|
}
|
|
26356
27089
|
const cssFile = findMainCss2(cwd);
|
|
26357
27090
|
if (cssFile) {
|
|
26358
|
-
const cssContent =
|
|
27091
|
+
const cssContent = fs42.readFileSync(cssFile, "utf-8");
|
|
26359
27092
|
const sourceLines = cssContent.split("\n").filter(
|
|
26360
27093
|
(l) => new RegExp(`^@source\\s+"[^"]*(?:admin|${namespace.segment})[^"]*";\\s*$`).test(l)
|
|
26361
27094
|
);
|
|
26362
27095
|
if (sourceLines.length > 0) {
|
|
26363
|
-
const relCss =
|
|
27096
|
+
const relCss = path55.relative(cwd, cssFile);
|
|
26364
27097
|
steps.push({
|
|
26365
27098
|
label: `CSS @source lines (${relCss})`,
|
|
26366
27099
|
items: [`@source lines in ${relCss}`],
|
|
@@ -26372,9 +27105,9 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
26372
27105
|
});
|
|
26373
27106
|
}
|
|
26374
27107
|
}
|
|
26375
|
-
const envPath =
|
|
26376
|
-
if (
|
|
26377
|
-
const envContent =
|
|
27108
|
+
const envPath = path55.join(cwd, ".env.local");
|
|
27109
|
+
if (fs42.existsSync(envPath)) {
|
|
27110
|
+
const envContent = fs42.readFileSync(envPath, "utf-8");
|
|
26378
27111
|
const bsVars = envContent.split("\n").filter((l) => l.trim().match(/^BETTERSTART_\w+=/)).map((l) => l.split("=")[0]);
|
|
26379
27112
|
if (bsVars.length > 0) {
|
|
26380
27113
|
steps.push({
|
|
@@ -26391,8 +27124,8 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
26391
27124
|
return steps;
|
|
26392
27125
|
}
|
|
26393
27126
|
async function runUninstallCommand(options) {
|
|
26394
|
-
const cwd = options.cwd ?
|
|
26395
|
-
|
|
27127
|
+
const cwd = options.cwd ? path55.resolve(options.cwd) : process.cwd();
|
|
27128
|
+
p21.intro(pc8.bgRed(pc8.white(" BetterStart Uninstall ")));
|
|
26396
27129
|
let namespace = DEFAULT_ADMIN_NAMESPACE;
|
|
26397
27130
|
try {
|
|
26398
27131
|
const config = await resolveConfig(cwd);
|
|
@@ -26401,27 +27134,27 @@ async function runUninstallCommand(options) {
|
|
|
26401
27134
|
}
|
|
26402
27135
|
const steps = buildUninstallPlan(cwd, namespace);
|
|
26403
27136
|
if (steps.length === 0) {
|
|
26404
|
-
|
|
26405
|
-
|
|
27137
|
+
p21.log.success(`${pc8.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
|
|
27138
|
+
p21.outro("Done");
|
|
26406
27139
|
return;
|
|
26407
27140
|
}
|
|
26408
27141
|
const planLines = steps.map((step) => {
|
|
26409
27142
|
const names = step.items.join(" ");
|
|
26410
|
-
const countLabel =
|
|
26411
|
-
return `${
|
|
27143
|
+
const countLabel = pc8.dim(`${step.count} ${step.unit}`);
|
|
27144
|
+
return `${pc8.red("\xD7")} ${names} ${countLabel}`;
|
|
26412
27145
|
});
|
|
26413
|
-
|
|
27146
|
+
p21.note(planLines.join("\n"), "Uninstall plan");
|
|
26414
27147
|
if (!options.force) {
|
|
26415
|
-
const confirmed = await
|
|
27148
|
+
const confirmed = await p21.confirm({
|
|
26416
27149
|
message: "Proceed with uninstall?",
|
|
26417
27150
|
initialValue: false
|
|
26418
27151
|
});
|
|
26419
|
-
if (
|
|
26420
|
-
|
|
27152
|
+
if (p21.isCancel(confirmed) || !confirmed) {
|
|
27153
|
+
p21.cancel("Uninstall cancelled.");
|
|
26421
27154
|
process.exit(0);
|
|
26422
27155
|
}
|
|
26423
27156
|
}
|
|
26424
|
-
const s =
|
|
27157
|
+
const s = spinner2();
|
|
26425
27158
|
s.start(steps[0].label);
|
|
26426
27159
|
for (const step of steps) {
|
|
26427
27160
|
s.message(step.label);
|
|
@@ -26429,14 +27162,14 @@ async function runUninstallCommand(options) {
|
|
|
26429
27162
|
}
|
|
26430
27163
|
const parts = steps.map((step) => `${step.count} ${step.unit}`);
|
|
26431
27164
|
s.stop(`Removed ${parts.join(", ")}`);
|
|
26432
|
-
|
|
26433
|
-
|
|
27165
|
+
p21.note(pc8.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
|
|
27166
|
+
p21.outro("Uninstall complete");
|
|
26434
27167
|
}
|
|
26435
27168
|
|
|
26436
27169
|
// adapters/next/commands/update-component.ts
|
|
26437
27170
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
26438
|
-
import
|
|
26439
|
-
import
|
|
27171
|
+
import fs43 from "fs";
|
|
27172
|
+
import path56 from "path";
|
|
26440
27173
|
import * as clack2 from "@clack/prompts";
|
|
26441
27174
|
import fsExtra from "fs-extra";
|
|
26442
27175
|
var STATIC_CUSTOM_DEPENDENCIES = {
|
|
@@ -26686,24 +27419,24 @@ function applyNamespaceToTemplateEntry(entry, config, cwd) {
|
|
|
26686
27419
|
};
|
|
26687
27420
|
}
|
|
26688
27421
|
function writeNamespacedFile(srcPath, destPath, namespace) {
|
|
26689
|
-
|
|
27422
|
+
fs43.writeFileSync(
|
|
26690
27423
|
destPath,
|
|
26691
|
-
applyAdminNamespaceToContent(
|
|
27424
|
+
applyAdminNamespaceToContent(fs43.readFileSync(srcPath, "utf-8"), namespace),
|
|
26692
27425
|
"utf-8"
|
|
26693
27426
|
);
|
|
26694
27427
|
}
|
|
26695
27428
|
function copyNamespacedDirectory(srcDir, destDir, namespace) {
|
|
26696
|
-
const entries =
|
|
27429
|
+
const entries = fs43.readdirSync(srcDir, { withFileTypes: true });
|
|
26697
27430
|
for (const entry of entries) {
|
|
26698
27431
|
const namespacedName = applyAdminNamespaceToPath(entry.name, namespace);
|
|
26699
|
-
const srcPath =
|
|
26700
|
-
const destPath =
|
|
27432
|
+
const srcPath = path56.join(srcDir, entry.name);
|
|
27433
|
+
const destPath = path56.join(destDir, namespacedName);
|
|
26701
27434
|
if (entry.isDirectory()) {
|
|
26702
27435
|
fsExtra.ensureDirSync(destPath);
|
|
26703
27436
|
copyNamespacedDirectory(srcPath, destPath, namespace);
|
|
26704
27437
|
continue;
|
|
26705
27438
|
}
|
|
26706
|
-
fsExtra.ensureDirSync(
|
|
27439
|
+
fsExtra.ensureDirSync(path56.dirname(destPath));
|
|
26707
27440
|
writeNamespacedFile(srcPath, destPath, namespace);
|
|
26708
27441
|
}
|
|
26709
27442
|
}
|
|
@@ -26714,10 +27447,10 @@ function hasIntegration(config, integrationId) {
|
|
|
26714
27447
|
return config.integrations.installed.includes(integrationId);
|
|
26715
27448
|
}
|
|
26716
27449
|
function readProjectPackageJson2(cwd) {
|
|
26717
|
-
const pkgPath =
|
|
26718
|
-
if (!
|
|
27450
|
+
const pkgPath = path56.join(cwd, "package.json");
|
|
27451
|
+
if (!fs43.existsSync(pkgPath)) return null;
|
|
26719
27452
|
try {
|
|
26720
|
-
return JSON.parse(
|
|
27453
|
+
return JSON.parse(fs43.readFileSync(pkgPath, "utf-8"));
|
|
26721
27454
|
} catch {
|
|
26722
27455
|
return null;
|
|
26723
27456
|
}
|
|
@@ -27630,6 +28363,11 @@ var TEMPLATE_REGISTRY = {
|
|
|
27630
28363
|
requiredIntegration: "r2",
|
|
27631
28364
|
content: () => readIntegrationTemplate("r2", "actions/r2.ts")
|
|
27632
28365
|
},
|
|
28366
|
+
"storage-vercel-blob-provider": {
|
|
28367
|
+
relPath: "lib/actions/vercel-blob/provider.ts",
|
|
28368
|
+
requiredIntegration: "vercel-blob",
|
|
28369
|
+
content: () => readIntegrationTemplate("vercel-blob", "actions/vercel-blob.ts")
|
|
28370
|
+
},
|
|
27633
28371
|
"form-settings-action": {
|
|
27634
28372
|
relPath: "lib/actions/forms/index.ts",
|
|
27635
28373
|
content: () => readTemplate("lib/actions/forms/index.ts"),
|
|
@@ -28008,9 +28746,9 @@ function getStaticAssetComponents(assetDirectory) {
|
|
|
28008
28746
|
}
|
|
28009
28747
|
function getStaticAssetComponentEntries(assetDirectory) {
|
|
28010
28748
|
const assetDir = resolveCliAssetPath("shared-assets", "react-admin", assetDirectory);
|
|
28011
|
-
if (!
|
|
28749
|
+
if (!fs43.existsSync(assetDir)) return [];
|
|
28012
28750
|
const components = [];
|
|
28013
|
-
for (const entry of
|
|
28751
|
+
for (const entry of fs43.readdirSync(assetDir, { withFileTypes: true })) {
|
|
28014
28752
|
if (entry.isFile() && (entry.name.endsWith(".tsx") || entry.name.endsWith(".ts"))) {
|
|
28015
28753
|
components.push({
|
|
28016
28754
|
name: entry.name.replace(/\.(tsx|ts)$/, ""),
|
|
@@ -28022,40 +28760,40 @@ function getStaticAssetComponentEntries(assetDirectory) {
|
|
|
28022
28760
|
continue;
|
|
28023
28761
|
}
|
|
28024
28762
|
const indexFile = ["index.tsx", "index.ts"].find(
|
|
28025
|
-
(file) =>
|
|
28763
|
+
(file) => fs43.existsSync(path56.join(assetDir, entry.name, file))
|
|
28026
28764
|
);
|
|
28027
28765
|
if (indexFile) {
|
|
28028
28766
|
components.push({
|
|
28029
28767
|
name: entry.name,
|
|
28030
|
-
file:
|
|
28768
|
+
file: path56.join(entry.name, indexFile)
|
|
28031
28769
|
});
|
|
28032
28770
|
}
|
|
28033
28771
|
}
|
|
28034
28772
|
return components.sort((a, b) => a.name.localeCompare(b.name));
|
|
28035
28773
|
}
|
|
28036
28774
|
function findStaticAssetFile(assetDir, componentName) {
|
|
28037
|
-
if (!
|
|
28775
|
+
if (!fs43.existsSync(assetDir)) return void 0;
|
|
28038
28776
|
const isNestedComponentName = /[\\/]/.test(componentName);
|
|
28039
|
-
const nestedComponentName = componentName.split(/[\\/]/).filter(Boolean).join(
|
|
28040
|
-
if (isNestedComponentName && nestedComponentName && !
|
|
28777
|
+
const nestedComponentName = componentName.split(/[\\/]/).filter(Boolean).join(path56.sep);
|
|
28778
|
+
if (isNestedComponentName && nestedComponentName && !path56.isAbsolute(componentName) && !nestedComponentName.split(path56.sep).includes("..")) {
|
|
28041
28779
|
for (const extension of [".tsx", ".ts"]) {
|
|
28042
28780
|
const relPath = `${nestedComponentName}${extension}`;
|
|
28043
|
-
const filePath =
|
|
28044
|
-
if (
|
|
28781
|
+
const filePath = path56.join(assetDir, relPath);
|
|
28782
|
+
if (fs43.existsSync(filePath) && fs43.statSync(filePath).isFile()) {
|
|
28045
28783
|
return relPath;
|
|
28046
28784
|
}
|
|
28047
28785
|
}
|
|
28048
28786
|
}
|
|
28049
|
-
if (!isNestedComponentName && !componentName.includes("..") && !
|
|
28787
|
+
if (!isNestedComponentName && !componentName.includes("..") && !path56.isAbsolute(componentName)) {
|
|
28050
28788
|
for (const extension of [".tsx", ".ts"]) {
|
|
28051
|
-
const relPath =
|
|
28052
|
-
const filePath =
|
|
28053
|
-
if (
|
|
28789
|
+
const relPath = path56.join(componentName, `index${extension}`);
|
|
28790
|
+
const filePath = path56.join(assetDir, relPath);
|
|
28791
|
+
if (fs43.existsSync(filePath) && fs43.statSync(filePath).isFile()) {
|
|
28054
28792
|
return relPath;
|
|
28055
28793
|
}
|
|
28056
28794
|
}
|
|
28057
28795
|
}
|
|
28058
|
-
return
|
|
28796
|
+
return fs43.readdirSync(assetDir, { withFileTypes: true }).find(
|
|
28059
28797
|
(entry) => entry.isFile() && (entry.name.endsWith(".tsx") || entry.name.endsWith(".ts")) && entry.name.replace(/\.(tsx|ts)$/, "") === componentName
|
|
28060
28798
|
)?.name;
|
|
28061
28799
|
}
|
|
@@ -28074,7 +28812,7 @@ function getAllComponentNamesForConfig(config) {
|
|
|
28074
28812
|
return [.../* @__PURE__ */ new Set([...staticUi, ...staticCustom, ...templateKeys, "tiptap"])].sort();
|
|
28075
28813
|
}
|
|
28076
28814
|
async function runUpdateCommand(components, options) {
|
|
28077
|
-
const cwd = options.cwd ?
|
|
28815
|
+
const cwd = options.cwd ? path56.resolve(options.cwd) : process.cwd();
|
|
28078
28816
|
const normalizedOnly = normalizeShadcnPresetOnly(options.only);
|
|
28079
28817
|
validateShadcnPresetOptions(components, options);
|
|
28080
28818
|
if (options.list) {
|
|
@@ -28118,8 +28856,8 @@ async function runUpdateCommand(components, options) {
|
|
|
28118
28856
|
return;
|
|
28119
28857
|
}
|
|
28120
28858
|
const config = await resolveConfig(cwd);
|
|
28121
|
-
const admin =
|
|
28122
|
-
if (!
|
|
28859
|
+
const admin = path56.resolve(cwd, config.paths.admin);
|
|
28860
|
+
if (!fs43.existsSync(admin)) {
|
|
28123
28861
|
clack2.cancel(
|
|
28124
28862
|
`Admin directory not found at ${config.paths.admin}. Run 'betterstart init' first.`
|
|
28125
28863
|
);
|
|
@@ -28172,15 +28910,15 @@ async function runUpdateCommand(components, options) {
|
|
|
28172
28910
|
}
|
|
28173
28911
|
const { relPath, content } = applyNamespaceToTemplateEntry(entry, config, cwd);
|
|
28174
28912
|
const baseDir = entry.base === "cwd" ? cwd : admin;
|
|
28175
|
-
const destPath =
|
|
28176
|
-
if (entry.preserveExisting &&
|
|
28913
|
+
const destPath = path56.join(baseDir, relPath);
|
|
28914
|
+
if (entry.preserveExisting && fs43.existsSync(destPath)) {
|
|
28177
28915
|
clack2.log.info(`Preserved ${relPath}`);
|
|
28178
28916
|
updatedTemplateNames.add(name);
|
|
28179
28917
|
skipped++;
|
|
28180
28918
|
return false;
|
|
28181
28919
|
}
|
|
28182
|
-
fsExtra.ensureDirSync(
|
|
28183
|
-
|
|
28920
|
+
fsExtra.ensureDirSync(path56.dirname(destPath));
|
|
28921
|
+
fs43.writeFileSync(destPath, content, "utf-8");
|
|
28184
28922
|
clack2.log.success(`Updated ${relPath}`);
|
|
28185
28923
|
updatedTemplateNames.add(name);
|
|
28186
28924
|
trackPackageDependencies(name);
|
|
@@ -28209,15 +28947,15 @@ async function runUpdateCommand(components, options) {
|
|
|
28209
28947
|
}
|
|
28210
28948
|
const namespace = config.frameworkConfig.next.namespace;
|
|
28211
28949
|
const namespacedAssetFile = applyAdminNamespaceToPath(assetFile, namespace);
|
|
28212
|
-
const destPath =
|
|
28213
|
-
fsExtra.ensureDirSync(
|
|
28214
|
-
writeNamespacedFile(
|
|
28950
|
+
const destPath = path56.join(admin, "components", assetDirectory, namespacedAssetFile);
|
|
28951
|
+
fsExtra.ensureDirSync(path56.dirname(destPath));
|
|
28952
|
+
writeNamespacedFile(path56.join(assetDir, assetFile), destPath, namespace);
|
|
28215
28953
|
clack2.log.success(`Updated components/${assetDirectory}/${namespacedAssetFile}`);
|
|
28216
28954
|
if (assetDirectory === "custom") {
|
|
28217
|
-
const assetSubdir =
|
|
28218
|
-
if (
|
|
28955
|
+
const assetSubdir = path56.join(assetDir, name);
|
|
28956
|
+
if (fs43.existsSync(assetSubdir) && fs43.statSync(assetSubdir).isDirectory()) {
|
|
28219
28957
|
const namespacedName = applyAdminNamespaceToPath(name, namespace);
|
|
28220
|
-
const destSubdir =
|
|
28958
|
+
const destSubdir = path56.join(admin, "components", assetDirectory, namespacedName);
|
|
28221
28959
|
fsExtra.emptyDirSync(destSubdir);
|
|
28222
28960
|
copyNamespacedDirectory(assetSubdir, destSubdir, namespace);
|
|
28223
28961
|
clack2.log.success(`Updated components/${assetDirectory}/${namespacedName}/ (all files)`);
|
|
@@ -28245,17 +28983,17 @@ async function runUpdateCommand(components, options) {
|
|
|
28245
28983
|
"content-editor"
|
|
28246
28984
|
);
|
|
28247
28985
|
const namespace = config.frameworkConfig.next.namespace;
|
|
28248
|
-
const destBaseDir =
|
|
28249
|
-
if (!
|
|
28986
|
+
const destBaseDir = path56.join(admin, "components", "custom", "content-editor");
|
|
28987
|
+
if (!fs43.existsSync(srcBaseDir)) {
|
|
28250
28988
|
return false;
|
|
28251
28989
|
}
|
|
28252
28990
|
let copied = false;
|
|
28253
28991
|
for (const directory of TIPTAP_CONTENT_EDITOR_DIRECTORIES) {
|
|
28254
|
-
const srcDir =
|
|
28255
|
-
if (!
|
|
28992
|
+
const srcDir = path56.join(srcBaseDir, directory);
|
|
28993
|
+
if (!fs43.existsSync(srcDir)) {
|
|
28256
28994
|
continue;
|
|
28257
28995
|
}
|
|
28258
|
-
const destDir =
|
|
28996
|
+
const destDir = path56.join(destBaseDir, applyAdminNamespaceToPath(directory, namespace));
|
|
28259
28997
|
fsExtra.emptyDirSync(destDir);
|
|
28260
28998
|
copyNamespacedDirectory(srcDir, destDir, namespace);
|
|
28261
28999
|
copied = true;
|
|
@@ -28267,7 +29005,7 @@ async function runUpdateCommand(components, options) {
|
|
|
28267
29005
|
updatedStaticNames.add(key);
|
|
28268
29006
|
trackPackageDependencies("tiptap");
|
|
28269
29007
|
updated++;
|
|
28270
|
-
removeLegacyDirectory(
|
|
29008
|
+
removeLegacyDirectory(path56.join(admin, "components", "custom", "tiptap"));
|
|
28271
29009
|
for (const dependencyName of TIPTAP_TEMPLATE_DEPENDENCIES) {
|
|
28272
29010
|
writeNamedDependency(dependencyName);
|
|
28273
29011
|
}
|
|
@@ -28327,8 +29065,8 @@ async function runUpdateCommand(components, options) {
|
|
|
28327
29065
|
);
|
|
28328
29066
|
}
|
|
28329
29067
|
function removeLegacyDirectory(dirPath) {
|
|
28330
|
-
if (
|
|
28331
|
-
|
|
29068
|
+
if (fs43.existsSync(dirPath)) {
|
|
29069
|
+
fs43.rmSync(dirPath, { recursive: true, force: true });
|
|
28332
29070
|
}
|
|
28333
29071
|
}
|
|
28334
29072
|
function validateShadcnPresetOptions(components, options) {
|
|
@@ -28372,22 +29110,22 @@ function runShadcnPresetUpdate({
|
|
|
28372
29110
|
only
|
|
28373
29111
|
}) {
|
|
28374
29112
|
const namespace = resolveAdminNamespace(config.frameworkConfig.next.namespace);
|
|
28375
|
-
const adminGlobalsPath =
|
|
28376
|
-
const componentsJsonPath =
|
|
29113
|
+
const adminGlobalsPath = path56.join(cwd, config.paths.admin, namespace.globalsFile);
|
|
29114
|
+
const componentsJsonPath = path56.join(cwd, "components.json");
|
|
28377
29115
|
const shadcnBackupPath = `${componentsJsonPath}.bak`;
|
|
28378
29116
|
const restoreAfterApplyPaths = [
|
|
28379
29117
|
componentsJsonPath,
|
|
28380
29118
|
shadcnBackupPath,
|
|
28381
|
-
|
|
29119
|
+
path56.join(cwd, config.paths.admin, "lib", "utils.ts"),
|
|
28382
29120
|
...getHostProjectFilesToRestore(cwd)
|
|
28383
29121
|
];
|
|
28384
29122
|
if (!preset) {
|
|
28385
29123
|
clack2.cancel("--shadcn-preset requires a preset code, preset name, or preset URL.");
|
|
28386
29124
|
process.exit(1);
|
|
28387
29125
|
}
|
|
28388
|
-
if (!
|
|
29126
|
+
if (!fs43.existsSync(adminGlobalsPath)) {
|
|
28389
29127
|
clack2.cancel(
|
|
28390
|
-
`Admin globals file not found at ${
|
|
29128
|
+
`Admin globals file not found at ${path56.relative(cwd, adminGlobalsPath)}. Run 'betterstart update admin-globals' first.`
|
|
28391
29129
|
);
|
|
28392
29130
|
process.exit(1);
|
|
28393
29131
|
}
|
|
@@ -28397,10 +29135,10 @@ function runShadcnPresetUpdate({
|
|
|
28397
29135
|
snapshot: snapshotFile(filePath)
|
|
28398
29136
|
}));
|
|
28399
29137
|
clack2.intro("BetterStart Shadcn Preset");
|
|
28400
|
-
clack2.log.info(`Applying preset to ${
|
|
29138
|
+
clack2.log.info(`Applying preset to ${path56.join(config.paths.admin, "components/ui")}`);
|
|
28401
29139
|
let failed = false;
|
|
28402
29140
|
try {
|
|
28403
|
-
|
|
29141
|
+
fs43.writeFileSync(
|
|
28404
29142
|
componentsJsonPath,
|
|
28405
29143
|
`${JSON.stringify(createAdminShadcnComponentsJson(config), null, 2)}
|
|
28406
29144
|
`,
|
|
@@ -28456,8 +29194,8 @@ function toPosixPath(value) {
|
|
|
28456
29194
|
}
|
|
28457
29195
|
function resolveLocalShadcnBin(cwd) {
|
|
28458
29196
|
const binName = process.platform === "win32" ? "shadcn.cmd" : "shadcn";
|
|
28459
|
-
const shadcnBin =
|
|
28460
|
-
if (!
|
|
29197
|
+
const shadcnBin = path56.join(cwd, "node_modules", ".bin", binName);
|
|
29198
|
+
if (!fs43.existsSync(shadcnBin)) {
|
|
28461
29199
|
clack2.cancel(
|
|
28462
29200
|
`shadcn is not installed in this project. Run 'betterstart update-deps' and try again.`
|
|
28463
29201
|
);
|
|
@@ -28478,29 +29216,29 @@ function getHostProjectFilesToRestore(cwd) {
|
|
|
28478
29216
|
"app/globals.css",
|
|
28479
29217
|
"src/app/globals.css"
|
|
28480
29218
|
];
|
|
28481
|
-
return hostRelativePaths.map((relativePath) =>
|
|
29219
|
+
return hostRelativePaths.map((relativePath) => path56.join(cwd, relativePath));
|
|
28482
29220
|
}
|
|
28483
29221
|
function snapshotFile(filePath) {
|
|
28484
|
-
if (!
|
|
29222
|
+
if (!fs43.existsSync(filePath)) {
|
|
28485
29223
|
return { existed: false };
|
|
28486
29224
|
}
|
|
28487
|
-
return { existed: true, content:
|
|
29225
|
+
return { existed: true, content: fs43.readFileSync(filePath, "utf-8") };
|
|
28488
29226
|
}
|
|
28489
29227
|
function restoreFile(filePath, snapshot) {
|
|
28490
29228
|
if (snapshot.existed) {
|
|
28491
|
-
|
|
29229
|
+
fs43.writeFileSync(filePath, snapshot.content ?? "", "utf-8");
|
|
28492
29230
|
return;
|
|
28493
29231
|
}
|
|
28494
|
-
if (
|
|
28495
|
-
|
|
29232
|
+
if (fs43.existsSync(filePath)) {
|
|
29233
|
+
fs43.rmSync(filePath, { force: true });
|
|
28496
29234
|
}
|
|
28497
29235
|
}
|
|
28498
29236
|
|
|
28499
29237
|
// adapters/next/commands/update-deps.ts
|
|
28500
|
-
import
|
|
29238
|
+
import path57 from "path";
|
|
28501
29239
|
import * as clack3 from "@clack/prompts";
|
|
28502
29240
|
async function runUpdateDepsCommand(options) {
|
|
28503
|
-
const cwd = options.cwd ?
|
|
29241
|
+
const cwd = options.cwd ? path57.resolve(options.cwd) : process.cwd();
|
|
28504
29242
|
clack3.intro("BetterStart Update Dependencies");
|
|
28505
29243
|
const pm = detectPackageManager(cwd);
|
|
28506
29244
|
clack3.log.info(`Package manager: ${pm}`);
|
|
@@ -28511,7 +29249,7 @@ async function runUpdateDepsCommand(options) {
|
|
|
28511
29249
|
false
|
|
28512
29250
|
);
|
|
28513
29251
|
const cliDependencyPlan = getCliDependencySyncPlan(cwd);
|
|
28514
|
-
const s =
|
|
29252
|
+
const s = spinner2();
|
|
28515
29253
|
s.start("Installing dependencies...");
|
|
28516
29254
|
const result = await installDependenciesAsync({
|
|
28517
29255
|
cwd,
|
|
@@ -28534,26 +29272,26 @@ async function runUpdateDepsCommand(options) {
|
|
|
28534
29272
|
}
|
|
28535
29273
|
|
|
28536
29274
|
// adapters/next/commands/update-styles.ts
|
|
28537
|
-
import
|
|
28538
|
-
import
|
|
29275
|
+
import fs44 from "fs";
|
|
29276
|
+
import path58 from "path";
|
|
28539
29277
|
import * as clack4 from "@clack/prompts";
|
|
28540
29278
|
async function runUpdateStylesCommand(options) {
|
|
28541
|
-
const cwd = options.cwd ?
|
|
29279
|
+
const cwd = options.cwd ? path58.resolve(options.cwd) : process.cwd();
|
|
28542
29280
|
clack4.intro("BetterStart Update Styles");
|
|
28543
29281
|
const config = await resolveConfig(cwd);
|
|
28544
29282
|
const adminDir = config.paths.admin;
|
|
28545
29283
|
const namespace = resolveAdminNamespace(config.frameworkConfig.next.namespace);
|
|
28546
|
-
const targetPath =
|
|
28547
|
-
if (!
|
|
28548
|
-
clack4.cancel(`${namespace.globalsFile} not found at ${
|
|
29284
|
+
const targetPath = path58.join(cwd, adminDir, namespace.globalsFile);
|
|
29285
|
+
if (!fs44.existsSync(targetPath)) {
|
|
29286
|
+
clack4.cancel(`${namespace.globalsFile} not found at ${path58.relative(cwd, targetPath)}`);
|
|
28549
29287
|
process.exit(1);
|
|
28550
29288
|
}
|
|
28551
|
-
|
|
29289
|
+
fs44.writeFileSync(
|
|
28552
29290
|
targetPath,
|
|
28553
29291
|
applyAdminNamespaceToContent(readTemplate("admin-globals.css"), namespace.segment),
|
|
28554
29292
|
"utf-8"
|
|
28555
29293
|
);
|
|
28556
|
-
clack4.log.success(`Updated ${
|
|
29294
|
+
clack4.log.success(`Updated ${path58.relative(cwd, targetPath)}`);
|
|
28557
29295
|
clack4.outro("Styles updated");
|
|
28558
29296
|
}
|
|
28559
29297
|
|