tegami 0.1.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/checks-Cwz-Ezkq.js +277 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +210 -211
- package/dist/error-DNy8R5ue.js +45 -0
- package/dist/generators/simple.d.ts +1 -1
- package/dist/generators/simple.js +1 -1
- package/dist/{graph-CUgwuRW5.js → graph-DLKPUXSD.js} +17 -12
- package/dist/index.d.ts +2 -2
- package/dist/index.js +233 -294
- package/dist/plugins/git.d.ts +1 -1
- package/dist/plugins/git.js +4 -5
- package/dist/plugins/github.d.ts +7 -1
- package/dist/plugins/github.js +24 -24
- package/dist/providers/cargo.d.ts +1 -1
- package/dist/providers/cargo.js +6 -6
- package/dist/providers/npm.d.ts +1 -1
- package/dist/providers/npm.js +34 -20
- package/dist/{semver-mWK2Khi2.js → semver-C4vJ4SK8.js} +16 -5
- package/dist/{types-Diwnh8Tn.d.ts → types-BXk2fMqa.d.ts} +52 -23
- package/package.json +1 -1
- package/dist/checks-Bz3Rf2OX.js +0 -98
- package/dist/constants-B9qjNfvr.js +0 -4
- package/dist/error-BaOQJtvf.js +0 -22
package/dist/index.js
CHANGED
|
@@ -1,167 +1,16 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
4
|
-
import { t as PackageGraph } from "./graph-
|
|
1
|
+
import { a as maxBump } from "./semver-C4vJ4SK8.js";
|
|
2
|
+
import { c as changelogFrontmatterSchema, i as readPlanStore, n as publishPlanStatus, o as generateChangelog, r as createPlanStore, t as assertPublishPlanFinished } from "./checks-Cwz-Ezkq.js";
|
|
3
|
+
import { r as handlePluginError } from "./error-DNy8R5ue.js";
|
|
4
|
+
import { t as PackageGraph } from "./graph-DLKPUXSD.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
|
|
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,35 @@ var DraftPlan = class {
|
|
|
263
247
|
return this.packages.get(id);
|
|
264
248
|
}
|
|
265
249
|
bumpPackage(pkg, { type, reason }) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
273
|
-
if (
|
|
274
|
-
plan
|
|
275
|
-
|
|
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) => pkg.configurePlan(plan, this.context.graph.getPackageGroup(pkg.id)), (plan) => {
|
|
276
|
+
(plan.bumpReasons ??= /* @__PURE__ */ new Set()).add("align with script-level configs");
|
|
277
|
+
});
|
|
278
|
+
}
|
|
283
279
|
hasPending() {
|
|
284
280
|
const { graph } = this.context;
|
|
285
281
|
for (const [id, plan] of this.packages) {
|
|
@@ -298,37 +294,36 @@ var DraftPlan = class {
|
|
|
298
294
|
this.changelogs.set(entry.id, entry);
|
|
299
295
|
const { graph } = this.context;
|
|
300
296
|
const groupPackages = /* @__PURE__ */ new Map();
|
|
301
|
-
for (const [name,
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
plan.changelogs ??= [];
|
|
305
|
-
plan.changelogs.push(entry);
|
|
297
|
+
for (const [name, config] of entry.packages) {
|
|
298
|
+
if (!config.type) continue;
|
|
299
|
+
for (const pkg of graph.getByName(name)) groupPackages.set(pkg, config.type);
|
|
306
300
|
}
|
|
301
|
+
for (const [pkg, bumpType] of groupPackages) attachChangelog(this.bumpPackage(pkg, { type: bumpType }), entry);
|
|
307
302
|
}
|
|
308
303
|
deleteChangelog(id) {
|
|
309
304
|
return this.changelogs.delete(id);
|
|
310
305
|
}
|
|
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
306
|
/** Apply the publish plan: update package versions, write the plan file, and consume changelog files. */
|
|
319
307
|
async applyPlan() {
|
|
320
308
|
if (this.#applied) throw new Error("This draft has already applied a publish plan.");
|
|
321
309
|
await assertPublishPlanFinished(this.context);
|
|
310
|
+
const { graph } = this.context;
|
|
322
311
|
this.#applied = true;
|
|
312
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
313
|
+
for (const pkg of graph.getPackages()) snapshots.set(pkg.id, { version: pkg.version });
|
|
323
314
|
for (const plugin of this.context.plugins) await handlePluginError(plugin, "applyPlan", () => plugin.applyPlan?.call(this.context, this));
|
|
324
|
-
const
|
|
315
|
+
const updatedChangelogs = this.applyReplays(snapshots);
|
|
325
316
|
const writes = [];
|
|
326
317
|
for (const [id, packagePlan] of this.packages) {
|
|
327
318
|
const pkg = graph.get(id);
|
|
328
319
|
if (!pkg) continue;
|
|
329
320
|
writes.push(this.appendChangelog(pkg, packagePlan));
|
|
330
321
|
}
|
|
331
|
-
for (const entry of this.changelogs.values())
|
|
322
|
+
for (const entry of this.changelogs.values()) {
|
|
323
|
+
const updated = updatedChangelogs.get(entry.id);
|
|
324
|
+
const filePath = path.join(this.context.changelogDir, entry.filename);
|
|
325
|
+
writes.push(updated ? writeFile(filePath, updated.getRawContent()) : rm(filePath, { force: true }));
|
|
326
|
+
}
|
|
332
327
|
await Promise.all(writes);
|
|
333
328
|
await mkdir(dirname(this.context.planPath), { recursive: true });
|
|
334
329
|
await writeFile(this.context.planPath, createPlanStore(this, this.context));
|
|
@@ -343,6 +338,46 @@ var DraftPlan = class {
|
|
|
343
338
|
canApply() {
|
|
344
339
|
return !this.#applied;
|
|
345
340
|
}
|
|
341
|
+
/** Attach replaying changelog entries to packages (already bumped), and return the updated changelog entries. */
|
|
342
|
+
applyReplays(snapshots) {
|
|
343
|
+
const updated = /* @__PURE__ */ new Map();
|
|
344
|
+
const { graph } = this.context;
|
|
345
|
+
const isMatch = (condition) => {
|
|
346
|
+
if (condition.type === "on-exit-prerelease") return graph.getByName(condition.name).some((pkg) => {
|
|
347
|
+
const previous = snapshots.get(pkg.id);
|
|
348
|
+
return previous && semver.inc(previous.version, "release") === pkg.version;
|
|
349
|
+
});
|
|
350
|
+
return graph.getByName(condition.name).some((pkg) => pkg.version === condition.version);
|
|
351
|
+
};
|
|
352
|
+
for (const entry of this.changelogs.values()) {
|
|
353
|
+
const updatedPackages = /* @__PURE__ */ new Map();
|
|
354
|
+
const matchedNames = /* @__PURE__ */ new Set();
|
|
355
|
+
for (const [name, config] of entry.packages) {
|
|
356
|
+
if (!config.replay || config.replay.length === 0) continue;
|
|
357
|
+
const replay = config.replay.filter((item) => {
|
|
358
|
+
const condition = parseReplayCondition(item);
|
|
359
|
+
if (condition && isMatch(condition)) {
|
|
360
|
+
matchedNames.add(name);
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
return true;
|
|
364
|
+
});
|
|
365
|
+
if (replay.length === 0) continue;
|
|
366
|
+
const updatedConfig = {
|
|
367
|
+
...config,
|
|
368
|
+
replay
|
|
369
|
+
};
|
|
370
|
+
delete updatedConfig.type;
|
|
371
|
+
updatedPackages.set(name, updatedConfig);
|
|
372
|
+
}
|
|
373
|
+
if (updatedPackages.size > 0) updated.set(entry.id, {
|
|
374
|
+
...entry,
|
|
375
|
+
packages: updatedPackages
|
|
376
|
+
});
|
|
377
|
+
for (const name of matchedNames) for (const pkg of graph.getByName(name)) attachChangelog(this.getOrInitPackage(pkg), entry);
|
|
378
|
+
}
|
|
379
|
+
return updated;
|
|
380
|
+
}
|
|
346
381
|
async appendChangelog(pkg, plan) {
|
|
347
382
|
if (!plan.changelogs || plan.changelogs.length === 0) return;
|
|
348
383
|
const { generator = simpleGenerator() } = this.context.options;
|
|
@@ -350,10 +385,9 @@ var DraftPlan = class {
|
|
|
350
385
|
packageId: pkg.id,
|
|
351
386
|
packageName: pkg.name,
|
|
352
387
|
version: pkg.version,
|
|
353
|
-
npm: plan.npm,
|
|
354
388
|
plan,
|
|
355
389
|
changelogs: plan.changelogs,
|
|
356
|
-
|
|
390
|
+
unstable_draft: this
|
|
357
391
|
});
|
|
358
392
|
const path = join(pkg.path, "CHANGELOG.md");
|
|
359
393
|
const existing = await readFile(path, "utf8").catch(() => "");
|
|
@@ -383,110 +417,14 @@ async function createDraftPlan(changelogs, context) {
|
|
|
383
417
|
const result = await handlePluginError(plugin, "initPlan", () => plugin.initPlan?.call(context, draft));
|
|
384
418
|
if (result) draft = result;
|
|
385
419
|
}
|
|
420
|
+
for (const pkg of context.graph.getPackages()) draft.getOrInitPackage(pkg);
|
|
386
421
|
for (const entry of changelogs) draft.addChangelog(entry);
|
|
387
422
|
return draft;
|
|
388
423
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
}
|
|
424
|
+
function attachChangelog(plan, entry) {
|
|
425
|
+
if (plan.changelogs?.some((item) => item.id === entry.id)) return;
|
|
426
|
+
plan.changelogs ??= [];
|
|
427
|
+
plan.changelogs.push(entry);
|
|
490
428
|
}
|
|
491
429
|
//#endregion
|
|
492
430
|
//#region src/publish.ts
|
|
@@ -529,18 +467,20 @@ async function publishFromPlan(context, store, options) {
|
|
|
529
467
|
const packages = [];
|
|
530
468
|
if ((await publishPlanStatus(store, context)).state !== "pending") return { state: "skipped" };
|
|
531
469
|
const orderedIds = await resolvePublishTargets(context, store);
|
|
470
|
+
const parsedChangelogs = /* @__PURE__ */ new Map();
|
|
471
|
+
for (const [id, entry] of Object.entries(store.changelogs)) {
|
|
472
|
+
const parsed = parseChangelogFile(entry.filename, entry.content);
|
|
473
|
+
if (!parsed) continue;
|
|
474
|
+
parsedChangelogs.set(id, parsed);
|
|
475
|
+
}
|
|
532
476
|
for (const id of orderedIds) {
|
|
533
477
|
const plan = store.packages[id];
|
|
534
478
|
const pkg = context.graph.get(id);
|
|
535
479
|
const registryClient = context.getRegistryClient(pkg);
|
|
536
480
|
const changelogs = [];
|
|
537
481
|
for (const id of plan.changelogIds ?? []) {
|
|
538
|
-
const entry =
|
|
539
|
-
if (entry) changelogs.push(
|
|
540
|
-
...entry,
|
|
541
|
-
packages: new Map(Object.entries(entry.packages)),
|
|
542
|
-
id
|
|
543
|
-
});
|
|
482
|
+
const entry = parsedChangelogs.get(id);
|
|
483
|
+
if (entry) changelogs.push(entry);
|
|
544
484
|
}
|
|
545
485
|
if (!dryRun) {
|
|
546
486
|
if (await registryClient.isPackagePublished(pkg)) {
|
|
@@ -643,7 +583,6 @@ function tegami(options = {}) {
|
|
|
643
583
|
},
|
|
644
584
|
async publish(publishOptions = {}) {
|
|
645
585
|
const context = await $context;
|
|
646
|
-
if ((await getChangelogFiles(context)).length > 0) return { state: "skipped" };
|
|
647
586
|
const parsed = await readPlanStore(context);
|
|
648
587
|
if (!parsed) return { state: "skipped" };
|
|
649
588
|
let result = await publishFromPlan(context, parsed, publishOptions);
|
package/dist/plugins/git.d.ts
CHANGED
package/dist/plugins/git.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as isCI } from "../constants-B9qjNfvr.js";
|
|
1
|
+
import { a as isCI, n as execFailure } from "../error-DNy8R5ue.js";
|
|
3
2
|
import { x } from "tinyexec";
|
|
4
3
|
//#region src/plugins/git.ts
|
|
5
4
|
/**
|
|
@@ -13,8 +12,7 @@ function git(options = {}) {
|
|
|
13
12
|
function resolveGitTag(context, result) {
|
|
14
13
|
const { graph } = context;
|
|
15
14
|
const pkg = graph.get(result.id);
|
|
16
|
-
|
|
17
|
-
const group = graph.getPackageGroup(pkg.id);
|
|
15
|
+
const group = pkg && graph.getPackageGroup(pkg.id);
|
|
18
16
|
if (group?.options.syncGitTag) return `${group.name}@${result.version}`;
|
|
19
17
|
return `${result.name}@${result.version}`;
|
|
20
18
|
}
|
|
@@ -39,9 +37,10 @@ function git(options = {}) {
|
|
|
39
37
|
} },
|
|
40
38
|
async afterPublishAll(result) {
|
|
41
39
|
const { cwd, publishOptions: { dryRun = false } } = this;
|
|
42
|
-
if (dryRun || !createTags || result.state
|
|
40
|
+
if (dryRun || !createTags || result.state === "skipped") return result;
|
|
43
41
|
const pendingTags = /* @__PURE__ */ new Set();
|
|
44
42
|
for (const pkg of result.packages) {
|
|
43
|
+
if (pkg.state !== "success") continue;
|
|
45
44
|
pkg.gitTag = resolveGitTag(this, pkg);
|
|
46
45
|
pendingTags.add(pkg.gitTag);
|
|
47
46
|
}
|
package/dist/plugins/github.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as PackagePublishResult, T as TegamiContext, c as TegamiPlugin, k as DraftPlan, t as Awaitable } from "../types-
|
|
1
|
+
import { S as PackagePublishResult, T as TegamiContext, c as TegamiPlugin, k as DraftPlan, t as Awaitable } from "../types-BXk2fMqa.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. */
|