pubz 0.2.0 → 0.2.2

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 (3) hide show
  1. package/README.md +0 -1
  2. package/dist/cli.js +207 -98
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -51,7 +51,6 @@ Bundled 6 modules in 4ms
51
51
 
52
52
  cli.js 20.0 KB (entry point)
53
53
 
54
-
55
54
  Build completed successfully
56
55
 
57
56
  Verifying builds...
package/dist/cli.js CHANGED
@@ -2,6 +2,32 @@
2
2
  import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
+ // src/colors.ts
6
+ var isColorSupported = process.env.FORCE_COLOR !== "0" && (process.env.FORCE_COLOR !== undefined || process.stdout.isTTY && process.env.TERM !== "dumb");
7
+ var fmt = (open, close) => {
8
+ if (!isColorSupported) {
9
+ return (str) => str;
10
+ }
11
+ return (str) => `\x1B[${open}m${str}\x1B[${close}m`;
12
+ };
13
+ var bold = fmt("1", "22");
14
+ var dim = fmt("2", "22");
15
+ var italic = fmt("3", "23");
16
+ var underline = fmt("4", "24");
17
+ var red = fmt("31", "39");
18
+ var green = fmt("32", "39");
19
+ var yellow = fmt("33", "39");
20
+ var blue = fmt("34", "39");
21
+ var magenta = fmt("35", "39");
22
+ var cyan = fmt("36", "39");
23
+ var white = fmt("37", "39");
24
+ var gray = fmt("90", "39");
25
+ var bgRed = fmt("41", "49");
26
+ var bgGreen = fmt("42", "49");
27
+ var bgYellow = fmt("43", "49");
28
+ var bgBlue = fmt("44", "49");
29
+ var muted = gray;
30
+
5
31
  // src/discovery.ts
6
32
  import { readFile, readdir as readdir2, stat as stat2 } from "node:fs/promises";
7
33
  import { join as join2, resolve } from "node:path";
@@ -164,23 +190,24 @@ function prompt(question) {
164
190
  function closePrompt() {
165
191
  rl.close();
166
192
  }
167
- async function confirm(message, defaultNo = true) {
168
- const hint = defaultNo ? "[y/N]" : "[Y/n]";
169
- const answer = await prompt(`${message} ${hint} `);
193
+ async function confirm(message, defaultYes = true) {
194
+ const hint = defaultYes ? `[${bold("Y")}/n]` : `[y/${bold("N")}]`;
195
+ const answer = await prompt(`${cyan("?")} ${message} ${hint} `);
170
196
  if (answer === "") {
171
- return !defaultNo;
197
+ return defaultYes;
172
198
  }
173
199
  return answer.toLowerCase() === "y";
174
200
  }
175
201
  async function select(message, options, defaultIndex = 0) {
176
- console.log(message);
202
+ console.log(`${cyan("?")} ${message}`);
177
203
  console.log("");
178
204
  for (let i = 0;i < options.length; i++) {
179
- const marker = i === defaultIndex ? ">" : " ";
180
- console.log(` ${marker} ${i + 1}) ${options[i].label}`);
205
+ const marker = i === defaultIndex ? cyan(">") : " ";
206
+ const num = dim(`${i + 1})`);
207
+ console.log(` ${marker} ${num} ${options[i].label}`);
181
208
  }
182
209
  console.log("");
183
- const answer = await prompt(`Enter choice [1-${options.length}] (default: ${defaultIndex + 1}): `);
210
+ const answer = await prompt(` Enter choice ${dim(`[1-${options.length}]`)} ${muted(`(default: ${defaultIndex + 1})`)}: `);
184
211
  if (answer === "") {
185
212
  return options[defaultIndex].value;
186
213
  }
@@ -188,55 +215,92 @@ async function select(message, options, defaultIndex = 0) {
188
215
  if (index >= 0 && index < options.length) {
189
216
  return options[index].value;
190
217
  }
191
- console.log(`Invalid choice. Using default: ${options[defaultIndex].label}`);
218
+ console.log(yellow(` Invalid choice. Using default: ${options[defaultIndex].label}`));
192
219
  return options[defaultIndex].value;
193
220
  }
194
221
  async function multiSelect(message, options, allSelectedByDefault = true) {
195
222
  const selected = new Set(allSelectedByDefault ? options.map((_, i) => i) : []);
196
- const printOptions = () => {
197
- console.log(message);
223
+ let cursor = 0;
224
+ const clearLines = (count) => {
225
+ for (let i = 0;i < count; i++) {
226
+ process.stdout.write("\x1B[A\x1B[2K");
227
+ }
228
+ };
229
+ const render = (initial = false) => {
230
+ if (!initial) {
231
+ clearLines(options.length + 3);
232
+ }
233
+ console.log(`${cyan("?")} ${message}`);
198
234
  console.log("");
199
235
  for (let i = 0;i < options.length; i++) {
200
- const checkbox = selected.has(i) ? "[x]" : "[ ]";
201
- console.log(` ${checkbox} ${i + 1}) ${options[i].label}`);
236
+ const isSelected = selected.has(i);
237
+ const isCursor = i === cursor;
238
+ const checkbox = isSelected ? green("[x]") : dim("[ ]");
239
+ const pointer = isCursor ? cyan(">") : " ";
240
+ const label = isCursor ? bold(options[i].label) : options[i].label;
241
+ console.log(` ${pointer} ${checkbox} ${label}`);
202
242
  }
203
243
  console.log("");
204
- console.log('Enter number to toggle, "a" to select all, "n" to select none, or press Enter to confirm:');
244
+ console.log(dim(" ↑/↓ navigate space toggle a all n none enter confirm"));
205
245
  };
206
- printOptions();
207
- while (true) {
208
- const answer = await prompt("> ");
209
- if (answer === "") {
210
- break;
211
- }
212
- if (answer.toLowerCase() === "a") {
213
- for (let i = 0;i < options.length; i++) {
214
- selected.add(i);
246
+ return new Promise((resolve2) => {
247
+ render(true);
248
+ rl.pause();
249
+ const stdin = process.stdin;
250
+ stdin.setRawMode(true);
251
+ stdin.resume();
252
+ const onKeypress = (key) => {
253
+ const str = key.toString();
254
+ if (str === "\x03") {
255
+ stdin.setRawMode(false);
256
+ stdin.removeListener("data", onKeypress);
257
+ rl.resume();
258
+ console.log("");
259
+ process.exit(0);
215
260
  }
216
- console.log("");
217
- printOptions();
218
- continue;
219
- }
220
- if (answer.toLowerCase() === "n") {
221
- selected.clear();
222
- console.log("");
223
- printOptions();
224
- continue;
225
- }
226
- const index = Number.parseInt(answer, 10) - 1;
227
- if (index >= 0 && index < options.length) {
228
- if (selected.has(index)) {
229
- selected.delete(index);
230
- } else {
231
- selected.add(index);
261
+ if (str === "\r" || str === `
262
+ `) {
263
+ stdin.setRawMode(false);
264
+ stdin.removeListener("data", onKeypress);
265
+ rl.resume();
266
+ console.log("");
267
+ resolve2(options.filter((_, i) => selected.has(i)).map((o) => o.value));
268
+ return;
232
269
  }
233
- console.log("");
234
- printOptions();
235
- } else {
236
- console.log("Invalid input. Try again.");
237
- }
238
- }
239
- return options.filter((_, i) => selected.has(i)).map((o) => o.value);
270
+ if (str === " ") {
271
+ if (selected.has(cursor)) {
272
+ selected.delete(cursor);
273
+ } else {
274
+ selected.add(cursor);
275
+ }
276
+ render();
277
+ return;
278
+ }
279
+ if (str === "\x1B[A" || str === "k") {
280
+ cursor = cursor > 0 ? cursor - 1 : options.length - 1;
281
+ render();
282
+ return;
283
+ }
284
+ if (str === "\x1B[B" || str === "j") {
285
+ cursor = cursor < options.length - 1 ? cursor + 1 : 0;
286
+ render();
287
+ return;
288
+ }
289
+ if (str === "a") {
290
+ for (let i = 0;i < options.length; i++) {
291
+ selected.add(i);
292
+ }
293
+ render();
294
+ return;
295
+ }
296
+ if (str === "n") {
297
+ selected.clear();
298
+ render();
299
+ return;
300
+ }
301
+ };
302
+ stdin.on("data", onKeypress);
303
+ });
240
304
  }
241
305
 
242
306
  // src/publish.ts
@@ -247,8 +311,7 @@ function run(command, args, cwd) {
247
311
  return new Promise((resolve2) => {
248
312
  const proc = spawn(command, args, {
249
313
  cwd,
250
- stdio: ["inherit", "pipe", "pipe"],
251
- shell: true
314
+ stdio: ["inherit", "pipe", "pipe"]
252
315
  });
253
316
  let output = "";
254
317
  proc.stdout?.on("data", (data) => {
@@ -330,18 +393,43 @@ async function publishPackage(pkg, registry, otp, dryRun) {
330
393
  console.log(` ${pkg.name} published successfully`);
331
394
  return { success: true };
332
395
  }
333
- async function createGitTag(version, cwd, dryRun) {
396
+ async function hasUncommittedChanges(cwd) {
397
+ const result = await run("git", ["status", "--porcelain"], cwd);
398
+ const output = result.output.trim();
399
+ if (!output) {
400
+ return { hasChanges: false, files: [] };
401
+ }
402
+ const files = output.split(`
403
+ `).map((line) => line.slice(3));
404
+ return { hasChanges: true, files };
405
+ }
406
+ async function commitVersionBump(version, cwd, dryRun) {
334
407
  const tagName = `v${version}`;
335
408
  if (dryRun) {
336
- console.log(`[DRY RUN] Would create git tag: ${tagName}`);
409
+ console.log(`[DRY RUN] Would commit version bump for ${tagName}`);
337
410
  return { success: true };
338
411
  }
339
412
  const statusResult = await run("git", ["status", "--porcelain"], cwd);
340
- if (statusResult.output.trim()) {
341
- console.log("Uncommitted changes detected. Committing...");
342
- await run("git", ["add", "-A"], cwd);
343
- await run("git", ["commit", "-m", `chore: release ${tagName}`], cwd);
344
- console.log(" Changes committed");
413
+ if (!statusResult.output.trim()) {
414
+ return { success: true };
415
+ }
416
+ console.log("Committing version bump...");
417
+ const addResult = await run("git", ["add", "-A"], cwd);
418
+ if (addResult.code !== 0) {
419
+ return { success: false, error: "Failed to stage changes" };
420
+ }
421
+ const commitResult = await run("git", ["commit", "-m", `chore: release ${tagName}`], cwd);
422
+ if (commitResult.code !== 0) {
423
+ return { success: false, error: "Failed to commit changes" };
424
+ }
425
+ console.log(" Changes committed");
426
+ return { success: true };
427
+ }
428
+ async function createGitTag(version, cwd, dryRun) {
429
+ const tagName = `v${version}`;
430
+ if (dryRun) {
431
+ console.log(`[DRY RUN] Would create git tag: ${tagName}`);
432
+ return { success: true };
345
433
  }
346
434
  const tagResult = await run("git", ["tag", tagName], cwd);
347
435
  if (tagResult.code !== 0) {
@@ -504,31 +592,46 @@ async function main() {
504
592
  }
505
593
  const cwd = process.cwd();
506
594
  if (options.dryRun) {
507
- console.log("DRY RUN MODE - No actual changes will be made");
595
+ console.log(yellow(bold("DRY RUN MODE")) + dim(" - No actual changes will be made"));
508
596
  console.log("");
509
597
  }
510
- console.log("pubz - npm package publisher");
511
- console.log("=============================");
598
+ console.log(bold("pubz") + dim(" - npm package publisher"));
599
+ console.log(dim("".repeat(30)));
512
600
  console.log("");
513
- console.log("Discovering packages...");
601
+ const uncommitted = await hasUncommittedChanges(cwd);
602
+ if (uncommitted.hasChanges && !options.dryRun) {
603
+ console.log(red(bold("Error:")) + " You have uncommitted changes:");
604
+ console.log("");
605
+ for (const file of uncommitted.files.slice(0, 10)) {
606
+ console.log(` ${yellow(file)}`);
607
+ }
608
+ if (uncommitted.files.length > 10) {
609
+ console.log(dim(` ... and ${uncommitted.files.length - 10} more`));
610
+ }
611
+ console.log("");
612
+ console.log(muted("Please commit or stash your changes before publishing."));
613
+ closePrompt();
614
+ process.exit(1);
615
+ }
616
+ console.log(cyan("Discovering packages..."));
514
617
  console.log("");
515
618
  let packages = await discoverPackages(cwd);
516
619
  const publishablePackages = packages.filter((p) => !p.isPrivate);
517
620
  if (publishablePackages.length === 0) {
518
- console.log("No publishable packages found.");
621
+ console.log(yellow("No publishable packages found."));
519
622
  console.log("");
520
- console.log("Make sure your packages:");
521
- console.log(' - Have a package.json with a "name" field');
522
- console.log(' - Do not have "private": true');
623
+ console.log(muted("Make sure your packages:"));
624
+ console.log(muted(' - Have a package.json with a "name" field'));
625
+ console.log(muted(' - Do not have "private": true'));
523
626
  console.log("");
524
627
  process.exit(1);
525
628
  }
526
629
  packages = sortByDependencyOrder(publishablePackages);
527
- console.log(`Found ${packages.length} publishable package(s):`);
630
+ console.log(`Found ${green(bold(String(packages.length)))} publishable package(s):`);
528
631
  console.log("");
529
632
  for (const pkg of packages) {
530
- const deps = pkg.localDependencies.length > 0 ? ` (depends on: ${pkg.localDependencies.join(", ")})` : "";
531
- console.log(` - ${pkg.name}@${pkg.version}${deps}`);
633
+ const deps = pkg.localDependencies.length > 0 ? dim(` (depends on: ${pkg.localDependencies.join(", ")})`) : "";
634
+ console.log(` ${dim("•")} ${cyan(pkg.name)}${dim("@")}${yellow(pkg.version)}${deps}`);
532
635
  }
533
636
  console.log("");
534
637
  if (packages.length > 1 && !options.skipPrompts) {
@@ -537,7 +640,7 @@ async function main() {
537
640
  value: pkg
538
641
  })));
539
642
  if (selectedPackages.length === 0) {
540
- console.log("No packages selected. Exiting.");
643
+ console.log(yellow("No packages selected. Exiting."));
541
644
  closePrompt();
542
645
  process.exit(0);
543
646
  }
@@ -545,10 +648,10 @@ async function main() {
545
648
  console.log("");
546
649
  }
547
650
  const currentVersion = packages[0].version;
548
- console.log("Step 1: Version Management");
549
- console.log("--------------------------");
651
+ console.log(bold(cyan("Step 1:")) + " Version Management");
652
+ console.log(dim("".repeat(30)));
550
653
  console.log("");
551
- console.log(`Current version: ${currentVersion}`);
654
+ console.log(`Current version: ${yellow(currentVersion)}`);
552
655
  console.log("");
553
656
  let newVersion = currentVersion;
554
657
  if (!options.skipPrompts) {
@@ -570,7 +673,7 @@ async function main() {
570
673
  ]);
571
674
  newVersion = bumpVersion(currentVersion, bumpType);
572
675
  console.log("");
573
- console.log(`Updating version to ${newVersion} in all packages...`);
676
+ console.log(`Updating version to ${green(newVersion)} in all packages...`);
574
677
  console.log("");
575
678
  for (const pkg of packages) {
576
679
  await updatePackageVersion(pkg, newVersion, options.dryRun);
@@ -579,6 +682,12 @@ async function main() {
579
682
  for (const pkg of packages) {
580
683
  pkg.version = newVersion;
581
684
  }
685
+ const commitResult = await commitVersionBump(newVersion, cwd, options.dryRun);
686
+ if (!commitResult.success) {
687
+ console.error(red(bold("Failed to commit version bump:")) + ` ${commitResult.error}`);
688
+ closePrompt();
689
+ process.exit(1);
690
+ }
582
691
  console.log("");
583
692
  }
584
693
  }
@@ -597,88 +706,88 @@ async function main() {
597
706
  }
598
707
  registry = registry || REGISTRIES.npm;
599
708
  console.log("");
600
- console.log(`Publishing to: ${registry}`);
709
+ console.log(`Publishing to: ${cyan(registry)}`);
601
710
  console.log("");
602
711
  if (!options.skipBuild) {
603
- console.log("Step 2: Building Packages");
604
- console.log("-------------------------");
712
+ console.log(bold(cyan("Step 2:")) + " Building Packages");
713
+ console.log(dim("".repeat(30)));
605
714
  console.log("");
606
715
  const buildResult = await runBuild(cwd, options.dryRun);
607
716
  if (!buildResult.success) {
608
- console.error(`Build failed: ${buildResult.error}`);
717
+ console.error(red(bold("Build failed:")) + ` ${buildResult.error}`);
609
718
  closePrompt();
610
719
  process.exit(1);
611
720
  }
612
721
  console.log("");
613
- console.log("Verifying builds...");
722
+ console.log(cyan("Verifying builds..."));
614
723
  console.log("");
615
724
  let allBuildsVerified = true;
616
725
  for (const pkg of packages) {
617
726
  const result = await verifyBuild(pkg);
618
727
  if (result.success) {
619
- console.log(` ${pkg.name} build verified`);
728
+ console.log(` ${green("✓")} ${pkg.name} build verified`);
620
729
  } else {
621
- console.error(` ${pkg.name}: ${result.error}`);
730
+ console.error(` ${red("✗")} ${pkg.name}: ${result.error}`);
622
731
  allBuildsVerified = false;
623
732
  }
624
733
  }
625
734
  console.log("");
626
735
  if (!allBuildsVerified) {
627
- console.error("Build verification failed. Please fix the issues and try again.");
736
+ console.error(red("Build verification failed.") + muted(" Please fix the issues and try again."));
628
737
  closePrompt();
629
738
  process.exit(1);
630
739
  }
631
740
  }
632
- console.log("Step 3: Publishing to npm");
633
- console.log("-------------------------");
741
+ console.log(bold(cyan("Step 3:")) + " Publishing to npm");
742
+ console.log(dim("".repeat(30)));
634
743
  console.log("");
635
744
  if (options.dryRun) {
636
- console.log(`[DRY RUN] Would publish the following packages to ${registry}:`);
745
+ console.log(yellow("[DRY RUN]") + ` Would publish the following packages to ${cyan(registry)}:`);
637
746
  console.log("");
638
747
  for (const pkg of packages) {
639
- console.log(` ${pkg.name}@${newVersion}`);
748
+ console.log(` ${dim("•")} ${cyan(pkg.name)}${dim("@")}${yellow(newVersion)}`);
640
749
  }
641
750
  console.log("");
642
- console.log("Run without --dry-run to actually publish.");
751
+ console.log(muted("Run without --dry-run to actually publish."));
643
752
  } else {
644
753
  console.log("About to publish the following packages:");
645
754
  console.log("");
646
755
  for (const pkg of packages) {
647
- console.log(` ${pkg.name}@${newVersion}`);
756
+ console.log(` ${dim("•")} ${cyan(pkg.name)}${dim("@")}${yellow(newVersion)}`);
648
757
  }
649
758
  console.log("");
650
- console.log(`Registry: ${registry}`);
759
+ console.log(`Registry: ${cyan(registry)}`);
651
760
  console.log("");
652
761
  if (!options.skipPrompts) {
653
762
  const shouldContinue = await confirm("Continue?");
654
763
  if (!shouldContinue) {
655
- console.log("Publish cancelled.");
764
+ console.log(yellow("Publish cancelled."));
656
765
  closePrompt();
657
766
  process.exit(0);
658
767
  }
659
768
  }
660
769
  console.log("");
661
- console.log("Publishing packages...");
770
+ console.log(cyan("Publishing packages..."));
662
771
  console.log("");
663
772
  for (const pkg of packages) {
664
773
  const result = await publishPackage(pkg, registry, options.otp, options.dryRun);
665
774
  if (!result.success) {
666
- console.error(`Failed to publish ${pkg.name}: ${result.error}`);
775
+ console.error(red(bold("Failed to publish")) + ` ${cyan(pkg.name)}: ${result.error}`);
667
776
  console.log("");
668
- console.log("Stopping publish process.");
777
+ console.log(red("Stopping publish process."));
669
778
  closePrompt();
670
779
  process.exit(1);
671
780
  }
672
781
  }
673
782
  }
674
783
  console.log("");
675
- console.log("==================================");
676
- console.log("Publishing complete!");
784
+ console.log(dim("".repeat(30)));
785
+ console.log(green(bold("Publishing complete!")));
677
786
  console.log("");
678
- console.log(`Published version: ${newVersion}`);
787
+ console.log(`Published version: ${green(bold(newVersion))}`);
679
788
  console.log("");
680
789
  if (!options.dryRun && !options.skipPrompts) {
681
- const shouldTag = await confirm(`Create a git tag for v${newVersion}?`);
790
+ const shouldTag = await confirm(`Create a git tag for ${cyan(`v${newVersion}`)}?`);
682
791
  if (shouldTag) {
683
792
  console.log("");
684
793
  const tagResult = await createGitTag(newVersion, cwd, options.dryRun);
@@ -687,19 +796,19 @@ async function main() {
687
796
  if (shouldPush) {
688
797
  await pushGitTag(newVersion, cwd, options.dryRun);
689
798
  } else {
690
- console.log(`Tag created locally. Push manually with: git push origin v${newVersion}`);
799
+ console.log(`Tag created locally. Push manually with: ${dim(`git push origin v${newVersion}`)}`);
691
800
  }
692
801
  } else {
693
- console.error(tagResult.error);
802
+ console.error(red(tagResult.error ?? "Failed to create git tag"));
694
803
  }
695
804
  console.log("");
696
805
  }
697
806
  }
698
- console.log("Done!");
807
+ console.log(green(bold("Done!")));
699
808
  closePrompt();
700
809
  }
701
810
  main().catch((error) => {
702
- console.error("Error:", error.message);
811
+ console.error(red(bold("Error:")) + ` ${error.message}`);
703
812
  closePrompt();
704
813
  process.exit(1);
705
814
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pubz",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Interactive CLI for publishing npm packages (single or monorepo)",
5
5
  "type": "module",
6
6
  "bin": {