tegami 0.1.1 → 0.1.3

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/dist/index.js CHANGED
@@ -1,167 +1,16 @@
1
- import { a as changelogFilename, i as readPlanStore, n as publishPlanStatus, r as createPlanStore, t as assertPublishPlanFinished } from "./checks-Bz3Rf2OX.js";
2
- import { n as handlePluginError, t as execFailure } from "./error-BaOQJtvf.js";
3
- import { i as maxBump } from "./semver-mWK2Khi2.js";
4
- import { t as PackageGraph } from "./graph-CUgwuRW5.js";
1
+ import { a as maxBump } from "./semver-cnZp9koa.js";
2
+ import { a as generateChangelog, i as readPlanStore, n as publishPlanStatus, r as createPlanStore, s as changelogFrontmatterSchema, t as assertPublishPlanFinished } from "./checks-Smwtq_Li.js";
3
+ import { r as handlePluginError } from "./error-qpTCB2ld.js";
4
+ import { t as PackageGraph } from "./graph-hBQUWKQA.js";
5
5
  import { cargo } from "./providers/cargo.js";
6
- import { t as bumpTypeSchema } from "./schemas-CurBAaW5.js";
7
6
  import { npm } from "./providers/npm.js";
8
7
  import { simpleGenerator } from "./generators/simple.js";
9
8
  import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
10
9
  import path, { basename, dirname, join } from "node:path";
11
- import { x } from "tinyexec";
12
- import { load } from "js-yaml";
13
- import z$1 from "zod";
10
+ import * as semver from "semver";
11
+ import { dump, load } from "js-yaml";
14
12
  import { fromMarkdown } from "mdast-util-from-markdown";
15
13
  import { toMarkdown } from "mdast-util-to-markdown";
16
- //#region src/utils/conventional-commit.ts
17
- /**
18
- * Conventional commit header per conventionalcommits.org and semantic-release defaults.
19
- * @see https://www.conventionalcommits.org/en/v1.0.0/
20
- */
21
- const CONVENTIONAL_COMMIT_HEADER = /^(?<type>\w+)(?:\((?<scope>[^)]*)\))?(?<breaking>!)?: (?<title>.+)$/;
22
- const BREAKING_CHANGE_FOOTER = /^BREAKING[ -]CHANGE:/m;
23
- /** Parse conventional commits and resolve scopes against the workspace graph. */
24
- function createConventionalCommitParser(graph) {
25
- const byShortName = /* @__PURE__ */ new Map();
26
- for (const pkg of graph.getPackages()) {
27
- const slash = pkg.name.lastIndexOf("/");
28
- const short = slash >= 0 ? pkg.name.slice(slash + 1) : pkg.name;
29
- const names = byShortName.get(short);
30
- if (names) names.push(pkg.name);
31
- else byShortName.set(short, [pkg.name]);
32
- }
33
- function resolvePackages(scope) {
34
- if (!scope) return [];
35
- const packages = /* @__PURE__ */ new Set();
36
- for (const item of scope.split(",")) {
37
- const name = item.trim();
38
- if (!name) continue;
39
- const direct = graph.getByName(name);
40
- if (direct.length > 0) {
41
- for (const pkg of direct) packages.add(pkg.name);
42
- continue;
43
- }
44
- const byShort = byShortName.get(name);
45
- if (byShort) {
46
- for (const pkgName of byShort) packages.add(pkgName);
47
- continue;
48
- }
49
- packages.add(name);
50
- }
51
- return Array.from(packages);
52
- }
53
- return function parseConventionalCommit(subject, body = "") {
54
- const match = CONVENTIONAL_COMMIT_HEADER.exec(subject.trim());
55
- if (!match?.groups) return;
56
- const { type, scope, breaking, title } = match.groups;
57
- if (!type || !title) return;
58
- const trimmedTitle = title.trim();
59
- if (!trimmedTitle) return;
60
- const trimmedScope = scope?.trim();
61
- return {
62
- type: type.toLowerCase(),
63
- packages: resolvePackages(trimmedScope || void 0),
64
- breaking: Boolean(breaking) || BREAKING_CHANGE_FOOTER.test(body),
65
- title: trimmedTitle
66
- };
67
- };
68
- }
69
- /** Map releasable conventional commit types to semver bumps (semantic-release defaults). */
70
- function conventionalCommitToBump(type, breaking) {
71
- if (breaking) return "major";
72
- switch (type) {
73
- case "feat": return "minor";
74
- case "fix":
75
- case "perf":
76
- case "revert": return "patch";
77
- default: return;
78
- }
79
- }
80
- //#endregion
81
- //#region src/changelog/generate.ts
82
- async function generateChangelog(context, options = {}) {
83
- const commits = await readConventionalCommits(context, options);
84
- const groups = /* @__PURE__ */ new Map();
85
- for (const commit of commits) {
86
- const key = commit.packages.join("\0");
87
- const group = groups.get(key);
88
- if (group) group.push(commit);
89
- else groups.set(key, [commit]);
90
- }
91
- await mkdir(context.changelogDir, { recursive: true });
92
- return Promise.all(Array.from(groups, async ([key, changes], index) => {
93
- const packages = key ? key.split("\0") : [];
94
- const filename = changelogFilename(index);
95
- const path = join(context.changelogDir, filename);
96
- await writeFile(path, renderChangelog(packages, changes));
97
- return {
98
- filename,
99
- path,
100
- packages,
101
- changes: changes.length
102
- };
103
- }));
104
- }
105
- async function readConventionalCommits(context, options) {
106
- const to = options.to ?? "HEAD";
107
- const from = options.from ?? await latestTag(context.cwd);
108
- const args = [
109
- "log",
110
- "--no-merges",
111
- "--format=%H%x1f%s%x1f%b%x1e"
112
- ];
113
- if (from) args.push(`${from}..${to}`);
114
- else if (to !== "HEAD") args.push(to);
115
- const result = await x("git", args, { nodeOptions: { cwd: context.cwd } });
116
- if (result.exitCode !== 0) throw execFailure(`Unable to read git commits from ${from ?? "start"} to ${to}`, result);
117
- const parseCommit = createConventionalCommitParser(context.graph);
118
- const changes = [];
119
- for (const record of result.stdout.split("")) {
120
- const [hash, subject, body = ""] = record.replace(/^\n+|\n+$/g, "").split("");
121
- if (!hash || !subject) continue;
122
- const parsed = parseCommit(subject, body);
123
- if (!parsed) continue;
124
- const bump = conventionalCommitToBump(parsed.type, parsed.breaking);
125
- if (!bump) continue;
126
- changes.push({
127
- hash,
128
- subject,
129
- body: body.trim(),
130
- packages: parsed.packages,
131
- type: bump,
132
- title: titleCase(parsed.title)
133
- });
134
- }
135
- return changes;
136
- }
137
- async function latestTag(cwd) {
138
- const result = await x("git", [
139
- "describe",
140
- "--tags",
141
- "--abbrev=0"
142
- ], { nodeOptions: { cwd } });
143
- if (result.exitCode !== 0) return;
144
- return result.stdout.trim() || void 0;
145
- }
146
- function renderChangelog(packages, changes) {
147
- return [
148
- "---",
149
- `packages: ${JSON.stringify(packages)}`,
150
- "---",
151
- "",
152
- changes.map(renderChange).join("\n\n"),
153
- ""
154
- ].join("\n");
155
- }
156
- function renderChange(change) {
157
- const heading = "#".repeat(change.type === "major" ? 1 : change.type === "minor" ? 2 : 3);
158
- if (!change.body) return `${heading} ${change.title}`;
159
- return `${heading} ${change.title}\n\n${change.body}`;
160
- }
161
- function titleCase(value) {
162
- return value.charAt(0).toUpperCase() + value.slice(1);
163
- }
164
- //#endregion
165
14
  //#region src/context.ts
166
15
  async function createTegamiContext(options = {}) {
167
16
  const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
@@ -226,6 +75,141 @@ function resolvePlugins(plugins = []) {
226
75
  return plugins.flat(Infinity).sort((a, b) => PLUGIN_ORDER[a.enforce ?? "default"] - PLUGIN_ORDER[b.enforce ?? "default"]);
227
76
  }
228
77
  //#endregion
78
+ //#region src/utils/frontmatter.ts
79
+ /**
80
+ * Inspired by https://github.com/jonschlinkert/gray-matter
81
+ */
82
+ const regex = /^---\r?\n(.+?)\r?\n---\r?\n?/s;
83
+ /**
84
+ * parse frontmatter, it supports only yaml format
85
+ */
86
+ function frontmatter(input) {
87
+ const output = {
88
+ matter: "",
89
+ data: {},
90
+ content: input
91
+ };
92
+ const match = regex.exec(input);
93
+ if (!match) return output;
94
+ output.matter = match[0];
95
+ output.content = input.slice(match[0].length);
96
+ output.data = load(match[1]) ?? {};
97
+ return output;
98
+ }
99
+ //#endregion
100
+ //#region src/changelog/parse.ts
101
+ async function getChangelogFiles(context) {
102
+ return (await readdir(context.changelogDir).catch(() => [])).filter((file) => file.endsWith(".md"));
103
+ }
104
+ async function readChangelogEntries(context) {
105
+ const dir = context.changelogDir;
106
+ const files = await getChangelogFiles(context);
107
+ return (await Promise.all(files.map(async (file) => {
108
+ const filePath = join(dir, file);
109
+ const content = await readFile(filePath, "utf8");
110
+ return parseChangelogFile(basename(filePath), content);
111
+ }))).filter((v) => v !== void 0);
112
+ }
113
+ /** Parse one changelog markdown file into release entries. */
114
+ function parseChangelogFile(filename, content) {
115
+ const parsed = frontmatter(content);
116
+ const { success, data } = changelogFrontmatterSchema.safeParse(parsed.data);
117
+ if (!success || !data.packages) return;
118
+ const tree = fromMarkdown(parsed.content);
119
+ let headingBump;
120
+ const packages = /* @__PURE__ */ new Map();
121
+ const sections = [];
122
+ for (const section of getHeadingSections(tree)) {
123
+ const sectionBumpType = headingToBump(section.heading.depth);
124
+ if (sectionBumpType) headingBump = headingBump ? maxBump(headingBump, sectionBumpType) : sectionBumpType;
125
+ sections.push({
126
+ depth: section.heading.depth,
127
+ title: sectionToMarkdown(section.heading.children),
128
+ content: sectionToMarkdown(section.children)
129
+ });
130
+ }
131
+ if (sections.length === 0) return;
132
+ if (Array.isArray(data.packages)) {
133
+ if (!headingBump) return;
134
+ for (const pkg of data.packages) packages.set(pkg, { type: headingBump });
135
+ } else for (const [k, v] of Object.entries(data.packages)) {
136
+ let config;
137
+ if (typeof v === "string") config = { type: v };
138
+ else if (v === null) config = { type: headingBump };
139
+ else config = v;
140
+ if (config.type || config.replay?.length) packages.set(k, config);
141
+ }
142
+ if (packages.size === 0) return;
143
+ const entry = {
144
+ id: filename,
145
+ filename,
146
+ subject: data.subject,
147
+ packages,
148
+ sections,
149
+ _raw_body: parsed.content,
150
+ getRawContent() {
151
+ return [
152
+ "---",
153
+ dump({
154
+ subject: this.subject,
155
+ packages: Object.fromEntries(this.packages.entries())
156
+ }).trim(),
157
+ "---",
158
+ "",
159
+ entry._raw_body.trim(),
160
+ ""
161
+ ].join("\n");
162
+ }
163
+ };
164
+ return entry;
165
+ }
166
+ function parseReplayCondition(condition) {
167
+ if (condition.startsWith("exit prerelease:")) return {
168
+ type: "on-exit-prerelease",
169
+ name: condition.slice(16).trimStart()
170
+ };
171
+ const idx = condition.lastIndexOf("@");
172
+ if (idx <= 0) return null;
173
+ return {
174
+ type: "on-version",
175
+ name: condition.slice(0, idx),
176
+ version: condition.slice(idx + 1)
177
+ };
178
+ }
179
+ function getHeadingSections(tree) {
180
+ const sections = [];
181
+ let current;
182
+ for (const child of tree.children) {
183
+ if (child.type === "heading") {
184
+ current = {
185
+ heading: child,
186
+ children: []
187
+ };
188
+ sections.push(current);
189
+ continue;
190
+ }
191
+ current?.children.push(child);
192
+ }
193
+ return sections;
194
+ }
195
+ function sectionToMarkdown(children) {
196
+ if (children.length === 0) return "";
197
+ return toMarkdown({
198
+ type: "root",
199
+ children
200
+ }, {
201
+ bullet: "-",
202
+ fence: "`"
203
+ }).trim();
204
+ }
205
+ function headingToBump(depth) {
206
+ switch (depth) {
207
+ case 1: return "major";
208
+ case 2: return "minor";
209
+ case 3: return "patch";
210
+ }
211
+ }
212
+ //#endregion
229
213
  //#region src/plans/policy.ts
230
214
  function groupPolicy({ graph }) {
231
215
  return {
@@ -263,23 +247,39 @@ var DraftPlan = class {
263
247
  return this.packages.get(id);
264
248
  }
265
249
  bumpPackage(pkg, { type, reason }) {
266
- let plan = this.packages.get(pkg.id);
267
- if (!plan) {
268
- plan = this.initPackagePlan(pkg);
269
- this.packages.set(pkg.id, plan);
270
- }
250
+ return this.dispatchPackage(pkg, (plan) => {
251
+ plan.type = plan.type ? maxBump(plan.type, type) : type;
252
+ if (reason) {
253
+ plan.bumpReasons ??= /* @__PURE__ */ new Set();
254
+ plan.bumpReasons.add(reason);
255
+ }
256
+ });
257
+ }
258
+ dispatchPackage(pkg, dispatch, onUpdate) {
259
+ const plan = this.getOrInitPackage(pkg);
271
260
  const prevVersion = plan.bumpVersion(pkg);
272
- plan.type = plan.type ? maxBump(plan.type, type) : type;
273
- if (reason) {
274
- plan.bumpReasons ??= /* @__PURE__ */ new Set();
275
- plan.bumpReasons.add(reason);
261
+ dispatch(plan);
262
+ if (prevVersion !== plan.bumpVersion(pkg)) {
263
+ onUpdate?.(plan);
264
+ for (const policy of this.policies) policy.onUpdate?.call(this, {
265
+ plan,
266
+ pkg
267
+ });
276
268
  }
277
- if (prevVersion !== plan.bumpVersion(pkg)) for (const policy of this.policies) policy.onUpdate?.call(this, {
278
- plan,
279
- pkg
280
- });
281
269
  return plan;
282
270
  }
271
+ getOrInitPackage(pkg) {
272
+ const existing = this.packages.get(pkg.id);
273
+ if (existing) return existing;
274
+ this.packages.set(pkg.id, pkg.initPlan());
275
+ return this.dispatchPackage(pkg, (plan) => {
276
+ const group = this.context.graph.getPackageGroup(pkg.id);
277
+ if (group?.options.prerelease) plan.prerelease = group.options.prerelease;
278
+ pkg.configurePlan(plan);
279
+ }, (plan) => {
280
+ (plan.bumpReasons ??= /* @__PURE__ */ new Set()).add("align with script-level configs");
281
+ });
282
+ }
283
283
  hasPending() {
284
284
  const { graph } = this.context;
285
285
  for (const [id, plan] of this.packages) {
@@ -298,37 +298,36 @@ var DraftPlan = class {
298
298
  this.changelogs.set(entry.id, entry);
299
299
  const { graph } = this.context;
300
300
  const groupPackages = /* @__PURE__ */ new Map();
301
- for (const [name, bumpType] of entry.packages) for (const pkg of graph.getByName(name)) groupPackages.set(pkg, bumpType);
302
- for (const [pkg, bumpType] of groupPackages) {
303
- const plan = this.bumpPackage(pkg, { type: bumpType });
304
- plan.changelogs ??= [];
305
- plan.changelogs.push(entry);
301
+ for (const [name, config] of entry.packages) {
302
+ if (!config.type) continue;
303
+ for (const pkg of graph.getByName(name)) groupPackages.set(pkg, config.type);
306
304
  }
305
+ for (const [pkg, bumpType] of groupPackages) attachChangelog(this.bumpPackage(pkg, { type: bumpType }), entry);
307
306
  }
308
307
  deleteChangelog(id) {
309
308
  return this.changelogs.delete(id);
310
309
  }
311
- initPackagePlan(pkg) {
312
- const context = this.context;
313
- const plan = pkg.onPlan(context);
314
- const group = context.graph.getPackageGroup(pkg.id);
315
- if (group) plan.prerelease ??= group.options.prerelease;
316
- return plan;
317
- }
318
310
  /** Apply the publish plan: update package versions, write the plan file, and consume changelog files. */
319
311
  async applyPlan() {
320
312
  if (this.#applied) throw new Error("This draft has already applied a publish plan.");
321
313
  await assertPublishPlanFinished(this.context);
314
+ const { graph } = this.context;
322
315
  this.#applied = true;
316
+ const snapshots = /* @__PURE__ */ new Map();
317
+ for (const pkg of graph.getPackages()) snapshots.set(pkg.id, { version: pkg.version });
323
318
  for (const plugin of this.context.plugins) await handlePluginError(plugin, "applyPlan", () => plugin.applyPlan?.call(this.context, this));
324
- const { graph } = this.context;
319
+ const updatedChangelogs = this.applyReplays(snapshots);
325
320
  const writes = [];
326
321
  for (const [id, packagePlan] of this.packages) {
327
322
  const pkg = graph.get(id);
328
323
  if (!pkg) continue;
329
324
  writes.push(this.appendChangelog(pkg, packagePlan));
330
325
  }
331
- for (const entry of this.changelogs.values()) writes.push(rm(path.join(this.context.changelogDir, entry.filename), { force: true }));
326
+ for (const entry of this.changelogs.values()) {
327
+ const updated = updatedChangelogs.get(entry.id);
328
+ const filePath = path.join(this.context.changelogDir, entry.filename);
329
+ writes.push(updated ? writeFile(filePath, updated.getRawContent()) : rm(filePath, { force: true }));
330
+ }
332
331
  await Promise.all(writes);
333
332
  await mkdir(dirname(this.context.planPath), { recursive: true });
334
333
  await writeFile(this.context.planPath, createPlanStore(this, this.context));
@@ -343,6 +342,47 @@ var DraftPlan = class {
343
342
  canApply() {
344
343
  return !this.#applied;
345
344
  }
345
+ /** Attach replaying changelog entries to packages (already bumped), and return the updated changelog entries. */
346
+ applyReplays(snapshots) {
347
+ const updated = /* @__PURE__ */ new Map();
348
+ const { graph } = this.context;
349
+ const isMatch = (condition) => {
350
+ if (condition.type === "on-exit-prerelease") return graph.getByName(condition.name).some((pkg) => {
351
+ const previous = snapshots.get(pkg.id);
352
+ if (!previous) return false;
353
+ return semver.inc(previous.version, "release") === pkg.version;
354
+ });
355
+ return graph.getByName(condition.name).some((pkg) => pkg.version === condition.version);
356
+ };
357
+ for (const entry of this.changelogs.values()) {
358
+ const updatedPackages = /* @__PURE__ */ new Map();
359
+ const matchedNames = /* @__PURE__ */ new Set();
360
+ for (const [name, config] of entry.packages) {
361
+ if (!config.replay || config.replay.length === 0) continue;
362
+ const replay = config.replay.filter((item) => {
363
+ const condition = parseReplayCondition(item);
364
+ if (condition && isMatch(condition)) {
365
+ matchedNames.add(name);
366
+ return false;
367
+ }
368
+ return true;
369
+ });
370
+ if (replay.length === 0) continue;
371
+ const updatedConfig = {
372
+ ...config,
373
+ replay
374
+ };
375
+ delete updatedConfig.type;
376
+ updatedPackages.set(name, updatedConfig);
377
+ }
378
+ if (updatedPackages.size > 0) updated.set(entry.id, {
379
+ ...entry,
380
+ packages: updatedPackages
381
+ });
382
+ for (const name of matchedNames) for (const pkg of graph.getByName(name)) attachChangelog(this.getOrInitPackage(pkg), entry);
383
+ }
384
+ return updated;
385
+ }
346
386
  async appendChangelog(pkg, plan) {
347
387
  if (!plan.changelogs || plan.changelogs.length === 0) return;
348
388
  const { generator = simpleGenerator() } = this.context.options;
@@ -350,10 +390,9 @@ var DraftPlan = class {
350
390
  packageId: pkg.id,
351
391
  packageName: pkg.name,
352
392
  version: pkg.version,
353
- npm: plan.npm,
354
393
  plan,
355
394
  changelogs: plan.changelogs,
356
- _draft: this
395
+ unstable_draft: this
357
396
  });
358
397
  const path = join(pkg.path, "CHANGELOG.md");
359
398
  const existing = await readFile(path, "utf8").catch(() => "");
@@ -383,110 +422,14 @@ async function createDraftPlan(changelogs, context) {
383
422
  const result = await handlePluginError(plugin, "initPlan", () => plugin.initPlan?.call(context, draft));
384
423
  if (result) draft = result;
385
424
  }
425
+ for (const pkg of context.graph.getPackages()) draft.getOrInitPackage(pkg);
386
426
  for (const entry of changelogs) draft.addChangelog(entry);
387
427
  return draft;
388
428
  }
389
- //#endregion
390
- //#region src/utils/frontmatter.ts
391
- /**
392
- * Inspired by https://github.com/jonschlinkert/gray-matter
393
- */
394
- const regex = /^---\r?\n(.+?)\r?\n---\r?\n?/s;
395
- /**
396
- * parse frontmatter, it supports only yaml format
397
- */
398
- function frontmatter(input) {
399
- const output = {
400
- matter: "",
401
- data: {},
402
- content: input
403
- };
404
- const match = regex.exec(input);
405
- if (!match) return output;
406
- output.matter = match[0];
407
- output.content = input.slice(match[0].length);
408
- output.data = load(match[1]) ?? {};
409
- return output;
410
- }
411
- //#endregion
412
- //#region src/changelog/parse.ts
413
- const changelogFrontmatterSchema = z$1.object({
414
- subject: z$1.string().optional(),
415
- packages: z$1.union([z$1.array(z$1.string()), z$1.record(z$1.string(), bumpTypeSchema.or(z$1.null()))]).optional()
416
- });
417
- async function getChangelogFiles(context) {
418
- return (await readdir(context.changelogDir).catch(() => [])).filter((file) => file.endsWith(".md"));
419
- }
420
- async function readChangelogEntries(context) {
421
- const dir = context.changelogDir;
422
- const files = await getChangelogFiles(context);
423
- return (await Promise.all(files.map(async (file) => {
424
- const filePath = join(dir, file);
425
- return parseChangelogFile(filePath, await readFile(filePath, "utf8"));
426
- }))).filter((v) => v !== void 0);
427
- }
428
- /** Parse one changelog markdown file into release entries. */
429
- function parseChangelogFile(file, content) {
430
- const parsed = frontmatter(content);
431
- const data = changelogFrontmatterSchema.parse(parsed.data);
432
- if (!data.packages) return;
433
- const tree = fromMarkdown(parsed.content);
434
- let bumpType;
435
- const packages = /* @__PURE__ */ new Map();
436
- const sections = [];
437
- const filename = basename(file);
438
- for (const section of getHeadingSections(tree)) {
439
- const sectionBumpType = headingToBump(section.heading.depth);
440
- if (sectionBumpType) bumpType = bumpType ? maxBump(bumpType, sectionBumpType) : sectionBumpType;
441
- sections.push({
442
- depth: section.heading.depth,
443
- title: sectionToMarkdown(section.heading.children),
444
- content: sectionToMarkdown(section.children)
445
- });
446
- }
447
- if (!bumpType) return;
448
- if (Array.isArray(data.packages)) for (const pkg of data.packages) packages.set(pkg, bumpType);
449
- else for (const [k, v] of Object.entries(data.packages)) packages.set(k, v ?? bumpType);
450
- return {
451
- id: filename,
452
- filename,
453
- subject: data.subject,
454
- packages,
455
- sections
456
- };
457
- }
458
- function getHeadingSections(tree) {
459
- const sections = [];
460
- let current;
461
- for (const child of tree.children) {
462
- if (child.type === "heading") {
463
- current = {
464
- heading: child,
465
- children: []
466
- };
467
- sections.push(current);
468
- continue;
469
- }
470
- current?.children.push(child);
471
- }
472
- return sections;
473
- }
474
- function sectionToMarkdown(children) {
475
- if (children.length === 0) return "";
476
- return toMarkdown({
477
- type: "root",
478
- children
479
- }, {
480
- bullet: "-",
481
- fence: "`"
482
- }).trim();
483
- }
484
- function headingToBump(depth) {
485
- switch (depth) {
486
- case 1: return "major";
487
- case 2: return "minor";
488
- case 3: return "patch";
489
- }
429
+ function attachChangelog(plan, entry) {
430
+ if (plan.changelogs?.some((item) => item.id === entry.id)) return;
431
+ plan.changelogs ??= [];
432
+ plan.changelogs.push(entry);
490
433
  }
491
434
  //#endregion
492
435
  //#region src/publish.ts
@@ -529,18 +472,20 @@ async function publishFromPlan(context, store, options) {
529
472
  const packages = [];
530
473
  if ((await publishPlanStatus(store, context)).state !== "pending") return { state: "skipped" };
531
474
  const orderedIds = await resolvePublishTargets(context, store);
475
+ const parsedChangelogs = /* @__PURE__ */ new Map();
476
+ for (const [id, entry] of Object.entries(store.changelogs)) {
477
+ const parsed = parseChangelogFile(entry.filename, entry.content);
478
+ if (!parsed) continue;
479
+ parsedChangelogs.set(id, parsed);
480
+ }
532
481
  for (const id of orderedIds) {
533
482
  const plan = store.packages[id];
534
483
  const pkg = context.graph.get(id);
535
484
  const registryClient = context.getRegistryClient(pkg);
536
485
  const changelogs = [];
537
486
  for (const id of plan.changelogIds ?? []) {
538
- const entry = store.changelogs[id];
539
- if (entry) changelogs.push({
540
- ...entry,
541
- packages: new Map(Object.entries(entry.packages)),
542
- id
543
- });
487
+ const entry = parsedChangelogs.get(id);
488
+ if (entry) changelogs.push(entry);
544
489
  }
545
490
  if (!dryRun) {
546
491
  if (await registryClient.isPackagePublished(pkg)) {
@@ -643,7 +588,6 @@ function tegami(options = {}) {
643
588
  },
644
589
  async publish(publishOptions = {}) {
645
590
  const context = await $context;
646
- if ((await getChangelogFiles(context)).length > 0) return { state: "skipped" };
647
591
  const parsed = await readPlanStore(context);
648
592
  if (!parsed) return { state: "skipped" };
649
593
  let result = await publishFromPlan(context, parsed, publishOptions);
@@ -1,4 +1,4 @@
1
- import { c as TegamiPlugin } from "../types-Diwnh8Tn.js";
1
+ import { c as TegamiPlugin } from "../types-Di--A9X1.js";
2
2
 
3
3
  //#region src/plugins/git.d.ts
4
4
  interface GitPluginOptions {
@@ -1,4 +1,4 @@
1
- import { t as execFailure } from "../error-BaOQJtvf.js";
1
+ import { n as execFailure } from "../error-qpTCB2ld.js";
2
2
  import { t as isCI } from "../constants-B9qjNfvr.js";
3
3
  import { x } from "tinyexec";
4
4
  //#region src/plugins/git.ts
@@ -13,8 +13,7 @@ function git(options = {}) {
13
13
  function resolveGitTag(context, result) {
14
14
  const { graph } = context;
15
15
  const pkg = graph.get(result.id);
16
- if (!pkg) return `${result.name}@${result.version}`;
17
- const group = graph.getPackageGroup(pkg.id);
16
+ const group = pkg && graph.getPackageGroup(pkg.id);
18
17
  if (group?.options.syncGitTag) return `${group.name}@${result.version}`;
19
18
  return `${result.name}@${result.version}`;
20
19
  }
@@ -39,9 +38,10 @@ function git(options = {}) {
39
38
  } },
40
39
  async afterPublishAll(result) {
41
40
  const { cwd, publishOptions: { dryRun = false } } = this;
42
- if (dryRun || !createTags || result.state !== "created") return result;
41
+ if (dryRun || !createTags || result.state === "skipped") return result;
43
42
  const pendingTags = /* @__PURE__ */ new Set();
44
43
  for (const pkg of result.packages) {
44
+ if (pkg.state !== "success") continue;
45
45
  pkg.gitTag = resolveGitTag(this, pkg);
46
46
  pendingTags.add(pkg.gitTag);
47
47
  }
@@ -1,4 +1,4 @@
1
- import { S as PackagePublishResult, T as TegamiContext, c as TegamiPlugin, k as DraftPlan, t as Awaitable } from "../types-Diwnh8Tn.js";
1
+ import { S as PackagePublishResult, T as TegamiContext, c as TegamiPlugin, k as DraftPlan, t as Awaitable } from "../types-Di--A9X1.js";
2
2
  import { GitPluginOptions } from "./git.js";
3
3
 
4
4
  //#region src/plugins/github.d.ts
@@ -42,6 +42,12 @@ interface GitHubPluginOptions extends GitPluginOptions {
42
42
  repo?: string;
43
43
  /** Optional GitHub token for Git & GitHub operations. */
44
44
  token?: string;
45
+ /**
46
+ * Create GitHub release immediately after successful publish, without waiting for others.
47
+ *
48
+ * @default false
49
+ */
50
+ eagerRelease?: boolean;
45
51
  /** Override release details for a single package, return `false` to skip. */
46
52
  onCreateRelease?: (this: TegamiContext, result: PackagePublishResult) => Awaitable<GithubRelease | false>;
47
53
  /** Override release details when multiple packages share a git tag, return `false` to skip. */