@rebasepro/cli 0.0.1-canary.94dff14 → 0.0.1-canary.b6f40f8

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/index.es.js CHANGED
@@ -11,6 +11,45 @@ import crypto from "crypto";
11
11
  import { generateSDK } from "@rebasepro/sdk-generator";
12
12
  import { execSync, spawn } from "child_process";
13
13
  import * as os from "os";
14
+ function detectPackageManager(targetDir) {
15
+ const userAgent = process.env.npm_config_user_agent ?? "";
16
+ if (userAgent.startsWith("npm/")) return "npm";
17
+ if (userAgent.startsWith("pnpm/")) return "pnpm";
18
+ if (targetDir) {
19
+ if (fs.existsSync(path.join(targetDir, "package-lock.json"))) return "npm";
20
+ if (fs.existsSync(path.join(targetDir, "pnpm-lock.yaml"))) return "pnpm";
21
+ }
22
+ const cwd = process.cwd();
23
+ if (cwd !== targetDir) {
24
+ if (fs.existsSync(path.join(cwd, "package-lock.json"))) return "npm";
25
+ if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
26
+ }
27
+ return "pnpm";
28
+ }
29
+ function getPMCommands(pm) {
30
+ if (pm === "npm") {
31
+ return {
32
+ name: "npm",
33
+ install: ["npm", "install"],
34
+ run: (script) => ["npm", "run", script],
35
+ exec: (bin, args) => ["npx", bin, ...args],
36
+ view: (pkg, field) => ["npm", "view", pkg, field],
37
+ runAll: (script) => ["npm", "run", script, "--workspaces", "--if-present"],
38
+ runWorkspace: (workspace, script) => ["npm", "run", script, "-w", workspace],
39
+ workspaceProtocol: "*"
40
+ };
41
+ }
42
+ return {
43
+ name: "pnpm",
44
+ install: ["pnpm", "install"],
45
+ run: (script) => ["pnpm", "run", script],
46
+ exec: (bin, args) => ["pnpm", "exec", bin, ...args],
47
+ view: (pkg, field) => ["pnpm", "view", pkg, field],
48
+ runAll: (script) => ["pnpm", "-r", "run", script],
49
+ runWorkspace: (workspace, script) => ["pnpm", "--filter", workspace, script],
50
+ workspaceProtocol: "workspace:*"
51
+ };
52
+ }
14
53
  const access = promisify(fs.access);
15
54
  const copy = promisify(ncp);
16
55
  const __filename$2 = fileURLToPath(import.meta.url);
@@ -30,10 +69,11 @@ async function createRebaseApp(rawArgs) {
30
69
  console.log(`
31
70
  ${chalk.bold("Rebase")} — Create a new project šŸš€
32
71
  `);
33
- const options = await promptForOptions(rawArgs);
72
+ const pm = detectPackageManager();
73
+ const options = await promptForOptions(rawArgs, pm);
34
74
  await createProject(options);
35
75
  }
36
- async function promptForOptions(rawArgs) {
76
+ async function promptForOptions(rawArgs, pm) {
37
77
  const args = arg(
38
78
  {
39
79
  "--git": Boolean,
@@ -76,7 +116,7 @@ async function promptForOptions(rawArgs) {
76
116
  questions.push({
77
117
  type: "confirm",
78
118
  name: "installDeps",
79
- message: "Install dependencies with pnpm?",
119
+ message: `Install dependencies with ${pm}?`,
80
120
  default: true
81
121
  });
82
122
  }
@@ -97,6 +137,7 @@ async function promptForOptions(rawArgs) {
97
137
  const targetDirectory = path.resolve(process.cwd(), nameArg || answers.projectName);
98
138
  const projectName = path.basename(targetDirectory);
99
139
  const templateDirectory = path.resolve(cliRoot, "templates", "template");
140
+ const pmCommands = getPMCommands(pm);
100
141
  return {
101
142
  projectName,
102
143
  git: args["--git"] || answers.git || false,
@@ -104,7 +145,9 @@ async function promptForOptions(rawArgs) {
104
145
  targetDirectory,
105
146
  templateDirectory,
106
147
  databaseUrl: answers.databaseUrl?.trim() || void 0,
107
- introspect: answers.introspect || false
148
+ introspect: answers.introspect || false,
149
+ pm,
150
+ pmCommands
108
151
  };
109
152
  }
110
153
  async function createProject(options) {
@@ -137,34 +180,7 @@ async function createProject(options) {
137
180
  process.exit(1);
138
181
  }
139
182
  await replacePlaceholders(options);
140
- const envTemplatePath = path.join(options.targetDirectory, ".env.template");
141
- const envPath = path.join(options.targetDirectory, ".env");
142
- if (fs.existsSync(envTemplatePath) && !fs.existsSync(envPath)) {
143
- fs.renameSync(envTemplatePath, envPath);
144
- const jwtSecret = crypto.randomBytes(32).toString("hex");
145
- let envContent = fs.readFileSync(envPath, "utf-8");
146
- envContent = envContent.replace(
147
- "change-this-to-a-secure-random-string",
148
- jwtSecret
149
- );
150
- if (options.databaseUrl) {
151
- envContent = envContent.replace(
152
- "postgresql://rebase:password@localhost:5432/rebase",
153
- options.databaseUrl
154
- );
155
- } else {
156
- const dbPassword = crypto.randomBytes(16).toString("hex");
157
- envContent = envContent.replace(
158
- "postgresql://rebase:password@localhost:5432/rebase",
159
- `postgresql://rebase:${dbPassword}@localhost:5432/rebase`
160
- );
161
- envContent += `
162
- # Docker Compose Database Password
163
- POSTGRES_PASSWORD=${dbPassword}
164
- `;
165
- }
166
- fs.writeFileSync(envPath, envContent, "utf-8");
167
- }
183
+ configureEnvFile(options.targetDirectory, options.databaseUrl);
168
184
  if (options.git) {
169
185
  console.log(chalk.gray(" Initializing git repository..."));
170
186
  try {
@@ -173,17 +189,20 @@ POSTGRES_PASSWORD=${dbPassword}
173
189
  console.warn(chalk.yellow(" Warning: Failed to initialize git repository"));
174
190
  }
175
191
  }
192
+ const { pm, pmCommands } = options;
193
+ const installCmd = pmCommands.install;
194
+ const execCmd = pmCommands.exec("rebase", ["schema", "introspect", "--force"]);
176
195
  if (options.installDeps) {
177
196
  console.log("");
178
- console.log(chalk.gray(" Installing dependencies with pnpm..."));
197
+ console.log(chalk.gray(` Installing dependencies with ${pm}...`));
179
198
  console.log("");
180
199
  try {
181
- await execa("pnpm", ["install"], {
200
+ await execa(installCmd[0], installCmd.slice(1), {
182
201
  cwd: options.targetDirectory,
183
202
  stdio: "inherit"
184
203
  });
185
204
  } catch {
186
- console.warn(chalk.yellow(" Warning: Failed to install dependencies. You may need to run `pnpm install` manually."));
205
+ console.warn(chalk.yellow(` Warning: Failed to install dependencies. You may need to run \`${installCmd.join(" ")}\` manually.`));
187
206
  }
188
207
  }
189
208
  if (options.introspect) {
@@ -192,18 +211,18 @@ POSTGRES_PASSWORD=${dbPassword}
192
211
  console.log(chalk.gray(" Introspecting database and generating collections..."));
193
212
  console.log("");
194
213
  try {
195
- await execa("pnpm", ["exec", "rebase", "schema", "introspect"], {
214
+ await execa(execCmd[0], execCmd.slice(1), {
196
215
  cwd: options.targetDirectory,
197
216
  stdio: "inherit"
198
217
  });
199
218
  console.log(chalk.green(" Database successfully introspected!"));
200
219
  } catch {
201
220
  console.warn(chalk.yellow(" Warning: Failed to introspect database automatically."));
202
- console.warn(chalk.yellow(" You can run `pnpm exec rebase schema introspect` manually after setup."));
221
+ console.warn(chalk.yellow(` You can run \`${execCmd.join(" ")}\` manually after setup.`));
203
222
  }
204
223
  } else {
205
224
  console.warn(chalk.yellow(" Skipping introspection because dependencies were not installed."));
206
- console.warn(chalk.yellow(" Run `pnpm install` then `pnpm exec rebase schema introspect` manually."));
225
+ console.warn(chalk.yellow(` Run \`${installCmd.join(" ")}\` then \`${execCmd.join(" ")}\` manually.`));
207
226
  }
208
227
  }
209
228
  console.log("");
@@ -211,22 +230,23 @@ POSTGRES_PASSWORD=${dbPassword}
211
230
  console.log("");
212
231
  console.log(chalk.bold("Next steps:"));
213
232
  console.log("");
233
+ const runDev = pmCommands.run("dev");
214
234
  console.log(` ${chalk.cyan("cd")} ${options.projectName}`);
215
235
  if (!options.installDeps) {
216
- console.log(` ${chalk.cyan("pnpm install")}`);
236
+ console.log(` ${chalk.cyan(installCmd.join(" "))}`);
217
237
  }
218
238
  console.log("");
219
239
  if (options.databaseUrl) {
220
240
  console.log(chalk.gray(" # Your database is configured! Start the dev server:"));
221
241
  } else {
222
- console.log(chalk.gray(" # A local database configuration has been generated in .env"));
242
+ console.log(chalk.gray(" # A local database configuration has been generated in .env."));
223
243
  console.log(chalk.gray(" # If using the included docker-compose.yml, start it with:"));
224
244
  console.log(` ${chalk.cyan("docker compose up -d")}`);
225
245
  console.log("");
226
246
  console.log(chalk.gray(" # Then start the dev server:"));
227
247
  }
228
248
  console.log("");
229
- console.log(` ${chalk.cyan("pnpm dev")}`);
249
+ console.log(` ${chalk.cyan(runDev.join(" "))}`);
230
250
  console.log("");
231
251
  console.log(chalk.gray("This starts both the backend (Hono + PostgreSQL)") + chalk.gray(" and the frontend (Vite + React) concurrently."));
232
252
  console.log("");
@@ -240,7 +260,8 @@ async function replacePlaceholders(options) {
240
260
  "frontend/package.json",
241
261
  "backend/package.json",
242
262
  "config/package.json",
243
- "frontend/index.html"
263
+ "frontend/index.html",
264
+ "README.md"
244
265
  ];
245
266
  const packageJsonPath = path.resolve(cliRoot, "package.json");
246
267
  let cliVersion = "latest";
@@ -249,22 +270,23 @@ async function replacePlaceholders(options) {
249
270
  cliVersion = pkg.version || "latest";
250
271
  }
251
272
  const versionCache = /* @__PURE__ */ new Map();
273
+ const viewBin = "npm";
252
274
  const getPackageVersion = async (pkgName) => {
253
275
  if (versionCache.has(pkgName)) return versionCache.get(pkgName);
254
276
  let versionToUse = cliVersion;
255
277
  try {
256
- const { stdout } = await execa("npm", ["--loglevel", "error", "info", `${pkgName}@${cliVersion}`, "version"]);
278
+ const { stdout } = await execa(viewBin, ["view", `${pkgName}@${cliVersion}`, "version"]);
257
279
  if (!stdout.trim()) throw new Error("Not found");
258
280
  versionToUse = stdout.trim();
259
281
  } catch {
260
282
  try {
261
283
  const tag = cliVersion.includes("canary") ? "canary" : "latest";
262
- const { stdout } = await execa("npm", ["--loglevel", "error", "info", `${pkgName}@${tag}`, "version"]);
284
+ const { stdout } = await execa(viewBin, ["view", `${pkgName}@${tag}`, "version"]);
263
285
  if (!stdout.trim()) throw new Error("Not found");
264
286
  versionToUse = stdout.trim();
265
287
  } catch {
266
288
  try {
267
- const { stdout } = await execa("npm", ["--loglevel", "error", "info", pkgName, "version"]);
289
+ const { stdout } = await execa(viewBin, ["view", pkgName, "version"]);
268
290
  versionToUse = stdout.trim() || "latest";
269
291
  } catch {
270
292
  versionToUse = "latest";
@@ -299,6 +321,32 @@ async function replacePlaceholders(options) {
299
321
  fs.writeFileSync(fullPath, content, "utf-8");
300
322
  }
301
323
  }
324
+ function configureEnvFile(targetDirectory, databaseUrl) {
325
+ const envExamplePath = path.join(targetDirectory, ".env.example");
326
+ const envPath = path.join(targetDirectory, ".env");
327
+ if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {
328
+ fs.copyFileSync(envExamplePath, envPath);
329
+ const jwtSecret = crypto.randomBytes(32).toString("hex");
330
+ const dbPassword = crypto.randomBytes(16).toString("hex");
331
+ let envContent = fs.readFileSync(envPath, "utf-8");
332
+ envContent = envContent.replace(
333
+ /^JWT_SECRET=.*$/m,
334
+ `JWT_SECRET=${jwtSecret}`
335
+ );
336
+ if (databaseUrl) {
337
+ envContent = envContent.replace(
338
+ /^DATABASE_URL=.*$/m,
339
+ `DATABASE_URL=${databaseUrl}`
340
+ );
341
+ } else {
342
+ envContent = envContent.replace(
343
+ /^DATABASE_URL=.*$/m,
344
+ `DATABASE_URL=postgresql://rebase:${dbPassword}@localhost:5432/rebase`
345
+ );
346
+ }
347
+ fs.writeFileSync(envPath, envContent, "utf-8");
348
+ }
349
+ }
302
350
  async function loadCollections(collectionsDir) {
303
351
  const absDir = path.resolve(collectionsDir);
304
352
  if (!fs.existsSync(absDir)) {
@@ -660,7 +708,6 @@ ${chalk.green.bold("Usage")}
660
708
  ${chalk.green.bold("Commands")}
661
709
  ${chalk.gray("(Commands are provided by your active database driver plugin)")}
662
710
  ${chalk.blue.bold("push")} Apply schema directly to database (development)
663
- ${chalk.blue.bold("pull")} Introspect database → Schema
664
711
  ${chalk.blue.bold("generate")} Generate migration files
665
712
  ${chalk.blue.bold("migrate")} Run pending migrations
666
713
  ${chalk.blue.bold("studio")} Open Studio viewer
@@ -791,9 +838,12 @@ async function devCommand(rawArgs) {
791
838
  frontendEnv.VITE_API_URL = `http://localhost:${backendPort}`;
792
839
  console.log(` ${chalk.gray("↳ VITE_API_URL")} = ${chalk.white(`http://localhost:${backendPort}`)}`);
793
840
  }
841
+ const pm = detectPackageManager(projectRoot);
842
+ const pmCmds = getPMCommands(pm);
843
+ const runDevCmd = pmCmds.run("dev");
794
844
  const frontendChild = execa(
795
- "pnpm",
796
- ["run", "dev"],
845
+ runDevCmd[0],
846
+ runDevCmd.slice(1),
797
847
  {
798
848
  cwd: frontendDir,
799
849
  stdio: ["inherit", "pipe", "pipe"],
@@ -827,8 +877,10 @@ async function devCommand(rawArgs) {
827
877
  if (!frontendOnly && backendDir) {
828
878
  const tsxBin = resolveTsx(projectRoot);
829
879
  if (!tsxBin) {
880
+ const pmName = detectPackageManager(projectRoot);
881
+ const addCmd = pmName === "npm" ? "npm install -D tsx" : "pnpm add -D tsx";
830
882
  console.error(chalk.red(" āœ— Could not find tsx binary for backend."));
831
- console.error(chalk.gray(" Install it with: pnpm add -D tsx"));
883
+ console.error(chalk.gray(` Install it with: ${addCmd}`));
832
884
  process.exit(1);
833
885
  }
834
886
  const envFile = findEnvFile(projectRoot);
@@ -929,6 +981,46 @@ ${chalk.green.bold("Description")}
929
981
  backend is ready, and VITE_API_URL is injected automatically.
930
982
  `);
931
983
  }
984
+ async function buildCommand() {
985
+ const projectRoot = requireProjectRoot();
986
+ const pm = detectPackageManager(projectRoot);
987
+ const cmds = getPMCommands(pm);
988
+ const buildCmd = cmds.runAll("build");
989
+ console.log(`${chalk.bold("Rebase")} — Building all workspaces with ${chalk.cyan(pm)}...
990
+ `);
991
+ try {
992
+ await execa(buildCmd[0], buildCmd.slice(1), {
993
+ cwd: projectRoot,
994
+ stdio: "inherit"
995
+ });
996
+ } catch {
997
+ console.error(chalk.red("\nāœ— Build failed."));
998
+ process.exit(1);
999
+ }
1000
+ }
1001
+ async function startCommand() {
1002
+ const projectRoot = requireProjectRoot();
1003
+ const pm = detectPackageManager(projectRoot);
1004
+ const cmds = getPMCommands(pm);
1005
+ const startCmd = cmds.runWorkspace("backend", "start");
1006
+ const envFile = findEnvFile(projectRoot);
1007
+ const env = { ...process.env };
1008
+ if (envFile) {
1009
+ env.DOTENV_CONFIG_PATH = envFile;
1010
+ }
1011
+ console.log(`${chalk.bold("Rebase")} — Starting backend server...
1012
+ `);
1013
+ try {
1014
+ await execa(startCmd[0], startCmd.slice(1), {
1015
+ cwd: projectRoot,
1016
+ stdio: "inherit",
1017
+ env
1018
+ });
1019
+ } catch {
1020
+ console.error(chalk.red("\nāœ— Failed to start server."));
1021
+ process.exit(1);
1022
+ }
1023
+ }
932
1024
  async function authCommand(subcommand, rawArgs) {
933
1025
  if (!subcommand || subcommand === "--help") {
934
1026
  printAuthHelp();
@@ -1137,7 +1229,7 @@ async function entry(args) {
1137
1229
  }
1138
1230
  const command = parsedArgs._[0];
1139
1231
  const subcommand = parsedArgs._[1];
1140
- const namespacedCommands = ["schema", "db", "dev", "auth", "doctor"];
1232
+ const namespacedCommands = ["schema", "db", "dev", "build", "start", "auth", "doctor"];
1141
1233
  if (!command || parsedArgs["--help"] && !namespacedCommands.includes(command)) {
1142
1234
  printHelp();
1143
1235
  return;
@@ -1176,6 +1268,12 @@ async function entry(args) {
1176
1268
  case "dev":
1177
1269
  await devCommand(args);
1178
1270
  break;
1271
+ case "build":
1272
+ await buildCommand();
1273
+ break;
1274
+ case "start":
1275
+ await startCommand();
1276
+ break;
1179
1277
  case "auth":
1180
1278
  await authCommand(effectiveSubcommand, args);
1181
1279
  break;
@@ -1198,14 +1296,16 @@ ${chalk.green.bold("Usage")}
1198
1296
  ${chalk.green.bold("Commands")}
1199
1297
  ${chalk.blue.bold("init")} Create a new Rebase project
1200
1298
  ${chalk.blue.bold("dev")} Start the development server
1299
+ ${chalk.blue.bold("build")} Build all workspace packages
1300
+ ${chalk.blue.bold("start")} Start the backend server ${chalk.gray("(production)")}
1201
1301
 
1202
1302
  ${chalk.green.bold("Schema")}
1203
1303
  ${chalk.blue.bold("schema generate")} Generate Drizzle schema from collections
1304
+ ${chalk.blue.bold("schema introspect")} Introspect database → Rebase collections
1204
1305
  ${chalk.blue.bold("schema")} ${chalk.gray("--help")} Show schema command help
1205
1306
 
1206
1307
  ${chalk.green.bold("Database")}
1207
1308
  ${chalk.blue.bold("db push")} Apply schema directly to database ${chalk.gray("(dev)")}
1208
- ${chalk.blue.bold("db pull")} Introspect database → Drizzle schema
1209
1309
  ${chalk.blue.bold("db generate")} Generate SQL migration files
1210
1310
  ${chalk.blue.bold("db migrate")} Run pending migrations
1211
1311
  ${chalk.blue.bold("db studio")} Open Drizzle Studio
@@ -1273,6 +1373,8 @@ async function logout(env, _debug) {
1273
1373
  }
1274
1374
  export {
1275
1375
  authCommand,
1376
+ buildCommand,
1377
+ configureEnvFile,
1276
1378
  createRebaseApp,
1277
1379
  dbCommand,
1278
1380
  devCommand,
@@ -1292,6 +1394,7 @@ export {
1292
1394
  resolveLocalBin,
1293
1395
  resolvePluginCliScript,
1294
1396
  resolveTsx,
1295
- schemaCommand
1397
+ schemaCommand,
1398
+ startCommand
1296
1399
  };
1297
1400
  //# sourceMappingURL=index.es.js.map