@vlandoss/vland 0.1.1 → 0.1.2-git-87ebfca.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.
@@ -1,7 +1,7 @@
1
1
  // @generated by @usage-spec/commander from Commander.js metadata
2
2
  name vland
3
3
  bin vland
4
- version "0.1.1"
4
+ version "0.1.2-git-87ebfca.0"
5
5
  usage "[options] [command]"
6
6
  flag --usage help="print KDL spec for this CLI (https://kdl.dev)"
7
7
  cmd completion help="print shell completion script 🐚 (usage)" {
@@ -25,7 +25,9 @@ cmd init help="init a new project 🚀 (giget)" {
25
25
  choices npm pnpm yarn bun
26
26
  }
27
27
  }
28
+ flag --install help="install dependencies (skip prompt)"
28
29
  flag --no-install help="skip dependency installation" negate=--install
30
+ flag --git help="initialise git repository (skip prompt)"
29
31
  flag --no-git help="skip git init" negate=--git
30
32
  flag "-f --force" help="overwrite existing directory"
31
33
  arg "[name]" help="project name (also used as the target directory)" required=#false
package/dist/run.mjs CHANGED
@@ -4,7 +4,7 @@ import { Argument, Command, Option, createCommand } from "commander";
4
4
  import fs from "node:fs";
5
5
  import { createLoggy } from "@vlandoss/loggy";
6
6
  import { cp, readFile, readdir, stat, writeFile } from "node:fs/promises";
7
- import { cancel, intro, isCancel, log, outro, select, spinner, text as text$1 } from "@clack/prompts";
7
+ import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text as text$1 } from "@clack/prompts";
8
8
  import { detectPackageManager, installDependencies } from "nypm";
9
9
  import { downloadTemplate } from "giget";
10
10
  import { generateToStdout } from "@usage-spec/commander";
@@ -19,7 +19,7 @@ async function createContext(binDir) {
19
19
  const binPkg = await createPkg(binPath);
20
20
  if (!binPkg) throw new Error("Could not find bin package.json");
21
21
  debug("bin pkg info: %O", binPkg.info());
22
- const shell = createShellService({ localBaseBinPath: [binDir] });
22
+ const shell = createShellService();
23
23
  debug("shell service options: %O", shell.options);
24
24
  return {
25
25
  binPkg,
@@ -230,7 +230,15 @@ async function isDirEmpty(dir) {
230
230
  }
231
231
  async function readGitAuthor(shell) {
232
232
  try {
233
- const [name, email] = await Promise.all([shell.$`git config --get user.name`.nothrow(), shell.$`git config --get user.email`.nothrow()]);
233
+ const [name, email] = await Promise.all([shell.runCaptured("git", [
234
+ "config",
235
+ "--get",
236
+ "user.name"
237
+ ], { throwOnError: false }), shell.runCaptured("git", [
238
+ "config",
239
+ "--get",
240
+ "user.email"
241
+ ], { throwOnError: false })]);
234
242
  const trimmedName = name.stdout.trim();
235
243
  const trimmedEmail = email.stdout.trim();
236
244
  if (!trimmedName) return void 0;
@@ -246,7 +254,7 @@ function abort(message) {
246
254
  async function runInit(ctx, options) {
247
255
  const debug = logger.subdebug("init");
248
256
  debug("options: %O", options);
249
- const shell = ctx.shell.mute();
257
+ const shell = ctx.shell;
250
258
  intro(`${palette.label(" vland init ")}`);
251
259
  let template = options.template;
252
260
  if (!template) {
@@ -311,7 +319,9 @@ async function runInit(ctx, options) {
311
319
  });
312
320
  await updateRootPackageName(dir, name);
313
321
  placeholderSpin.stop("Placeholders applied");
314
- if (options.install) {
322
+ const shouldInstall = await resolveYesNo(options.install, "Install dependencies?");
323
+ const shouldGit = await resolveYesNo(options.git, "Initialise a git repository?");
324
+ if (shouldInstall) {
315
325
  const detected = options.pm ?? (await detectPackageManager(dir, { ignorePackageJSON: false }))?.name ?? "pnpm";
316
326
  const installSpin = spinner();
317
327
  installSpin.start(`Installing dependencies with ${palette.highlight(detected)}`);
@@ -329,36 +339,49 @@ async function runInit(ctx, options) {
329
339
  log.warn("You can install manually later with `cd <dir> && <pm> install`.");
330
340
  debug("install error: %O", error);
331
341
  }
332
- } else log.info(`Skipping ${palette.highlight("install")} (--no-install).`);
333
- if (options.git) {
342
+ } else log.info(`Skipping ${palette.highlight("install")}.`);
343
+ if (shouldGit) {
334
344
  const gitSpin = spinner();
335
345
  gitSpin.start("Initialising git repository");
336
346
  try {
337
347
  const gitShell = shell.at(dir).child({ env: {
338
- ...process.env,
339
348
  GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME ?? "vland",
340
349
  GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL ?? "noreply@variable.land",
341
350
  GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME ?? "vland",
342
351
  GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL ?? "noreply@variable.land"
343
352
  } });
344
- await gitShell.$`git init`;
345
- await gitShell.$`git add -A`;
346
- await gitShell.$`git commit -m ${"chore: initial commit from vland"}`;
353
+ await gitShell.runCaptured("git", ["init"]);
354
+ await gitShell.runCaptured("git", ["add", "-A"]);
355
+ await gitShell.runCaptured("git", [
356
+ "commit",
357
+ "-m",
358
+ "chore: initial commit from vland"
359
+ ]);
347
360
  gitSpin.stop("Initialised git repository");
348
361
  } catch (error) {
349
362
  gitSpin.stop("Failed to initialise git", 1);
350
363
  debug("git error: %O", error);
351
364
  }
352
- } else log.info(`Skipping ${palette.highlight("git init")} (--no-git).`);
365
+ } else log.info(`Skipping ${palette.highlight("git init")}.`);
353
366
  const detectedPm = options.pm ?? (await detectPackageManager(dir, { ignorePackageJSON: false }))?.name ?? "pnpm";
354
367
  outro([
355
368
  palette.success("Done!"),
356
369
  "",
357
370
  palette.muted("Next steps:"),
358
371
  ` cd ${name}`,
359
- options.install ? ` ${detectedPm} dev` : ` ${detectedPm} install && ${detectedPm} dev`
372
+ shouldInstall ? ` ${detectedPm} dev` : ` ${detectedPm} install && ${detectedPm} dev`
360
373
  ].join("\n"));
361
374
  }
375
+ async function resolveYesNo(explicit, message) {
376
+ if (typeof explicit === "boolean") return explicit;
377
+ if (!hasTTY) return true;
378
+ const value = await confirm({
379
+ message,
380
+ initialValue: true
381
+ });
382
+ if (isCancel(value)) abort("Cancelled.");
383
+ return value;
384
+ }
362
385
  //#endregion
363
386
  //#region src/program/commands/init.ts
364
387
  function createInitCommand(ctx) {
@@ -367,11 +390,15 @@ function createInitCommand(ctx) {
367
390
  "pnpm",
368
391
  "yarn",
369
392
  "bun"
370
- ])).addOption(new Option("--no-install", "skip dependency installation")).addOption(new Option("--no-git", "skip git init")).addOption(new Option("-f, --force", "overwrite existing directory").default(false)).action(async (name, options) => {
393
+ ])).addOption(new Option("--install", "install dependencies (skip prompt)")).addOption(new Option("--no-install", "skip dependency installation")).addOption(new Option("--git", "initialise git repository (skip prompt)")).addOption(new Option("--no-git", "skip git init")).addOption(new Option("-f, --force", "overwrite existing directory").default(false)).action(async function(name, options) {
371
394
  console.log(getBannerText(ctx.binPkg.version));
395
+ const installSource = this.getOptionValueSource("install");
396
+ const gitSource = this.getOptionValueSource("git");
372
397
  await runInit(ctx, {
373
398
  name,
374
- ...options
399
+ ...options,
400
+ install: installSource === "cli" ? options.install : void 0,
401
+ git: gitSource === "cli" ? options.git : void 0
375
402
  });
376
403
  });
377
404
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vlandoss/vland",
3
- "version": "0.1.1",
3
+ "version": "0.1.2-git-87ebfca.0",
4
4
  "description": "The CLI to init a new project in Variable Land",
5
5
  "homepage": "https://github.com/variableland/dx/tree/main/packages/vland#readme",
6
6
  "bugs": {
@@ -35,8 +35,8 @@
35
35
  "commander": "14.0.3",
36
36
  "giget": "2.0.0",
37
37
  "nypm": "0.6.0",
38
- "@vlandoss/clibuddy": "0.5.0",
39
- "@vlandoss/loggy": "0.2.0"
38
+ "@vlandoss/loggy": "0.2.0",
39
+ "@vlandoss/clibuddy": "0.5.1-git-87ebfca.0"
40
40
  },
41
41
  "publishConfig": {
42
42
  "access": "public"
@@ -1,6 +1,6 @@
1
1
  import { readdir } from "node:fs/promises";
2
2
  import { isAbsolute, resolve } from "node:path";
3
- import { cancel, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
3
+ import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
4
4
  import { hasTTY, palette } from "@vlandoss/clibuddy";
5
5
  import { detectPackageManager, installDependencies } from "nypm";
6
6
  import type { Context } from "#src/services/ctx.ts";
@@ -13,8 +13,8 @@ export type InitOptions = {
13
13
  template?: TemplateName;
14
14
  dir?: string;
15
15
  pm?: "npm" | "pnpm" | "yarn" | "bun";
16
- install: boolean;
17
- git: boolean;
16
+ install?: boolean;
17
+ git?: boolean;
18
18
  force: boolean;
19
19
  };
20
20
 
@@ -46,8 +46,8 @@ async function isDirEmpty(dir: string): Promise<boolean> {
46
46
  async function readGitAuthor(shell: Context["shell"]): Promise<string | undefined> {
47
47
  try {
48
48
  const [name, email] = await Promise.all([
49
- shell.$`git config --get user.name`.nothrow(),
50
- shell.$`git config --get user.email`.nothrow(),
49
+ shell.runCaptured("git", ["config", "--get", "user.name"], { throwOnError: false }),
50
+ shell.runCaptured("git", ["config", "--get", "user.email"], { throwOnError: false }),
51
51
  ]);
52
52
  const trimmedName = name.stdout.trim();
53
53
  const trimmedEmail = email.stdout.trim();
@@ -67,8 +67,7 @@ export async function runInit(ctx: Context, options: InitOptions) {
67
67
  const debug = logger.subdebug("init");
68
68
  debug("options: %O", options);
69
69
 
70
- // Mute zx so the @clack/prompts UI stays clean while git output is captured.
71
- const shell = ctx.shell.mute();
70
+ const shell = ctx.shell;
72
71
 
73
72
  intro(`${palette.label(" vland init ")}`);
74
73
 
@@ -153,8 +152,12 @@ export async function runInit(ctx: Context, options: InitOptions) {
153
152
  await updateRootPackageName(dir, name);
154
153
  placeholderSpin.stop("Placeholders applied");
155
154
 
156
- // 7. Install deps
157
- if (options.install) {
155
+ // 7. Resolve install / git decisions (prompt with default-yes when not set on CLI)
156
+ const shouldInstall = await resolveYesNo(options.install, "Install dependencies?");
157
+ const shouldGit = await resolveYesNo(options.git, "Initialise a git repository?");
158
+
159
+ // 8. Install deps
160
+ if (shouldInstall) {
158
161
  const detected = options.pm ?? (await detectPackageManager(dir, { ignorePackageJSON: false }))?.name ?? "pnpm";
159
162
  const installSpin = spinner();
160
163
  installSpin.start(`Installing dependencies with ${palette.highlight(detected)}`);
@@ -167,36 +170,35 @@ export async function runInit(ctx: Context, options: InitOptions) {
167
170
  debug("install error: %O", error);
168
171
  }
169
172
  } else {
170
- log.info(`Skipping ${palette.highlight("install")} (--no-install).`);
173
+ log.info(`Skipping ${palette.highlight("install")}.`);
171
174
  }
172
175
 
173
- // 8. Git init
174
- if (options.git) {
176
+ // 9. Git init
177
+ if (shouldGit) {
175
178
  const gitSpin = spinner();
176
179
  gitSpin.start("Initialising git repository");
177
180
  try {
178
181
  const gitShell = shell.at(dir).child({
179
182
  env: {
180
- ...process.env,
181
183
  GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME ?? "vland",
182
184
  GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL ?? "noreply@variable.land",
183
185
  GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME ?? "vland",
184
186
  GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL ?? "noreply@variable.land",
185
187
  },
186
188
  });
187
- await gitShell.$`git init`;
188
- await gitShell.$`git add -A`;
189
- await gitShell.$`git commit -m ${"chore: initial commit from vland"}`;
189
+ await gitShell.runCaptured("git", ["init"]);
190
+ await gitShell.runCaptured("git", ["add", "-A"]);
191
+ await gitShell.runCaptured("git", ["commit", "-m", "chore: initial commit from vland"]);
190
192
  gitSpin.stop("Initialised git repository");
191
193
  } catch (error) {
192
194
  gitSpin.stop("Failed to initialise git", 1);
193
195
  debug("git error: %O", error);
194
196
  }
195
197
  } else {
196
- log.info(`Skipping ${palette.highlight("git init")} (--no-git).`);
198
+ log.info(`Skipping ${palette.highlight("git init")}.`);
197
199
  }
198
200
 
199
- // 9. Outro with next steps
201
+ // 10. Outro with next steps
200
202
  const detectedPm = options.pm ?? (await detectPackageManager(dir, { ignorePackageJSON: false }))?.name ?? "pnpm";
201
203
  outro(
202
204
  [
@@ -204,7 +206,15 @@ export async function runInit(ctx: Context, options: InitOptions) {
204
206
  "",
205
207
  palette.muted("Next steps:"),
206
208
  ` cd ${name}`,
207
- options.install ? ` ${detectedPm} dev` : ` ${detectedPm} install && ${detectedPm} dev`,
209
+ shouldInstall ? ` ${detectedPm} dev` : ` ${detectedPm} install && ${detectedPm} dev`,
208
210
  ].join("\n"),
209
211
  );
210
212
  }
213
+
214
+ async function resolveYesNo(explicit: boolean | undefined, message: string): Promise<boolean> {
215
+ if (typeof explicit === "boolean") return explicit;
216
+ if (!hasTTY) return true;
217
+ const value = await confirm({ message, initialValue: true });
218
+ if (isCancel(value)) abort("Cancelled.");
219
+ return value as boolean;
220
+ }
@@ -21,14 +21,20 @@ export function createInitCommand(ctx: Context) {
21
21
  .addOption(new Option("-t, --template <name>", "template to use").choices([...TEMPLATES]))
22
22
  .addOption(new Option("-d, --dir <path>", "target directory (default: ./<name>)"))
23
23
  .addOption(new Option("--pm <manager>", "package manager to use").choices(["npm", "pnpm", "yarn", "bun"]))
24
+ .addOption(new Option("--install", "install dependencies (skip prompt)"))
24
25
  .addOption(new Option("--no-install", "skip dependency installation"))
26
+ .addOption(new Option("--git", "initialise git repository (skip prompt)"))
25
27
  .addOption(new Option("--no-git", "skip git init"))
26
28
  .addOption(new Option("-f, --force", "overwrite existing directory").default(false))
27
- .action(async (name: string | undefined, options: InitOptions) => {
29
+ .action(async function (this: import("commander").Command, name: string | undefined, options: InitOptions) {
28
30
  console.log(getBannerText(ctx.binPkg.version));
31
+ const installSource = this.getOptionValueSource("install");
32
+ const gitSource = this.getOptionValueSource("git");
29
33
  await runInit(ctx, {
30
34
  name,
31
35
  ...options,
36
+ install: installSource === "cli" ? options.install : undefined,
37
+ git: gitSource === "cli" ? options.git : undefined,
32
38
  });
33
39
  });
34
40
  }
@@ -22,9 +22,7 @@ export async function createContext(binDir: string): Promise<Context> {
22
22
 
23
23
  debug("bin pkg info: %O", binPkg.info());
24
24
 
25
- const shell = createShellService({
26
- localBaseBinPath: [binDir],
27
- });
25
+ const shell = createShellService();
28
26
 
29
27
  debug("shell service options: %O", shell.options);
30
28