pubm 0.1.6 → 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.
package/README.md CHANGED
@@ -58,6 +58,11 @@ npm i -g pubm
58
58
  pubm patch --preview
59
59
  ```
60
60
 
61
+ > **Publishing to jsr?** Install the jsr CLI as a devDependency in your project:
62
+ > ```bash
63
+ > pnpm add -D jsr # or npm i -D jsr / yarn add -D jsr
64
+ > ```
65
+
61
66
  ---
62
67
 
63
68
  ## 🔑 Core CLI Options
package/bin/cli.js CHANGED
@@ -5249,50 +5249,6 @@ function link2(text, url) {
5249
5249
  return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
5250
5250
  }
5251
5251
 
5252
- // src/utils/rollback.ts
5253
- var rollbacks = [];
5254
- function addRollback(rollback2, context) {
5255
- rollbacks.push({ fn: rollback2, ctx: context });
5256
- }
5257
- var called = false;
5258
- async function rollback() {
5259
- if (called) return void 0;
5260
- called = true;
5261
- if (rollbacks.length <= 0) return void 0;
5262
- console.log("Rollback...");
5263
- const results = await Promise.allSettled(
5264
- rollbacks.map(({ fn, ctx }) => fn(ctx))
5265
- );
5266
- const failures = results.filter(
5267
- (r) => r.status === "rejected"
5268
- );
5269
- if (failures.length > 0) {
5270
- for (const failure of failures) {
5271
- console.error(
5272
- `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
5273
- );
5274
- }
5275
- console.log(
5276
- "Rollback completed with errors. Some operations may require manual recovery."
5277
- );
5278
- } else {
5279
- console.log("Rollback completed");
5280
- }
5281
- }
5282
-
5283
- // src/utils/listr.ts
5284
- function createListr(...args) {
5285
- const listr = new Listr(...args);
5286
- listr.isRoot = () => false;
5287
- listr.externalSignalHandler = rollback;
5288
- return listr;
5289
- }
5290
-
5291
- // src/utils/package.ts
5292
- import { readFile as readFile2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
5293
- import path8 from "node:path";
5294
- import process11 from "node:process";
5295
-
5296
5252
  // src/ecosystem/rust.ts
5297
5253
  import { readFile, stat as stat2, writeFile } from "node:fs/promises";
5298
5254
  import path7 from "node:path";
@@ -5339,6 +5295,17 @@ var RustEcosystem = class extends Ecosystem {
5339
5295
  pkg.version = newVersion;
5340
5296
  await writeFile(filePath, stringify(cargo));
5341
5297
  }
5298
+ async dependencies() {
5299
+ const cargo = await this.readCargoToml();
5300
+ const deps = [];
5301
+ for (const section of ["dependencies", "build-dependencies"]) {
5302
+ const sectionData = cargo[section];
5303
+ if (sectionData) {
5304
+ deps.push(...Object.keys(sectionData));
5305
+ }
5306
+ }
5307
+ return deps;
5308
+ }
5342
5309
  manifestFiles() {
5343
5310
  return ["Cargo.toml"];
5344
5311
  }
@@ -5353,7 +5320,96 @@ var RustEcosystem = class extends Ecosystem {
5353
5320
  }
5354
5321
  };
5355
5322
 
5323
+ // src/utils/crate-graph.ts
5324
+ async function sortCratesByDependencyOrder(cratePaths) {
5325
+ if (cratePaths.length <= 1) return cratePaths;
5326
+ const crateInfos = await Promise.all(
5327
+ cratePaths.map(async (cratePath) => {
5328
+ const eco = new RustEcosystem(cratePath);
5329
+ const name = await eco.packageName();
5330
+ const deps = await eco.dependencies();
5331
+ return { cratePath, name, deps };
5332
+ })
5333
+ );
5334
+ const nameSet = new Set(crateInfos.map((c) => c.name));
5335
+ const nameToPath = new Map(crateInfos.map((c) => [c.name, c.cratePath]));
5336
+ const internalDeps = /* @__PURE__ */ new Map();
5337
+ const inDegree = /* @__PURE__ */ new Map();
5338
+ for (const name of nameSet) {
5339
+ inDegree.set(name, 0);
5340
+ }
5341
+ for (const crate of crateInfos) {
5342
+ const filtered = crate.deps.filter((d2) => nameSet.has(d2));
5343
+ internalDeps.set(crate.name, filtered);
5344
+ for (const _dep of filtered) {
5345
+ inDegree.set(crate.name, (inDegree.get(crate.name) ?? 0) + 1);
5346
+ }
5347
+ }
5348
+ const queue = [];
5349
+ for (const [name, degree] of inDegree) {
5350
+ if (degree === 0) queue.push(name);
5351
+ }
5352
+ const sorted = [];
5353
+ while (queue.length > 0) {
5354
+ const current = queue.shift();
5355
+ sorted.push(current);
5356
+ for (const [name, deps] of internalDeps) {
5357
+ if (deps.includes(current)) {
5358
+ const newDegree = (inDegree.get(name) ?? 0) - 1;
5359
+ inDegree.set(name, newDegree);
5360
+ if (newDegree === 0) queue.push(name);
5361
+ }
5362
+ }
5363
+ }
5364
+ if (sorted.length !== nameSet.size) {
5365
+ throw new Error("Circular dependency detected among configured crates");
5366
+ }
5367
+ return sorted.map((name) => nameToPath.get(name));
5368
+ }
5369
+
5370
+ // src/utils/rollback.ts
5371
+ var rollbacks = [];
5372
+ function addRollback(rollback2, context) {
5373
+ rollbacks.push({ fn: rollback2, ctx: context });
5374
+ }
5375
+ var called = false;
5376
+ async function rollback() {
5377
+ if (called) return void 0;
5378
+ called = true;
5379
+ if (rollbacks.length <= 0) return void 0;
5380
+ console.log("Rollback...");
5381
+ const results = await Promise.allSettled(
5382
+ rollbacks.map(({ fn, ctx }) => fn(ctx))
5383
+ );
5384
+ const failures = results.filter(
5385
+ (r) => r.status === "rejected"
5386
+ );
5387
+ if (failures.length > 0) {
5388
+ for (const failure of failures) {
5389
+ console.error(
5390
+ `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
5391
+ );
5392
+ }
5393
+ console.log(
5394
+ "Rollback completed with errors. Some operations may require manual recovery."
5395
+ );
5396
+ } else {
5397
+ console.log("Rollback completed");
5398
+ }
5399
+ }
5400
+
5401
+ // src/utils/listr.ts
5402
+ function createListr(...args) {
5403
+ const listr = new Listr(...args);
5404
+ listr.isRoot = () => false;
5405
+ listr.externalSignalHandler = rollback;
5406
+ return listr;
5407
+ }
5408
+
5356
5409
  // src/utils/package.ts
5410
+ import { readFile as readFile2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
5411
+ import path8 from "node:path";
5412
+ import process11 from "node:process";
5357
5413
  var cachedPackageJson = {};
5358
5414
  var cachedJsrJson = {};
5359
5415
  function patchCachedJsrJson(contents, { cwd = process11.cwd() } = {}) {
@@ -5588,7 +5644,7 @@ function collectRegistries(ctx) {
5588
5644
 
5589
5645
  // src/registry/crates.ts
5590
5646
  import path9 from "node:path";
5591
- import { exec as exec3 } from "tinyexec";
5647
+ import { exec as exec3, NonZeroExitError } from "tinyexec";
5592
5648
 
5593
5649
  // src/registry/registry.ts
5594
5650
  var Registry = class {
@@ -5670,9 +5726,10 @@ var CratesRegistry = class extends Registry {
5670
5726
  await exec3("cargo", args, { throwOnError: true });
5671
5727
  return true;
5672
5728
  } catch (error) {
5673
- throw new CratesError("Failed to run `cargo publish`", {
5674
- cause: error
5675
- });
5729
+ const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5730
+ const message = stderr ? `Failed to run \`cargo publish\`:
5731
+ ${stderr}` : "Failed to run `cargo publish`";
5732
+ throw new CratesError(message, { cause: error });
5676
5733
  }
5677
5734
  }
5678
5735
  async isPublished() {
@@ -5767,7 +5824,7 @@ import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
5767
5824
  import npmCli from "@npmcli/promise-spawn";
5768
5825
 
5769
5826
  // src/registry/jsr.ts
5770
- import { exec as exec4, NonZeroExitError } from "tinyexec";
5827
+ import { exec as exec4, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5771
5828
 
5772
5829
  // src/utils/db.ts
5773
5830
  import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
@@ -5946,7 +6003,7 @@ var JsrRegisry = class extends Registry {
5946
6003
  this.packageCreationUrls = void 0;
5947
6004
  return true;
5948
6005
  } catch (error) {
5949
- const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
6006
+ const stderr = error instanceof NonZeroExitError2 ? error.output?.stderr : void 0;
5950
6007
  if (stderr?.includes("don't exist")) {
5951
6008
  const urls = [...stderr.matchAll(/https:\/\/jsr\.io\/new\S+/g)].map(
5952
6009
  (m) => m[0]
@@ -6206,7 +6263,7 @@ async function jsrRegistry() {
6206
6263
  }
6207
6264
 
6208
6265
  // src/registry/npm.ts
6209
- import { exec as exec5, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
6266
+ import { exec as exec5, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
6210
6267
  var NpmError = class extends AbstractError {
6211
6268
  constructor() {
6212
6269
  super(...arguments);
@@ -6263,7 +6320,7 @@ var NpmRegistry = class extends Registry {
6263
6320
  await this.npm(["whoami"]);
6264
6321
  return true;
6265
6322
  } catch (error) {
6266
- if (error instanceof NonZeroExitError2) {
6323
+ if (error instanceof NonZeroExitError3) {
6267
6324
  return false;
6268
6325
  }
6269
6326
  throw new NpmError("Failed to run `npm whoami`", { cause: error });
@@ -6341,7 +6398,7 @@ var NpmRegistry = class extends Registry {
6341
6398
  await this.npm(["publish", "--provenance", "--access", "public"]);
6342
6399
  return true;
6343
6400
  } catch (error) {
6344
- if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("EOTP")) {
6401
+ if (error instanceof NonZeroExitError3 && error.output?.stderr.includes("EOTP")) {
6345
6402
  return false;
6346
6403
  }
6347
6404
  throw this.classifyPublishError(error);
@@ -6353,7 +6410,7 @@ var NpmRegistry = class extends Registry {
6353
6410
  await this.npm(args);
6354
6411
  return true;
6355
6412
  } catch (error) {
6356
- if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("EOTP")) {
6413
+ if (error instanceof NonZeroExitError3 && error.output?.stderr.includes("EOTP")) {
6357
6414
  return false;
6358
6415
  }
6359
6416
  throw this.classifyPublishError(error);
@@ -6369,7 +6426,7 @@ var NpmRegistry = class extends Registry {
6369
6426
  };
6370
6427
  }
6371
6428
  classifyPublishError(error) {
6372
- if (error instanceof NonZeroExitError2) {
6429
+ if (error instanceof NonZeroExitError3) {
6373
6430
  const stderr = error.output?.stderr ?? "";
6374
6431
  if (stderr.includes("EOTP")) {
6375
6432
  return new NpmError("OTP required for publishing", { cause: error });
@@ -6976,10 +7033,8 @@ var requiredConditionsCheckTask = (options) => createListr({
6976
7033
  task: async (_2, parentTask2) => parentTask2.newListr(
6977
7034
  [
6978
7035
  {
6979
- enabled: (ctx) => collectRegistries(ctx).some(
6980
- (registry) => registry !== "jsr"
6981
- ),
6982
- title: "Verifying if npm are installed",
7036
+ enabled: (ctx) => collectRegistries(ctx).includes("npm"),
7037
+ title: "Verifying if npm is installed",
6983
7038
  task: async () => {
6984
7039
  const npm = await npmRegistry();
6985
7040
  if (!await npm.isInstalled()) {
@@ -7110,13 +7165,24 @@ function registryTask(registry) {
7110
7165
  return npmPublishTasks;
7111
7166
  }
7112
7167
  }
7113
- function collectPublishTasks(ctx) {
7168
+ async function collectPublishTasks(ctx) {
7114
7169
  if (ctx.packages?.length) {
7115
- return ctx.packages.flatMap(
7116
- (pkg) => pkg.registries.map(
7117
- (reg) => reg === "crates" ? createCratesPublishTask(pkg.path) : registryTask(reg)
7118
- )
7170
+ const nonCratesTasks = ctx.packages.flatMap(
7171
+ (pkg) => pkg.registries.filter((reg) => reg !== "crates").map((reg) => registryTask(reg))
7119
7172
  );
7173
+ const cratesPaths = ctx.packages.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path);
7174
+ if (cratesPaths.length === 0) {
7175
+ return nonCratesTasks;
7176
+ }
7177
+ const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
7178
+ const sequentialCratesTask = {
7179
+ title: "Publishing to crates.io (sequential)",
7180
+ task: (_ctx, task) => task.newListr(
7181
+ sortedPaths.map((p) => createCratesPublishTask(p)),
7182
+ { concurrent: false }
7183
+ )
7184
+ };
7185
+ return [...nonCratesTasks, sequentialCratesTask];
7120
7186
  }
7121
7187
  return collectRegistries(ctx).map(registryTask);
7122
7188
  }
@@ -7138,7 +7204,7 @@ async function run(options) {
7138
7204
  await createListr(
7139
7205
  options.publishOnly ? {
7140
7206
  title: "Publishing",
7141
- task: (ctx2, parentTask) => parentTask.newListr(collectPublishTasks(ctx2), {
7207
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7142
7208
  concurrent: true
7143
7209
  })
7144
7210
  } : [
@@ -7227,7 +7293,7 @@ async function run(options) {
7227
7293
  {
7228
7294
  skip: (ctx2) => options.skipPublish || !!ctx2.preview,
7229
7295
  title: "Publishing",
7230
- task: (ctx2, parentTask) => parentTask.newListr(collectPublishTasks(ctx2), {
7296
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7231
7297
  concurrent: true
7232
7298
  })
7233
7299
  },
@@ -7274,12 +7340,26 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
7274
7340
  }
7275
7341
  ]
7276
7342
  ).run(ctx);
7277
- const npmPackageName = (await getPackageJson()).name;
7278
- const jsrPackageName = (await getJsrJson()).name;
7343
+ const registries = collectRegistries(ctx);
7344
+ const parts = [];
7345
+ if (registries.includes("npm")) {
7346
+ const npmPackageName = (await getPackageJson()).name;
7347
+ parts.push(`${color.bold(npmPackageName)} on ${color.green("npm")}`);
7348
+ }
7349
+ if (registries.includes("jsr")) {
7350
+ const jsrPackageName = (await getJsrJson()).name;
7351
+ parts.push(`${color.bold(jsrPackageName)} on ${color.yellow("jsr")}`);
7352
+ }
7353
+ if (registries.includes("crates")) {
7354
+ const crateNames = ctx.packages?.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path) ?? ["crate"];
7355
+ for (const name of crateNames) {
7356
+ parts.push(`${color.bold(name)} on ${color.red("crates.io")}`);
7357
+ }
7358
+ }
7279
7359
  console.log(
7280
7360
  `
7281
7361
 
7282
- \u{1F680} Successfully published ${color.bold(npmPackageName)} on ${color.green("npm")} and ${color.bold(jsrPackageName)} on ${color.yellow("jsr")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
7362
+ \u{1F680} Successfully published ${parts.join(", ")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
7283
7363
  `
7284
7364
  );
7285
7365
  } catch (e2) {
package/dist/index.cjs CHANGED
@@ -4814,50 +4814,6 @@ function link2(text, url) {
4814
4814
  return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
4815
4815
  }
4816
4816
 
4817
- // src/utils/rollback.ts
4818
- var rollbacks = [];
4819
- function addRollback(rollback2, context) {
4820
- rollbacks.push({ fn: rollback2, ctx: context });
4821
- }
4822
- var called = false;
4823
- async function rollback() {
4824
- if (called) return void 0;
4825
- called = true;
4826
- if (rollbacks.length <= 0) return void 0;
4827
- console.log("Rollback...");
4828
- const results = await Promise.allSettled(
4829
- rollbacks.map(({ fn, ctx }) => fn(ctx))
4830
- );
4831
- const failures = results.filter(
4832
- (r) => r.status === "rejected"
4833
- );
4834
- if (failures.length > 0) {
4835
- for (const failure of failures) {
4836
- console.error(
4837
- `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
4838
- );
4839
- }
4840
- console.log(
4841
- "Rollback completed with errors. Some operations may require manual recovery."
4842
- );
4843
- } else {
4844
- console.log("Rollback completed");
4845
- }
4846
- }
4847
-
4848
- // src/utils/listr.ts
4849
- function createListr(...args) {
4850
- const listr = new Listr(...args);
4851
- listr.isRoot = () => false;
4852
- listr.externalSignalHandler = rollback;
4853
- return listr;
4854
- }
4855
-
4856
- // src/utils/package.ts
4857
- var import_promises3 = require("fs/promises");
4858
- var import_node_path3 = __toESM(require("path"), 1);
4859
- var import_node_process5 = __toESM(require("process"), 1);
4860
-
4861
4817
  // src/ecosystem/rust.ts
4862
4818
  var import_promises2 = require("fs/promises");
4863
4819
  var import_node_path2 = __toESM(require("path"), 1);
@@ -4904,6 +4860,17 @@ var RustEcosystem = class extends Ecosystem {
4904
4860
  pkg.version = newVersion;
4905
4861
  await (0, import_promises2.writeFile)(filePath, (0, import_smol_toml.stringify)(cargo));
4906
4862
  }
4863
+ async dependencies() {
4864
+ const cargo = await this.readCargoToml();
4865
+ const deps = [];
4866
+ for (const section of ["dependencies", "build-dependencies"]) {
4867
+ const sectionData = cargo[section];
4868
+ if (sectionData) {
4869
+ deps.push(...Object.keys(sectionData));
4870
+ }
4871
+ }
4872
+ return deps;
4873
+ }
4907
4874
  manifestFiles() {
4908
4875
  return ["Cargo.toml"];
4909
4876
  }
@@ -4918,7 +4885,96 @@ var RustEcosystem = class extends Ecosystem {
4918
4885
  }
4919
4886
  };
4920
4887
 
4888
+ // src/utils/crate-graph.ts
4889
+ async function sortCratesByDependencyOrder(cratePaths) {
4890
+ if (cratePaths.length <= 1) return cratePaths;
4891
+ const crateInfos = await Promise.all(
4892
+ cratePaths.map(async (cratePath) => {
4893
+ const eco = new RustEcosystem(cratePath);
4894
+ const name = await eco.packageName();
4895
+ const deps = await eco.dependencies();
4896
+ return { cratePath, name, deps };
4897
+ })
4898
+ );
4899
+ const nameSet = new Set(crateInfos.map((c) => c.name));
4900
+ const nameToPath = new Map(crateInfos.map((c) => [c.name, c.cratePath]));
4901
+ const internalDeps = /* @__PURE__ */ new Map();
4902
+ const inDegree = /* @__PURE__ */ new Map();
4903
+ for (const name of nameSet) {
4904
+ inDegree.set(name, 0);
4905
+ }
4906
+ for (const crate of crateInfos) {
4907
+ const filtered = crate.deps.filter((d2) => nameSet.has(d2));
4908
+ internalDeps.set(crate.name, filtered);
4909
+ for (const _dep of filtered) {
4910
+ inDegree.set(crate.name, (inDegree.get(crate.name) ?? 0) + 1);
4911
+ }
4912
+ }
4913
+ const queue = [];
4914
+ for (const [name, degree] of inDegree) {
4915
+ if (degree === 0) queue.push(name);
4916
+ }
4917
+ const sorted = [];
4918
+ while (queue.length > 0) {
4919
+ const current = queue.shift();
4920
+ sorted.push(current);
4921
+ for (const [name, deps] of internalDeps) {
4922
+ if (deps.includes(current)) {
4923
+ const newDegree = (inDegree.get(name) ?? 0) - 1;
4924
+ inDegree.set(name, newDegree);
4925
+ if (newDegree === 0) queue.push(name);
4926
+ }
4927
+ }
4928
+ }
4929
+ if (sorted.length !== nameSet.size) {
4930
+ throw new Error("Circular dependency detected among configured crates");
4931
+ }
4932
+ return sorted.map((name) => nameToPath.get(name));
4933
+ }
4934
+
4935
+ // src/utils/rollback.ts
4936
+ var rollbacks = [];
4937
+ function addRollback(rollback2, context) {
4938
+ rollbacks.push({ fn: rollback2, ctx: context });
4939
+ }
4940
+ var called = false;
4941
+ async function rollback() {
4942
+ if (called) return void 0;
4943
+ called = true;
4944
+ if (rollbacks.length <= 0) return void 0;
4945
+ console.log("Rollback...");
4946
+ const results = await Promise.allSettled(
4947
+ rollbacks.map(({ fn, ctx }) => fn(ctx))
4948
+ );
4949
+ const failures = results.filter(
4950
+ (r) => r.status === "rejected"
4951
+ );
4952
+ if (failures.length > 0) {
4953
+ for (const failure of failures) {
4954
+ console.error(
4955
+ `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
4956
+ );
4957
+ }
4958
+ console.log(
4959
+ "Rollback completed with errors. Some operations may require manual recovery."
4960
+ );
4961
+ } else {
4962
+ console.log("Rollback completed");
4963
+ }
4964
+ }
4965
+
4966
+ // src/utils/listr.ts
4967
+ function createListr(...args) {
4968
+ const listr = new Listr(...args);
4969
+ listr.isRoot = () => false;
4970
+ listr.externalSignalHandler = rollback;
4971
+ return listr;
4972
+ }
4973
+
4921
4974
  // src/utils/package.ts
4975
+ var import_promises3 = require("fs/promises");
4976
+ var import_node_path3 = __toESM(require("path"), 1);
4977
+ var import_node_process5 = __toESM(require("process"), 1);
4922
4978
  var cachedPackageJson = {};
4923
4979
  var cachedJsrJson = {};
4924
4980
  function patchCachedJsrJson(contents, { cwd = import_node_process5.default.cwd() } = {}) {
@@ -5235,9 +5291,10 @@ var CratesRegistry = class extends Registry {
5235
5291
  await (0, import_tinyexec2.exec)("cargo", args, { throwOnError: true });
5236
5292
  return true;
5237
5293
  } catch (error) {
5238
- throw new CratesError("Failed to run `cargo publish`", {
5239
- cause: error
5240
- });
5294
+ const stderr = error instanceof import_tinyexec2.NonZeroExitError ? error.output?.stderr : void 0;
5295
+ const message = stderr ? `Failed to run \`cargo publish\`:
5296
+ ${stderr}` : "Failed to run `cargo publish`";
5297
+ throw new CratesError(message, { cause: error });
5241
5298
  }
5242
5299
  }
5243
5300
  async isPublished() {
@@ -6544,10 +6601,8 @@ var requiredConditionsCheckTask = (options) => createListr({
6544
6601
  task: async (_2, parentTask2) => parentTask2.newListr(
6545
6602
  [
6546
6603
  {
6547
- enabled: (ctx) => collectRegistries(ctx).some(
6548
- (registry) => registry !== "jsr"
6549
- ),
6550
- title: "Verifying if npm are installed",
6604
+ enabled: (ctx) => collectRegistries(ctx).includes("npm"),
6605
+ title: "Verifying if npm is installed",
6551
6606
  task: async () => {
6552
6607
  const npm = await npmRegistry();
6553
6608
  if (!await npm.isInstalled()) {
@@ -6678,13 +6733,24 @@ function registryTask(registry) {
6678
6733
  return npmPublishTasks;
6679
6734
  }
6680
6735
  }
6681
- function collectPublishTasks(ctx) {
6736
+ async function collectPublishTasks(ctx) {
6682
6737
  if (ctx.packages?.length) {
6683
- return ctx.packages.flatMap(
6684
- (pkg) => pkg.registries.map(
6685
- (reg) => reg === "crates" ? createCratesPublishTask(pkg.path) : registryTask(reg)
6686
- )
6738
+ const nonCratesTasks = ctx.packages.flatMap(
6739
+ (pkg) => pkg.registries.filter((reg) => reg !== "crates").map((reg) => registryTask(reg))
6687
6740
  );
6741
+ const cratesPaths = ctx.packages.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path);
6742
+ if (cratesPaths.length === 0) {
6743
+ return nonCratesTasks;
6744
+ }
6745
+ const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
6746
+ const sequentialCratesTask = {
6747
+ title: "Publishing to crates.io (sequential)",
6748
+ task: (_ctx, task) => task.newListr(
6749
+ sortedPaths.map((p) => createCratesPublishTask(p)),
6750
+ { concurrent: false }
6751
+ )
6752
+ };
6753
+ return [...nonCratesTasks, sequentialCratesTask];
6688
6754
  }
6689
6755
  return collectRegistries(ctx).map(registryTask);
6690
6756
  }
@@ -6706,7 +6772,7 @@ async function run(options) {
6706
6772
  await createListr(
6707
6773
  options.publishOnly ? {
6708
6774
  title: "Publishing",
6709
- task: (ctx2, parentTask) => parentTask.newListr(collectPublishTasks(ctx2), {
6775
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
6710
6776
  concurrent: true
6711
6777
  })
6712
6778
  } : [
@@ -6795,7 +6861,7 @@ async function run(options) {
6795
6861
  {
6796
6862
  skip: (ctx2) => options.skipPublish || !!ctx2.preview,
6797
6863
  title: "Publishing",
6798
- task: (ctx2, parentTask) => parentTask.newListr(collectPublishTasks(ctx2), {
6864
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
6799
6865
  concurrent: true
6800
6866
  })
6801
6867
  },
@@ -6842,12 +6908,26 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
6842
6908
  }
6843
6909
  ]
6844
6910
  ).run(ctx);
6845
- const npmPackageName = (await getPackageJson()).name;
6846
- const jsrPackageName = (await getJsrJson()).name;
6911
+ const registries = collectRegistries(ctx);
6912
+ const parts = [];
6913
+ if (registries.includes("npm")) {
6914
+ const npmPackageName = (await getPackageJson()).name;
6915
+ parts.push(`${color.bold(npmPackageName)} on ${color.green("npm")}`);
6916
+ }
6917
+ if (registries.includes("jsr")) {
6918
+ const jsrPackageName = (await getJsrJson()).name;
6919
+ parts.push(`${color.bold(jsrPackageName)} on ${color.yellow("jsr")}`);
6920
+ }
6921
+ if (registries.includes("crates")) {
6922
+ const crateNames = ctx.packages?.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path) ?? ["crate"];
6923
+ for (const name of crateNames) {
6924
+ parts.push(`${color.bold(name)} on ${color.red("crates.io")}`);
6925
+ }
6926
+ }
6847
6927
  console.log(
6848
6928
  `
6849
6929
 
6850
- \u{1F680} Successfully published ${color.bold(npmPackageName)} on ${color.green("npm")} and ${color.bold(jsrPackageName)} on ${color.yellow("jsr")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
6930
+ \u{1F680} Successfully published ${parts.join(", ")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
6851
6931
  `
6852
6932
  );
6853
6933
  } catch (e2) {
package/dist/index.js CHANGED
@@ -4781,50 +4781,6 @@ function link2(text, url) {
4781
4781
  return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
4782
4782
  }
4783
4783
 
4784
- // src/utils/rollback.ts
4785
- var rollbacks = [];
4786
- function addRollback(rollback2, context) {
4787
- rollbacks.push({ fn: rollback2, ctx: context });
4788
- }
4789
- var called = false;
4790
- async function rollback() {
4791
- if (called) return void 0;
4792
- called = true;
4793
- if (rollbacks.length <= 0) return void 0;
4794
- console.log("Rollback...");
4795
- const results = await Promise.allSettled(
4796
- rollbacks.map(({ fn, ctx }) => fn(ctx))
4797
- );
4798
- const failures = results.filter(
4799
- (r) => r.status === "rejected"
4800
- );
4801
- if (failures.length > 0) {
4802
- for (const failure of failures) {
4803
- console.error(
4804
- `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
4805
- );
4806
- }
4807
- console.log(
4808
- "Rollback completed with errors. Some operations may require manual recovery."
4809
- );
4810
- } else {
4811
- console.log("Rollback completed");
4812
- }
4813
- }
4814
-
4815
- // src/utils/listr.ts
4816
- function createListr(...args) {
4817
- const listr = new Listr(...args);
4818
- listr.isRoot = () => false;
4819
- listr.externalSignalHandler = rollback;
4820
- return listr;
4821
- }
4822
-
4823
- // src/utils/package.ts
4824
- import { readFile as readFile2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
4825
- import path3 from "node:path";
4826
- import process7 from "node:process";
4827
-
4828
4784
  // src/ecosystem/rust.ts
4829
4785
  import { readFile, stat as stat2, writeFile } from "node:fs/promises";
4830
4786
  import path2 from "node:path";
@@ -4871,6 +4827,17 @@ var RustEcosystem = class extends Ecosystem {
4871
4827
  pkg.version = newVersion;
4872
4828
  await writeFile(filePath, stringify(cargo));
4873
4829
  }
4830
+ async dependencies() {
4831
+ const cargo = await this.readCargoToml();
4832
+ const deps = [];
4833
+ for (const section of ["dependencies", "build-dependencies"]) {
4834
+ const sectionData = cargo[section];
4835
+ if (sectionData) {
4836
+ deps.push(...Object.keys(sectionData));
4837
+ }
4838
+ }
4839
+ return deps;
4840
+ }
4874
4841
  manifestFiles() {
4875
4842
  return ["Cargo.toml"];
4876
4843
  }
@@ -4885,7 +4852,96 @@ var RustEcosystem = class extends Ecosystem {
4885
4852
  }
4886
4853
  };
4887
4854
 
4855
+ // src/utils/crate-graph.ts
4856
+ async function sortCratesByDependencyOrder(cratePaths) {
4857
+ if (cratePaths.length <= 1) return cratePaths;
4858
+ const crateInfos = await Promise.all(
4859
+ cratePaths.map(async (cratePath) => {
4860
+ const eco = new RustEcosystem(cratePath);
4861
+ const name = await eco.packageName();
4862
+ const deps = await eco.dependencies();
4863
+ return { cratePath, name, deps };
4864
+ })
4865
+ );
4866
+ const nameSet = new Set(crateInfos.map((c) => c.name));
4867
+ const nameToPath = new Map(crateInfos.map((c) => [c.name, c.cratePath]));
4868
+ const internalDeps = /* @__PURE__ */ new Map();
4869
+ const inDegree = /* @__PURE__ */ new Map();
4870
+ for (const name of nameSet) {
4871
+ inDegree.set(name, 0);
4872
+ }
4873
+ for (const crate of crateInfos) {
4874
+ const filtered = crate.deps.filter((d2) => nameSet.has(d2));
4875
+ internalDeps.set(crate.name, filtered);
4876
+ for (const _dep of filtered) {
4877
+ inDegree.set(crate.name, (inDegree.get(crate.name) ?? 0) + 1);
4878
+ }
4879
+ }
4880
+ const queue = [];
4881
+ for (const [name, degree] of inDegree) {
4882
+ if (degree === 0) queue.push(name);
4883
+ }
4884
+ const sorted = [];
4885
+ while (queue.length > 0) {
4886
+ const current = queue.shift();
4887
+ sorted.push(current);
4888
+ for (const [name, deps] of internalDeps) {
4889
+ if (deps.includes(current)) {
4890
+ const newDegree = (inDegree.get(name) ?? 0) - 1;
4891
+ inDegree.set(name, newDegree);
4892
+ if (newDegree === 0) queue.push(name);
4893
+ }
4894
+ }
4895
+ }
4896
+ if (sorted.length !== nameSet.size) {
4897
+ throw new Error("Circular dependency detected among configured crates");
4898
+ }
4899
+ return sorted.map((name) => nameToPath.get(name));
4900
+ }
4901
+
4902
+ // src/utils/rollback.ts
4903
+ var rollbacks = [];
4904
+ function addRollback(rollback2, context) {
4905
+ rollbacks.push({ fn: rollback2, ctx: context });
4906
+ }
4907
+ var called = false;
4908
+ async function rollback() {
4909
+ if (called) return void 0;
4910
+ called = true;
4911
+ if (rollbacks.length <= 0) return void 0;
4912
+ console.log("Rollback...");
4913
+ const results = await Promise.allSettled(
4914
+ rollbacks.map(({ fn, ctx }) => fn(ctx))
4915
+ );
4916
+ const failures = results.filter(
4917
+ (r) => r.status === "rejected"
4918
+ );
4919
+ if (failures.length > 0) {
4920
+ for (const failure of failures) {
4921
+ console.error(
4922
+ `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
4923
+ );
4924
+ }
4925
+ console.log(
4926
+ "Rollback completed with errors. Some operations may require manual recovery."
4927
+ );
4928
+ } else {
4929
+ console.log("Rollback completed");
4930
+ }
4931
+ }
4932
+
4933
+ // src/utils/listr.ts
4934
+ function createListr(...args) {
4935
+ const listr = new Listr(...args);
4936
+ listr.isRoot = () => false;
4937
+ listr.externalSignalHandler = rollback;
4938
+ return listr;
4939
+ }
4940
+
4888
4941
  // src/utils/package.ts
4942
+ import { readFile as readFile2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
4943
+ import path3 from "node:path";
4944
+ import process7 from "node:process";
4889
4945
  var cachedPackageJson = {};
4890
4946
  var cachedJsrJson = {};
4891
4947
  function patchCachedJsrJson(contents, { cwd = process7.cwd() } = {}) {
@@ -5120,7 +5176,7 @@ function collectRegistries(ctx) {
5120
5176
 
5121
5177
  // src/registry/crates.ts
5122
5178
  import path4 from "node:path";
5123
- import { exec as exec3 } from "tinyexec";
5179
+ import { exec as exec3, NonZeroExitError } from "tinyexec";
5124
5180
 
5125
5181
  // src/registry/registry.ts
5126
5182
  var Registry = class {
@@ -5202,9 +5258,10 @@ var CratesRegistry = class extends Registry {
5202
5258
  await exec3("cargo", args, { throwOnError: true });
5203
5259
  return true;
5204
5260
  } catch (error) {
5205
- throw new CratesError("Failed to run `cargo publish`", {
5206
- cause: error
5207
- });
5261
+ const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5262
+ const message = stderr ? `Failed to run \`cargo publish\`:
5263
+ ${stderr}` : "Failed to run `cargo publish`";
5264
+ throw new CratesError(message, { cause: error });
5208
5265
  }
5209
5266
  }
5210
5267
  async isPublished() {
@@ -5299,7 +5356,7 @@ import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
5299
5356
  import npmCli from "@npmcli/promise-spawn";
5300
5357
 
5301
5358
  // src/registry/jsr.ts
5302
- import { exec as exec4, NonZeroExitError } from "tinyexec";
5359
+ import { exec as exec4, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5303
5360
 
5304
5361
  // src/utils/db.ts
5305
5362
  import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
@@ -5478,7 +5535,7 @@ var JsrRegisry = class extends Registry {
5478
5535
  this.packageCreationUrls = void 0;
5479
5536
  return true;
5480
5537
  } catch (error) {
5481
- const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5538
+ const stderr = error instanceof NonZeroExitError2 ? error.output?.stderr : void 0;
5482
5539
  if (stderr?.includes("don't exist")) {
5483
5540
  const urls = [...stderr.matchAll(/https:\/\/jsr\.io\/new\S+/g)].map(
5484
5541
  (m) => m[0]
@@ -5738,7 +5795,7 @@ async function jsrRegistry() {
5738
5795
  }
5739
5796
 
5740
5797
  // src/registry/npm.ts
5741
- import { exec as exec5, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5798
+ import { exec as exec5, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
5742
5799
  var NpmError = class extends AbstractError {
5743
5800
  constructor() {
5744
5801
  super(...arguments);
@@ -5795,7 +5852,7 @@ var NpmRegistry = class extends Registry {
5795
5852
  await this.npm(["whoami"]);
5796
5853
  return true;
5797
5854
  } catch (error) {
5798
- if (error instanceof NonZeroExitError2) {
5855
+ if (error instanceof NonZeroExitError3) {
5799
5856
  return false;
5800
5857
  }
5801
5858
  throw new NpmError("Failed to run `npm whoami`", { cause: error });
@@ -5873,7 +5930,7 @@ var NpmRegistry = class extends Registry {
5873
5930
  await this.npm(["publish", "--provenance", "--access", "public"]);
5874
5931
  return true;
5875
5932
  } catch (error) {
5876
- if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("EOTP")) {
5933
+ if (error instanceof NonZeroExitError3 && error.output?.stderr.includes("EOTP")) {
5877
5934
  return false;
5878
5935
  }
5879
5936
  throw this.classifyPublishError(error);
@@ -5885,7 +5942,7 @@ var NpmRegistry = class extends Registry {
5885
5942
  await this.npm(args);
5886
5943
  return true;
5887
5944
  } catch (error) {
5888
- if (error instanceof NonZeroExitError2 && error.output?.stderr.includes("EOTP")) {
5945
+ if (error instanceof NonZeroExitError3 && error.output?.stderr.includes("EOTP")) {
5889
5946
  return false;
5890
5947
  }
5891
5948
  throw this.classifyPublishError(error);
@@ -5901,7 +5958,7 @@ var NpmRegistry = class extends Registry {
5901
5958
  };
5902
5959
  }
5903
5960
  classifyPublishError(error) {
5904
- if (error instanceof NonZeroExitError2) {
5961
+ if (error instanceof NonZeroExitError3) {
5905
5962
  const stderr = error.output?.stderr ?? "";
5906
5963
  if (stderr.includes("EOTP")) {
5907
5964
  return new NpmError("OTP required for publishing", { cause: error });
@@ -6508,10 +6565,8 @@ var requiredConditionsCheckTask = (options) => createListr({
6508
6565
  task: async (_2, parentTask2) => parentTask2.newListr(
6509
6566
  [
6510
6567
  {
6511
- enabled: (ctx) => collectRegistries(ctx).some(
6512
- (registry) => registry !== "jsr"
6513
- ),
6514
- title: "Verifying if npm are installed",
6568
+ enabled: (ctx) => collectRegistries(ctx).includes("npm"),
6569
+ title: "Verifying if npm is installed",
6515
6570
  task: async () => {
6516
6571
  const npm = await npmRegistry();
6517
6572
  if (!await npm.isInstalled()) {
@@ -6642,13 +6697,24 @@ function registryTask(registry) {
6642
6697
  return npmPublishTasks;
6643
6698
  }
6644
6699
  }
6645
- function collectPublishTasks(ctx) {
6700
+ async function collectPublishTasks(ctx) {
6646
6701
  if (ctx.packages?.length) {
6647
- return ctx.packages.flatMap(
6648
- (pkg) => pkg.registries.map(
6649
- (reg) => reg === "crates" ? createCratesPublishTask(pkg.path) : registryTask(reg)
6650
- )
6702
+ const nonCratesTasks = ctx.packages.flatMap(
6703
+ (pkg) => pkg.registries.filter((reg) => reg !== "crates").map((reg) => registryTask(reg))
6651
6704
  );
6705
+ const cratesPaths = ctx.packages.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path);
6706
+ if (cratesPaths.length === 0) {
6707
+ return nonCratesTasks;
6708
+ }
6709
+ const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
6710
+ const sequentialCratesTask = {
6711
+ title: "Publishing to crates.io (sequential)",
6712
+ task: (_ctx, task) => task.newListr(
6713
+ sortedPaths.map((p) => createCratesPublishTask(p)),
6714
+ { concurrent: false }
6715
+ )
6716
+ };
6717
+ return [...nonCratesTasks, sequentialCratesTask];
6652
6718
  }
6653
6719
  return collectRegistries(ctx).map(registryTask);
6654
6720
  }
@@ -6670,7 +6736,7 @@ async function run(options) {
6670
6736
  await createListr(
6671
6737
  options.publishOnly ? {
6672
6738
  title: "Publishing",
6673
- task: (ctx2, parentTask) => parentTask.newListr(collectPublishTasks(ctx2), {
6739
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
6674
6740
  concurrent: true
6675
6741
  })
6676
6742
  } : [
@@ -6759,7 +6825,7 @@ async function run(options) {
6759
6825
  {
6760
6826
  skip: (ctx2) => options.skipPublish || !!ctx2.preview,
6761
6827
  title: "Publishing",
6762
- task: (ctx2, parentTask) => parentTask.newListr(collectPublishTasks(ctx2), {
6828
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
6763
6829
  concurrent: true
6764
6830
  })
6765
6831
  },
@@ -6806,12 +6872,26 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
6806
6872
  }
6807
6873
  ]
6808
6874
  ).run(ctx);
6809
- const npmPackageName = (await getPackageJson()).name;
6810
- const jsrPackageName = (await getJsrJson()).name;
6875
+ const registries = collectRegistries(ctx);
6876
+ const parts = [];
6877
+ if (registries.includes("npm")) {
6878
+ const npmPackageName = (await getPackageJson()).name;
6879
+ parts.push(`${color.bold(npmPackageName)} on ${color.green("npm")}`);
6880
+ }
6881
+ if (registries.includes("jsr")) {
6882
+ const jsrPackageName = (await getJsrJson()).name;
6883
+ parts.push(`${color.bold(jsrPackageName)} on ${color.yellow("jsr")}`);
6884
+ }
6885
+ if (registries.includes("crates")) {
6886
+ const crateNames = ctx.packages?.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path) ?? ["crate"];
6887
+ for (const name of crateNames) {
6888
+ parts.push(`${color.bold(name)} on ${color.red("crates.io")}`);
6889
+ }
6890
+ }
6811
6891
  console.log(
6812
6892
  `
6813
6893
 
6814
- \u{1F680} Successfully published ${color.bold(npmPackageName)} on ${color.green("npm")} and ${color.bold(jsrPackageName)} on ${color.yellow("jsr")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
6894
+ \u{1F680} Successfully published ${parts.join(", ")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
6815
6895
  `
6816
6896
  );
6817
6897
  } catch (e2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pubm",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "engines": {
5
5
  "node": ">=18",
6
6
  "git": ">=2.11.0"