@rebasepro/cli 0.0.1-canary.eae7889 → 0.0.1-canary.f81da60

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,16 +80,31 @@ 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
- const projectName = nameArg || answers.projectName;
85
- const targetDirectory = path.resolve(process.cwd(), projectName);
97
+ const targetDirectory = path.resolve(process.cwd(), nameArg || answers.projectName);
98
+ const projectName = path.basename(targetDirectory);
86
99
  const templateDirectory = path.resolve(cliRoot, "templates", "template");
87
100
  return {
88
101
  projectName,
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) {
@@ -122,27 +137,7 @@ async function createProject(options) {
122
137
  process.exit(1);
123
138
  }
124
139
  await replacePlaceholders(options);
125
- const envTemplatePath = path.join(options.targetDirectory, ".env.template");
126
- const envPath = path.join(options.targetDirectory, ".env");
127
- if (fs.existsSync(envTemplatePath) && !fs.existsSync(envPath)) {
128
- fs.renameSync(envTemplatePath, envPath);
129
- const jwtSecret = crypto.randomBytes(32).toString("hex");
130
- const dbPassword = crypto.randomBytes(16).toString("hex");
131
- 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
- envContent = envContent.replace(
137
- "change-this-to-a-secure-random-string",
138
- jwtSecret
139
- );
140
- envContent += `
141
- # Docker Compose Database Password
142
- POSTGRES_PASSWORD=${dbPassword}
143
- `;
144
- fs.writeFileSync(envPath, envContent, "utf-8");
145
- }
140
+ configureEnvFile(options.targetDirectory, options.databaseUrl);
146
141
  if (options.git) {
147
142
  console.log(chalk.gray(" Initializing git repository..."));
148
143
  try {
@@ -164,6 +159,26 @@ POSTGRES_PASSWORD=${dbPassword}
164
159
  console.warn(chalk.yellow(" Warning: Failed to install dependencies. You may need to run `pnpm install` manually."));
165
160
  }
166
161
  }
162
+ if (options.introspect) {
163
+ console.log("");
164
+ if (options.installDeps) {
165
+ console.log(chalk.gray(" Introspecting database and generating collections..."));
166
+ console.log("");
167
+ try {
168
+ await execa("pnpm", ["exec", "rebase", "schema", "introspect", "--force"], {
169
+ cwd: options.targetDirectory,
170
+ stdio: "inherit"
171
+ });
172
+ console.log(chalk.green(" Database successfully introspected!"));
173
+ } catch {
174
+ console.warn(chalk.yellow(" Warning: Failed to introspect database automatically."));
175
+ console.warn(chalk.yellow(" You can run `pnpm exec rebase schema introspect` manually after setup."));
176
+ }
177
+ } else {
178
+ console.warn(chalk.yellow(" Skipping introspection because dependencies were not installed."));
179
+ console.warn(chalk.yellow(" Run `pnpm install` then `pnpm exec rebase schema introspect` manually."));
180
+ }
181
+ }
167
182
  console.log("");
168
183
  console.log(`${chalk.green.bold("✓")} Project ${chalk.bold(options.projectName)} created successfully!`);
169
184
  console.log("");
@@ -174,8 +189,15 @@ POSTGRES_PASSWORD=${dbPassword}
174
189
  console.log(` ${chalk.cyan("pnpm install")}`);
175
190
  }
176
191
  console.log("");
177
- console.log(chalk.gray(" # Set up your database connection in .env"));
178
- console.log(chalk.gray(" # Then run:"));
192
+ if (options.databaseUrl) {
193
+ console.log(chalk.gray(" # Your database is configured! Start the dev server:"));
194
+ } else {
195
+ console.log(chalk.gray(" # A local database configuration has been generated in .env."));
196
+ console.log(chalk.gray(" # If using the included docker-compose.yml, start it with:"));
197
+ console.log(` ${chalk.cyan("docker compose up -d")}`);
198
+ console.log("");
199
+ console.log(chalk.gray(" # Then start the dev server:"));
200
+ }
179
201
  console.log("");
180
202
  console.log(` ${chalk.cyan("pnpm dev")}`);
181
203
  console.log("");
@@ -193,14 +215,89 @@ async function replacePlaceholders(options) {
193
215
  "config/package.json",
194
216
  "frontend/index.html"
195
217
  ];
218
+ const packageJsonPath = path.resolve(cliRoot, "package.json");
219
+ let cliVersion = "latest";
220
+ if (fs.existsSync(packageJsonPath)) {
221
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
222
+ cliVersion = pkg.version || "latest";
223
+ }
224
+ const versionCache = /* @__PURE__ */ new Map();
225
+ const getPackageVersion = async (pkgName) => {
226
+ if (versionCache.has(pkgName)) return versionCache.get(pkgName);
227
+ let versionToUse = cliVersion;
228
+ try {
229
+ const { stdout } = await execa("pnpm", ["view", `${pkgName}@${cliVersion}`, "version"]);
230
+ if (!stdout.trim()) throw new Error("Not found");
231
+ versionToUse = stdout.trim();
232
+ } catch {
233
+ try {
234
+ const tag = cliVersion.includes("canary") ? "canary" : "latest";
235
+ const { stdout } = await execa("pnpm", ["view", `${pkgName}@${tag}`, "version"]);
236
+ if (!stdout.trim()) throw new Error("Not found");
237
+ versionToUse = stdout.trim();
238
+ } catch {
239
+ try {
240
+ const { stdout } = await execa("pnpm", ["view", pkgName, "version"]);
241
+ versionToUse = stdout.trim() || "latest";
242
+ } catch {
243
+ versionToUse = "latest";
244
+ }
245
+ }
246
+ }
247
+ versionCache.set(pkgName, versionToUse);
248
+ return versionToUse;
249
+ };
250
+ const allPackages = /* @__PURE__ */ new Set();
251
+ const fileContents = /* @__PURE__ */ new Map();
196
252
  for (const file of filesToProcess) {
197
253
  const fullPath = path.resolve(options.targetDirectory, file);
198
254
  if (!fs.existsSync(fullPath)) continue;
199
- let content = fs.readFileSync(fullPath, "utf-8");
200
- content = content.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
255
+ const content = fs.readFileSync(fullPath, "utf-8");
256
+ fileContents.set(fullPath, content);
257
+ const matches = [...content.matchAll(/"(@rebasepro\/[^"]+)":\s*"workspace:\*"/g)];
258
+ for (const match of matches) {
259
+ allPackages.add(match[1]);
260
+ }
261
+ }
262
+ console.log(chalk.gray(" Resolving package versions..."));
263
+ await Promise.all(Array.from(allPackages).map(getPackageVersion));
264
+ for (const [fullPath, originalContent] of fileContents.entries()) {
265
+ let content = originalContent.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
266
+ const matches = [...content.matchAll(/"(@rebasepro\/[^"]+)":\s*"workspace:\*"/g)];
267
+ for (const match of matches) {
268
+ const pkgName = match[1];
269
+ const resolvedVersion = versionCache.get(pkgName) || "latest";
270
+ content = content.replace(new RegExp(`"${pkgName}":\\s*"workspace:\\*"`, "g"), `"${pkgName}": "${resolvedVersion}"`);
271
+ }
201
272
  fs.writeFileSync(fullPath, content, "utf-8");
202
273
  }
203
274
  }
275
+ function configureEnvFile(targetDirectory, databaseUrl) {
276
+ const envExamplePath = path.join(targetDirectory, ".env.example");
277
+ const envPath = path.join(targetDirectory, ".env");
278
+ if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {
279
+ fs.copyFileSync(envExamplePath, envPath);
280
+ const jwtSecret = crypto.randomBytes(32).toString("hex");
281
+ const dbPassword = crypto.randomBytes(16).toString("hex");
282
+ let envContent = fs.readFileSync(envPath, "utf-8");
283
+ envContent = envContent.replace(
284
+ /^JWT_SECRET=.*$/m,
285
+ `JWT_SECRET=${jwtSecret}`
286
+ );
287
+ if (databaseUrl) {
288
+ envContent = envContent.replace(
289
+ /^DATABASE_URL=.*$/m,
290
+ `DATABASE_URL=${databaseUrl}`
291
+ );
292
+ } else {
293
+ envContent = envContent.replace(
294
+ /^DATABASE_URL=.*$/m,
295
+ `DATABASE_URL=postgresql://rebase:${dbPassword}@localhost:5432/rebase`
296
+ );
297
+ }
298
+ fs.writeFileSync(envPath, envContent, "utf-8");
299
+ }
300
+ }
204
301
  async function loadCollections(collectionsDir) {
205
302
  const absDir = path.resolve(collectionsDir);
206
303
  if (!fs.existsSync(absDir)) {
@@ -371,6 +468,7 @@ function resolvePluginCliScript(backendDir, pluginName) {
371
468
  path.join(backendDir, "node_modules", pluginName, "src", "cli.ts"),
372
469
  path.join(backendDir, "node_modules", pluginName, "dist", "cli.js"),
373
470
  // For monorepo dev mode:
471
+ path.resolve(backendDir, "..", "..", "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts"),
374
472
  path.resolve(backendDir, "..", "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts"),
375
473
  path.resolve(backendDir, "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts")
376
474
  ];
@@ -493,11 +591,15 @@ ${chalk.green.bold("Usage")}
493
591
  ${chalk.green.bold("Commands")}
494
592
  ${chalk.gray("(Commands are provided by your active database driver plugin)")}
495
593
  ${chalk.blue.bold("generate")} Generate Schema from collection definitions
594
+ ${chalk.blue.bold("introspect")} Introspect an existing database to generate collection definitions
496
595
 
497
596
  ${chalk.green.bold("generate Options")}
498
597
  ${chalk.blue("--collections, -c")} Path to collections directory
499
598
  ${chalk.blue("--output, -o")} Output path for generated schema
500
599
  ${chalk.blue("--watch, -w")} Watch for changes and regenerate automatically
600
+
601
+ ${chalk.green.bold("introspect Options")}
602
+ ${chalk.blue("--output, -o")} Output directory for generated collection files
501
603
  `);
502
604
  }
503
605
  async function dbCommand(subcommand, rawArgs) {
@@ -1170,6 +1272,7 @@ async function logout(env, _debug) {
1170
1272
  }
1171
1273
  export {
1172
1274
  authCommand,
1275
+ configureEnvFile,
1173
1276
  createRebaseApp,
1174
1277
  dbCommand,
1175
1278
  devCommand,