@rebasepro/cli 0.0.1-canary.ca2cb6e → 0.0.1-canary.dbf160a

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.
@@ -4,5 +4,7 @@ export interface InitOptions {
4
4
  installDeps: boolean;
5
5
  targetDirectory: string;
6
6
  templateDirectory: string;
7
+ databaseUrl?: string;
8
+ introspect?: boolean;
7
9
  }
8
10
  export declare function createRebaseApp(rawArgs: string[]): Promise<void>;
package/dist/index.cjs CHANGED
@@ -89,6 +89,19 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
89
89
  default: true
90
90
  });
91
91
  }
92
+ questions.push({
93
+ type: "input",
94
+ name: "databaseUrl",
95
+ message: "Enter your PostgreSQL database connection string (leave blank to use a local default):",
96
+ default: ""
97
+ });
98
+ questions.push({
99
+ type: "confirm",
100
+ name: "introspect",
101
+ message: "Would you like to introspect this database to automatically generate collections?",
102
+ default: true,
103
+ when: (answers2) => !!answers2.databaseUrl?.trim()
104
+ });
92
105
  const answers = await inquirer.prompt(questions);
93
106
  const targetDirectory = path.resolve(process.cwd(), nameArg || answers.projectName);
94
107
  const projectName = path.basename(targetDirectory);
@@ -98,7 +111,9 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
98
111
  git: args["--git"] || answers.git || false,
99
112
  installDeps: args["--install"] || answers.installDeps || false,
100
113
  targetDirectory,
101
- templateDirectory
114
+ templateDirectory,
115
+ databaseUrl: answers.databaseUrl?.trim() || void 0,
116
+ introspect: answers.introspect || false
102
117
  };
103
118
  }
104
119
  async function createProject(options) {
@@ -136,20 +151,27 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
136
151
  if (fs.existsSync(envTemplatePath) && !fs.existsSync(envPath)) {
137
152
  fs.renameSync(envTemplatePath, envPath);
138
153
  const jwtSecret = crypto.randomBytes(32).toString("hex");
139
- const dbPassword = crypto.randomBytes(16).toString("hex");
140
154
  let envContent = fs.readFileSync(envPath, "utf-8");
141
- envContent = envContent.replace(
142
- "postgresql://rebase:password@localhost:5432/rebase",
143
- `postgresql://rebase:${dbPassword}@localhost:5432/rebase`
144
- );
145
155
  envContent = envContent.replace(
146
156
  "change-this-to-a-secure-random-string",
147
157
  jwtSecret
148
158
  );
149
- envContent += `
159
+ if (options.databaseUrl) {
160
+ envContent = envContent.replace(
161
+ "postgresql://rebase:password@localhost:5432/rebase",
162
+ options.databaseUrl
163
+ );
164
+ } else {
165
+ const dbPassword = crypto.randomBytes(16).toString("hex");
166
+ envContent = envContent.replace(
167
+ "postgresql://rebase:password@localhost:5432/rebase",
168
+ `postgresql://rebase:${dbPassword}@localhost:5432/rebase`
169
+ );
170
+ envContent += `
150
171
  # Docker Compose Database Password
151
172
  POSTGRES_PASSWORD=${dbPassword}
152
173
  `;
174
+ }
153
175
  fs.writeFileSync(envPath, envContent, "utf-8");
154
176
  }
155
177
  if (options.git) {
@@ -173,6 +195,26 @@ POSTGRES_PASSWORD=${dbPassword}
173
195
  console.warn(chalk.yellow(" Warning: Failed to install dependencies. You may need to run `pnpm install` manually."));
174
196
  }
175
197
  }
198
+ if (options.introspect) {
199
+ console.log("");
200
+ if (options.installDeps) {
201
+ console.log(chalk.gray(" Introspecting database and generating collections..."));
202
+ console.log("");
203
+ try {
204
+ await execa("pnpm", ["exec", "rebase", "schema", "introspect"], {
205
+ cwd: options.targetDirectory,
206
+ stdio: "inherit"
207
+ });
208
+ console.log(chalk.green(" Database successfully introspected!"));
209
+ } catch {
210
+ console.warn(chalk.yellow(" Warning: Failed to introspect database automatically."));
211
+ console.warn(chalk.yellow(" You can run `pnpm exec rebase schema introspect` manually after setup."));
212
+ }
213
+ } else {
214
+ console.warn(chalk.yellow(" Skipping introspection because dependencies were not installed."));
215
+ console.warn(chalk.yellow(" Run `pnpm install` then `pnpm exec rebase schema introspect` manually."));
216
+ }
217
+ }
176
218
  console.log("");
177
219
  console.log(`${chalk.green.bold("✓")} Project ${chalk.bold(options.projectName)} created successfully!`);
178
220
  console.log("");
@@ -183,8 +225,15 @@ POSTGRES_PASSWORD=${dbPassword}
183
225
  console.log(` ${chalk.cyan("pnpm install")}`);
184
226
  }
185
227
  console.log("");
186
- console.log(chalk.gray(" # Set up your database connection in .env"));
187
- console.log(chalk.gray(" # Then run:"));
228
+ if (options.databaseUrl) {
229
+ console.log(chalk.gray(" # Your database is configured! Start the dev server:"));
230
+ } else {
231
+ console.log(chalk.gray(" # A local database configuration has been generated in .env"));
232
+ console.log(chalk.gray(" # If using the included docker-compose.yml, start it with:"));
233
+ console.log(` ${chalk.cyan("docker compose up -d")}`);
234
+ console.log("");
235
+ console.log(chalk.gray(" # Then start the dev server:"));
236
+ }
188
237
  console.log("");
189
238
  console.log(` ${chalk.cyan("pnpm dev")}`);
190
239
  console.log("");
@@ -208,12 +257,54 @@ POSTGRES_PASSWORD=${dbPassword}
208
257
  const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
209
258
  cliVersion = pkg.version || "latest";
210
259
  }
260
+ const versionCache = /* @__PURE__ */ new Map();
261
+ const getPackageVersion = async (pkgName) => {
262
+ if (versionCache.has(pkgName)) return versionCache.get(pkgName);
263
+ let versionToUse = cliVersion;
264
+ try {
265
+ const { stdout } = await execa("npm", ["--loglevel", "error", "info", `${pkgName}@${cliVersion}`, "version"]);
266
+ if (!stdout.trim()) throw new Error("Not found");
267
+ versionToUse = stdout.trim();
268
+ } catch {
269
+ try {
270
+ const tag = cliVersion.includes("canary") ? "canary" : "latest";
271
+ const { stdout } = await execa("npm", ["--loglevel", "error", "info", `${pkgName}@${tag}`, "version"]);
272
+ if (!stdout.trim()) throw new Error("Not found");
273
+ versionToUse = stdout.trim();
274
+ } catch {
275
+ try {
276
+ const { stdout } = await execa("npm", ["--loglevel", "error", "info", pkgName, "version"]);
277
+ versionToUse = stdout.trim() || "latest";
278
+ } catch {
279
+ versionToUse = "latest";
280
+ }
281
+ }
282
+ }
283
+ versionCache.set(pkgName, versionToUse);
284
+ return versionToUse;
285
+ };
286
+ const allPackages = /* @__PURE__ */ new Set();
287
+ const fileContents = /* @__PURE__ */ new Map();
211
288
  for (const file of filesToProcess) {
212
289
  const fullPath = path.resolve(options.targetDirectory, file);
213
290
  if (!fs.existsSync(fullPath)) continue;
214
- let content = fs.readFileSync(fullPath, "utf-8");
215
- content = content.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
216
- content = content.replace(/("@rebasepro\/[^"]+":\s*)"workspace:\*"/g, `$1"${cliVersion}"`);
291
+ const content = fs.readFileSync(fullPath, "utf-8");
292
+ fileContents.set(fullPath, content);
293
+ const matches = [...content.matchAll(/"(@rebasepro\/[^"]+)":\s*"workspace:\*"/g)];
294
+ for (const match of matches) {
295
+ allPackages.add(match[1]);
296
+ }
297
+ }
298
+ console.log(chalk.gray(" Resolving package versions..."));
299
+ await Promise.all(Array.from(allPackages).map(getPackageVersion));
300
+ for (const [fullPath, originalContent] of fileContents.entries()) {
301
+ let content = originalContent.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
302
+ const matches = [...content.matchAll(/"(@rebasepro\/[^"]+)":\s*"workspace:\*"/g)];
303
+ for (const match of matches) {
304
+ const pkgName = match[1];
305
+ const resolvedVersion = versionCache.get(pkgName) || "latest";
306
+ content = content.replace(new RegExp(`"${pkgName}":\\s*"workspace:\\*"`, "g"), `"${pkgName}": "${resolvedVersion}"`);
307
+ }
217
308
  fs.writeFileSync(fullPath, content, "utf-8");
218
309
  }
219
310
  }
@@ -387,6 +478,7 @@ Expected a default export of EntityCollection[] or an object with named collecti
387
478
  path.join(backendDir, "node_modules", pluginName, "src", "cli.ts"),
388
479
  path.join(backendDir, "node_modules", pluginName, "dist", "cli.js"),
389
480
  // For monorepo dev mode:
481
+ path.resolve(backendDir, "..", "..", "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts"),
390
482
  path.resolve(backendDir, "..", "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts"),
391
483
  path.resolve(backendDir, "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts")
392
484
  ];
@@ -509,11 +601,15 @@ ${chalk.green.bold("Usage")}
509
601
  ${chalk.green.bold("Commands")}
510
602
  ${chalk.gray("(Commands are provided by your active database driver plugin)")}
511
603
  ${chalk.blue.bold("generate")} Generate Schema from collection definitions
604
+ ${chalk.blue.bold("introspect")} Introspect an existing database to generate collection definitions
512
605
 
513
606
  ${chalk.green.bold("generate Options")}
514
607
  ${chalk.blue("--collections, -c")} Path to collections directory
515
608
  ${chalk.blue("--output, -o")} Output path for generated schema
516
609
  ${chalk.blue("--watch, -w")} Watch for changes and regenerate automatically
610
+
611
+ ${chalk.green.bold("introspect Options")}
612
+ ${chalk.blue("--output, -o")} Output directory for generated collection files
517
613
  `);
518
614
  }
519
615
  async function dbCommand(subcommand, rawArgs) {