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/checks-Smwtq_Li.js +279 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +207 -201
- package/dist/{error-BaOQJtvf.js → error-qpTCB2ld.js} +6 -1
- package/dist/generators/simple.d.ts +1 -1
- package/dist/generators/simple.js +1 -1
- package/dist/{graph-CUgwuRW5.js → graph-hBQUWKQA.js} +15 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +238 -294
- package/dist/plugins/git.d.ts +1 -1
- package/dist/plugins/git.js +4 -4
- package/dist/plugins/github.d.ts +7 -1
- package/dist/plugins/github.js +24 -23
- 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 +32 -16
- package/dist/{semver-mWK2Khi2.js → semver-cnZp9koa.js} +16 -5
- package/dist/{types-Diwnh8Tn.d.ts → types-Di--A9X1.d.ts} +40 -19
- package/package.json +1 -1
- package/dist/checks-Bz3Rf2OX.js +0 -98
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-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
|
|
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
|
-
|
|
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) => {
|
|
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,
|
|
302
|
-
|
|
303
|
-
const
|
|
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
|
|
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())
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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 =
|
|
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);
|
package/dist/plugins/git.d.ts
CHANGED
package/dist/plugins/git.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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
|
|
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
|
}
|
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-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. */
|