tegami 0.1.0-beta.1 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,10 @@
1
1
  import { n as handlePluginError } from "./error-DBK-9uBa.js";
2
- import { n as jsonCodec } from "./schemas-Cc4h6bq5.js";
2
+ import { n as jsonCodec, t as bumpTypeSchema } from "./schemas-CurBAaW5.js";
3
3
  import z$1 from "zod";
4
4
  import { readFile } from "fs/promises";
5
5
  //#region src/plans/store.ts
6
6
  const packagePlanStoreSchema = z$1.object({
7
- type: z$1.enum([
8
- "major",
9
- "minor",
10
- "patch"
11
- ]).optional(),
7
+ type: bumpTypeSchema.optional(),
12
8
  changelogIds: z$1.array(z$1.string()).optional(),
13
9
  bumpReasons: z$1.array(z$1.string()).optional(),
14
10
  npm: z$1.object({ distTag: z$1.string().optional() }).optional(),
@@ -23,14 +19,11 @@ const planStoreSchema = jsonCodec(z$1.object({
23
19
  changelogs: z$1.record(z$1.string(), z$1.object({
24
20
  filename: z$1.string(),
25
21
  subject: z$1.string().optional(),
26
- packages: z$1.array(z$1.string()),
27
- type: z$1.enum([
28
- "major",
29
- "minor",
30
- "patch"
31
- ]),
32
- title: z$1.string(),
33
- content: z$1.string()
22
+ packages: z$1.record(z$1.string(), bumpTypeSchema),
23
+ sections: z$1.array(z$1.object({
24
+ title: z$1.string(),
25
+ content: z$1.string()
26
+ }))
34
27
  })),
35
28
  /** package id -> package info */
36
29
  packages: z$1.record(z$1.string(), packagePlanStoreSchema)
@@ -46,10 +39,8 @@ function createPlanStore(draft, context) {
46
39
  for (const entry of draft.getChangelogs()) store.changelogs[entry.id] = {
47
40
  filename: entry.filename,
48
41
  subject: entry.subject,
49
- packages: Array.from(entry.packages),
50
- type: entry.type,
51
- title: entry.title,
52
- content: entry.content
42
+ packages: Object.fromEntries(entry.packages.entries()),
43
+ sections: entry.sections
53
44
  };
54
45
  for (const pkg of context.graph.getPackages()) {
55
46
  const plan = draft.getPackagePlan(pkg.id);
@@ -1,4 +1,4 @@
1
- import { C as PublishResult, a as Awaitable, t as Tegami, w as DraftPlan } from "../index-Da6P6gSc.js";
1
+ import { C as PublishResult, O as DraftPlan, _ as Tegami, t as Awaitable } from "../types-BNhcu4-X.js";
2
2
  import { Command } from "commander";
3
3
 
4
4
  //#region src/cli/index.d.ts
package/dist/cli/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { n as handlePluginError } from "../error-DBK-9uBa.js";
2
- import { t as assertPublishPlanFinished } from "../checks-CKBRRjgQ.js";
2
+ import { t as bumpDepth } from "../semver-CPtl0XNq.js";
3
+ import { t as assertPublishPlanFinished } from "../checks-Fz8-N09y.js";
3
4
  import { t as isCI } from "../constants-B9qjNfvr.js";
4
5
  import { mkdir, writeFile } from "node:fs/promises";
5
6
  import { join } from "node:path";
6
- import { confirm, intro, isCancel, multiselect, note, outro, select, spinner, text } from "@clack/prompts";
7
+ import { dump } from "js-yaml";
8
+ import { autocompleteMultiselect, confirm, intro, isCancel, multiline, note, outro, select, spinner } from "@clack/prompts";
7
9
  import { Command } from "commander";
8
10
  //#region src/cli/index.ts
9
11
  var CancelledError = class extends Error {
@@ -40,18 +42,29 @@ async function createChangelogs(tegami, _options) {
40
42
  const context = await tegami._internal.context();
41
43
  await assertPublishPlanFinished(context);
42
44
  intro("Create changelogs");
43
- const packages = context.graph.getPackages();
44
45
  let selectedPackages = [];
45
- if (isCI()) selectedPackages = [];
46
- else {
47
- const selected = await multiselect({
46
+ if (!isCI()) {
47
+ const packages = context.graph.getPackages();
48
+ const useShortname = /* @__PURE__ */ new Map();
49
+ for (const pkg of packages) if (useShortname.has(pkg.name)) useShortname.set(pkg.name, false);
50
+ else useShortname.set(pkg.name, true);
51
+ const getPackageLabel = (pkg) => {
52
+ return useShortname.get(pkg.name) ? pkg.name : pkg.id;
53
+ };
54
+ const selectOptions = [];
55
+ for (const group of context.graph.getGroups()) selectOptions.push({
56
+ label: `Group ${group.name}`,
57
+ value: `group:${group.name}`,
58
+ hint: group.packages.map(getPackageLabel).join(", ")
59
+ });
60
+ for (const pkg of packages) selectOptions.push({
61
+ label: getPackageLabel(pkg),
62
+ value: pkg.id
63
+ });
64
+ const selected = await autocompleteMultiselect({
48
65
  message: "Select packages (leave empty to auto-generate from commits)",
49
66
  required: false,
50
- options: packages.map((pkg) => ({
51
- value: pkg.id,
52
- label: pkg.id,
53
- hint: pkg.version
54
- }))
67
+ options: selectOptions
55
68
  });
56
69
  if (isCancel(selected)) throw new CancelledError();
57
70
  selectedPackages = selected;
@@ -95,9 +108,10 @@ async function createChangelogs(tegami, _options) {
95
108
  ]
96
109
  });
97
110
  if (isCancel(type)) throw new CancelledError();
98
- const message = await text({
99
- message: "Change message",
100
- placeholder: "Add a concise release note",
111
+ const message = await multiline({
112
+ message: "Describe change (Markdown supported, press tab then enter to exit)",
113
+ placeholder: "The first line is heading\n\nAdditional description.",
114
+ showSubmit: true,
101
115
  validate(value) {
102
116
  if (!value?.trim()) return "Enter a message.";
103
117
  }
@@ -190,13 +204,15 @@ async function runCleanup(tegami) {
190
204
  outro(`Publish plan at ${planPath} is still pending. Publish it before cleanup.`);
191
205
  }
192
206
  function renderManualChangelog(packages, type, message) {
193
- const heading = "#".repeat(type === "major" ? 1 : type === "minor" ? 2 : 3);
207
+ const prefix = "#".repeat(bumpDepth(type));
208
+ const packageMap = {};
209
+ for (const name of packages) packageMap[name] = type;
194
210
  return [
195
211
  "---",
196
- `packages: ${JSON.stringify(packages)}`,
212
+ dump({ packages: packageMap }).trim(),
197
213
  "---",
198
214
  "",
199
- `${heading} ${message}`,
215
+ `${prefix} ${message}`,
200
216
  ""
201
217
  ].join("\n");
202
218
  }
@@ -1,4 +1,4 @@
1
- import { s as LogGenerator } from "../index-Da6P6gSc.js";
1
+ import { r as LogGenerator } from "../types-BNhcu4-X.js";
2
2
 
3
3
  //#region src/generators/simple.d.ts
4
4
  declare function simpleGenerator(): LogGenerator;
@@ -1,17 +1,18 @@
1
- import { r as formatPackageVersion } from "../semver-mWK2Khi2.js";
1
+ import { i as formatPackageVersion } from "../semver-CPtl0XNq.js";
2
2
  //#region src/generators/simple.ts
3
3
  function simpleGenerator() {
4
4
  return { generate({ changelogs, version, packageName, plan }) {
5
- return [
6
- `## ${formatPackageVersion(packageName, version, plan.npm?.distTag)}`,
7
- "",
8
- ...changelogs.flatMap((entry) => [
9
- `### ${entry.title}`,
10
- "",
11
- entry.content,
12
- ""
13
- ])
14
- ].join("\n").trim();
5
+ const lines = [`## ${formatPackageVersion(packageName, version, plan.npm?.distTag)}`, ""];
6
+ for (const entry of changelogs) {
7
+ let sectionDepth = 4;
8
+ if (entry.subject) lines.push(`### ${entry.subject}`, "");
9
+ else sectionDepth--;
10
+ for (const section of entry.sections) {
11
+ const prefix = "#".repeat(sectionDepth);
12
+ lines.push(`${prefix} ${section.title}`, "", section.content, "");
13
+ }
14
+ }
15
+ return lines.join("\n").trim();
15
16
  } };
16
17
  }
17
18
  //#endregion
@@ -1,4 +1,4 @@
1
- import { t as bumpVersion } from "./semver-mWK2Khi2.js";
1
+ import { n as bumpVersion } from "./semver-CPtl0XNq.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 PackageGroup, E as PackageGraph, O as WorkspacePackage, S as PublishOptions, T as PackagePlan, c as PackageOptions, d as TegamiPlugin, f as TegamiPluginOption, i as CreatedChangelog, l as RegistryClient, n as tegami, o as GroupOptions, r as CreateChangelogOptions, s as LogGenerator, t as Tegami, u as TegamiOptions, w as DraftPlan, x as PackagePublishResult } from "./index-Da6P6gSc.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-BNhcu4-X.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,15 +1,16 @@
1
1
  import { n as handlePluginError } from "./error-DBK-9uBa.js";
2
- import { i as maxBump } from "./semver-mWK2Khi2.js";
3
- import { t as PackageGraph } from "./graph-CUgwuRW5.js";
2
+ import { a as maxBump } from "./semver-CPtl0XNq.js";
3
+ import { t as PackageGraph } from "./graph-B22NBRUG.js";
4
4
  import { cargo } from "./providers/cargo.js";
5
- import { t as changelogFrontmatterSchema } from "./schemas-Cc4h6bq5.js";
5
+ import { t as bumpTypeSchema } from "./schemas-CurBAaW5.js";
6
6
  import { npm } from "./providers/npm.js";
7
7
  import { simpleGenerator } from "./generators/simple.js";
8
- import { i as readPlanStore, n as publishPlanStatus, r as createPlanStore, t as assertPublishPlanFinished } from "./checks-CKBRRjgQ.js";
8
+ import { i as readPlanStore, n as publishPlanStatus, r as createPlanStore, t as assertPublishPlanFinished } from "./checks-Fz8-N09y.js";
9
9
  import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
10
10
  import path, { basename, dirname, join } from "node:path";
11
11
  import { x } from "tinyexec";
12
12
  import { load } from "js-yaml";
13
+ import z$1 from "zod";
13
14
  import { fromMarkdown } from "mdast-util-from-markdown";
14
15
  import { toMarkdown } from "mdast-util-to-markdown";
15
16
  //#region src/changelog/create.ts
@@ -268,10 +269,10 @@ var DraftPlan = class {
268
269
  addChangelog(entry) {
269
270
  this.changelogs.set(entry.id, entry);
270
271
  const { graph } = this.context;
271
- const groupPackages = /* @__PURE__ */ new Set();
272
- for (const name of entry.packages) for (const pkg of graph.getByName(name)) groupPackages.add(pkg);
273
- for (const pkg of groupPackages) {
274
- const plan = this.bumpPackage(pkg, { type: entry.type });
272
+ const groupPackages = /* @__PURE__ */ new Map();
273
+ for (const [name, bumpType] of entry.packages) for (const pkg of graph.getByName(name)) groupPackages.set(pkg, bumpType);
274
+ for (const [pkg, bumpType] of groupPackages) {
275
+ const plan = this.bumpPackage(pkg, { type: bumpType });
275
276
  plan.changelogs ??= [];
276
277
  plan.changelogs.push(entry);
277
278
  }
@@ -381,6 +382,10 @@ function frontmatter(input) {
381
382
  }
382
383
  //#endregion
383
384
  //#region src/changelog/parse.ts
385
+ const changelogFrontmatterSchema = z$1.object({
386
+ subject: z$1.string().optional(),
387
+ packages: z$1.union([z$1.array(z$1.string()), z$1.record(z$1.string(), bumpTypeSchema.or(z$1.null()))]).optional()
388
+ });
384
389
  async function getChangelogFiles(context) {
385
390
  return (await readdir(context.changelogDir).catch(() => [])).filter((file) => file.endsWith(".md"));
386
391
  }
@@ -390,29 +395,37 @@ async function readChangelogEntries(context) {
390
395
  return (await Promise.all(files.map(async (file) => {
391
396
  const filePath = join(dir, file);
392
397
  return parseChangelogFile(filePath, await readFile(filePath, "utf8"));
393
- }))).flat();
398
+ }))).filter((v) => v !== void 0);
394
399
  }
395
400
  /** Parse one changelog markdown file into release entries. */
396
401
  function parseChangelogFile(file, content) {
397
402
  const parsed = frontmatter(content);
398
403
  const data = changelogFrontmatterSchema.parse(parsed.data);
404
+ if (!data.packages) return;
399
405
  const tree = fromMarkdown(parsed.content);
400
- const entries = [];
406
+ let bumpType;
407
+ const packages = /* @__PURE__ */ new Map();
408
+ const sections = [];
409
+ const filename = basename(file);
401
410
  for (const section of getHeadingSections(tree)) {
402
- const type = headingToBump(section.heading.depth);
403
- if (!type) continue;
404
- const filename = basename(file);
405
- entries.push({
406
- id: `${filename}:${entries.length}`,
407
- filename,
408
- subject: data.subject,
409
- packages: new Set(data.packages),
410
- type,
411
+ const sectionBumpType = headingToBump(section.heading.depth);
412
+ if (sectionBumpType) bumpType = bumpType ? maxBump(bumpType, sectionBumpType) : sectionBumpType;
413
+ sections.push({
414
+ depth: section.heading.depth,
411
415
  title: headingText(section.heading),
412
416
  content: sectionToMarkdown(section.children)
413
417
  });
414
418
  }
415
- return entries;
419
+ if (!bumpType) return;
420
+ if (Array.isArray(data.packages)) for (const pkg of data.packages) packages.set(pkg, bumpType);
421
+ else for (const [k, v] of Object.entries(data.packages)) packages.set(k, v ?? bumpType);
422
+ return {
423
+ id: filename,
424
+ filename,
425
+ subject: data.subject,
426
+ packages,
427
+ sections
428
+ };
416
429
  }
417
430
  function getHeadingSections(tree) {
418
431
  const sections = [];
@@ -468,7 +481,7 @@ async function publishFromPlan(context, store, options) {
468
481
  const entry = store.changelogs[id];
469
482
  if (entry) changelogs.push({
470
483
  ...entry,
471
- packages: new Set(entry.packages),
484
+ packages: new Map(Object.entries(entry.packages)),
472
485
  id
473
486
  });
474
487
  }
@@ -1,4 +1,4 @@
1
- import { d as TegamiPlugin } from "../index-Da6P6gSc.js";
1
+ import { s as TegamiPlugin } from "../types-BNhcu4-X.js";
2
2
 
3
3
  //#region src/plugins/git.d.ts
4
4
  interface GitPluginOptions {
@@ -1,4 +1,4 @@
1
- import { a as Awaitable, d as TegamiPlugin, w as DraftPlan, x as PackagePublishResult } from "../index-Da6P6gSc.js";
1
+ import { O as DraftPlan, s as TegamiPlugin, t as Awaitable, w as TegamiContext, x as PackagePublishResult } from "../types-BNhcu4-X.js";
2
2
  import { GitPluginOptions } from "./git.js";
3
3
 
4
4
  //#region src/plugins/github.d.ts
@@ -34,12 +34,14 @@ interface VersionPullRequestOptions {
34
34
  interface GitHubPluginOptions extends GitPluginOptions {
35
35
  /** GitHub repository. */
36
36
  repo?: string;
37
+ /** Optional GitHub token for Git & GitHub operations. */
38
+ token?: string;
37
39
  /** Override release details for a single package, return `false` to skip. */
38
- onCreateRelease?: (result: PackagePublishResult) => Awaitable<GithubRelease | false>;
40
+ onCreateRelease?: (this: TegamiContext, result: PackagePublishResult) => Awaitable<GithubRelease | false>;
39
41
  /** Override release details when multiple packages share a git tag, return `false` to skip. */
40
- onCreateGroupedRelease?: (packages: PackagePublishResult[]) => Awaitable<GithubRelease | false>;
42
+ onCreateGroupedRelease?: (this: TegamiContext, packages: PackagePublishResult[]) => Awaitable<GithubRelease | false>;
41
43
  /** Override details for "Version Packages" PR. */
42
- onCreateVersionPullRequest?: (publishPlan: DraftPlan) => Awaitable<VersionPullRequest | false>;
44
+ onCreateVersionPullRequest?: (this: TegamiContext, publishPlan: DraftPlan) => Awaitable<VersionPullRequest | false>;
43
45
  cli?: {
44
46
  /**
45
47
  * Open a version pull request after versioning.
@@ -1,7 +1,8 @@
1
1
  import { t as execFailure } from "../error-DBK-9uBa.js";
2
- import { n as formatNpmDistTag, r as formatPackageVersion } from "../semver-mWK2Khi2.js";
2
+ import { i as formatPackageVersion, r as formatNpmDistTag } from "../semver-CPtl0XNq.js";
3
3
  import { t as isCI } from "../constants-B9qjNfvr.js";
4
4
  import { git } from "./git.js";
5
+ import { join, relative } from "node:path";
5
6
  import { x } from "tinyexec";
6
7
  import { prerelease } from "semver";
7
8
  //#region src/plugins/github.ts
@@ -22,18 +23,18 @@ function github(options = {}) {
22
23
  const result = await x("gh", args);
23
24
  if (result.exitCode !== 0) throw execFailure(`Failed to create GitHub release for ${gitTag}.`, result);
24
25
  }
25
- async function createRelease(pkg) {
26
+ async function createRelease(context, pkg) {
26
27
  if (!pkg.gitTag) return;
27
- const release = await options.onCreateRelease?.(pkg) ?? {};
28
+ const release = await options.onCreateRelease?.call(context, pkg) ?? {};
28
29
  if (release === false) return;
29
- await runGithubRelease(pkg.gitTag, release.title ?? defaultTitle(pkg), release.notes ?? defaultNotes(pkg), release.prerelease ?? prerelease(pkg.version) !== null);
30
+ await runGithubRelease(pkg.gitTag, release.title ?? defaultTitle(pkg), release.notes ?? await defaultNotes(context, pkg), release.prerelease ?? prerelease(pkg.version) !== null);
30
31
  }
31
- async function createGroupedRelease(packages) {
32
+ async function createGroupedRelease(context, packages) {
32
33
  const primary = packages[0];
33
34
  if (!primary?.gitTag) return;
34
- const release = await options.onCreateGroupedRelease?.(packages) ?? {};
35
+ const release = await options.onCreateGroupedRelease?.call(context, packages) ?? {};
35
36
  if (release === false) return;
36
- await runGithubRelease(primary.gitTag, release.title ?? defaultGroupedTitle(packages), release.notes ?? defaultGroupedNotes(packages), release.prerelease ?? packages.some((pkg) => prerelease(pkg.version) !== null));
37
+ await runGithubRelease(primary.gitTag, release.title ?? defaultGroupedTitle(packages), release.notes ?? await defaultGroupedNotes(context, packages), release.prerelease ?? packages.some((pkg) => prerelease(pkg.version) !== null));
37
38
  }
38
39
  function resolvePROptions() {
39
40
  const setting = options.cli?.versionPr ?? isCI();
@@ -49,20 +50,41 @@ function github(options = {}) {
49
50
  const distTagTxt = formatNpmDistTag(packagePlan.npm?.distTag);
50
51
  packageLines.push(`- ${pkg.name}@${cliOriginalPackageVersions.get(pkg.id) ?? pkg.version} → ${pkg.name}@${pkg.version}${distTagTxt}${publishTxt}`);
51
52
  }
52
- const changelogLines = draft.getChangelogs().map((entry) => `- ${entry.title}`);
53
- const sections = ["## Summary", ...packageLines];
53
+ const changelogLines = [];
54
+ for (const entry of draft.getChangelogs()) {
55
+ changelogLines.push(`### ${entry.subject ?? `\`${entry.filename}\``}`, "");
56
+ for (const section of entry.sections) {
57
+ changelogLines.push(`#### ${section.title}`, "");
58
+ changelogLines.push(section.content);
59
+ }
60
+ }
61
+ const sections = [
62
+ "## Summary",
63
+ "",
64
+ "Merge this PR to publish the versioned packages.",
65
+ "",
66
+ ...packageLines
67
+ ];
54
68
  if (changelogLines.length > 0) sections.push("", "## Changelogs", ...changelogLines);
55
- sections.push("", "Merge this PR to publish the versioned packages.");
69
+ sections.push("");
56
70
  return sections.join("\n");
57
71
  }
58
72
  const cliOriginalPackageVersions = /* @__PURE__ */ new Map();
59
73
  return [git(options), {
60
74
  name: "github",
75
+ init() {
76
+ const repository = options.repo ?? process.env.GITHUB_REPOSITORY;
77
+ const token = options.token ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
78
+ this.github = {
79
+ repo: repository,
80
+ token
81
+ };
82
+ },
61
83
  cli: {
62
84
  async init() {
63
85
  if (!isCI()) return;
64
- const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
65
- const repository = options.repo ?? process.env.GITHUB_REPOSITORY;
86
+ const repository = this.github?.repo;
87
+ const token = this.github?.token;
66
88
  if (!token || !repository) return;
67
89
  const result = await x("git", [
68
90
  "remote",
@@ -80,7 +102,7 @@ function github(options = {}) {
80
102
  const [enabled, config] = resolvePROptions();
81
103
  if (!enabled || !await hasGitChanges(cwd)) return;
82
104
  const { branch = "tegami/version-packages", base = "main" } = config;
83
- const basePR = await options.onCreateVersionPullRequest?.(draft);
105
+ const basePR = await options.onCreateVersionPullRequest?.call(this, draft);
84
106
  if (basePR === false) return;
85
107
  const pr = {
86
108
  title: basePR?.title ?? "Version Packages",
@@ -109,7 +131,22 @@ function github(options = {}) {
109
131
  branch
110
132
  ], gitOptions);
111
133
  if (result.exitCode !== 0) throw execFailure("Failed to push the version branch to origin. Ensure `origin` is configured and you have push access.", result);
112
- if (await hasOpenPullRequest(branch, options.repo)) return;
134
+ const openPr = await findOpenPullRequest(branch, options.repo);
135
+ if (openPr !== void 0) {
136
+ const editArgs = [
137
+ "pr",
138
+ "edit",
139
+ String(openPr),
140
+ "--title",
141
+ pr.title,
142
+ "--body",
143
+ pr.body
144
+ ];
145
+ if (options.repo) editArgs.push("--repo", options.repo);
146
+ const editResult = await x("gh", editArgs);
147
+ if (editResult.exitCode !== 0) throw execFailure("Failed to update the version pull request.", editResult);
148
+ return;
149
+ }
113
150
  const args = [
114
151
  "pr",
115
152
  "create",
@@ -129,15 +166,15 @@ function github(options = {}) {
129
166
  },
130
167
  async afterPublish(result) {
131
168
  if (result.state !== "created") return;
132
- for (const packages of groupPackagesByGitTag(result.packages).values()) if (packages.length > 1) await createGroupedRelease(packages);
133
- else await createRelease(packages[0]);
169
+ for (const packages of groupPackagesByGitTag(result.packages).values()) if (packages.length > 1) await createGroupedRelease(this, packages);
170
+ else await createRelease(this, packages[0]);
134
171
  }
135
172
  }];
136
173
  }
137
174
  async function hasGitChanges(cwd) {
138
175
  return (await x("git", ["status", "--porcelain"], { nodeOptions: { cwd } })).stdout.trim().length > 0;
139
176
  }
140
- async function hasOpenPullRequest(branch, repo) {
177
+ async function findOpenPullRequest(branch, repo) {
141
178
  const args = [
142
179
  "pr",
143
180
  "list",
@@ -151,7 +188,7 @@ async function hasOpenPullRequest(branch, repo) {
151
188
  if (repo) args.push("--repo", repo);
152
189
  const result = await x("gh", args);
153
190
  if (result.exitCode !== 0) throw execFailure("Failed to check for an existing version pull request.", result);
154
- return result.stdout.trim() !== "[]";
191
+ return JSON.parse(result.stdout)[0]?.number;
155
192
  }
156
193
  function groupPackagesByGitTag(packages) {
157
194
  const groups = /* @__PURE__ */ new Map();
@@ -171,16 +208,46 @@ function defaultGroupedTitle(packages) {
171
208
  const distTag = packages.every((pkg) => pkg.npm?.distTag === primary.npm?.distTag) ? primary.npm?.distTag : void 0;
172
209
  return formatPackageVersion(primary.gitTag.slice(0, primary.gitTag.lastIndexOf("@")), primary.version, distTag);
173
210
  }
174
- function defaultNotes(pkg) {
175
- if (pkg.changelogs.length > 0) return pkg.changelogs.map((entry) => [`### ${entry.title}`, entry.content].filter(Boolean).join("\n\n")).join("\n\n");
211
+ function formatCommitLink(commit, repo) {
212
+ const short = commit.slice(0, 7);
213
+ if (!repo) return `\`${short}\``;
214
+ return `[${short}](https://github.com/${repo}/commit/${commit})`;
215
+ }
216
+ async function resolveChangelogEntryCommit(context, filename) {
217
+ const result = await x("git", [
218
+ "log",
219
+ "--diff-filter=A",
220
+ "-1",
221
+ "--format=%H",
222
+ "--",
223
+ relative(context.cwd, join(context.changelogDir, filename))
224
+ ], { nodeOptions: { cwd: context.cwd } });
225
+ if (result.exitCode !== 0) return;
226
+ return result.stdout.trim() || void 0;
227
+ }
228
+ async function renderChangelogEntryNotes(context, entry) {
229
+ const repo = context.github?.repo;
230
+ const commit = await resolveChangelogEntryCommit(context, entry.filename);
231
+ const commitSuffix = commit ? ` (${formatCommitLink(commit, repo)})` : "";
232
+ const lines = [];
233
+ for (const section of entry.sections) {
234
+ lines.push(`### ${section.title}${commitSuffix}`, "");
235
+ if (section.content) lines.push(section.content, "");
236
+ }
237
+ return lines.join("\n").trim();
238
+ }
239
+ async function defaultNotes(context, pkg) {
240
+ if (pkg.changelogs.length > 0) return (await Promise.all(pkg.changelogs.map(async (entry) => renderChangelogEntryNotes(context, entry)))).join("\n\n");
176
241
  return `Published ${formatPackageVersion(pkg.name, pkg.version, pkg.npm?.distTag)}.`;
177
242
  }
178
- function defaultGroupedNotes(packages) {
243
+ async function defaultGroupedNotes(context, packages) {
179
244
  const changelogs = /* @__PURE__ */ new Map();
180
245
  for (const pkg of packages) for (const entry of pkg.changelogs) changelogs.set(entry.id, entry);
181
246
  const sections = [packages.map((pkg) => `- ${formatPackageVersion(pkg.name, pkg.version, pkg.npm?.distTag)}`).join("\n")];
182
- if (changelogs.size > 0) sections.push("", Array.from(changelogs.values()).map((entry) => [`### ${entry.title}`, entry.content].filter(Boolean).join("\n\n")).join("\n\n"));
183
- else sections.push("", `Published ${packages[0].gitTag}.`);
247
+ if (changelogs.size > 0) {
248
+ const notes = await Promise.all(Array.from(changelogs.values()).map((entry) => renderChangelogEntryNotes(context, entry)));
249
+ sections.push("", notes.join("\n\n"));
250
+ } else sections.push("", `Published ${packages[0].gitTag}.`);
184
251
  return sections.join("\n");
185
252
  }
186
253
  //#endregion
@@ -1,2 +1,2 @@
1
- import { g as cargo, h as CargoRegistryClient, m as CargoPluginOptions, p as CargoPackage } from "../index-Da6P6gSc.js";
1
+ import { d as CargoRegistryClient, f as cargo, l as CargoPackage, u as CargoPluginOptions } from "../types-BNhcu4-X.js";
2
2
  export { CargoPackage, CargoPluginOptions, CargoRegistryClient, cargo };
@@ -1,5 +1,5 @@
1
1
  import { r as isNodeError } from "../error-DBK-9uBa.js";
2
- import { n as WorkspacePackage } from "../graph-CUgwuRW5.js";
2
+ import { n as WorkspacePackage } from "../graph-B22NBRUG.js";
3
3
  import { readFile, writeFile } from "node:fs/promises";
4
4
  import { join, normalize } from "node:path";
5
5
  import { x } from "tinyexec";
@@ -1,2 +1,2 @@
1
- import { _ as NpmPackage, b as npm, v as NpmPluginOptions, y as NpmRegistryClient } from "../index-Da6P6gSc.js";
1
+ import { g as npm, h as NpmRegistryClient, m as NpmPluginOptions, p as NpmPackage } from "../types-BNhcu4-X.js";
2
2
  export { NpmPackage, NpmPluginOptions, NpmRegistryClient, npm };
@@ -1,6 +1,6 @@
1
1
  import { r as isNodeError, t as execFailure } from "../error-DBK-9uBa.js";
2
- import { n as WorkspacePackage } from "../graph-CUgwuRW5.js";
3
- import { i as pnpmWorkspaceSchema, r as packageManifestSchema } from "../schemas-Cc4h6bq5.js";
2
+ import { n as WorkspacePackage } from "../graph-B22NBRUG.js";
3
+ import { i as pnpmWorkspaceSchema, r as packageManifestSchema } from "../schemas-CurBAaW5.js";
4
4
  import { readFile, writeFile } from "node:fs/promises";
5
5
  import { join } from "node:path";
6
6
  import { x } from "tinyexec";
@@ -1,10 +1,11 @@
1
1
  import { z } from "zod";
2
2
  //#region src/schemas.ts
3
- const changelogFrontmatterSchema = z.object({
4
- subject: z.string().optional(),
5
- packages: z.array(z.string()).default([])
6
- });
7
3
  const stringRecordSchema = z.record(z.string(), z.string());
4
+ const bumpTypeSchema = z.enum([
5
+ "major",
6
+ "minor",
7
+ "patch"
8
+ ]);
8
9
  const jsonCodec = (schema) => z.codec(z.string(), schema, {
9
10
  decode: (jsonString, ctx) => {
10
11
  try {
@@ -38,4 +39,4 @@ const packageManifestSchema = z.looseObject({
38
39
  optionalDependencies: stringRecordSchema.optional()
39
40
  });
40
41
  //#endregion
41
- export { pnpmWorkspaceSchema as i, jsonCodec as n, packageManifestSchema as r, changelogFrontmatterSchema as t };
42
+ export { pnpmWorkspaceSchema as i, jsonCodec as n, packageManifestSchema as r, bumpTypeSchema as t };
@@ -11,6 +11,11 @@ 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
+ };
14
19
  const PRE = {
15
20
  major: "premajor",
16
21
  minor: "preminor",
@@ -20,6 +25,9 @@ function maxBump(a, b) {
20
25
  if (WEIGHTS[a] > WEIGHTS[b]) return a;
21
26
  return b;
22
27
  }
28
+ function bumpDepth(type) {
29
+ return DEPTH[type];
30
+ }
23
31
  function bumpVersion(version, type, prerelease) {
24
32
  let next = version;
25
33
  if (prerelease) {
@@ -30,4 +38,4 @@ function bumpVersion(version, type, prerelease) {
30
38
  return next;
31
39
  }
32
40
  //#endregion
33
- export { maxBump as i, formatNpmDistTag as n, formatPackageVersion as r, bumpVersion as t };
41
+ export { maxBump as a, formatPackageVersion as i, bumpVersion as n, formatNpmDistTag as r, bumpDepth as t };
@@ -5,58 +5,19 @@ import { AgentName } from "package-manager-detector";
5
5
  //#region src/utils/semver.d.ts
6
6
  type BumpType = "major" | "minor" | "patch";
7
7
  //#endregion
8
- //#region src/graph.d.ts
9
- /** Package discovered in the workspace. */
10
- declare abstract class WorkspacePackage {
11
- abstract readonly name: string;
12
- abstract readonly path: string;
13
- abstract readonly manager: string;
14
- abstract readonly version: string;
15
- get id(): string;
16
- private opts;
17
- /** note: this will only be available after package graph is resolved */
18
- getPackageOptions(): PackageOptions;
19
- setPackageOptions(options: PackageOptions): void;
20
- /** create an empty draft plan. */
21
- onPlan(_context: TegamiContext): PackagePlan;
22
- }
23
- interface PackageGroup {
24
- name: string;
25
- options: GroupOptions;
26
- packages: WorkspacePackage[];
27
- }
28
- /** Dependency graph for discovered workspace packages. */
29
- declare class PackageGraph {
30
- private readonly packages;
31
- private readonly groups;
32
- constructor(packages?: WorkspacePackage[]);
33
- getPackages(): WorkspacePackage[];
34
- /** Get a package by exact id. */
35
- get(id: string): WorkspacePackage | undefined;
36
- /** Get packages by id, `group:name`, or every package matching a name. */
37
- getByName(nameOrId: string): WorkspacePackage[];
38
- /** scan package into graph, if the package id already exists, replace the existing one in graph */
39
- add(pkg: WorkspacePackage): void;
40
- delete(id: string): void;
41
- getPackageGroup(pkgId: string): PackageGroup | undefined;
42
- getGroups(): PackageGroup[];
43
- getGroup(name: string): PackageGroup | undefined;
44
- registerGroup(name: string, options: GroupOptions): PackageGroup;
45
- addGroupMember(groupId: string, id: string): void;
46
- removeGroupMember(group: string, id: string): void;
47
- unregisterGroup(name: string): void;
48
- }
49
- //#endregion
50
8
  //#region src/changelog/parse.d.ts
51
9
  interface ChangelogEntry {
52
10
  id: string;
53
11
  /** file name like `my-change.md` */
54
12
  filename: string;
55
13
  subject?: string;
56
- packages: Set<string>;
57
- type: BumpType;
58
- title: string;
59
- content: string;
14
+ packages: Map<string, BumpType>;
15
+ /** will not be empty */
16
+ sections: {
17
+ depth: number;
18
+ title: string;
19
+ content: string;
20
+ }[];
60
21
  }
61
22
  //#endregion
62
23
  //#region src/plans/draft.d.ts
@@ -117,6 +78,68 @@ type CleanupResult = {
117
78
  reason: "missing" | "pending";
118
79
  };
119
80
  //#endregion
81
+ //#region src/graph.d.ts
82
+ /** Package discovered in the workspace. */
83
+ declare abstract class WorkspacePackage {
84
+ abstract readonly name: string;
85
+ abstract readonly path: string;
86
+ abstract readonly manager: string;
87
+ abstract readonly version: string;
88
+ get id(): string;
89
+ private opts;
90
+ /** note: this will only be available after package graph is resolved */
91
+ getPackageOptions(): PackageOptions;
92
+ setPackageOptions(options: PackageOptions): void;
93
+ /** create an empty draft plan. */
94
+ onPlan(_context: TegamiContext): PackagePlan;
95
+ }
96
+ interface PackageGroup {
97
+ name: string;
98
+ options: GroupOptions;
99
+ packages: WorkspacePackage[];
100
+ }
101
+ /** Dependency graph for discovered workspace packages. */
102
+ declare class PackageGraph {
103
+ private readonly packages;
104
+ private readonly groups;
105
+ constructor(packages?: WorkspacePackage[]);
106
+ getPackages(): WorkspacePackage[];
107
+ /** Get a package by exact id. */
108
+ get(id: string): WorkspacePackage | undefined;
109
+ /** Get packages by id, `group:name`, or every package matching a name. */
110
+ getByName(nameOrId: string): WorkspacePackage[];
111
+ /** scan package into graph, if the package id already exists, replace the existing one in graph */
112
+ add(pkg: WorkspacePackage): void;
113
+ delete(id: string): void;
114
+ getPackageGroup(pkgId: string): PackageGroup | undefined;
115
+ getGroups(): PackageGroup[];
116
+ getGroup(name: string): PackageGroup | undefined;
117
+ registerGroup(name: string, options: GroupOptions): PackageGroup;
118
+ addGroupMember(groupId: string, id: string): void;
119
+ removeGroupMember(group: string, id: string): void;
120
+ unregisterGroup(name: string): void;
121
+ }
122
+ //#endregion
123
+ //#region src/context.d.ts
124
+ interface TegamiContext {
125
+ /** absolute path */
126
+ cwd: string;
127
+ /** absolute path */
128
+ changelogDir: string;
129
+ /** absolute path */
130
+ planPath: string;
131
+ options: TegamiOptions;
132
+ plugins: TegamiPlugin[];
133
+ graph: PackageGraph;
134
+ /** error if doesn't exist */
135
+ getRegistryClient(pkgOrId: WorkspacePackage | string): RegistryClient;
136
+ /** additional context when GitHub plugin is configured */
137
+ github?: {
138
+ repo?: string;
139
+ token?: string;
140
+ };
141
+ }
142
+ //#endregion
120
143
  //#region src/plans/store.d.ts
121
144
  declare const packagePlanStoreSchema: z$1.ZodObject<{
122
145
  type: z$1.ZodOptional<z$1.ZodEnum<{
@@ -139,14 +162,15 @@ declare const planStoreSchema: z$1.ZodCodec<z$1.ZodString, z$1.ZodObject<{
139
162
  changelogs: z$1.ZodRecord<z$1.ZodString, z$1.ZodObject<{
140
163
  filename: z$1.ZodString;
141
164
  subject: z$1.ZodOptional<z$1.ZodString>;
142
- packages: z$1.ZodArray<z$1.ZodString>;
143
- type: z$1.ZodEnum<{
165
+ packages: z$1.ZodRecord<z$1.ZodString, z$1.ZodEnum<{
144
166
  major: "major";
145
167
  minor: "minor";
146
168
  patch: "patch";
147
- }>;
148
- title: z$1.ZodString;
149
- content: z$1.ZodString;
169
+ }>>;
170
+ sections: z$1.ZodArray<z$1.ZodObject<{
171
+ title: z$1.ZodString;
172
+ content: z$1.ZodString;
173
+ }, z$1.core.$strip>>;
150
174
  }, z$1.core.$strip>>;
151
175
  packages: z$1.ZodRecord<z$1.ZodString, z$1.ZodObject<{
152
176
  type: z$1.ZodOptional<z$1.ZodEnum<{
@@ -198,6 +222,40 @@ type PackagePublishResult = ({
198
222
  changelogs: ChangelogEntry[];
199
223
  };
200
224
  //#endregion
225
+ //#region src/changelog/create.d.ts
226
+ interface CreateChangelogOptions {
227
+ /** Start revision. Defaults to the latest reachable git tag, or all history if none exists. */
228
+ from?: string;
229
+ /** End revision. Defaults to HEAD. */
230
+ to?: string;
231
+ }
232
+ interface CreatedChangelog {
233
+ filename: string;
234
+ path: string;
235
+ packages: string[];
236
+ changes: number;
237
+ }
238
+ //#endregion
239
+ //#region src/index.d.ts
240
+ interface Tegami {
241
+ /** Create pending changelog files from git commit history. */
242
+ generateChangelog(options?: CreateChangelogOptions): Promise<CreatedChangelog[]>;
243
+ /** Build a draft from pending changelog files. */
244
+ draft(): Promise<DraftPlan>;
245
+ /** Publish the current publish plan. */
246
+ publish(options?: PublishOptions): Promise<PublishResult>;
247
+ /** Remove the publish plan file after it has finished successfully. */
248
+ cleanup(): Promise<CleanupResult>;
249
+ /** Internal APIs, do not use it unless you know what you are doing */
250
+ _internal: {
251
+ context(): Promise<TegamiContext>;
252
+ graph(): Promise<PackageGraph>;
253
+ options: TegamiOptions;
254
+ };
255
+ }
256
+ /** Create a Tegami project handle. */
257
+ declare function tegami<const Groups extends string = string>(options?: TegamiOptions<Groups>): Tegami;
258
+ //#endregion
201
259
  //#region src/schemas.d.ts
202
260
  declare const packageManifestSchema: z.ZodObject<{
203
261
  name: z.ZodString;
@@ -418,50 +476,4 @@ interface RegistryClient {
418
476
  }): Promise<void>;
419
477
  }
420
478
  //#endregion
421
- //#region src/context.d.ts
422
- interface TegamiContext {
423
- cwd: string;
424
- changelogDir: string;
425
- planPath: string;
426
- options: TegamiOptions;
427
- plugins: TegamiPlugin[];
428
- graph: PackageGraph;
429
- /** error if doesn't exist */
430
- getRegistryClient(pkgOrId: WorkspacePackage | string): RegistryClient;
431
- }
432
- //#endregion
433
- //#region src/changelog/create.d.ts
434
- interface CreateChangelogOptions {
435
- /** Start revision. Defaults to the latest reachable git tag, or all history if none exists. */
436
- from?: string;
437
- /** End revision. Defaults to HEAD. */
438
- to?: string;
439
- }
440
- interface CreatedChangelog {
441
- filename: string;
442
- path: string;
443
- packages: string[];
444
- changes: number;
445
- }
446
- //#endregion
447
- //#region src/index.d.ts
448
- interface Tegami {
449
- /** Create pending changelog files from git commit history. */
450
- generateChangelog(options?: CreateChangelogOptions): Promise<CreatedChangelog[]>;
451
- /** Build a draft from pending changelog files. */
452
- draft(): Promise<DraftPlan>;
453
- /** Publish the current publish plan. */
454
- publish(options?: PublishOptions): Promise<PublishResult>;
455
- /** Remove the publish plan file after it has finished successfully. */
456
- cleanup(): Promise<CleanupResult>;
457
- /** Internal APIs, do not use it unless you know what you are doing */
458
- _internal: {
459
- context(): Promise<TegamiContext>;
460
- graph(): Promise<PackageGraph>;
461
- options: TegamiOptions;
462
- };
463
- }
464
- /** Create a Tegami project handle. */
465
- declare function tegami<const Groups extends string = string>(options?: TegamiOptions<Groups>): Tegami;
466
- //#endregion
467
- export { PublishResult as C, PackageGroup as D, PackageGraph as E, WorkspacePackage as O, PublishOptions as S, PackagePlan as T, NpmPackage as _, Awaitable as a, npm as b, PackageOptions as c, TegamiPlugin as d, TegamiPluginOption as f, cargo as g, CargoRegistryClient as h, CreatedChangelog as i, RegistryClient as l, CargoPluginOptions as m, tegami as n, GroupOptions as o, CargoPackage as p, CreateChangelogOptions as r, LogGenerator as s, Tegami as t, TegamiOptions as u, NpmPluginOptions as v, DraftPlan as w, PackagePublishResult as x, NpmRegistryClient as y };
479
+ export { PublishResult as C, WorkspacePackage as D, PackageGroup as E, DraftPlan as O, PublishOptions as S, PackageGraph as T, Tegami as _, RegistryClient as a, CreatedChangelog as b, TegamiPluginOption as c, CargoRegistryClient as d, cargo as f, npm as g, NpmRegistryClient as h, PackageOptions as i, PackagePlan as k, CargoPackage as l, NpmPluginOptions as m, GroupOptions as n, TegamiOptions as o, NpmPackage as p, LogGenerator as r, TegamiPlugin as s, Awaitable as t, CargoPluginOptions as u, tegami as v, TegamiContext as w, PackagePublishResult as x, CreateChangelogOptions as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tegami",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Utility for package versioning & publish",
5
5
  "license": "MIT",
6
6
  "author": "Fuma Nama",