@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.
package/dist/index.es.js CHANGED
@@ -80,6 +80,19 @@ async function promptForOptions(rawArgs) {
80
80
  default: true
81
81
  });
82
82
  }
83
+ questions.push({
84
+ type: "input",
85
+ name: "databaseUrl",
86
+ message: "Enter your PostgreSQL database connection string (leave blank to use a local default):",
87
+ default: ""
88
+ });
89
+ questions.push({
90
+ type: "confirm",
91
+ name: "introspect",
92
+ message: "Would you like to introspect this database to automatically generate collections?",
93
+ default: true,
94
+ when: (answers2) => !!answers2.databaseUrl?.trim()
95
+ });
83
96
  const answers = await inquirer.prompt(questions);
84
97
  const targetDirectory = path.resolve(process.cwd(), nameArg || answers.projectName);
85
98
  const projectName = path.basename(targetDirectory);
@@ -89,7 +102,9 @@ async function promptForOptions(rawArgs) {
89
102
  git: args["--git"] || answers.git || false,
90
103
  installDeps: args["--install"] || answers.installDeps || false,
91
104
  targetDirectory,
92
- templateDirectory
105
+ templateDirectory,
106
+ databaseUrl: answers.databaseUrl?.trim() || void 0,
107
+ introspect: answers.introspect || false
93
108
  };
94
109
  }
95
110
  async function createProject(options) {
@@ -127,20 +142,27 @@ async function createProject(options) {
127
142
  if (fs.existsSync(envTemplatePath) && !fs.existsSync(envPath)) {
128
143
  fs.renameSync(envTemplatePath, envPath);
129
144
  const jwtSecret = crypto.randomBytes(32).toString("hex");
130
- const dbPassword = crypto.randomBytes(16).toString("hex");
131
145
  let envContent = fs.readFileSync(envPath, "utf-8");
132
- envContent = envContent.replace(
133
- "postgresql://rebase:password@localhost:5432/rebase",
134
- `postgresql://rebase:${dbPassword}@localhost:5432/rebase`
135
- );
136
146
  envContent = envContent.replace(
137
147
  "change-this-to-a-secure-random-string",
138
148
  jwtSecret
139
149
  );
140
- envContent += `
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 += `
141
162
  # Docker Compose Database Password
142
163
  POSTGRES_PASSWORD=${dbPassword}
143
164
  `;
165
+ }
144
166
  fs.writeFileSync(envPath, envContent, "utf-8");
145
167
  }
146
168
  if (options.git) {
@@ -164,6 +186,26 @@ POSTGRES_PASSWORD=${dbPassword}
164
186
  console.warn(chalk.yellow(" Warning: Failed to install dependencies. You may need to run `pnpm install` manually."));
165
187
  }
166
188
  }
189
+ if (options.introspect) {
190
+ console.log("");
191
+ if (options.installDeps) {
192
+ console.log(chalk.gray(" Introspecting database and generating collections..."));
193
+ console.log("");
194
+ try {
195
+ await execa("pnpm", ["exec", "rebase", "schema", "introspect"], {
196
+ cwd: options.targetDirectory,
197
+ stdio: "inherit"
198
+ });
199
+ console.log(chalk.green(" Database successfully introspected!"));
200
+ } catch {
201
+ 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."));
203
+ }
204
+ } else {
205
+ 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."));
207
+ }
208
+ }
167
209
  console.log("");
168
210
  console.log(`${chalk.green.bold("✓")} Project ${chalk.bold(options.projectName)} created successfully!`);
169
211
  console.log("");
@@ -174,8 +216,15 @@ POSTGRES_PASSWORD=${dbPassword}
174
216
  console.log(` ${chalk.cyan("pnpm install")}`);
175
217
  }
176
218
  console.log("");
177
- console.log(chalk.gray(" # Set up your database connection in .env"));
178
- console.log(chalk.gray(" # Then run:"));
219
+ if (options.databaseUrl) {
220
+ console.log(chalk.gray(" # Your database is configured! Start the dev server:"));
221
+ } else {
222
+ console.log(chalk.gray(" # A local database configuration has been generated in .env"));
223
+ console.log(chalk.gray(" # If using the included docker-compose.yml, start it with:"));
224
+ console.log(` ${chalk.cyan("docker compose up -d")}`);
225
+ console.log("");
226
+ console.log(chalk.gray(" # Then start the dev server:"));
227
+ }
179
228
  console.log("");
180
229
  console.log(` ${chalk.cyan("pnpm dev")}`);
181
230
  console.log("");
@@ -199,12 +248,54 @@ async function replacePlaceholders(options) {
199
248
  const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
200
249
  cliVersion = pkg.version || "latest";
201
250
  }
251
+ const versionCache = /* @__PURE__ */ new Map();
252
+ const getPackageVersion = async (pkgName) => {
253
+ if (versionCache.has(pkgName)) return versionCache.get(pkgName);
254
+ let versionToUse = cliVersion;
255
+ try {
256
+ const { stdout } = await execa("npm", ["--loglevel", "error", "info", `${pkgName}@${cliVersion}`, "version"]);
257
+ if (!stdout.trim()) throw new Error("Not found");
258
+ versionToUse = stdout.trim();
259
+ } catch {
260
+ try {
261
+ const tag = cliVersion.includes("canary") ? "canary" : "latest";
262
+ const { stdout } = await execa("npm", ["--loglevel", "error", "info", `${pkgName}@${tag}`, "version"]);
263
+ if (!stdout.trim()) throw new Error("Not found");
264
+ versionToUse = stdout.trim();
265
+ } catch {
266
+ try {
267
+ const { stdout } = await execa("npm", ["--loglevel", "error", "info", pkgName, "version"]);
268
+ versionToUse = stdout.trim() || "latest";
269
+ } catch {
270
+ versionToUse = "latest";
271
+ }
272
+ }
273
+ }
274
+ versionCache.set(pkgName, versionToUse);
275
+ return versionToUse;
276
+ };
277
+ const allPackages = /* @__PURE__ */ new Set();
278
+ const fileContents = /* @__PURE__ */ new Map();
202
279
  for (const file of filesToProcess) {
203
280
  const fullPath = path.resolve(options.targetDirectory, file);
204
281
  if (!fs.existsSync(fullPath)) continue;
205
- let content = fs.readFileSync(fullPath, "utf-8");
206
- content = content.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
207
- content = content.replace(/("@rebasepro\/[^"]+":\s*)"workspace:\*"/g, `$1"${cliVersion}"`);
282
+ const content = fs.readFileSync(fullPath, "utf-8");
283
+ fileContents.set(fullPath, content);
284
+ const matches = [...content.matchAll(/"(@rebasepro\/[^"]+)":\s*"workspace:\*"/g)];
285
+ for (const match of matches) {
286
+ allPackages.add(match[1]);
287
+ }
288
+ }
289
+ console.log(chalk.gray(" Resolving package versions..."));
290
+ await Promise.all(Array.from(allPackages).map(getPackageVersion));
291
+ for (const [fullPath, originalContent] of fileContents.entries()) {
292
+ let content = originalContent.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
293
+ const matches = [...content.matchAll(/"(@rebasepro\/[^"]+)":\s*"workspace:\*"/g)];
294
+ for (const match of matches) {
295
+ const pkgName = match[1];
296
+ const resolvedVersion = versionCache.get(pkgName) || "latest";
297
+ content = content.replace(new RegExp(`"${pkgName}":\\s*"workspace:\\*"`, "g"), `"${pkgName}": "${resolvedVersion}"`);
298
+ }
208
299
  fs.writeFileSync(fullPath, content, "utf-8");
209
300
  }
210
301
  }
@@ -378,6 +469,7 @@ function resolvePluginCliScript(backendDir, pluginName) {
378
469
  path.join(backendDir, "node_modules", pluginName, "src", "cli.ts"),
379
470
  path.join(backendDir, "node_modules", pluginName, "dist", "cli.js"),
380
471
  // For monorepo dev mode:
472
+ path.resolve(backendDir, "..", "..", "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts"),
381
473
  path.resolve(backendDir, "..", "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts"),
382
474
  path.resolve(backendDir, "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts")
383
475
  ];
@@ -500,11 +592,15 @@ ${chalk.green.bold("Usage")}
500
592
  ${chalk.green.bold("Commands")}
501
593
  ${chalk.gray("(Commands are provided by your active database driver plugin)")}
502
594
  ${chalk.blue.bold("generate")} Generate Schema from collection definitions
595
+ ${chalk.blue.bold("introspect")} Introspect an existing database to generate collection definitions
503
596
 
504
597
  ${chalk.green.bold("generate Options")}
505
598
  ${chalk.blue("--collections, -c")} Path to collections directory
506
599
  ${chalk.blue("--output, -o")} Output path for generated schema
507
600
  ${chalk.blue("--watch, -w")} Watch for changes and regenerate automatically
601
+
602
+ ${chalk.green.bold("introspect Options")}
603
+ ${chalk.blue("--output, -o")} Output directory for generated collection files
508
604
  `);
509
605
  }
510
606
  async function dbCommand(subcommand, rawArgs) {