@rebasepro/cli 0.0.1-canary.4d4fb3e → 0.0.1-canary.ca2cb6e
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/commands/cli.test.d.ts +1 -0
- package/dist/commands/dev.test.d.ts +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/generate_sdk.d.ts +1 -1
- package/dist/commands/init.test.d.ts +1 -0
- package/dist/index.cjs +188 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +188 -62
- package/dist/index.es.js.map +1 -1
- package/dist/utils/project.test.d.ts +1 -0
- package/package.json +67 -66
- package/templates/template/.env.template +22 -1
- package/templates/template/README.md +2 -2
- package/templates/template/backend/Dockerfile +6 -6
- package/templates/template/backend/functions/hello.ts +24 -6
- package/templates/template/backend/package.json +2 -2
- package/templates/template/backend/src/env.ts +52 -0
- package/templates/template/backend/src/index.ts +86 -34
- package/templates/template/backend/tsconfig.json +1 -1
- package/templates/template/config/collections/authors.ts +45 -0
- package/templates/template/config/collections/index.ts +5 -0
- package/templates/template/{shared → config}/collections/posts.ts +39 -1
- package/templates/template/config/collections/tags.ts +27 -0
- package/templates/template/{shared → config}/package.json +1 -1
- package/templates/template/docker-compose.yml +12 -0
- package/templates/template/frontend/Dockerfile +3 -3
- package/templates/template/frontend/package.json +2 -2
- package/templates/template/frontend/src/App.tsx +11 -12
- package/templates/template/frontend/src/main.tsx +2 -2
- package/templates/template/frontend/vite.config.ts +1 -1
- package/templates/template/pnpm-workspace.yaml +1 -1
- package/templates/template/scripts/example.ts +91 -0
- package/templates/template/shared/collections/index.ts +0 -3
- /package/templates/template/{shared → config}/index.ts +0 -0
- /package/templates/template/{shared → config}/tsconfig.json +0 -0
package/dist/index.es.js
CHANGED
|
@@ -81,8 +81,8 @@ async function promptForOptions(rawArgs) {
|
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
const answers = await inquirer.prompt(questions);
|
|
84
|
-
const
|
|
85
|
-
const
|
|
84
|
+
const targetDirectory = path.resolve(process.cwd(), nameArg || answers.projectName);
|
|
85
|
+
const projectName = path.basename(targetDirectory);
|
|
86
86
|
const templateDirectory = path.resolve(cliRoot, "templates", "template");
|
|
87
87
|
return {
|
|
88
88
|
projectName,
|
|
@@ -179,7 +179,7 @@ POSTGRES_PASSWORD=${dbPassword}
|
|
|
179
179
|
console.log("");
|
|
180
180
|
console.log(` ${chalk.cyan("pnpm dev")}`);
|
|
181
181
|
console.log("");
|
|
182
|
-
console.log(chalk.gray("This starts both the backend (
|
|
182
|
+
console.log(chalk.gray("This starts both the backend (Hono + PostgreSQL)") + chalk.gray(" and the frontend (Vite + React) concurrently."));
|
|
183
183
|
console.log("");
|
|
184
184
|
console.log(chalk.gray("Docs: https://rebase.pro/docs"));
|
|
185
185
|
console.log(chalk.gray("GitHub: https://github.com/rebasepro/rebase"));
|
|
@@ -190,14 +190,21 @@ async function replacePlaceholders(options) {
|
|
|
190
190
|
"package.json",
|
|
191
191
|
"frontend/package.json",
|
|
192
192
|
"backend/package.json",
|
|
193
|
-
"
|
|
193
|
+
"config/package.json",
|
|
194
194
|
"frontend/index.html"
|
|
195
195
|
];
|
|
196
|
+
const packageJsonPath = path.resolve(cliRoot, "package.json");
|
|
197
|
+
let cliVersion = "latest";
|
|
198
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
199
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
200
|
+
cliVersion = pkg.version || "latest";
|
|
201
|
+
}
|
|
196
202
|
for (const file of filesToProcess) {
|
|
197
203
|
const fullPath = path.resolve(options.targetDirectory, file);
|
|
198
204
|
if (!fs.existsSync(fullPath)) continue;
|
|
199
205
|
let content = fs.readFileSync(fullPath, "utf-8");
|
|
200
206
|
content = content.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
|
|
207
|
+
content = content.replace(/("@rebasepro\/[^"]+":\s*)"workspace:\*"/g, `$1"${cliVersion}"`);
|
|
201
208
|
fs.writeFileSync(fullPath, content, "utf-8");
|
|
202
209
|
}
|
|
203
210
|
}
|
|
@@ -309,7 +316,7 @@ async function generateSdkCommand(args) {
|
|
|
309
316
|
console.log(chalk.green.bold(" ✓ SDK generated successfully!"));
|
|
310
317
|
console.log("");
|
|
311
318
|
console.log(chalk.gray(" Usage:"));
|
|
312
|
-
console.log(chalk.gray(
|
|
319
|
+
console.log(chalk.gray(" import { createRebaseClient } from '@rebasepro/client';"));
|
|
313
320
|
console.log(chalk.gray(` import type { Database } from './${path.relative(cwd, path.join(resolvedOutput, "database.types"))}';`));
|
|
314
321
|
console.log("");
|
|
315
322
|
console.log(chalk.gray(" const rebase = createRebaseClient<Database>({"));
|
|
@@ -336,7 +343,7 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
336
343
|
}
|
|
337
344
|
} catch {
|
|
338
345
|
}
|
|
339
|
-
if (fs.existsSync(path.join(dir, "backend")) && fs.existsSync(path.join(dir, "
|
|
346
|
+
if (fs.existsSync(path.join(dir, "backend")) && fs.existsSync(path.join(dir, "config"))) {
|
|
340
347
|
return dir;
|
|
341
348
|
}
|
|
342
349
|
}
|
|
@@ -353,7 +360,10 @@ function getActiveBackendPlugin(backendDir) {
|
|
|
353
360
|
if (!fs.existsSync(pkgPath)) return null;
|
|
354
361
|
try {
|
|
355
362
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
356
|
-
const deps = {
|
|
363
|
+
const deps = {
|
|
364
|
+
...pkg.dependencies,
|
|
365
|
+
...pkg.devDependencies
|
|
366
|
+
};
|
|
357
367
|
for (const dep of Object.keys(deps)) {
|
|
358
368
|
if (dep.startsWith("@rebasepro/server-") && dep !== "@rebasepro/server-core") {
|
|
359
369
|
return dep;
|
|
@@ -464,13 +474,13 @@ async function schemaCommand(subcommand, rawArgs) {
|
|
|
464
474
|
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
465
475
|
process.exit(1);
|
|
466
476
|
}
|
|
467
|
-
await execa(tsxBin, [pluginCli,
|
|
477
|
+
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
468
478
|
cwd: backendDir,
|
|
469
479
|
stdio: "inherit",
|
|
470
480
|
env
|
|
471
481
|
});
|
|
472
482
|
} else {
|
|
473
|
-
await execa("node", [pluginCli,
|
|
483
|
+
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
474
484
|
cwd: backendDir,
|
|
475
485
|
stdio: "inherit",
|
|
476
486
|
env
|
|
@@ -528,13 +538,13 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
528
538
|
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
529
539
|
process.exit(1);
|
|
530
540
|
}
|
|
531
|
-
await execa(tsxBin, [pluginCli,
|
|
541
|
+
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
532
542
|
cwd: backendDir,
|
|
533
543
|
stdio: "inherit",
|
|
534
544
|
env
|
|
535
545
|
});
|
|
536
546
|
} else {
|
|
537
|
-
await execa("node", [pluginCli,
|
|
547
|
+
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
538
548
|
cwd: backendDir,
|
|
539
549
|
stdio: "inherit",
|
|
540
550
|
env
|
|
@@ -572,6 +582,27 @@ ${chalk.green.bold("Examples")}
|
|
|
572
582
|
rebase db branch create feature_auth
|
|
573
583
|
`);
|
|
574
584
|
}
|
|
585
|
+
const DEV_PORT_FILENAME = ".rebase-dev-port";
|
|
586
|
+
function getProjectPort(projectRoot) {
|
|
587
|
+
let hash = 0;
|
|
588
|
+
for (let i = 0; i < projectRoot.length; i++) {
|
|
589
|
+
hash = (hash << 5) - hash + projectRoot.charCodeAt(i) | 0;
|
|
590
|
+
}
|
|
591
|
+
return 3001 + Math.abs(hash) % 999;
|
|
592
|
+
}
|
|
593
|
+
function resolveStartPort(projectRoot, explicitPort) {
|
|
594
|
+
if (explicitPort) return explicitPort;
|
|
595
|
+
if (process.env.PORT) return parseInt(process.env.PORT, 10);
|
|
596
|
+
try {
|
|
597
|
+
const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
|
|
598
|
+
if (fs.existsSync(portFile)) {
|
|
599
|
+
const saved = parseInt(fs.readFileSync(portFile, "utf-8").trim(), 10);
|
|
600
|
+
if (saved > 0 && saved < 65536) return saved;
|
|
601
|
+
}
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
return getProjectPort(projectRoot);
|
|
605
|
+
}
|
|
575
606
|
async function devCommand(rawArgs) {
|
|
576
607
|
const args = arg(
|
|
577
608
|
{
|
|
@@ -599,6 +630,7 @@ async function devCommand(rawArgs) {
|
|
|
599
630
|
const frontendDir = findFrontendDir(projectRoot);
|
|
600
631
|
const backendOnly = args["--backend-only"] || false;
|
|
601
632
|
const frontendOnly = args["--frontend-only"] || false;
|
|
633
|
+
const startPort = resolveStartPort(projectRoot, args["--port"]);
|
|
602
634
|
console.log("");
|
|
603
635
|
console.log(chalk.bold(" 🚀 Rebase Dev Server"));
|
|
604
636
|
console.log("");
|
|
@@ -607,6 +639,7 @@ async function devCommand(rawArgs) {
|
|
|
607
639
|
let backendUrl = "";
|
|
608
640
|
let debounceSummary = null;
|
|
609
641
|
let bannerPrinted = false;
|
|
642
|
+
let resolvedBackendPort = null;
|
|
610
643
|
const stripAnsi = (str) => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
|
|
611
644
|
function printSummary() {
|
|
612
645
|
if (!frontendUrl || !backendUrl) return;
|
|
@@ -619,7 +652,7 @@ async function devCommand(rawArgs) {
|
|
|
619
652
|
console.log(chalk.cyan("│ ✨ Rebase Admin App is ready! │"));
|
|
620
653
|
const cleanUrl = stripAnsi(frontendUrl);
|
|
621
654
|
const paddedUrl = cleanUrl.padEnd(40);
|
|
622
|
-
console.log(chalk.cyan(
|
|
655
|
+
console.log(chalk.cyan("│ 👉 Frontend URL: ") + chalk.white(paddedUrl) + chalk.cyan("│"));
|
|
623
656
|
console.log(chalk.cyan("│ │"));
|
|
624
657
|
console.log(chalk.cyan("└────────────────────────────────────────────────────────────┘"));
|
|
625
658
|
console.log("");
|
|
@@ -627,15 +660,74 @@ async function devCommand(rawArgs) {
|
|
|
627
660
|
}, 500);
|
|
628
661
|
}
|
|
629
662
|
const cleanup = () => {
|
|
663
|
+
try {
|
|
664
|
+
const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
|
|
665
|
+
if (fs.existsSync(portFile)) fs.unlinkSync(portFile);
|
|
666
|
+
const urlFile = path.join(projectRoot, ".rebase-dev-url");
|
|
667
|
+
if (fs.existsSync(urlFile)) fs.unlinkSync(urlFile);
|
|
668
|
+
} catch {
|
|
669
|
+
}
|
|
630
670
|
children.forEach((child) => {
|
|
631
|
-
if (!child.killed) {
|
|
632
|
-
|
|
671
|
+
if (child.pid && !child.killed) {
|
|
672
|
+
try {
|
|
673
|
+
if (process.platform === "win32") {
|
|
674
|
+
execa.commandSync(`taskkill /pid ${child.pid} /T /F`);
|
|
675
|
+
} else {
|
|
676
|
+
process.kill(-child.pid, "SIGKILL");
|
|
677
|
+
}
|
|
678
|
+
} catch (e) {
|
|
679
|
+
try {
|
|
680
|
+
child.kill("SIGKILL");
|
|
681
|
+
} catch (err) {
|
|
682
|
+
}
|
|
683
|
+
}
|
|
633
684
|
}
|
|
634
685
|
});
|
|
635
686
|
process.exit(0);
|
|
636
687
|
};
|
|
637
688
|
process.on("SIGINT", cleanup);
|
|
638
689
|
process.on("SIGTERM", cleanup);
|
|
690
|
+
function startFrontend(backendPort) {
|
|
691
|
+
if (!frontendDir) return;
|
|
692
|
+
console.log(` ${chalk.magenta("▶")} Frontend: ${chalk.gray(frontendDir)}`);
|
|
693
|
+
const frontendEnv = { ...process.env };
|
|
694
|
+
if (backendPort) {
|
|
695
|
+
frontendEnv.VITE_API_URL = `http://localhost:${backendPort}`;
|
|
696
|
+
console.log(` ${chalk.gray("↳ VITE_API_URL")} = ${chalk.white(`http://localhost:${backendPort}`)}`);
|
|
697
|
+
}
|
|
698
|
+
const frontendChild = execa(
|
|
699
|
+
"pnpm",
|
|
700
|
+
["run", "dev"],
|
|
701
|
+
{
|
|
702
|
+
cwd: frontendDir,
|
|
703
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
704
|
+
env: frontendEnv,
|
|
705
|
+
shell: true,
|
|
706
|
+
detached: process.platform !== "win32"
|
|
707
|
+
}
|
|
708
|
+
);
|
|
709
|
+
frontendChild.catch(() => {
|
|
710
|
+
});
|
|
711
|
+
frontendChild.stdout?.on("data", (data) => {
|
|
712
|
+
const lines = data.toString().split("\n").filter(Boolean);
|
|
713
|
+
lines.forEach((line) => {
|
|
714
|
+
console.log(`${chalk.magenta.bold("[admin]")} ${line}`);
|
|
715
|
+
const cleanLine = stripAnsi(line);
|
|
716
|
+
const urlMatch = cleanLine.match(/(http:\/\/(?:localhost|127\.0\.0\.1):\d+)/);
|
|
717
|
+
if (cleanLine.includes("Local:") && urlMatch) {
|
|
718
|
+
frontendUrl = urlMatch[1];
|
|
719
|
+
printSummary();
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
frontendChild.stderr?.on("data", (data) => {
|
|
724
|
+
const lines = data.toString().split("\n").filter(Boolean);
|
|
725
|
+
lines.forEach((line) => {
|
|
726
|
+
console.log(`${chalk.magenta.bold("[admin]")} ${line}`);
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
children.push(frontendChild);
|
|
730
|
+
}
|
|
639
731
|
if (!frontendOnly && backendDir) {
|
|
640
732
|
const tsxBin = resolveTsx(projectRoot);
|
|
641
733
|
if (!tsxBin) {
|
|
@@ -648,22 +740,19 @@ async function devCommand(rawArgs) {
|
|
|
648
740
|
if (envFile) {
|
|
649
741
|
env.DOTENV_CONFIG_PATH = envFile;
|
|
650
742
|
}
|
|
651
|
-
|
|
652
|
-
env.PORT = String(args["--port"]);
|
|
653
|
-
}
|
|
654
|
-
path.join(backendDir, "src", "index.ts");
|
|
655
|
-
const watchDirs = [
|
|
656
|
-
`--watch="${path.join("..", "shared", "**", "*")}"`
|
|
657
|
-
];
|
|
743
|
+
env.PORT = String(startPort);
|
|
658
744
|
console.log(` ${chalk.cyan("▶")} Backend: ${chalk.gray(backendDir)}`);
|
|
745
|
+
console.log(` ${chalk.gray("↳ PORT")} = ${chalk.white(String(startPort))}`);
|
|
746
|
+
let frontendLaunched = false;
|
|
659
747
|
const backendChild = execa(
|
|
660
748
|
tsxBin,
|
|
661
|
-
["watch",
|
|
749
|
+
["watch", `--watch="${path.join("..", "config", "**", "*")}"`, "--conditions", "development", "src/index.ts"],
|
|
662
750
|
{
|
|
663
751
|
cwd: backendDir,
|
|
664
752
|
stdio: ["inherit", "pipe", "pipe"],
|
|
665
753
|
env,
|
|
666
|
-
shell: true
|
|
754
|
+
shell: true,
|
|
755
|
+
detached: process.platform !== "win32"
|
|
667
756
|
}
|
|
668
757
|
);
|
|
669
758
|
backendChild.catch(() => {
|
|
@@ -673,9 +762,19 @@ async function devCommand(rawArgs) {
|
|
|
673
762
|
lines.forEach((line) => {
|
|
674
763
|
console.log(`${chalk.cyan.bold("[backend]")} ${line}`);
|
|
675
764
|
const cleanLine = stripAnsi(line);
|
|
676
|
-
|
|
765
|
+
const serverMatch = cleanLine.match(/Server running at http:\/\/(?:localhost|127\.0\.0\.1):(\d+)/);
|
|
766
|
+
if (serverMatch) {
|
|
767
|
+
resolvedBackendPort = parseInt(serverMatch[1], 10);
|
|
677
768
|
backendUrl = "started";
|
|
678
769
|
printSummary();
|
|
770
|
+
const urlFile = path.join(projectRoot, ".rebase-dev-url");
|
|
771
|
+
fs.writeFileSync(urlFile, `http://localhost:${resolvedBackendPort}`, "utf-8");
|
|
772
|
+
const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
|
|
773
|
+
fs.writeFileSync(portFile, String(resolvedBackendPort), "utf-8");
|
|
774
|
+
if (!backendOnly && frontendDir && !frontendLaunched) {
|
|
775
|
+
frontendLaunched = true;
|
|
776
|
+
startFrontend(resolvedBackendPort);
|
|
777
|
+
}
|
|
679
778
|
}
|
|
680
779
|
});
|
|
681
780
|
});
|
|
@@ -689,39 +788,8 @@ async function devCommand(rawArgs) {
|
|
|
689
788
|
} else if (!frontendOnly && !backendDir) {
|
|
690
789
|
console.warn(chalk.yellow(" ⚠ No backend/ directory found, skipping backend."));
|
|
691
790
|
}
|
|
692
|
-
if (!backendOnly && frontendDir) {
|
|
693
|
-
|
|
694
|
-
const frontendChild = execa(
|
|
695
|
-
"pnpm",
|
|
696
|
-
["run", "dev"],
|
|
697
|
-
{
|
|
698
|
-
cwd: frontendDir,
|
|
699
|
-
stdio: ["inherit", "pipe", "pipe"],
|
|
700
|
-
env: process.env,
|
|
701
|
-
shell: true
|
|
702
|
-
}
|
|
703
|
-
);
|
|
704
|
-
frontendChild.catch(() => {
|
|
705
|
-
});
|
|
706
|
-
frontendChild.stdout?.on("data", (data) => {
|
|
707
|
-
const lines = data.toString().split("\n").filter(Boolean);
|
|
708
|
-
lines.forEach((line) => {
|
|
709
|
-
console.log(`${chalk.magenta.bold("[frontend]")} ${line}`);
|
|
710
|
-
const cleanLine = stripAnsi(line);
|
|
711
|
-
const urlMatch = cleanLine.match(/(http:\/\/(?:localhost|127\.0\.0\.1):\d+)/);
|
|
712
|
-
if (cleanLine.includes("Local:") && urlMatch) {
|
|
713
|
-
frontendUrl = urlMatch[1];
|
|
714
|
-
printSummary();
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
});
|
|
718
|
-
frontendChild.stderr?.on("data", (data) => {
|
|
719
|
-
const lines = data.toString().split("\n").filter(Boolean);
|
|
720
|
-
lines.forEach((line) => {
|
|
721
|
-
console.log(`${chalk.magenta.bold("[frontend]")} ${line}`);
|
|
722
|
-
});
|
|
723
|
-
});
|
|
724
|
-
children.push(frontendChild);
|
|
791
|
+
if (!backendOnly && frontendDir && (frontendOnly || !backendDir)) {
|
|
792
|
+
startFrontend(null);
|
|
725
793
|
} else if (!backendOnly && !frontendDir) {
|
|
726
794
|
console.warn(chalk.yellow(" ⚠ No frontend/ directory found, skipping frontend."));
|
|
727
795
|
}
|
|
@@ -750,11 +818,19 @@ ${chalk.green.bold("Usage")}
|
|
|
750
818
|
${chalk.green.bold("Options")}
|
|
751
819
|
${chalk.blue("--backend-only, -b")} Only start the backend server
|
|
752
820
|
${chalk.blue("--frontend-only, -f")} Only start the frontend server
|
|
753
|
-
${chalk.blue("--port, -p")} Backend port (default:
|
|
821
|
+
${chalk.blue("--port, -p")} Backend port (default: auto-detected per project)
|
|
754
822
|
|
|
755
823
|
${chalk.green.bold("Description")}
|
|
756
|
-
Starts both the backend (tsx watch +
|
|
824
|
+
Starts both the backend (tsx watch + Hono) and frontend (Vite)
|
|
757
825
|
dev servers concurrently with color-coded output prefixes.
|
|
826
|
+
|
|
827
|
+
Each project automatically receives a unique default port derived
|
|
828
|
+
from its directory path, preventing collisions when running multiple
|
|
829
|
+
Rebase instances simultaneously.
|
|
830
|
+
|
|
831
|
+
If the assigned port is already in use, the server will automatically
|
|
832
|
+
try the next available port. The frontend is started only after the
|
|
833
|
+
backend is ready, and VITE_API_URL is injected automatically.
|
|
758
834
|
`);
|
|
759
835
|
}
|
|
760
836
|
async function authCommand(subcommand, rawArgs) {
|
|
@@ -891,6 +967,49 @@ ${chalk.green.bold("Examples")}
|
|
|
891
967
|
rebase auth reset-password --email user@example.com --password MyNewPass!
|
|
892
968
|
`);
|
|
893
969
|
}
|
|
970
|
+
async function doctorCommand(rawArgs) {
|
|
971
|
+
const projectRoot = requireProjectRoot();
|
|
972
|
+
const backendDir = requireBackendDir(projectRoot);
|
|
973
|
+
const activePlugin = getActiveBackendPlugin(backendDir);
|
|
974
|
+
if (!activePlugin) {
|
|
975
|
+
console.error(chalk.red("✗ Could not detect an active database plugin."));
|
|
976
|
+
console.error(chalk.gray(" Make sure a package like @rebasepro/server-postgresql is installed in backend/package.json."));
|
|
977
|
+
process.exit(1);
|
|
978
|
+
}
|
|
979
|
+
const pluginCli = resolvePluginCliScript(backendDir, activePlugin);
|
|
980
|
+
if (!pluginCli) {
|
|
981
|
+
console.error(chalk.red(`✗ Could not find CLI entry point for ${activePlugin}.`));
|
|
982
|
+
process.exit(1);
|
|
983
|
+
}
|
|
984
|
+
const envFile = findEnvFile(projectRoot);
|
|
985
|
+
const env = { ...process.env };
|
|
986
|
+
if (envFile) {
|
|
987
|
+
env.DOTENV_CONFIG_PATH = envFile;
|
|
988
|
+
}
|
|
989
|
+
try {
|
|
990
|
+
const isTs = pluginCli.endsWith(".ts");
|
|
991
|
+
if (isTs) {
|
|
992
|
+
const tsxBin = resolveTsx(projectRoot);
|
|
993
|
+
if (!tsxBin) {
|
|
994
|
+
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
995
|
+
process.exit(1);
|
|
996
|
+
}
|
|
997
|
+
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
998
|
+
cwd: backendDir,
|
|
999
|
+
stdio: "inherit",
|
|
1000
|
+
env
|
|
1001
|
+
});
|
|
1002
|
+
} else {
|
|
1003
|
+
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
1004
|
+
cwd: backendDir,
|
|
1005
|
+
stdio: "inherit",
|
|
1006
|
+
env
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
} catch {
|
|
1010
|
+
process.exit(1);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
894
1013
|
const __filename$1 = fileURLToPath(import.meta.url);
|
|
895
1014
|
const __dirname$1 = path.dirname(__filename$1);
|
|
896
1015
|
function getVersion() {
|
|
@@ -922,7 +1041,7 @@ async function entry(args) {
|
|
|
922
1041
|
}
|
|
923
1042
|
const command = parsedArgs._[0];
|
|
924
1043
|
const subcommand = parsedArgs._[1];
|
|
925
|
-
const namespacedCommands = ["schema", "db", "dev", "auth"];
|
|
1044
|
+
const namespacedCommands = ["schema", "db", "dev", "auth", "doctor"];
|
|
926
1045
|
if (!command || parsedArgs["--help"] && !namespacedCommands.includes(command)) {
|
|
927
1046
|
printHelp();
|
|
928
1047
|
return;
|
|
@@ -932,7 +1051,7 @@ async function entry(args) {
|
|
|
932
1051
|
case "init":
|
|
933
1052
|
await createRebaseApp(args);
|
|
934
1053
|
break;
|
|
935
|
-
case "generate-sdk":
|
|
1054
|
+
case "generate-sdk": {
|
|
936
1055
|
const sdkArgs = arg(
|
|
937
1056
|
{
|
|
938
1057
|
"--collections-dir": String,
|
|
@@ -946,11 +1065,12 @@ async function entry(args) {
|
|
|
946
1065
|
}
|
|
947
1066
|
);
|
|
948
1067
|
await generateSdkCommand({
|
|
949
|
-
collectionsDir: sdkArgs["--collections-dir"] || "./
|
|
1068
|
+
collectionsDir: sdkArgs["--collections-dir"] || "./config/collections",
|
|
950
1069
|
output: sdkArgs["--output"] || "./generated/sdk",
|
|
951
1070
|
cwd: process.cwd()
|
|
952
1071
|
});
|
|
953
1072
|
break;
|
|
1073
|
+
}
|
|
954
1074
|
case "schema":
|
|
955
1075
|
await schemaCommand(effectiveSubcommand, args);
|
|
956
1076
|
break;
|
|
@@ -963,6 +1083,9 @@ async function entry(args) {
|
|
|
963
1083
|
case "auth":
|
|
964
1084
|
await authCommand(effectiveSubcommand, args);
|
|
965
1085
|
break;
|
|
1086
|
+
case "doctor":
|
|
1087
|
+
await doctorCommand(args);
|
|
1088
|
+
break;
|
|
966
1089
|
default:
|
|
967
1090
|
console.log(chalk.red(`Unknown command: ${command}`));
|
|
968
1091
|
console.log("");
|
|
@@ -999,6 +1122,9 @@ ${chalk.green.bold("Auth")}
|
|
|
999
1122
|
${chalk.blue.bold("auth reset-password")} Reset a user's password
|
|
1000
1123
|
${chalk.blue.bold("auth")} ${chalk.gray("--help")} Show auth command help
|
|
1001
1124
|
|
|
1125
|
+
${chalk.green.bold("Diagnostics")}
|
|
1126
|
+
${chalk.blue.bold("doctor")} Detect schema drift between collections, schema, and DB
|
|
1127
|
+
|
|
1002
1128
|
${chalk.green.bold("Options")}
|
|
1003
1129
|
${chalk.blue("--version, -v")} Show version number
|
|
1004
1130
|
${chalk.blue("--help, -h")} Show this help message
|