pubz 0.7.3 → 0.7.4

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/cli.js +126 -90
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -47,24 +47,92 @@ function frameLine(text = "") {
47
47
 
48
48
  // src/discovery.ts
49
49
  import { readFile, readdir as readdir2, stat as stat2 } from "node:fs/promises";
50
- import { join as join2, resolve } from "node:path";
50
+ import { join as join3, resolve } from "node:path";
51
+
52
+ // src/config.ts
53
+ import { existsSync, readFileSync } from "node:fs";
54
+ import { join } from "node:path";
55
+
56
+ // src/log.ts
57
+ var verboseEnabled = false;
58
+ function setVerbose(enabled) {
59
+ verboseEnabled = enabled;
60
+ }
61
+ function debug(...args) {
62
+ if (verboseEnabled) {
63
+ console.error("[debug]", ...args);
64
+ }
65
+ }
66
+
67
+ // src/config.ts
68
+ var CONFIG_FILENAME = ".pubz";
69
+ var VALID_KEYS = new Set(["skip-build", "skip-publish", "always-publish", "registry"]);
70
+ function loadConfig(cwd) {
71
+ const configPath = join(cwd, CONFIG_FILENAME);
72
+ if (!existsSync(configPath)) {
73
+ debug(`No ${CONFIG_FILENAME} found at ${configPath}`);
74
+ return {};
75
+ }
76
+ const content = readFileSync(configPath, "utf-8");
77
+ const config = {};
78
+ for (const rawLine of content.split(`
79
+ `)) {
80
+ const line = rawLine.trim();
81
+ if (!line || line.startsWith("#"))
82
+ continue;
83
+ const eqIndex = line.indexOf("=");
84
+ let key;
85
+ let value;
86
+ if (eqIndex === -1) {
87
+ key = line;
88
+ } else {
89
+ key = line.slice(0, eqIndex).trim();
90
+ value = line.slice(eqIndex + 1).trim();
91
+ }
92
+ if (!VALID_KEYS.has(key)) {
93
+ debug(`Ignoring unknown config key: ${key}`);
94
+ continue;
95
+ }
96
+ if (key === "registry") {
97
+ config.registry = value ?? "";
98
+ } else {
99
+ const boolKey = key;
100
+ if (value === undefined || value === "true") {
101
+ config[boolKey] = true;
102
+ } else if (value === "false") {
103
+ config[boolKey] = false;
104
+ } else {
105
+ debug(`Invalid boolean value for ${key}: ${value}`);
106
+ }
107
+ }
108
+ }
109
+ debug(`Loaded ${CONFIG_FILENAME}: ${JSON.stringify(config)}`);
110
+ return config;
111
+ }
51
112
 
52
113
  // src/glob.ts
53
114
  import { readdir, stat } from "node:fs/promises";
54
- import { join } from "node:path";
115
+ import { join as join2 } from "node:path";
55
116
  async function glob(pattern, cwd) {
56
117
  const results = [];
118
+ const isGlob = /\/\*\*?$/.test(pattern);
57
119
  const basePattern = pattern.replace(/\/\*\*?$/, "");
58
120
  const isRecursive = pattern.endsWith("/**");
59
- const basePath = join(cwd, basePattern);
121
+ const basePath = join2(cwd, basePattern);
122
+ if (!isGlob) {
123
+ try {
124
+ await stat(join2(basePath, "package.json"));
125
+ return [basePattern];
126
+ } catch {}
127
+ }
60
128
  try {
61
129
  const entries = await readdir(basePath, { withFileTypes: true });
62
130
  for (const entry of entries) {
63
131
  if (entry.isDirectory()) {
64
- const entryPath = join(basePattern, entry.name);
65
- const fullPath = join(cwd, entryPath);
132
+ const entryPath = join2(basePattern, entry.name);
133
+ const fullPath = join2(cwd, entryPath);
66
134
  try {
67
- await stat(join(fullPath, "package.json"));
135
+ await stat(join2(fullPath, "package.json"));
68
136
  results.push(entryPath);
69
137
  } catch {
70
138
  if (isRecursive) {
@@ -80,7 +148,7 @@ async function glob(pattern, cwd) {
80
148
 
81
149
  // src/discovery.ts
82
150
  async function findRootPackageJson(cwd) {
83
- const packageJsonPath = join2(cwd, "package.json");
151
+ const packageJsonPath = join3(cwd, "package.json");
84
152
  try {
85
153
  await stat2(packageJsonPath);
86
154
  return packageJsonPath;
@@ -116,10 +184,10 @@ async function discoverPackages(cwd) {
116
184
  packageDirs.push(...matches);
117
185
  }
118
186
  } else {
119
- const packagesDir = join2(cwd, "packages");
187
+ const packagesDir = join3(cwd, "packages");
120
188
  try {
121
189
  const entries = await readdir2(packagesDir, { withFileTypes: true });
122
- packageDirs = entries.filter((entry) => entry.isDirectory()).map((entry) => join2("packages", entry.name));
190
+ packageDirs = entries.filter((entry) => entry.isDirectory()).map((entry) => join3("packages", entry.name));
123
191
  } catch {
124
192
  if (!rootPackageJson.private) {
125
193
  return [
@@ -137,7 +205,7 @@ async function discoverPackages(cwd) {
137
205
  }
138
206
  for (const dir of packageDirs) {
139
207
  const pkgPath = resolve(cwd, dir);
140
- const pkgJsonPath = join2(pkgPath, "package.json");
208
+ const pkgJsonPath = join3(pkgPath, "package.json");
141
209
  try {
142
210
  const pkgJson = await readPackageJson(pkgJsonPath);
143
211
  packageNames.add(pkgJson.name);
@@ -150,13 +218,16 @@ async function discoverPackages(cwd) {
150
218
  return packages;
151
219
  }
152
220
  async function packageFromPath(path, packageJsonPath, packageJson, localDependencies) {
221
+ const pkgConfig = loadConfig(path);
153
222
  return {
154
223
  name: packageJson.name,
155
224
  version: packageJson.version,
156
225
  path,
157
226
  packageJsonPath,
158
227
  isPrivate: packageJson.private === true,
159
- localDependencies
228
+ localDependencies,
229
+ skipPublish: pkgConfig["skip-publish"],
230
+ alwaysPublish: pkgConfig["always-publish"]
160
231
  };
161
232
  }
162
233
  function findLocalDependencies(pkg, packageNames) {
@@ -338,19 +409,6 @@ async function multiSelect(message, options, allSelectedByDefault = true) {
338
409
  // src/auth.ts
339
410
  import { spawn } from "node:child_process";
340
411
  import { homedir } from "node:os";
341
-
342
- // src/log.ts
343
- var verboseEnabled = false;
344
- function setVerbose(enabled) {
345
- verboseEnabled = enabled;
346
- }
347
- function debug(...args) {
348
- if (verboseEnabled) {
349
- console.error("[debug]", ...args);
350
- }
351
- }
352
-
353
- // src/auth.ts
354
412
  async function checkNpmAuth(registry) {
355
413
  return new Promise((resolve2) => {
356
414
  const proc = spawn("npm", ["whoami", "--registry", registry], {
@@ -397,10 +455,10 @@ async function npmLogin(registry) {
397
455
  import { spawn as spawn2 } from "node:child_process";
398
456
  import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
399
457
  import { homedir as homedir2 } from "node:os";
400
- import { join as join3 } from "node:path";
458
+ import { join as join4 } from "node:path";
401
459
  var DIM_ON = "\x1B[90m";
402
460
  var DIM_OFF = "\x1B[39m";
403
- function run(command, args, cwd) {
461
+ function run(command, args, cwd, options) {
404
462
  return new Promise((resolve2) => {
405
463
  const proc = spawn2(command, args, {
406
464
  cwd,
@@ -409,17 +467,29 @@ function run(command, args, cwd) {
409
467
  let output = "";
410
468
  proc.stdout?.on("data", (data) => {
411
469
  output += data.toString();
412
- process.stdout.write(DIM_ON + data.toString() + DIM_OFF);
470
+ if (!options?.silent)
471
+ process.stdout.write(DIM_ON + data.toString() + DIM_OFF);
413
472
  });
414
473
  proc.stderr?.on("data", (data) => {
415
474
  output += data.toString();
416
- process.stderr.write(DIM_ON + data.toString() + DIM_OFF);
475
+ if (!options?.silent)
476
+ process.stderr.write(DIM_ON + data.toString() + DIM_OFF);
417
477
  });
418
478
  proc.on("close", (code) => {
419
479
  resolve2({ code: code ?? 1, output });
420
480
  });
421
481
  });
422
482
  }
483
+ function runStdout(command, args, cwd) {
484
+ return new Promise((resolve2) => {
485
+ const proc = spawn2(command, args, { cwd, stdio: ["inherit", "pipe", "ignore"] });
486
+ let stdout = "";
487
+ proc.stdout?.on("data", (data) => {
488
+ stdout += data.toString();
489
+ });
490
+ proc.on("close", () => resolve2(stdout));
491
+ });
492
+ }
423
493
  function runInteractive(command, args, cwd) {
424
494
  return new Promise((resolve2) => {
425
495
  const proc = spawn2(command, args, {
@@ -466,7 +536,7 @@ async function verifyBuild(pkg) {
466
536
  filesToCheck.push("./dist/index.js");
467
537
  }
468
538
  for (const file of filesToCheck) {
469
- const filePath = join3(pkg.path, file);
539
+ const filePath = join4(pkg.path, file);
470
540
  try {
471
541
  await stat3(filePath);
472
542
  } catch {
@@ -510,8 +580,8 @@ async function publishPackage(pkg, registry, context, dryRun) {
510
580
  return { success: true };
511
581
  }
512
582
  async function hasUncommittedChanges(cwd) {
513
- const result = await run("git", ["status", "--porcelain"], cwd);
514
- const output = result.output.trim();
583
+ const stdout = await runStdout("git", ["status", "--porcelain"], cwd);
584
+ const output = stdout.trim();
515
585
  if (!output) {
516
586
  return { hasChanges: false, files: [] };
517
587
  }
@@ -524,8 +594,8 @@ async function commitVersionBump(version, cwd, dryRun) {
524
594
  if (dryRun) {
525
595
  return { success: true };
526
596
  }
527
- const statusResult = await run("git", ["status", "--porcelain"], cwd);
528
- if (!statusResult.output.trim()) {
597
+ const statusOutput = await runStdout("git", ["status", "--porcelain"], cwd);
598
+ if (!statusOutput.trim()) {
529
599
  return { success: true };
530
600
  }
531
601
  const addResult = await run("git", ["add", "-A"], cwd);
@@ -574,12 +644,12 @@ import { spawn as spawn4 } from "node:child_process";
574
644
  // src/claude.ts
575
645
  import { spawn as spawn3 } from "node:child_process";
576
646
  import { readFile as readFile3 } from "node:fs/promises";
577
- import { join as join4 } from "node:path";
647
+ import { join as join5 } from "node:path";
578
648
  async function resolveCredentials() {
579
649
  const home = process.env.HOME ?? "";
580
650
  if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
581
651
  try {
582
- const tokenFile = join4(home, ".claude", "agent-oauth-token");
652
+ const tokenFile = join5(home, ".claude", "agent-oauth-token");
583
653
  const token = (await readFile3(tokenFile, "utf-8")).trim();
584
654
  if (token) {
585
655
  process.env.CLAUDE_CODE_OAUTH_TOKEN = token;
@@ -619,7 +689,7 @@ async function resolveCredentials() {
619
689
  }
620
690
  if (!process.env.CLAUDE_CODE_OAUTH_TOKEN) {
621
691
  try {
622
- const raw = await readFile3(join4(home, ".claude.json"), "utf-8");
692
+ const raw = await readFile3(join5(home, ".claude.json"), "utf-8");
623
693
  const creds = JSON.parse(raw);
624
694
  const accessToken = creds?.claudeAiOauth?.accessToken;
625
695
  if (typeof accessToken === "string" && accessToken.length > 0) {
@@ -805,54 +875,6 @@ async function createGitHubRelease(version, body, cwd, dryRun) {
805
875
  return { success: true, url };
806
876
  }
807
877
 
808
- // src/config.ts
809
- import { existsSync, readFileSync } from "node:fs";
810
- import { join as join5 } from "node:path";
811
- var CONFIG_FILENAME = ".pubz";
812
- var VALID_KEYS = new Set(["skip-build", "skip-publish", "registry"]);
813
- function loadConfig(cwd) {
814
- const configPath = join5(cwd, CONFIG_FILENAME);
815
- if (!existsSync(configPath)) {
816
- debug(`No ${CONFIG_FILENAME} found at ${configPath}`);
817
- return {};
818
- }
819
- const content = readFileSync(configPath, "utf-8");
820
- const config = {};
821
- for (const rawLine of content.split(`
822
- `)) {
823
- const line = rawLine.trim();
824
- if (!line || line.startsWith("#"))
825
- continue;
826
- const eqIndex = line.indexOf("=");
827
- let key;
828
- let value;
829
- if (eqIndex === -1) {
830
- key = line;
831
- } else {
832
- key = line.slice(0, eqIndex).trim();
833
- value = line.slice(eqIndex + 1).trim();
834
- }
835
- if (!VALID_KEYS.has(key)) {
836
- debug(`Ignoring unknown config key: ${key}`);
837
- continue;
838
- }
839
- if (key === "registry") {
840
- config.registry = value ?? "";
841
- } else {
842
- const boolKey = key;
843
- if (value === undefined || value === "true") {
844
- config[boolKey] = true;
845
- } else if (value === "false") {
846
- config[boolKey] = false;
847
- } else {
848
- debug(`Invalid boolean value for ${key}: ${value}`);
849
- }
850
- }
851
- }
852
- debug(`Loaded ${CONFIG_FILENAME}: ${JSON.stringify(config)}`);
853
- return config;
854
- }
855
-
856
878
  // src/version.ts
857
879
  import { readFile as readFile4, writeFile } from "node:fs/promises";
858
880
  async function transformWorkspaceProtocolForPublish(packages, newVersion, dryRun) {
@@ -1149,13 +1171,20 @@ async function main() {
1149
1171
  process.exit(1);
1150
1172
  }
1151
1173
  let packages = await discoverPackages(cwd);
1152
- const publishablePackages = packages.filter((p) => !p.isPrivate || options.skipPublish);
1174
+ const publishablePackages = packages.filter((p) => {
1175
+ if (p.alwaysPublish)
1176
+ return true;
1177
+ if (p.isPrivate)
1178
+ return p.skipPublish || options.skipPublish;
1179
+ return true;
1180
+ });
1153
1181
  if (publishablePackages.length === 0) {
1154
1182
  console.log(yellow("No publishable packages found."));
1155
1183
  console.log("");
1156
1184
  console.log(muted("Make sure your packages:"));
1157
1185
  console.log(muted(' - Have a package.json with a "name" field'));
1158
1186
  console.log(muted(' - Do not have "private": true (or use --skip-publish for private packages)'));
1187
+ console.log(muted(' - Do not have "skip-publish" in a per-package .pubz file'));
1159
1188
  console.log("");
1160
1189
  process.exit(1);
1161
1190
  }
@@ -1283,7 +1312,14 @@ async function main() {
1283
1312
  process.exit(1);
1284
1313
  }
1285
1314
  }
1286
- if (options.skipPublish) {
1315
+ const packagesToPublish = packages.filter((p) => {
1316
+ if (p.alwaysPublish)
1317
+ return true;
1318
+ if (p.skipPublish || options.skipPublish)
1319
+ return false;
1320
+ return true;
1321
+ });
1322
+ if (packagesToPublish.length === 0) {
1287
1323
  console.log(yellow(bold("⏭️ Skipping npm publish")) + dim(" — use without --skip-publish to publish to npm"));
1288
1324
  console.log("");
1289
1325
  } else {
@@ -1308,7 +1344,7 @@ async function main() {
1308
1344
  console.log(`Publishing to ${cyan(registry)}:`);
1309
1345
  }
1310
1346
  console.log("");
1311
- for (const pkg of packages) {
1347
+ for (const pkg of packagesToPublish) {
1312
1348
  console.log(` ${dim("•")} ${cyan(pkg.name)}${dim("@")}${yellow(newVersion)}`);
1313
1349
  }
1314
1350
  console.log("");
@@ -1352,7 +1388,7 @@ async function main() {
1352
1388
  }
1353
1389
  }
1354
1390
  frameLine(dim("Preparing packages..."));
1355
- const workspaceTransforms = await transformWorkspaceProtocolForPublish(packages, newVersion, options.dryRun);
1391
+ const workspaceTransforms = await transformWorkspaceProtocolForPublish(packagesToPublish, newVersion, options.dryRun);
1356
1392
  const publishContext = {
1357
1393
  otp: options.otp,
1358
1394
  useBrowserAuth: !options.ci,
@@ -1363,7 +1399,7 @@ async function main() {
1363
1399
  let failedPackageName = "";
1364
1400
  let failedError = "";
1365
1401
  try {
1366
- for (const pkg of packages) {
1402
+ for (const pkg of packagesToPublish) {
1367
1403
  if (options.dryRun) {
1368
1404
  frameLine(` ${dim("[dry run]")} ${cyan(pkg.name)}${dim("@")}${yellow(newVersion)}`);
1369
1405
  } else {
@@ -1400,7 +1436,7 @@ async function main() {
1400
1436
  console.log("");
1401
1437
  }
1402
1438
  }
1403
- if (!options.skipPublish) {
1439
+ if (packagesToPublish.length > 0) {
1404
1440
  console.log("✅ " + green(bold(`Published v${newVersion}!`)));
1405
1441
  console.log("");
1406
1442
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pubz",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "Interactive CLI for publishing npm packages (single or monorepo)",
5
5
  "type": "module",
6
6
  "bin": {