astro-users 0.1.0 → 0.2.0

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.
Files changed (2) hide show
  1. package/dist/index.js +81 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -93,6 +93,7 @@ async function resolveInstallPlan(source, name) {
93
93
  import { existsSync as existsSync2 } from "fs";
94
94
  import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
95
95
  import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
96
+ import { spawnSync } from "child_process";
96
97
  async function detectProject(cwd) {
97
98
  const root = resolve2(cwd);
98
99
  const pkgPath = join2(root, "package.json");
@@ -172,8 +173,30 @@ async function checkAstroConfig(root) {
172
173
  function escapeRegExp(s) {
173
174
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
174
175
  }
176
+ function runCommand(cmd, args, cwd) {
177
+ const result = spawnSync(cmd, args, { cwd, stdio: "inherit", shell: process.platform === "win32" });
178
+ return result.status === 0;
179
+ }
180
+ async function ensureServerOutput(root, configPath) {
181
+ const abs = join2(root, configPath);
182
+ const src = await readFile2(abs, "utf8");
183
+ if (/output\s*:\s*['"](server|hybrid)['"]/.test(src)) return true;
184
+ const match = src.match(/defineConfig\(\s*\{/);
185
+ if (!match || match.index === void 0) return false;
186
+ const insertAt = match.index + match[0].length;
187
+ const patched = `${src.slice(0, insertAt)}
188
+ output: 'server',${src.slice(insertAt)}`;
189
+ await writeFile(abs, patched, "utf8");
190
+ return true;
191
+ }
192
+ function detectPackageManager(root) {
193
+ if (existsSync2(join2(root, "pnpm-lock.yaml"))) return "pnpm";
194
+ if (existsSync2(join2(root, "yarn.lock"))) return "yarn";
195
+ return "npm";
196
+ }
175
197
 
176
198
  // src/commands/add.ts
199
+ var KNOWN_ADAPTERS = ["node", "cloudflare", "vercel", "netlify"];
177
200
  async function runAdd(names, opts) {
178
201
  p.intro(pc.bgMagenta(pc.black(" Astro Users ")));
179
202
  if (names.length === 0) {
@@ -211,6 +234,18 @@ async function runAdd(names, opts) {
211
234
  return 1;
212
235
  }
213
236
  spin.stop(`Resolved ${manifests.length} component${manifests.length === 1 ? "" : "s"}`);
237
+ const needsServer = manifests.some((m) => m.astro?.output === "server" || m.astro?.output === "hybrid");
238
+ const needsAdapter = manifests.some((m) => m.astro?.adapterRequired);
239
+ let cfg = await checkAstroConfig(project.root);
240
+ if ((needsServer || needsAdapter) && !cfg.found) {
241
+ p.log.error(
242
+ `No astro.config.* found in ${pc.cyan(project.root)}.
243
+ This installer adds components to an existing Astro project \u2014 it doesn't create one.
244
+ Run ${pc.cyan("npm create astro@latest")} first, then re-run this command inside it.`
245
+ );
246
+ p.outro(pc.red("Install aborted."));
247
+ return 1;
248
+ }
214
249
  const pro = manifests.filter((m) => m.tier === "pro");
215
250
  if (pro.length > 0) {
216
251
  p.log.error(
@@ -252,26 +287,44 @@ async function runAdd(names, opts) {
252
287
  write.stop("Files written");
253
288
  renderFileResults(results);
254
289
  await handleEnv(project.root, manifests, opts, source);
255
- const needsServer = manifests.some((m) => m.astro?.output === "server" || m.astro?.output === "hybrid");
256
- const needsAdapter = manifests.some((m) => m.astro?.adapterRequired);
257
- if (needsServer || needsAdapter) {
258
- const cfg = await checkAstroConfig(project.root);
259
- const warns = [];
260
- if (!cfg.found) warns.push("No astro.config.* found \u2014 add one with an SSR adapter.");
261
- else {
262
- if (needsServer && !cfg.isServer)
263
- warns.push(`Set ${pc.cyan("output: 'server'")} in ${cfg.configPath}.`);
264
- if (needsAdapter && !cfg.hasAdapter)
265
- warns.push(`Add an SSR ${pc.cyan("adapter")} (e.g. @astrojs/cloudflare) in ${cfg.configPath}.`);
290
+ if (needsAdapter && !cfg.hasAdapter) {
291
+ const adapter = opts.adapter ?? KNOWN_ADAPTERS[0];
292
+ p.log.step(`Adding SSR adapter (${pc.cyan(`@astrojs/${adapter}`)})\u2026`);
293
+ const ok = runCommand("npx", ["astro", "add", adapter, "--yes"], project.root);
294
+ if (!ok) {
295
+ p.log.error(
296
+ `Failed to auto-install the ${adapter} adapter. Run ${pc.cyan(`npx astro add ${adapter}`)} yourself, then re-run this command.`
297
+ );
298
+ } else {
299
+ cfg = await checkAstroConfig(project.root);
300
+ }
301
+ }
302
+ if (needsServer && cfg.found && !cfg.isServer) {
303
+ const patched = await ensureServerOutput(project.root, cfg.configPath);
304
+ if (patched) {
305
+ p.log.success(`Set ${pc.cyan("output: 'server'")} in ${cfg.configPath}.`);
306
+ } else {
307
+ p.log.warn(`Could not automatically set ${pc.cyan("output: 'server'")} in ${cfg.configPath} \u2014 add it yourself.`);
266
308
  }
267
- if (warns.length > 0) p.log.warn("Astro config:\n " + warns.join("\n "));
268
309
  }
269
310
  if (depNames.length > 0) {
270
311
  const pm = detectPackageManager(project.root);
271
- p.log.info(
272
- `Install dependencies:
312
+ if (opts.noInstall) {
313
+ p.log.info(
314
+ `Install dependencies:
273
315
  ${pc.cyan(`${pm} ${pm === "npm" ? "install" : "add"} ${depNames.join(" ")}`)}`
274
- );
316
+ );
317
+ } else {
318
+ p.log.step(`Installing dependencies with ${pc.cyan(pm)}\u2026`);
319
+ const installArgs = pm === "npm" ? ["install", ...depNames] : ["add", ...depNames];
320
+ const ok = runCommand(pm, installArgs, project.root);
321
+ if (!ok) {
322
+ p.log.error(
323
+ `Dependency install failed. Run it yourself:
324
+ ${pc.cyan(`${pm} ${pm === "npm" ? "install" : "add"} ${depNames.join(" ")}`)}`
325
+ );
326
+ }
327
+ }
275
328
  }
276
329
  const notes = manifests.flatMap((m) => (m.postInstall ?? []).map((line) => `\u2022 ${line}`));
277
330
  if (notes.length > 0) p.note(notes.join("\n"), "Next steps");
@@ -335,11 +388,6 @@ async function handleEnv(root, manifests, opts, _source) {
335
388
  p.log.success(`Wrote ${merged.added.length} variable(s) to .env: ${merged.added.join(", ")}`);
336
389
  if (merged.skipped.length > 0) p.log.info(pc.dim(`Left existing: ${merged.skipped.join(", ")}`));
337
390
  }
338
- function detectPackageManager(root) {
339
- if (existsSync3(join3(root, "pnpm-lock.yaml"))) return "pnpm";
340
- if (existsSync3(join3(root, "yarn.lock"))) return "yarn";
341
- return "npm";
342
- }
343
391
 
344
392
  // src/commands/list.ts
345
393
  import { readdir } from "fs/promises";
@@ -395,7 +443,8 @@ function parseArgs(argv) {
395
443
  force: false,
396
444
  yes: false,
397
445
  help: false,
398
- version: false
446
+ version: false,
447
+ noInstall: false
399
448
  };
400
449
  for (let i = 0; i < argv.length; i++) {
401
450
  const arg = argv[i];
@@ -414,6 +463,12 @@ function parseArgs(argv) {
414
463
  case "-y":
415
464
  out.yes = true;
416
465
  break;
466
+ case "--no-install":
467
+ out.noInstall = true;
468
+ break;
469
+ case "--adapter":
470
+ out.adapter = argv[++i];
471
+ break;
417
472
  case "--help":
418
473
  case "-h":
419
474
  out.help = true;
@@ -446,6 +501,8 @@ ${pc3.bold("Options")}
446
501
  --registry <ref> Registry URL or local path (default: local dev / hosted)
447
502
  -y, --yes Non-interactive; skip prompts and confirmations
448
503
  -f, --force Overwrite existing files instead of writing *.astro-users-new
504
+ --adapter <name> SSR adapter to auto-install if missing (default: node)
505
+ --no-install Skip auto-installing npm dependencies
449
506
  -h, --help Show help
450
507
  -v, --version Show version
451
508
 
@@ -470,7 +527,9 @@ async function main() {
470
527
  cwd: args.cwd,
471
528
  registry: args.registry,
472
529
  force: args.force,
473
- yes: args.yes
530
+ yes: args.yes,
531
+ noInstall: args.noInstall,
532
+ adapter: args.adapter
474
533
  });
475
534
  case "list":
476
535
  case "ls":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-users",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Install Astro Users components into your Astro project — copied into your src/, fully yours to edit.",
5
5
  "type": "module",
6
6
  "bin": {