tegami 0.1.0-beta.3 → 0.1.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,4 +1,4 @@
1
- import { C as PublishResult, O as DraftPlan, _ as Tegami, t as Awaitable } from "../types-CrA7ZKxb.js";
1
+ import { C as PublishResult, O as DraftPlan, _ as Tegami, t as Awaitable } from "../types-UjsZkz42.js";
2
2
  import { Command } from "commander";
3
3
 
4
4
  //#region src/cli/index.d.ts
package/dist/cli/index.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import { a as changelogFilename, t as assertPublishPlanFinished } from "../checks-BQISvt_o.js";
2
2
  import { n as handlePluginError } from "../error-DBK-9uBa.js";
3
- import { r as formatNpmDistTag, t as bumpDepth } from "../semver-CPtl0XNq.js";
3
+ import { n as formatNpmDistTag } from "../semver-mWK2Khi2.js";
4
4
  import { t as isCI } from "../constants-B9qjNfvr.js";
5
- import { mkdir, readFile, writeFile } from "node:fs/promises";
6
- import path, { basename, isAbsolute, join, normalize, relative } from "node:path";
5
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
6
+ import path, { basename, isAbsolute, join, normalize, relative, resolve } from "node:path";
7
7
  import { x } from "tinyexec";
8
8
  import { dump } from "js-yaml";
9
9
  import { detect, resolveCommand } from "package-manager-detector";
10
10
  import { autocompleteMultiselect, confirm, intro, isCancel, multiline, note, outro, select, spinner } from "@clack/prompts";
11
- import { Command } from "commander";
11
+ import { Command, InvalidArgumentError } from "commander";
12
+ import { tmpdir } from "node:os";
12
13
  //#region src/cli/init-agent.ts
13
14
  const CHANGELOG_DOCS_URL$1 = "https://tegami.fuma-nama.dev/changelog";
14
15
  async function initAgent(context, options = {}) {
@@ -80,32 +81,37 @@ async function formatRunScriptCommand(cwd, script, agent) {
80
81
  return [resolved.command, ...resolved.args].join(" ");
81
82
  }
82
83
  //#endregion
83
- //#region src/cli/ci-pr.ts
84
+ //#region src/cli/pr.ts
84
85
  const TEGAMI_DOCS_URL = "https://tegami.fuma-nama.dev";
85
86
  const CHANGELOG_DOCS_URL = `${TEGAMI_DOCS_URL}/changelog`;
86
- async function runCiPr(context, draft, options = {}) {
87
- const event = await resolvePullRequestEvent();
88
- return renderCiPrPreview(context, draft, {
89
- repo: options.repo ?? context.github?.repo ?? process.env.GITHUB_REPOSITORY,
90
- branch: options.branch ?? event?.headRef,
91
- prChangelogFiles: event && !options.branch ? await getPullRequestChangelogFiles(context, event.baseSha, event.headSha) : [],
92
- tegamiCommand: await formatRunScriptCommand(context.cwd, "tegami", context.options.npm?.client)
93
- });
94
- }
95
- function renderCiPrPreview(context, draft, options = {}) {
96
- const changelogDir = relative(context.cwd, context.changelogDir) || ".tegami";
97
- const tegamiCommand = options.tegamiCommand ?? "npm run tegami";
98
- const createLink = options.repo && options.branch ? createChangelogUrl(options.repo, options.branch, changelogDir, changelogFilename()) : void 0;
87
+ const COMMENT_MARKER = "<!-- tegami -->";
88
+ async function buildPrPreview(context, draft, options = {}) {
89
+ const pullRequest = await resolvePullRequest(context, options);
90
+ const tegamiCommand = await formatRunScriptCommand(context.cwd, "tegami", context.options.npm?.client);
91
+ const prChangelogFiles = await listPullRequestChangelogFiles(context, pullRequest.baseSha, pullRequest.headSha);
92
+ const createLink = createChangelogUrl(context, pullRequest.headRepo, pullRequest.headRef, changelogFilename());
99
93
  const lines = [
100
94
  "### Tegami",
101
95
  "",
102
96
  `This repository uses [Tegami](${TEGAMI_DOCS_URL}) to manage releases. When your changes affect published packages, add a changelog file under \`.tegami/\` before merging.`,
97
+ "",
98
+ `[**Create a changelog →**](${createLink}) · [Changelog format](${CHANGELOG_DOCS_URL})`,
103
99
  ""
104
100
  ];
105
- if (createLink) lines.push(`[**Create a changelog →**](${createLink}) · [Changelog format](${CHANGELOG_DOCS_URL})`, "");
106
- else lines.push(`Run \`${tegamiCommand}\` locally or see the [changelog format](${CHANGELOG_DOCS_URL}).`, "");
107
- const pendingPackages = getPendingPackages(context, draft);
108
- const prChangelogs = filterChangelogsInPullRequest(draft, options.prChangelogFiles);
101
+ const pendingPackages = [];
102
+ for (const pkg of context.graph.getPackages()) {
103
+ const plan = draft.getPackagePlan(pkg.id);
104
+ if (!plan?.type) continue;
105
+ pendingPackages.push({
106
+ name: pkg.name,
107
+ type: plan.type,
108
+ from: pkg.version,
109
+ to: plan.bumpVersion(pkg),
110
+ distTag: plan.npm?.distTag,
111
+ publish: plan.publish ?? false
112
+ });
113
+ }
114
+ const prChangelogs = draft.getChangelogs().filter((entry) => prChangelogFiles.has(entry.filename));
109
115
  if (pendingPackages.length > 0) {
110
116
  lines.push("#### Release preview", "", "| Package | Bump | Version |", "| --- | --- | --- |");
111
117
  for (const { name, type, from, to, distTag, publish } of pendingPackages) {
@@ -122,11 +128,109 @@ function renderCiPrPreview(context, draft, options = {}) {
122
128
  }
123
129
  lines.push("");
124
130
  } else if (pendingPackages.length === 0) lines.push("#### No changelogs yet", "", "This PR has no pending changelog files. If your changes require a release, add a changelog before merging.", "");
125
- else if (options.prChangelogFiles?.length === 0) lines.push("This PR does not add changelog files. Pending changelogs from other branches are included in the preview above.", "");
131
+ else if (prChangelogFiles.size === 0) lines.push("This PR does not add changelog files. Pending changelogs from other branches are included in the preview above.", "");
126
132
  lines.push(`Run \`${tegamiCommand}\` locally to create a changelog interactively.`, "", `<sub>Managed by [Tegami](${TEGAMI_DOCS_URL}).</sub>`, "");
127
133
  return lines.join("\n");
128
134
  }
129
- async function resolvePullRequestEvent() {
135
+ async function postPrComment(body) {
136
+ const repo = process.env.GITHUB_REPOSITORY;
137
+ if (!repo) throw new Error("GITHUB_REPOSITORY is required.");
138
+ const number = await readPullRequestNumberFromWorkflowRunEvent();
139
+ const markedBody = `${COMMENT_MARKER}\n${body}`;
140
+ const listResult = await x("gh", [
141
+ "api",
142
+ `repos/${repo}/issues/${number}/comments`,
143
+ "--paginate",
144
+ "--jq",
145
+ `[.[] | select(.body | startswith("${COMMENT_MARKER}")) | .id][0] // empty`
146
+ ]);
147
+ if (listResult.exitCode !== 0) throw new Error(listResult.stderr || "Failed to list pull request comments.");
148
+ const existingId = listResult.stdout.trim();
149
+ if (existingId) {
150
+ const dir = await mkdtemp(join(tmpdir(), "tegami-pr-comment-"));
151
+ const inputPath = join(dir, "body.json");
152
+ try {
153
+ await writeFile(inputPath, JSON.stringify({ body: markedBody }));
154
+ const updateResult = await x("gh", [
155
+ "api",
156
+ "-X",
157
+ "PATCH",
158
+ `repos/${repo}/issues/comments/${existingId}`,
159
+ "--input",
160
+ inputPath
161
+ ]);
162
+ if (updateResult.exitCode !== 0) throw new Error(updateResult.stderr || "Failed to update pull request comment.");
163
+ } finally {
164
+ await rm(dir, {
165
+ recursive: true,
166
+ force: true
167
+ });
168
+ }
169
+ return;
170
+ }
171
+ const createResult = await x("gh", [
172
+ "pr",
173
+ "comment",
174
+ String(number),
175
+ "--body",
176
+ markedBody,
177
+ "--repo",
178
+ repo
179
+ ]);
180
+ if (createResult.exitCode !== 0) throw new Error(createResult.stderr || "Failed to create pull request comment.");
181
+ }
182
+ async function readPullRequestNumberFromWorkflowRunEvent() {
183
+ const eventPath = process.env.GITHUB_EVENT_PATH;
184
+ if (!eventPath) throw new Error("GITHUB_EVENT_PATH is required.");
185
+ let event;
186
+ try {
187
+ event = JSON.parse(await readFile(eventPath, "utf8"));
188
+ } catch {
189
+ throw new Error("Failed to read workflow_run event.");
190
+ }
191
+ const workflowRun = event.workflow_run;
192
+ if (!workflowRun) throw new Error("A workflow_run event is required.");
193
+ if (workflowRun.event !== "pull_request") throw new Error("The preview workflow was not triggered by a pull request.");
194
+ if (workflowRun.conclusion !== "success") throw new Error("The preview workflow did not complete successfully.");
195
+ const number = workflowRun.pull_requests?.[0]?.number;
196
+ if (!Number.isInteger(number) || !number || number <= 0) throw new Error("The preview workflow is not associated with a pull request.");
197
+ return number;
198
+ }
199
+ async function resolvePullRequest(context, options) {
200
+ const repo = context.github?.repo ?? process.env.GITHUB_REPOSITORY;
201
+ if (!repo) throw new Error("GITHUB_REPOSITORY is required.");
202
+ if (options.number !== void 0) {
203
+ if (!Number.isInteger(options.number) || options.number <= 0) throw new Error("--number must be a positive integer.");
204
+ return readPullRequestFromGh(repo, options.number);
205
+ }
206
+ const event = await readPullRequestEvent();
207
+ if (event) return {
208
+ repo,
209
+ ...event
210
+ };
211
+ throw new Error("A pull request event or --number is required.");
212
+ }
213
+ async function readPullRequestFromGh(repo, number) {
214
+ const result = await x("gh", [
215
+ "pr",
216
+ "view",
217
+ String(number),
218
+ "--repo",
219
+ repo,
220
+ "--json",
221
+ "headRefName,baseRefOid,headRefOid,headRepository"
222
+ ]);
223
+ if (result.exitCode !== 0) throw new Error(result.stderr || `Failed to resolve pull request #${number}.`);
224
+ const data = JSON.parse(result.stdout);
225
+ return {
226
+ repo,
227
+ headRepo: data.headRepository ? `${data.headRepository.owner.login}/${data.headRepository.name}` : repo,
228
+ headRef: data.headRefName,
229
+ baseSha: data.baseRefOid,
230
+ headSha: data.headRefOid
231
+ };
232
+ }
233
+ async function readPullRequestEvent() {
130
234
  const eventPath = process.env.GITHUB_EVENT_PATH;
131
235
  if (!eventPath) return;
132
236
  let event;
@@ -138,49 +242,36 @@ async function resolvePullRequestEvent() {
138
242
  const pullRequest = event.pull_request;
139
243
  if (!pullRequest) return;
140
244
  return {
141
- number: pullRequest.number,
245
+ headRepo: pullRequest.head.repo.full_name,
142
246
  headRef: pullRequest.head.ref,
143
247
  baseSha: pullRequest.base.sha,
144
248
  headSha: pullRequest.head.sha
145
249
  };
146
250
  }
147
- async function getPullRequestChangelogFiles(context, baseSha, headSha) {
251
+ async function listPullRequestChangelogFiles(context, baseSha, headSha) {
148
252
  const dir = relative(context.cwd, context.changelogDir);
149
253
  const result = await x("git", [
150
254
  "diff",
151
255
  "--name-only",
152
- "--diff-filter=AM",
256
+ "--diff-filter=ACMRD",
153
257
  `${baseSha}...${headSha}`,
154
258
  "--",
155
259
  `${dir}/`
156
260
  ], { nodeOptions: { cwd: context.cwd } });
157
- if (result.exitCode !== 0) return [];
158
- return result.stdout.split("\n").map((line) => line.trim()).filter((line) => line.endsWith(".md")).map((line) => basename(line));
159
- }
160
- function getPendingPackages(context, draft) {
161
- const packages = [];
162
- for (const pkg of context.graph.getPackages()) {
163
- const plan = draft.getPackagePlan(pkg.id);
164
- if (!plan?.type) continue;
165
- packages.push({
166
- name: pkg.name,
167
- type: plan.type,
168
- from: pkg.version,
169
- to: plan.bumpVersion(pkg),
170
- distTag: plan.npm?.distTag,
171
- publish: plan.publish ?? false
172
- });
261
+ if (result.exitCode !== 0) {
262
+ const detail = result.stderr.trim();
263
+ throw new Error(detail ? `Failed to list pull request changelog files: ${detail}` : "Failed to list pull request changelog files.");
173
264
  }
174
- return packages;
175
- }
176
- function filterChangelogsInPullRequest(draft, filenames) {
177
- if (!filenames) return draft.getChangelogs();
178
- const names = new Set(filenames);
179
- return draft.getChangelogs().filter((entry) => names.has(entry.filename));
265
+ const files = /* @__PURE__ */ new Set();
266
+ for (const line of result.stdout.split("\n")) {
267
+ const trimmed = line.trim();
268
+ if (trimmed.endsWith(".md")) files.add(basename(trimmed));
269
+ }
270
+ return files;
180
271
  }
181
- function createChangelogUrl(repo, branch, changelogDir, filename) {
182
- const segments = [...changelogDir.split("/").filter(Boolean), filename].map((part) => encodeURIComponent(part));
183
- return `https://github.com/${repo}/new/${branch.split("/").map(encodeURIComponent).join("/")}/${segments.join("/")}`;
272
+ function createChangelogUrl(context, repo, branch, filename) {
273
+ const filePath = join(relative(context.cwd, context.changelogDir), filename).replaceAll("\\", "/");
274
+ return `https://github.com/${repo}/new/${branch.split("/").map(encodeURIComponent).join("/")}?${new URLSearchParams({ filename: filePath })}`;
184
275
  }
185
276
  //#endregion
186
277
  //#region src/utils/git-changes.ts
@@ -241,7 +332,40 @@ function createCli(tegami, options = {}) {
241
332
  if (await versionPackages(tegami, { cli: options })) return;
242
333
  await publishPackages(tegami, { cli: options });
243
334
  }));
244
- program.command("ci-pr").description("show a pull request release preview and changelog guidance").option("--repo <repo>", "GitHub repository (owner/name)").option("--branch <branch>", "PR head branch for the create-changelog link").option("--print", "print the preview markdown to stdout").action((commandOptions) => runAction(tegami, () => runCiPrCommand(tegami, commandOptions)));
335
+ const programPr = program.command("pr");
336
+ programPr.command("preview").description("(should be executed in a GitHub action) show a pull request release preview and changelog guidance").option("--artifact <path>", "write preview markdown to a file").option("--number <number>", "pull request number", (value) => {
337
+ const number = Number(value);
338
+ if (!Number.isInteger(number) || number <= 0) throw new InvalidArgumentError("--number must be a positive integer.");
339
+ return number;
340
+ }).action((commandOptions) => runAction(tegami, async () => {
341
+ const context = await tegami._internal.context();
342
+ const body = await buildPrPreview(context, await tegami.draft(), commandOptions);
343
+ if (commandOptions.artifact) {
344
+ const artifactPath = resolve(context.cwd, commandOptions.artifact);
345
+ await writeFile(artifactPath, body);
346
+ if (!isCI()) {
347
+ note(relative(context.cwd, artifactPath) || commandOptions.artifact, "Release preview");
348
+ outro("Release preview ready.");
349
+ }
350
+ return;
351
+ }
352
+ if (isCI()) {
353
+ process.stdout.write(`${body}\n`);
354
+ return;
355
+ }
356
+ note(body, "Release preview");
357
+ outro("Release preview ready.");
358
+ }));
359
+ programPr.command("comment").description("(should be used with 'pr preview') post the pull request release preview as a comment").argument("<artifact>", "the file path of GitHub artifact").action(async (artifact) => {
360
+ try {
361
+ await postPrComment(await readFile(artifact, "utf8"));
362
+ outro("Pull request comment updated.");
363
+ } catch (error) {
364
+ note(error instanceof Error ? error.message : String(error), "Error");
365
+ outro("Command failed.");
366
+ process.exit(1);
367
+ }
368
+ });
245
369
  program.command("publish").description("publish packages from the applied publish plan").option("--dry-run", "validate the publish plan without publishing packages").action((commandOptions) => runAction(tegami, async () => {
246
370
  await publishPackages(tegami, {
247
371
  ...commandOptions,
@@ -275,7 +399,7 @@ async function createChangelogs(tegami, _options) {
275
399
  for (const [group, changed] of groups) {
276
400
  const members = group.packages.map(getPackageLabel).join(", ");
277
401
  selectOptions.push({
278
- label: `Group ${group.name}`,
402
+ label: `(Group) ${group.name}`,
279
403
  value: `group:${group.name}`,
280
404
  hint: changed ? `changed · ${members}` : members
281
405
  });
@@ -315,7 +439,8 @@ async function createChangelogs(tegami, _options) {
315
439
  outro("Changelogs ready.");
316
440
  return;
317
441
  }
318
- const type = await select({
442
+ const packageBumpMap = {};
443
+ const bumpType = await select({
319
444
  message: "Select release type",
320
445
  options: [
321
446
  {
@@ -329,10 +454,36 @@ async function createChangelogs(tegami, _options) {
329
454
  {
330
455
  value: "major",
331
456
  label: "major"
457
+ },
458
+ {
459
+ value: "per-package",
460
+ label: "choose per-package"
332
461
  }
333
462
  ]
334
463
  });
335
- if (isCancel(type)) throw new CancelledError();
464
+ if (isCancel(bumpType)) throw new CancelledError();
465
+ if (bumpType === "per-package") for (const pkg of selectedPackages) {
466
+ const bumpType = await select({
467
+ message: `Select release type for "${pkg}"`,
468
+ options: [
469
+ {
470
+ value: "patch",
471
+ label: "patch"
472
+ },
473
+ {
474
+ value: "minor",
475
+ label: "minor"
476
+ },
477
+ {
478
+ value: "major",
479
+ label: "major"
480
+ }
481
+ ]
482
+ });
483
+ if (isCancel(bumpType)) throw new CancelledError();
484
+ packageBumpMap[pkg] = bumpType;
485
+ }
486
+ else for (const pkg of selectedPackages) packageBumpMap[pkg] = bumpType;
336
487
  const message = await multiline({
337
488
  message: "Describe change (Markdown supported, press tab then enter to exit)",
338
489
  placeholder: "The first line is heading\n\nAdditional description.",
@@ -346,9 +497,11 @@ async function createChangelogs(tegami, _options) {
346
497
  const s = spinner();
347
498
  s.start("Creating changelog");
348
499
  await mkdir(context.changelogDir, { recursive: true });
349
- await writeFile(join(context.changelogDir, filename), renderManualChangelog(selectedPackages, type, message.trim()));
500
+ await writeFile(join(context.changelogDir, filename), renderManualChangelog(packageBumpMap, message.trim()));
350
501
  s.stop("Created changelog file");
351
- note(`${filename}\n${selectedPackages.join(", ")}: ${type}`, "Created changelog");
502
+ const notes = [filename];
503
+ for (const pkg of selectedPackages) notes.push(`${pkg}: ${packageBumpMap[pkg]}`);
504
+ note(notes.join("\n"), "Created changelog");
352
505
  outro("Changelog ready.");
353
506
  }
354
507
  async function versionPackages(tegami, options) {
@@ -411,16 +564,6 @@ async function publishPackages(tegami, options) {
411
564
  outro(dryRun ? "Publish plan is valid." : "Packages published.");
412
565
  return true;
413
566
  }
414
- async function runCiPrCommand(tegami, options) {
415
- const body = await runCiPr(await tegami._internal.context(), await tegami.draft(), options);
416
- if (options.print) {
417
- process.stdout.write(`${body}\n`);
418
- if (!isCI()) outro("Release preview ready.");
419
- return;
420
- }
421
- note(body, "Release preview");
422
- outro("Release preview ready.");
423
- }
424
567
  async function runInitAgent(tegami, options) {
425
568
  intro("Init agent instructions");
426
569
  const context = await tegami._internal.context();
@@ -448,16 +591,13 @@ async function runCleanup(tegami) {
448
591
  }
449
592
  outro(`Publish plan at ${planPath} is still pending. Publish it before cleanup.`);
450
593
  }
451
- function renderManualChangelog(packages, type, message) {
452
- const prefix = "#".repeat(bumpDepth(type));
453
- const packageMap = {};
454
- for (const name of packages) packageMap[name] = type;
594
+ function renderManualChangelog(packageBumpMap, message) {
455
595
  return [
456
596
  "---",
457
- dump({ packages: packageMap }).trim(),
597
+ dump({ packages: packageBumpMap }).trim(),
458
598
  "---",
459
599
  "",
460
- `${prefix} ${message}`,
600
+ `## ${message}`,
461
601
  ""
462
602
  ].join("\n");
463
603
  }
@@ -1,4 +1,4 @@
1
- import { r as LogGenerator } from "../types-CrA7ZKxb.js";
1
+ import { r as LogGenerator } from "../types-UjsZkz42.js";
2
2
 
3
3
  //#region src/generators/simple.d.ts
4
4
  declare function simpleGenerator(): LogGenerator;
@@ -1,4 +1,4 @@
1
- import { i as formatPackageVersion } from "../semver-CPtl0XNq.js";
1
+ import { r as formatPackageVersion } from "../semver-mWK2Khi2.js";
2
2
  //#region src/generators/simple.ts
3
3
  function simpleGenerator() {
4
4
  return { generate({ changelogs, version, packageName, plan }) {
@@ -1,4 +1,4 @@
1
- import { n as bumpVersion } from "./semver-CPtl0XNq.js";
1
+ import { t as bumpVersion } from "./semver-mWK2Khi2.js";
2
2
  //#region src/graph.ts
3
3
  /** Package discovered in the workspace. */
4
4
  var WorkspacePackage = class {
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as PublishResult, D as WorkspacePackage, E as PackageGroup, O as DraftPlan, S as PublishOptions, T as PackageGraph, _ as Tegami, a as RegistryClient, b as CreatedChangelog, c as TegamiPluginOption, i as PackageOptions, k as PackagePlan, n as GroupOptions, o as TegamiOptions, r as LogGenerator, s as TegamiPlugin, v as tegami, x as PackagePublishResult, y as CreateChangelogOptions } from "./types-CrA7ZKxb.js";
1
+ import { C as PublishResult, D as WorkspacePackage, E as PackageGroup, O as DraftPlan, S as PublishOptions, T as PackageGraph, _ as Tegami, a as RegistryClient, b as CreatedChangelog, c as TegamiPluginOption, i as PackageOptions, k as PackagePlan, n as GroupOptions, o as TegamiOptions, r as LogGenerator, s as TegamiPlugin, v as tegami, x as PackagePublishResult, y as CreateChangelogOptions } from "./types-UjsZkz42.js";
2
2
  export { type CreateChangelogOptions, type CreatedChangelog, type DraftPlan, type GroupOptions, type LogGenerator, type PackageGraph, type PackageGroup, type PackageOptions, type PackagePlan, type PackagePublishResult, type PublishOptions, type PublishResult, type RegistryClient, Tegami, type TegamiOptions, type TegamiPlugin, type TegamiPluginOption, type WorkspacePackage, tegami };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as changelogFilename, i as readPlanStore, n as publishPlanStatus, r as createPlanStore, t as assertPublishPlanFinished } from "./checks-BQISvt_o.js";
2
2
  import { n as handlePluginError, t as execFailure } from "./error-DBK-9uBa.js";
3
- import { a as maxBump } from "./semver-CPtl0XNq.js";
4
- import { t as PackageGraph } from "./graph-B22NBRUG.js";
3
+ import { i as maxBump } from "./semver-mWK2Khi2.js";
4
+ import { t as PackageGraph } from "./graph-CUgwuRW5.js";
5
5
  import { cargo } from "./providers/cargo.js";
6
6
  import { t as bumpTypeSchema } from "./schemas-CurBAaW5.js";
7
7
  import { npm } from "./providers/npm.js";
@@ -440,7 +440,7 @@ function parseChangelogFile(file, content) {
440
440
  if (sectionBumpType) bumpType = bumpType ? maxBump(bumpType, sectionBumpType) : sectionBumpType;
441
441
  sections.push({
442
442
  depth: section.heading.depth,
443
- title: headingText(section.heading),
443
+ title: sectionToMarkdown(section.heading.children),
444
444
  content: sectionToMarkdown(section.children)
445
445
  });
446
446
  }
@@ -471,14 +471,6 @@ function getHeadingSections(tree) {
471
471
  }
472
472
  return sections;
473
473
  }
474
- function headingText(heading) {
475
- return heading.children.map((child) => nodeText(child)).join("").trim();
476
- }
477
- function nodeText(node) {
478
- if ("value" in node && typeof node.value === "string") return node.value;
479
- if ("children" in node && Array.isArray(node.children)) return node.children.map((child) => nodeText(child)).join("");
480
- return "";
481
- }
482
474
  function sectionToMarkdown(children) {
483
475
  if (children.length === 0) return "";
484
476
  return toMarkdown({
@@ -490,9 +482,11 @@ function sectionToMarkdown(children) {
490
482
  }).trim();
491
483
  }
492
484
  function headingToBump(depth) {
493
- if (depth === 1) return "major";
494
- if (depth === 2) return "minor";
495
- if (depth === 3) return "patch";
485
+ switch (depth) {
486
+ case 1: return "major";
487
+ case 2: return "minor";
488
+ case 3: return "patch";
489
+ }
496
490
  }
497
491
  //#endregion
498
492
  //#region src/publish.ts
@@ -504,6 +498,7 @@ async function publishFromPlan(context, store, options) {
504
498
  if (!plan.publish) continue;
505
499
  const pkg = context.graph.get(id);
506
500
  if (!pkg) continue;
501
+ const registryClient = context.getRegistryClient(pkg);
507
502
  const changelogs = [];
508
503
  for (const id of plan.changelogIds ?? []) {
509
504
  const entry = store.changelogs[id];
@@ -514,7 +509,7 @@ async function publishFromPlan(context, store, options) {
514
509
  });
515
510
  }
516
511
  if (!dryRun) {
517
- if (await context.getRegistryClient(pkg).isPackagePublished(pkg)) {
512
+ if (await registryClient.isPackagePublished(pkg)) {
518
513
  packages.push({
519
514
  id: pkg.id,
520
515
  name: pkg.name,
@@ -525,15 +520,7 @@ async function publishFromPlan(context, store, options) {
525
520
  });
526
521
  continue;
527
522
  }
528
- }
529
- try {
530
- if (!dryRun) {
531
- for (const plugin of context.plugins) await handlePluginError(plugin, "willPublish", () => plugin.willPublish?.call(context, { pkg }));
532
- await context.getRegistryClient(pkg).publish(pkg, {
533
- packageStore: plan,
534
- store
535
- });
536
- }
523
+ } else {
537
524
  packages.push({
538
525
  id: pkg.id,
539
526
  name: pkg.name,
@@ -542,6 +529,40 @@ async function publishFromPlan(context, store, options) {
542
529
  state: "success",
543
530
  changelogs
544
531
  });
532
+ continue;
533
+ }
534
+ try {
535
+ let result;
536
+ for (const plugin of context.plugins) {
537
+ const next = await handlePluginError(plugin, "willPublish", () => plugin.willPublish?.call(context, { pkg }));
538
+ if (next !== void 0) {
539
+ result = next;
540
+ break;
541
+ }
542
+ }
543
+ if (result === void 0) {
544
+ await registryClient.publish(pkg, {
545
+ packageStore: plan,
546
+ store
547
+ });
548
+ result = {
549
+ id: pkg.id,
550
+ name: pkg.name,
551
+ version: pkg.version,
552
+ npm: plan.npm,
553
+ state: "success",
554
+ changelogs
555
+ };
556
+ }
557
+ if (result === false) continue;
558
+ for (const plugin of context.plugins) {
559
+ const next = await handlePluginError(plugin, "afterPublish", () => plugin.afterPublish?.call(context, {
560
+ pkg,
561
+ result
562
+ }));
563
+ if (next) result = next;
564
+ }
565
+ packages.push(result);
545
566
  } catch (error) {
546
567
  packages.push({
547
568
  id: pkg.id,
@@ -596,7 +617,10 @@ function tegami(options = {}) {
596
617
  ...context,
597
618
  publishOptions
598
619
  };
599
- for (const plugin of context.plugins) result = await handlePluginError(plugin, "afterPublish", () => plugin.afterPublish?.call(publishCtx, result)) ?? result;
620
+ for (const plugin of context.plugins) {
621
+ const next = await handlePluginError(plugin, "afterPublishAll", () => plugin.afterPublishAll?.call(publishCtx, result));
622
+ if (next) result = next;
623
+ }
600
624
  return result;
601
625
  },
602
626
  async cleanup() {
@@ -1,4 +1,4 @@
1
- import { s as TegamiPlugin } from "../types-CrA7ZKxb.js";
1
+ import { s as TegamiPlugin } from "../types-UjsZkz42.js";
2
2
 
3
3
  //#region src/plugins/git.d.ts
4
4
  interface GitPluginOptions {
@@ -37,7 +37,7 @@ function git(options = {}) {
37
37
  if (result.exitCode !== 0) throw execFailure("Failed to configure git user for GitHub Actions.", result);
38
38
  }
39
39
  } },
40
- async afterPublish(result) {
40
+ async afterPublishAll(result) {
41
41
  const { cwd, publishOptions: { dryRun = false } } = this;
42
42
  if (dryRun || !createTags || result.state !== "created") return result;
43
43
  const pendingTags = /* @__PURE__ */ new Set();
@@ -1,4 +1,4 @@
1
- import { O as DraftPlan, s as TegamiPlugin, t as Awaitable, w as TegamiContext, x as PackagePublishResult } from "../types-CrA7ZKxb.js";
1
+ import { O as DraftPlan, s as TegamiPlugin, t as Awaitable, w as TegamiContext, x as PackagePublishResult } from "../types-UjsZkz42.js";
2
2
  import { GitPluginOptions } from "./git.js";
3
3
 
4
4
  //#region src/plugins/github.d.ts
@@ -1,5 +1,5 @@
1
1
  import { t as execFailure } from "../error-DBK-9uBa.js";
2
- import { i as formatPackageVersion, r as formatNpmDistTag } from "../semver-CPtl0XNq.js";
2
+ import { n as formatNpmDistTag, r as formatPackageVersion } from "../semver-mWK2Khi2.js";
3
3
  import { t as isCI } from "../constants-B9qjNfvr.js";
4
4
  import { git } from "./git.js";
5
5
  import { join, relative } from "node:path";
@@ -168,7 +168,7 @@ function github(options = {}) {
168
168
  if (prResult.exitCode !== 0) throw execFailure("Failed to create the version pull request.", prResult);
169
169
  }
170
170
  },
171
- async afterPublish(result) {
171
+ async afterPublishAll(result) {
172
172
  if (result.state !== "created") return;
173
173
  for (const packages of groupPackagesByGitTag(result.packages).values()) if (packages.length > 1) await createGroupedRelease(this, packages);
174
174
  else await createRelease(this, packages[0]);
@@ -1,2 +1,2 @@
1
- import { d as CargoRegistryClient, f as cargo, l as CargoPackage, u as CargoPluginOptions } from "../types-CrA7ZKxb.js";
1
+ import { d as CargoRegistryClient, f as cargo, l as CargoPackage, u as CargoPluginOptions } from "../types-UjsZkz42.js";
2
2
  export { CargoPackage, CargoPluginOptions, CargoRegistryClient, cargo };
@@ -1,10 +1,10 @@
1
1
  import { r as isNodeError } from "../error-DBK-9uBa.js";
2
- import { n as WorkspacePackage } from "../graph-B22NBRUG.js";
2
+ import { n as WorkspacePackage } from "../graph-CUgwuRW5.js";
3
3
  import { readFile, writeFile } from "node:fs/promises";
4
4
  import { join, normalize } from "node:path";
5
5
  import { x } from "tinyexec";
6
+ import initToml, { edit, parse } from "@rainbowatcher/toml-edit-js";
6
7
  import * as semver from "semver";
7
- import { parse as parse$1, stringify } from "smol-toml";
8
8
  import { glob } from "tinyglobby";
9
9
  //#region src/providers/cargo.ts
10
10
  const DEP_FIELDS = [
@@ -15,12 +15,14 @@ const DEP_FIELDS = [
15
15
  var CargoPackage = class extends WorkspacePackage {
16
16
  path;
17
17
  manifest;
18
+ content;
18
19
  workspaceManifest;
19
20
  manager = "cargo";
20
- constructor(path, manifest, workspaceManifest) {
21
+ constructor(path, manifest, content, workspaceManifest) {
21
22
  super();
22
23
  this.path = path;
23
24
  this.manifest = manifest;
25
+ this.content = content;
24
26
  this.workspaceManifest = workspaceManifest;
25
27
  }
26
28
  get name() {
@@ -34,8 +36,15 @@ var CargoPackage = class extends WorkspacePackage {
34
36
  defaults.publish ??= this.packageInfo.publish !== false;
35
37
  return defaults;
36
38
  }
39
+ setVersion(version) {
40
+ this.packageInfo.version = version;
41
+ this.patch("package.version", version);
42
+ }
37
43
  async write() {
38
- await writeFile(join(this.path, "Cargo.toml"), stringify(this.manifest));
44
+ await writeFile(join(this.path, "Cargo.toml"), this.content);
45
+ }
46
+ patch(path, value) {
47
+ this.content = edit(this.content, path, value);
39
48
  }
40
49
  get packageInfo() {
41
50
  this.manifest.package ??= {};
@@ -90,7 +99,7 @@ function cargo({ bumpDep: getBumpDepType = ({ kind }) => {
90
99
  onUpdate({ pkg, plan }) {
91
100
  for (const other of graph.getPackages()) {
92
101
  if (!(other instanceof CargoPackage)) continue;
93
- for (const { table, kind } of dependencyTables(other.manifest)) for (const [rawName, rawSpec] of Object.entries(table)) {
102
+ for (const { table, kind } of dependencyTables(other.manifest, "")) for (const [rawName, rawSpec] of Object.entries(table)) {
94
103
  const spec = parseSpec(rawSpec);
95
104
  if (!spec) continue;
96
105
  const packageName = spec.package ?? rawName;
@@ -114,6 +123,9 @@ function cargo({ bumpDep: getBumpDepType = ({ kind }) => {
114
123
  return {
115
124
  name: "cargo",
116
125
  enforce: "pre",
126
+ async init() {
127
+ await initToml();
128
+ },
117
129
  async resolve() {
118
130
  await discoverCargoPackages(this.cwd, (pkg) => this.graph.add(pkg));
119
131
  },
@@ -129,11 +141,11 @@ function cargo({ bumpDep: getBumpDepType = ({ kind }) => {
129
141
  for (const pkg of graph.getPackages()) {
130
142
  if (!(pkg instanceof CargoPackage)) continue;
131
143
  const plan = draft.getPackagePlan(pkg.id);
132
- if (plan) pkg.packageInfo.version = plan.bumpVersion(pkg);
144
+ if (plan) pkg.setVersion(plan.bumpVersion(pkg));
133
145
  }
134
146
  for (const pkg of graph.getPackages()) {
135
147
  if (!(pkg instanceof CargoPackage)) continue;
136
- for (const { table } of dependencyTables(pkg.manifest)) for (const [rawName, rawSpec] of Object.entries(table)) {
148
+ for (const { table, path: tablePath } of dependencyTables(pkg.manifest, "")) for (const [rawName, rawSpec] of Object.entries(table)) {
137
149
  const spec = parseSpec(rawSpec);
138
150
  if (!spec) continue;
139
151
  const packageName = spec.package ?? rawName;
@@ -142,6 +154,7 @@ function cargo({ bumpDep: getBumpDepType = ({ kind }) => {
142
154
  const result = updateRange(spec.version, linked.version);
143
155
  if (result === false) continue;
144
156
  table[rawName] = spec.setVersion(result);
157
+ pkg.patch(typeof rawSpec === "string" ? `${tablePath}.${rawName}` : `${tablePath}.${rawName}.version`, result);
145
158
  }
146
159
  writes.push(pkg.write());
147
160
  }
@@ -155,8 +168,8 @@ async function discoverCargoPackages(cwd, add) {
155
168
  throw error;
156
169
  });
157
170
  if (!root) return;
158
- addCargoPackage(cwd, root, root, add);
159
- const workspace = tableValue(root.workspace);
171
+ addCargoPackage(cwd, root.manifest, root.content, root.manifest, add);
172
+ const workspace = tableValue(root.manifest.workspace);
160
173
  const members = workspace?.members;
161
174
  if (!workspace || !Array.isArray(members)) return;
162
175
  const exclude = Array.isArray(workspace.exclude) ? workspace.exclude.filter((member) => typeof member === "string") : [];
@@ -165,14 +178,14 @@ async function discoverCargoPackages(cwd, add) {
165
178
  path,
166
179
  manifest
167
180
  })).catch(() => void 0)));
168
- for (const entry of manifests) if (entry) addCargoPackage(entry.path, entry.manifest, root, add);
181
+ for (const entry of manifests) if (entry) addCargoPackage(entry.path, entry.manifest.manifest, entry.manifest.content, root.manifest, add);
169
182
  }
170
- function addCargoPackage(path, manifest, workspaceManifest, add) {
183
+ function addCargoPackage(path, manifest, content, workspaceManifest, add) {
171
184
  const packageInfo = tableValue(manifest.package);
172
185
  const workspacePackage = tableValue(workspaceManifest.workspace)?.package;
173
186
  if (!packageInfo?.name) return;
174
187
  if (!packageInfo.version && !tableValue(workspacePackage)?.version) return;
175
- add(new CargoPackage(path, manifest, workspaceManifest));
188
+ add(new CargoPackage(path, manifest, content, workspaceManifest));
176
189
  }
177
190
  async function expandWorkspaceMembers(cwd, members, exclude) {
178
191
  const paths = members.includes(".") ? [cwd] : [];
@@ -186,24 +199,30 @@ async function expandWorkspaceMembers(cwd, members, exclude) {
186
199
  }));
187
200
  return paths.map(normalize);
188
201
  }
189
- function dependencyTables(manifest) {
202
+ function dependencyTables(manifest, prefix) {
190
203
  const tables = [];
191
204
  for (const field of DEP_FIELDS) {
192
205
  const table = tableValue(manifest[field]);
193
- if (table) tables.push({
194
- kind: field,
195
- table
196
- });
206
+ if (table) {
207
+ const path = prefix ? `${prefix}.${field}` : field;
208
+ tables.push({
209
+ kind: field,
210
+ table,
211
+ path
212
+ });
213
+ }
197
214
  }
198
215
  const target = tableValue(manifest.target);
199
- if (target) for (const targetConfig of Object.values(target)) {
216
+ if (target) for (const [targetKey, targetConfig] of Object.entries(target)) {
200
217
  const targetTable = tableValue(targetConfig);
201
218
  if (!targetTable) continue;
219
+ const targetPath = prefix ? `${prefix}.target.${targetKey}` : `target.${targetKey}`;
202
220
  for (const field of DEP_FIELDS) {
203
221
  const table = tableValue(targetTable[field]);
204
222
  if (table) tables.push({
205
223
  kind: field,
206
- table
224
+ table,
225
+ path: `${targetPath}.${field}`
207
226
  });
208
227
  }
209
228
  }
@@ -228,7 +247,11 @@ function parseSpec(v) {
228
247
  };
229
248
  }
230
249
  async function readCargoManifest(path) {
231
- return parse$1(await readFile(join(path, "Cargo.toml"), "utf8"));
250
+ const content = await readFile(join(path, "Cargo.toml"), "utf8");
251
+ return {
252
+ manifest: parse(content),
253
+ content
254
+ };
232
255
  }
233
256
  function isTableValue(value) {
234
257
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -1,2 +1,2 @@
1
- import { g as npm, h as NpmRegistryClient, m as NpmPluginOptions, p as NpmPackage } from "../types-CrA7ZKxb.js";
1
+ import { g as npm, h as NpmRegistryClient, m as NpmPluginOptions, p as NpmPackage } from "../types-UjsZkz42.js";
2
2
  export { NpmPackage, NpmPluginOptions, NpmRegistryClient, npm };
@@ -1,5 +1,5 @@
1
1
  import { r as isNodeError, t as execFailure } from "../error-DBK-9uBa.js";
2
- import { n as WorkspacePackage } from "../graph-B22NBRUG.js";
2
+ import { n as WorkspacePackage } from "../graph-CUgwuRW5.js";
3
3
  import { i as pnpmWorkspaceSchema, r as packageManifestSchema } from "../schemas-CurBAaW5.js";
4
4
  import { readFile, writeFile } from "node:fs/promises";
5
5
  import path from "node:path";
@@ -11,11 +11,6 @@ const WEIGHTS = {
11
11
  minor: 2,
12
12
  patch: 1
13
13
  };
14
- const DEPTH = {
15
- major: 1,
16
- minor: 2,
17
- patch: 3
18
- };
19
14
  const PRE = {
20
15
  major: "premajor",
21
16
  minor: "preminor",
@@ -25,9 +20,6 @@ function maxBump(a, b) {
25
20
  if (WEIGHTS[a] > WEIGHTS[b]) return a;
26
21
  return b;
27
22
  }
28
- function bumpDepth(type) {
29
- return DEPTH[type];
30
- }
31
23
  function bumpVersion(version, type, prerelease) {
32
24
  let next = version;
33
25
  if (prerelease) {
@@ -38,4 +30,4 @@ function bumpVersion(version, type, prerelease) {
38
30
  return next;
39
31
  }
40
32
  //#endregion
41
- export { maxBump as a, formatPackageVersion as i, bumpVersion as n, formatNpmDistTag as r, bumpDepth as t };
33
+ export { maxBump as i, formatNpmDistTag as n, formatPackageVersion as r, bumpVersion as t };
@@ -1,4 +1,3 @@
1
- import { TomlTable } from "smol-toml";
2
1
  import z$1, { z } from "zod";
3
2
  import { AgentName } from "package-manager-detector";
4
3
 
@@ -355,17 +354,24 @@ declare function npm({
355
354
  }?: NpmPluginOptions): TegamiPlugin;
356
355
  //#endregion
357
356
  //#region src/providers/cargo.d.ts
357
+ interface TomlTable {
358
+ [key: string]: TomlValue;
359
+ }
360
+ type TomlValue = string | number | boolean | TomlTable | TomlValue[];
358
361
  declare const DEP_FIELDS: readonly ["dependencies", "dev-dependencies", "build-dependencies"];
359
362
  declare class CargoPackage extends WorkspacePackage {
360
363
  readonly path: string;
361
364
  readonly manifest: TomlTable;
365
+ private content;
362
366
  private readonly workspaceManifest?;
363
367
  readonly manager = "cargo";
364
- constructor(path: string, manifest: TomlTable, workspaceManifest?: TomlTable | undefined);
368
+ constructor(path: string, manifest: TomlTable, content: string, workspaceManifest?: TomlTable | undefined);
365
369
  get name(): string;
366
370
  get version(): string;
367
371
  onPlan(context: TegamiContext): PackagePlan;
372
+ setVersion(version: string): void;
368
373
  write(): Promise<void>;
374
+ patch(path: string, value: unknown): void;
369
375
  get packageInfo(): TomlTable;
370
376
  private get workspaceVersion();
371
377
  }
@@ -456,12 +462,17 @@ interface TegamiPlugin {
456
462
  resolvePlanStatus?(this: TegamiContext, status: PublishPlanStatus, env: {
457
463
  plan: PlanStore;
458
464
  }): Awaitable<PublishPlanStatus>;
459
- /** Called before a package will be published. */
465
+ /** Called before a package will be published, return `false` to prevent from publishing. */
460
466
  willPublish?(this: TegamiContext, opts: {
461
467
  pkg: WorkspacePackage;
462
- }): Awaitable<PublishResult | void | undefined>;
468
+ }): Awaitable<PackagePublishResult | false | void | undefined>;
469
+ /** Called after a package is published. */
470
+ afterPublish?(this: TegamiContext, opts: {
471
+ pkg: WorkspacePackage;
472
+ result: PackagePublishResult;
473
+ }): Awaitable<PackagePublishResult | void | undefined>;
463
474
  /** Called after publishing finishes. */
464
- afterPublish?(this: TegamiContext & {
475
+ afterPublishAll?(this: TegamiContext & {
465
476
  publishOptions: PublishOptions;
466
477
  }, result: PublishResult): Awaitable<PublishResult | void | undefined>;
467
478
  /** CLI lifecycle hooks. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tegami",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0",
4
4
  "description": "Utility for package versioning & publish",
5
5
  "license": "MIT",
6
6
  "author": "Fuma Nama",
@@ -27,13 +27,13 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@clack/prompts": "^1.5.1",
30
+ "@rainbowatcher/toml-edit-js": "^0.6.5",
30
31
  "commander": "^15.0.0",
31
32
  "js-yaml": "^4.2.0",
32
33
  "mdast-util-from-markdown": "^2.0.3",
33
34
  "mdast-util-to-markdown": "^2.1.2",
34
35
  "package-manager-detector": "^1.6.0",
35
36
  "semver": "^7.8.4",
36
- "smol-toml": "^1.6.1",
37
37
  "tinyexec": "^1.2.4",
38
38
  "tinyglobby": "^0.2.17",
39
39
  "zod": "^4.4.3"