@riebeckite/plugin-changelog 0.0.22 → 0.0.24
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/README.md +12 -16
- package/README_ja.md +2 -25
- package/dist/components/changelog.d.ts +7 -0
- package/dist/components/changelog.js +20 -0
- package/dist/index.d.ts +10 -10
- package/dist/index.js +91 -141
- package/dist/src/changelog.d.ts +1 -1
- package/dist/src/component-data.d.ts +9 -0
- package/dist/src/git/history_reader.d.ts +1 -0
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -15,13 +15,12 @@ commits. No client-side JavaScript is required.
|
|
|
15
15
|
`git` access as [`@riebeckite/plugin-diff`](../diff/README.md) (`execFile` with
|
|
16
16
|
graceful failure). It never invents its own filesystem layer.
|
|
17
17
|
|
|
18
|
-
Results are published through the
|
|
19
|
-
entry receives its own history
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
author, not by this plugin. No client script is registered.
|
|
18
|
+
Results are published through the plugin's built-in HTML renderers. Each public
|
|
19
|
+
entry receives its own change history section, and the site's layout decides
|
|
20
|
+
whether and where to render it. When `siteWide` is enabled, the site-wide
|
|
21
|
+
changelog dataset is built and available for the app to render itself. The
|
|
22
|
+
plugin contributes no markup to the manifest entry; the Site renders the
|
|
23
|
+
changelog sections it wants. No client script is registered.
|
|
25
24
|
|
|
26
25
|
## Usage
|
|
27
26
|
|
|
@@ -63,17 +62,18 @@ export default defineConfig({
|
|
|
63
62
|
|
|
64
63
|
## Display
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
Each note's change history section is published as build-time HTML. When
|
|
66
|
+
`perNote` is enabled, it appears after the article on eligible pages. The
|
|
67
|
+
site-wide changelog dataset is built and available for the app to render
|
|
68
|
+
itself via the exported renderers.
|
|
67
69
|
|
|
68
70
|
`data-changelog-note` and `data-changelog-site` mark the two fragments for
|
|
69
71
|
styling and idempotency checks.
|
|
70
72
|
|
|
71
73
|
## App wiring (site-wide changelog)
|
|
72
74
|
|
|
73
|
-
The site-wide list is data, not a page. The app owns the route
|
|
74
|
-
|
|
75
|
-
(`siteWide: true` + `siteWideSlug`, which requires a public note at that slug)
|
|
76
|
-
or exposes the dataset for the app to render itself:
|
|
75
|
+
The site-wide list is data, not a page. The app owns the route. The plugin
|
|
76
|
+
exposes the dataset for the app to render itself via the exported renderers:
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
79
|
import {
|
|
@@ -96,10 +96,6 @@ const dataset = buildSiteChangelog({
|
|
|
96
96
|
const html = renderSiteChangelog(dataset, options);
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
The reference app renders manifest `bodySlots` in
|
|
100
|
-
`apps/web/app/components/article/article.tsx`; a dedicated route can render the
|
|
101
|
-
exported HTML directly.
|
|
102
|
-
|
|
103
99
|
## Failure behavior
|
|
104
100
|
|
|
105
101
|
When `git` cannot be started, the plugin reports a `changelog-git-unavailable`
|
package/README_ja.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
`changelog()` は [`@riebeckite/plugin-diff`](../diff/README_ja.md) と同じ方法でローカル Git を読み取ります(`execFile` で `git` を実行し、失敗時は安全に縮退)。独自のファイルシステム層は作りません。
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
結果はプラグインの組み込み HTML レンダラーを通じて公開されます。公開エントリはそれぞれ変更履歴セクションを受け取り、サイト側のレイアウトが描画するかどうかを決めます。`siteWide` を有効にすると、サイト全体の変更履歴データセットがビルドされ、アプリが自身で描画できます。プラグインは manifest entry に markup を注入しません。描画する変更履歴セクションは Site が選びます。クライアントスクリプトは登録しません。
|
|
14
14
|
|
|
15
15
|
## 使い方
|
|
16
16
|
|
|
@@ -52,7 +52,7 @@ export default defineConfig({
|
|
|
52
52
|
|
|
53
53
|
## 表示
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
各ノートの変更履歴セクションはビルド時の HTML として公開されます。`perNote` が有効な場合、記事の後に表示されます。サイト全体の変更履歴データセットはビルドされ、アプリが renderer を通じて描画できます。
|
|
56
56
|
|
|
57
57
|
`data-changelog-note` と `data-changelog-site` は、2 種類の断片をスタイリングと重複防止のために識別する属性です。
|
|
58
58
|
|
|
@@ -60,29 +60,6 @@ export default defineConfig({
|
|
|
60
60
|
|
|
61
61
|
サイト全体の一覧はページではなくデータです。ルートはアプリが所有します。プラグインは既存ノートの `article.after-content` スロットに書き込む(`siteWide: true` と `siteWideSlug`。その slug の公開ノートが必要)か、データセットを公開してアプリ自身に描画させます。
|
|
62
62
|
|
|
63
|
-
```ts
|
|
64
|
-
import {
|
|
65
|
-
buildSiteChangelog,
|
|
66
|
-
GitChangelogReader,
|
|
67
|
-
renderSiteChangelog,
|
|
68
|
-
resolveChangelogOptions,
|
|
69
|
-
} from "@riebeckite/plugin-changelog";
|
|
70
|
-
|
|
71
|
-
const options = resolveChangelogOptions({ lookbackDays: 90 });
|
|
72
|
-
const manifest = await content.getManifest();
|
|
73
|
-
const reader = new GitChangelogReader({ cwd: "./content" });
|
|
74
|
-
const commits = await reader.getRecentCommits();
|
|
75
|
-
const dataset = buildSiteChangelog({
|
|
76
|
-
entries: manifest.publicEntries,
|
|
77
|
-
commits,
|
|
78
|
-
contentIndex: manifest.contentIndex,
|
|
79
|
-
options,
|
|
80
|
-
});
|
|
81
|
-
const html = renderSiteChangelog(dataset, options);
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
参照アプリはマニフェストの `bodySlots` を `apps/web/app/components/article/article.tsx` で描画します。専用ルートでは公開された HTML をそのまま描画できます。
|
|
85
|
-
|
|
86
63
|
## 失敗時の挙動
|
|
87
64
|
|
|
88
65
|
`git` を起動できない場合は `changelog-git-unavailable` 警告診断、コンテンツディレクトリが Git ワーキングツリーの外にある場合は `changelog-content-outside-repository` 警告診断を出し、どちらの場合もロガー警告を伴い、ビルド成果物には何も加えません。履歴が取得できないことを理由にビルドが失敗することはなく、空のリポジトリでも同じです。
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NoteChangeHistory } from "../src/types.js";
|
|
2
|
+
export type ChangelogProps = {
|
|
3
|
+
history: NoteChangeHistory | null;
|
|
4
|
+
className?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const Changelog: ({ history, className, }: Readonly<ChangelogProps>) => import("hono/jsx/jsx-dev-runtime").JSX.Element;
|
|
7
|
+
export default Changelog;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// components/changelog.tsx
|
|
2
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
3
|
+
var Changelog = (({
|
|
4
|
+
history,
|
|
5
|
+
className = "rb-changelog"
|
|
6
|
+
}) => {
|
|
7
|
+
if (!history || history.commits.length === 0) return null;
|
|
8
|
+
return /* @__PURE__ */ jsxs("section", { class: className, "data-changelog-note": true, children: [
|
|
9
|
+
/* @__PURE__ */ jsx("h2", { children: "Change history" }),
|
|
10
|
+
/* @__PURE__ */ jsx("ol", { children: history.commits.map((record) => /* @__PURE__ */ jsxs("li", { children: [
|
|
11
|
+
/* @__PURE__ */ jsx("time", { datetime: record.dateIso, children: record.date }),
|
|
12
|
+
" ",
|
|
13
|
+
record.subject
|
|
14
|
+
] }, record.hash)) })
|
|
15
|
+
] });
|
|
16
|
+
});
|
|
17
|
+
var changelog_default = Changelog;
|
|
18
|
+
export {
|
|
19
|
+
changelog_default as default
|
|
20
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import type { ChangelogOptions } from "./src/types.js";
|
|
2
|
+
export type { ChangelogProps } from "./components/changelog.js";
|
|
3
|
+
export { default as Changelog } from "./components/changelog.js";
|
|
2
4
|
export { buildNoteChangeHistory, buildSiteChangelog, filterChangelogCommits, formatChangelogDate, resolveContentFilePath, resolveLookbackSince, } from "./src/changelog.js";
|
|
5
|
+
export type { ChangelogHistoryLoader } from "./src/component-data.js";
|
|
6
|
+
export { getChangelog } from "./src/component-data.js";
|
|
3
7
|
export { GitChangelogReader, type GitLogWindow, } from "./src/git/history_reader.js";
|
|
4
8
|
export { DEFAULT_CHANGELOG_CLASS_NAME, DEFAULT_CHANGELOG_HEADING, DEFAULT_CHANGELOG_LOCALE, DEFAULT_CHANGELOG_MAX_PER_NOTE, DEFAULT_CHANGELOG_MAX_SITE_WIDE, DEFAULT_CHANGELOG_PER_NOTE_HEADING, DEFAULT_CHANGELOG_SITE_WIDE_HEADING, DEFAULT_CHANGELOG_SITE_WIDE_SLUG, resolveChangelogOptions, } from "./src/options.js";
|
|
5
9
|
export { CHANGELOG_NOTE_ATTRIBUTE, CHANGELOG_SITE_ATTRIBUTE, renderNoteChangeHistory, renderSiteChangelog, } from "./src/render.js";
|
|
6
10
|
export type { ChangelogCommit, ChangelogDateFormat, ChangelogOptions, ChangelogRecord, GitChangelogReaderOptions, NoteChangeHistory, ResolvedChangelogOptions, SiteChangelog, SiteChangelogEntry, SiteChangelogNote, } from "./src/types.js";
|
|
7
11
|
export declare const CHANGELOG_PLUGIN_NAME = "changelog";
|
|
8
|
-
/** Site layout slot the generated history is written to. */
|
|
9
|
-
export declare const CHANGELOG_BODY_SLOT = "article.after-content";
|
|
10
12
|
/**
|
|
11
13
|
* Git-backed change history for Riebeckite sites.
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
* fragments through the manifest `bodySlots` mechanism:
|
|
15
|
+
* The plugin exposes data helpers and components the Site composes directly:
|
|
15
16
|
*
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
17
|
+
* - `getChangelog` / `Changelog` render a note's own "change history" section;
|
|
18
|
+
* - `buildSiteChangelog` / `renderSiteChangelog` render the site-wide changelog
|
|
19
|
+
* for a designated note.
|
|
18
20
|
*
|
|
19
|
-
* The plugin never creates a route. The Site owns the note and the layout
|
|
20
|
-
*
|
|
21
|
-
* exported (`buildSiteChangelog`, `renderSiteChangelog`) so a Site can render
|
|
22
|
-
* it from its own route. No client JavaScript is required.
|
|
21
|
+
* The plugin never creates a route. The Site owns the note and the layout, and
|
|
22
|
+
* decides where the sections appear. No client JavaScript is required.
|
|
23
23
|
*
|
|
24
24
|
* When Git is unavailable the build is left untouched and a warning diagnostic
|
|
25
25
|
* is reported instead of failing.
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
// index.ts
|
|
2
2
|
import {
|
|
3
|
-
appendContentBodySlot,
|
|
4
3
|
createStyleAsset,
|
|
5
4
|
definePlugin
|
|
6
5
|
} from "@riebeckite/core";
|
|
7
6
|
|
|
7
|
+
// components/changelog.tsx
|
|
8
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
9
|
+
var Changelog = (({
|
|
10
|
+
history,
|
|
11
|
+
className = "rb-changelog"
|
|
12
|
+
}) => {
|
|
13
|
+
if (!history || history.commits.length === 0) return null;
|
|
14
|
+
return /* @__PURE__ */ jsxs("section", { class: className, "data-changelog-note": true, children: [
|
|
15
|
+
/* @__PURE__ */ jsx("h2", { children: "Change history" }),
|
|
16
|
+
/* @__PURE__ */ jsx("ol", { children: history.commits.map((record) => /* @__PURE__ */ jsxs("li", { children: [
|
|
17
|
+
/* @__PURE__ */ jsx("time", { datetime: record.dateIso, children: record.date }),
|
|
18
|
+
" ",
|
|
19
|
+
record.subject
|
|
20
|
+
] }, record.hash)) })
|
|
21
|
+
] });
|
|
22
|
+
});
|
|
23
|
+
var changelog_default = Changelog;
|
|
24
|
+
|
|
8
25
|
// src/changelog.ts
|
|
9
26
|
function resolveLookbackSince(lookbackDays, now = /* @__PURE__ */ new Date()) {
|
|
10
27
|
if (lookbackDays === void 0) return void 0;
|
|
@@ -93,6 +110,54 @@ function normalizePath(filePath) {
|
|
|
93
110
|
return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
94
111
|
}
|
|
95
112
|
|
|
113
|
+
// src/options.ts
|
|
114
|
+
var DEFAULT_CHANGELOG_DATE_FORMAT = "iso";
|
|
115
|
+
var DEFAULT_CHANGELOG_LOCALE = "en";
|
|
116
|
+
var DEFAULT_CHANGELOG_PER_NOTE = true;
|
|
117
|
+
var DEFAULT_CHANGELOG_SITE_WIDE = false;
|
|
118
|
+
var DEFAULT_CHANGELOG_SITE_WIDE_SLUG = "changelog";
|
|
119
|
+
var DEFAULT_CHANGELOG_MAX_PER_NOTE = 10;
|
|
120
|
+
var DEFAULT_CHANGELOG_MAX_SITE_WIDE = 50;
|
|
121
|
+
var DEFAULT_CHANGELOG_SHOW_AUTHOR = true;
|
|
122
|
+
var DEFAULT_CHANGELOG_HEADING = true;
|
|
123
|
+
var DEFAULT_CHANGELOG_PER_NOTE_HEADING = "Change history";
|
|
124
|
+
var DEFAULT_CHANGELOG_SITE_WIDE_HEADING = "Changelog";
|
|
125
|
+
var DEFAULT_CHANGELOG_CLASS_NAME = "rr-changelog";
|
|
126
|
+
function resolveChangelogOptions(options = {}) {
|
|
127
|
+
return {
|
|
128
|
+
cwd: optionalText(options.cwd),
|
|
129
|
+
lookbackDays: options.lookbackDays,
|
|
130
|
+
dateFormat: options.dateFormat ?? DEFAULT_CHANGELOG_DATE_FORMAT,
|
|
131
|
+
locale: optionalText(options.locale) ?? DEFAULT_CHANGELOG_LOCALE,
|
|
132
|
+
perNote: options.perNote ?? DEFAULT_CHANGELOG_PER_NOTE,
|
|
133
|
+
siteWide: options.siteWide ?? DEFAULT_CHANGELOG_SITE_WIDE,
|
|
134
|
+
siteWideSlug: optionalText(options.siteWideSlug) ?? DEFAULT_CHANGELOG_SITE_WIDE_SLUG,
|
|
135
|
+
maxPerNote: options.maxPerNote ?? DEFAULT_CHANGELOG_MAX_PER_NOTE,
|
|
136
|
+
maxSiteWide: options.maxSiteWide ?? DEFAULT_CHANGELOG_MAX_SITE_WIDE,
|
|
137
|
+
showAuthor: options.showAuthor ?? DEFAULT_CHANGELOG_SHOW_AUTHOR,
|
|
138
|
+
heading: options.heading ?? DEFAULT_CHANGELOG_HEADING,
|
|
139
|
+
perNoteHeading: optionalText(options.perNoteHeading) ?? DEFAULT_CHANGELOG_PER_NOTE_HEADING,
|
|
140
|
+
siteWideHeading: optionalText(options.siteWideHeading) ?? DEFAULT_CHANGELOG_SITE_WIDE_HEADING,
|
|
141
|
+
className: optionalText(options.className) ?? DEFAULT_CHANGELOG_CLASS_NAME
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function optionalText(value) {
|
|
145
|
+
if (typeof value !== "string") return void 0;
|
|
146
|
+
const trimmed = value.trim();
|
|
147
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/component-data.ts
|
|
151
|
+
var getChangelog = async ({ view, entry, loadCommits, options }) => {
|
|
152
|
+
if (!view.findRoutableBySlug(entry.slug)) return null;
|
|
153
|
+
const commits = await loadCommits(entry);
|
|
154
|
+
return buildNoteChangeHistory(
|
|
155
|
+
entry,
|
|
156
|
+
commits,
|
|
157
|
+
resolveChangelogOptions(options)
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
96
161
|
// src/git/history_reader.ts
|
|
97
162
|
import path2 from "node:path";
|
|
98
163
|
|
|
@@ -189,9 +254,6 @@ function normalizeContentPath(value) {
|
|
|
189
254
|
function isResolvedRepository(resolution) {
|
|
190
255
|
return resolution.kind === "resolved";
|
|
191
256
|
}
|
|
192
|
-
function isRepositoryUnavailable(resolution) {
|
|
193
|
-
return resolution.kind !== "resolved";
|
|
194
|
-
}
|
|
195
257
|
function parseRevParseOutput(stdout) {
|
|
196
258
|
const [toplevel = "", prefix = ""] = stdout.split(/\r?\n/);
|
|
197
259
|
return [toplevel.trim(), prefix.trim()];
|
|
@@ -224,6 +286,23 @@ var GitChangelogReader = class {
|
|
|
224
286
|
async isAvailable() {
|
|
225
287
|
return isResolvedRepository(await this.resolveRepository());
|
|
226
288
|
}
|
|
289
|
+
async getStateFingerprint() {
|
|
290
|
+
const repository = await this.#resolvePaths();
|
|
291
|
+
if (!repository) return null;
|
|
292
|
+
const results = await Promise.all([
|
|
293
|
+
runGit(["rev-parse", "HEAD"], repository.repositoryRoot),
|
|
294
|
+
runGit(
|
|
295
|
+
["status", "--porcelain=v1", "--untracked-files=all"],
|
|
296
|
+
repository.repositoryRoot
|
|
297
|
+
),
|
|
298
|
+
runGit(
|
|
299
|
+
["for-each-ref", "--format=%(refname):%(objectname)"],
|
|
300
|
+
repository.repositoryRoot
|
|
301
|
+
)
|
|
302
|
+
]);
|
|
303
|
+
if (results.some((result) => !result.ok)) return null;
|
|
304
|
+
return results.filter((result) => result.ok).map((result) => result.stdout).join("");
|
|
305
|
+
}
|
|
227
306
|
/**
|
|
228
307
|
* Commit history for one file, newest first, following renames.
|
|
229
308
|
*
|
|
@@ -300,43 +379,6 @@ function parseCommitRecord(chunk) {
|
|
|
300
379
|
};
|
|
301
380
|
}
|
|
302
381
|
|
|
303
|
-
// src/options.ts
|
|
304
|
-
var DEFAULT_CHANGELOG_DATE_FORMAT = "iso";
|
|
305
|
-
var DEFAULT_CHANGELOG_LOCALE = "en";
|
|
306
|
-
var DEFAULT_CHANGELOG_PER_NOTE = true;
|
|
307
|
-
var DEFAULT_CHANGELOG_SITE_WIDE = false;
|
|
308
|
-
var DEFAULT_CHANGELOG_SITE_WIDE_SLUG = "changelog";
|
|
309
|
-
var DEFAULT_CHANGELOG_MAX_PER_NOTE = 10;
|
|
310
|
-
var DEFAULT_CHANGELOG_MAX_SITE_WIDE = 50;
|
|
311
|
-
var DEFAULT_CHANGELOG_SHOW_AUTHOR = true;
|
|
312
|
-
var DEFAULT_CHANGELOG_HEADING = true;
|
|
313
|
-
var DEFAULT_CHANGELOG_PER_NOTE_HEADING = "Change history";
|
|
314
|
-
var DEFAULT_CHANGELOG_SITE_WIDE_HEADING = "Changelog";
|
|
315
|
-
var DEFAULT_CHANGELOG_CLASS_NAME = "rr-changelog";
|
|
316
|
-
function resolveChangelogOptions(options = {}) {
|
|
317
|
-
return {
|
|
318
|
-
cwd: optionalText(options.cwd),
|
|
319
|
-
lookbackDays: options.lookbackDays,
|
|
320
|
-
dateFormat: options.dateFormat ?? DEFAULT_CHANGELOG_DATE_FORMAT,
|
|
321
|
-
locale: optionalText(options.locale) ?? DEFAULT_CHANGELOG_LOCALE,
|
|
322
|
-
perNote: options.perNote ?? DEFAULT_CHANGELOG_PER_NOTE,
|
|
323
|
-
siteWide: options.siteWide ?? DEFAULT_CHANGELOG_SITE_WIDE,
|
|
324
|
-
siteWideSlug: optionalText(options.siteWideSlug) ?? DEFAULT_CHANGELOG_SITE_WIDE_SLUG,
|
|
325
|
-
maxPerNote: options.maxPerNote ?? DEFAULT_CHANGELOG_MAX_PER_NOTE,
|
|
326
|
-
maxSiteWide: options.maxSiteWide ?? DEFAULT_CHANGELOG_MAX_SITE_WIDE,
|
|
327
|
-
showAuthor: options.showAuthor ?? DEFAULT_CHANGELOG_SHOW_AUTHOR,
|
|
328
|
-
heading: options.heading ?? DEFAULT_CHANGELOG_HEADING,
|
|
329
|
-
perNoteHeading: optionalText(options.perNoteHeading) ?? DEFAULT_CHANGELOG_PER_NOTE_HEADING,
|
|
330
|
-
siteWideHeading: optionalText(options.siteWideHeading) ?? DEFAULT_CHANGELOG_SITE_WIDE_HEADING,
|
|
331
|
-
className: optionalText(options.className) ?? DEFAULT_CHANGELOG_CLASS_NAME
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
function optionalText(value) {
|
|
335
|
-
if (typeof value !== "string") return void 0;
|
|
336
|
-
const trimmed = value.trim();
|
|
337
|
-
return trimmed === "" ? void 0 : trimmed;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
382
|
// src/render.ts
|
|
341
383
|
import { escapeHtml, escapeHtmlAttribute } from "@riebeckite/core";
|
|
342
384
|
var CHANGELOG_NOTE_ATTRIBUTE = "data-changelog-note";
|
|
@@ -384,113 +426,20 @@ function renderNotes(notes, className) {
|
|
|
384
426
|
|
|
385
427
|
// index.ts
|
|
386
428
|
var CHANGELOG_PLUGIN_NAME = "changelog";
|
|
387
|
-
var CHANGELOG_BODY_SLOT = "article.after-content";
|
|
388
|
-
var PER_NOTE_CONCURRENCY = 4;
|
|
389
429
|
function changelog(options = {}) {
|
|
390
|
-
const resolved = resolveChangelogOptions(options);
|
|
391
430
|
return definePlugin({
|
|
392
431
|
name: CHANGELOG_PLUGIN_NAME,
|
|
393
|
-
outputDependencies: [{ type: "unknown" }],
|
|
394
432
|
options,
|
|
395
433
|
validateOptions: validateChangelogOptions,
|
|
396
|
-
|
|
397
|
-
|
|
434
|
+
assets: [
|
|
435
|
+
createStyleAsset(
|
|
436
|
+
CHANGELOG_PLUGIN_NAME,
|
|
437
|
+
"@riebeckite/plugin-changelog/style.css"
|
|
438
|
+
)
|
|
439
|
+
]
|
|
398
440
|
});
|
|
399
441
|
}
|
|
400
442
|
var changelogPlugin = changelog;
|
|
401
|
-
async function applyChangelog(context, options) {
|
|
402
|
-
if (!options.perNote && !options.siteWide) return;
|
|
403
|
-
const reader = new GitChangelogReader({
|
|
404
|
-
cwd: resolveContentRoot(context, options)
|
|
405
|
-
});
|
|
406
|
-
const resolution = await reader.resolveRepository();
|
|
407
|
-
if (isRepositoryUnavailable(resolution)) {
|
|
408
|
-
reportRepositoryUnavailable(context, resolution);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
const since = resolveLookbackSince(options.lookbackDays);
|
|
412
|
-
if (options.perNote) {
|
|
413
|
-
await forEachWithConcurrency(
|
|
414
|
-
context.manifest.publicEntries,
|
|
415
|
-
PER_NOTE_CONCURRENCY,
|
|
416
|
-
async (entry) => {
|
|
417
|
-
if (options.siteWide && entry.slug === options.siteWideSlug) return;
|
|
418
|
-
await applyNoteHistory(context, options, reader, entry, since);
|
|
419
|
-
}
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
if (options.siteWide) {
|
|
423
|
-
await applySiteChangelog(context, options, reader, since);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
async function applyNoteHistory(context, options, reader, entry, since) {
|
|
427
|
-
if (hasSlot(entry, CHANGELOG_NOTE_ATTRIBUTE)) return;
|
|
428
|
-
const filePath = resolveEntryFilePath(entry, context.manifest.contentIndex);
|
|
429
|
-
const commits = await reader.getFileHistory(filePath, { since });
|
|
430
|
-
const history = buildNoteChangeHistory(entry, commits, options);
|
|
431
|
-
const html = renderNoteChangeHistory(history, options);
|
|
432
|
-
if (html) appendContentBodySlot(entry, CHANGELOG_BODY_SLOT, html);
|
|
433
|
-
}
|
|
434
|
-
async function applySiteChangelog(context, options, reader, since) {
|
|
435
|
-
const target = context.manifest.publicEntries.find(
|
|
436
|
-
(entry) => entry.slug === options.siteWideSlug
|
|
437
|
-
);
|
|
438
|
-
if (!target) {
|
|
439
|
-
context.diagnostics.push({
|
|
440
|
-
code: "changelog-target-missing",
|
|
441
|
-
severity: "warning",
|
|
442
|
-
pluginName: CHANGELOG_PLUGIN_NAME,
|
|
443
|
-
message: `changelog: the site-wide target note "${options.siteWideSlug}" was not found; the site-wide changelog was skipped.`,
|
|
444
|
-
suggestion: `Create a public note with slug "${options.siteWideSlug}" or set siteWideSlug to an existing note.`
|
|
445
|
-
});
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
if (hasSlot(target, CHANGELOG_SITE_ATTRIBUTE)) return;
|
|
449
|
-
const commits = await reader.getRecentCommits({ since });
|
|
450
|
-
const changelog2 = buildSiteChangelog({
|
|
451
|
-
entries: context.manifest.publicEntries,
|
|
452
|
-
commits,
|
|
453
|
-
contentIndex: context.manifest.contentIndex,
|
|
454
|
-
options
|
|
455
|
-
});
|
|
456
|
-
const html = renderSiteChangelog(changelog2, options);
|
|
457
|
-
if (html) appendContentBodySlot(target, CHANGELOG_BODY_SLOT, html);
|
|
458
|
-
}
|
|
459
|
-
function resolveEntryFilePath(entry, contentIndex) {
|
|
460
|
-
return resolveContentFilePath(contentIndex, entry.slug) ?? `${entry.slug}.md`;
|
|
461
|
-
}
|
|
462
|
-
function hasSlot(entry, attribute) {
|
|
463
|
-
return entry.bodySlots?.[CHANGELOG_BODY_SLOT]?.includes(attribute) ?? false;
|
|
464
|
-
}
|
|
465
|
-
function resolveContentRoot(context, options) {
|
|
466
|
-
return options.cwd ?? context.config?.content.directory ?? process.cwd();
|
|
467
|
-
}
|
|
468
|
-
function reportRepositoryUnavailable(context, resolution) {
|
|
469
|
-
const gitUnavailable = resolution.kind === "git-unavailable";
|
|
470
|
-
const message = gitUnavailable ? `changelog: Git could not be started; change history was skipped. (${resolution.detail})` : `changelog: the content directory is not inside a Git working tree; change history was skipped. (${resolution.detail})`;
|
|
471
|
-
context.diagnostics.push({
|
|
472
|
-
code: gitUnavailable ? "changelog-git-unavailable" : "changelog-content-outside-repository",
|
|
473
|
-
severity: "warning",
|
|
474
|
-
pluginName: CHANGELOG_PLUGIN_NAME,
|
|
475
|
-
message,
|
|
476
|
-
suggestion: gitUnavailable ? "Install Git and make sure it is available on PATH." : "Move the content directory inside a Git working tree, or set the plugin's cwd option to a path inside one."
|
|
477
|
-
});
|
|
478
|
-
context.logger.warn(message);
|
|
479
|
-
}
|
|
480
|
-
async function forEachWithConcurrency(items, limit, work) {
|
|
481
|
-
let index = 0;
|
|
482
|
-
const workers = Array.from(
|
|
483
|
-
{ length: Math.min(limit, items.length) },
|
|
484
|
-
async () => {
|
|
485
|
-
while (index < items.length) {
|
|
486
|
-
const current = items[index];
|
|
487
|
-
index += 1;
|
|
488
|
-
await work(current);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
);
|
|
492
|
-
await Promise.all(workers);
|
|
493
|
-
}
|
|
494
443
|
function validateChangelogOptions(options) {
|
|
495
444
|
if (!options) return [];
|
|
496
445
|
const issues = [];
|
|
@@ -539,10 +488,10 @@ function validateChangelogOptions(options) {
|
|
|
539
488
|
return issues;
|
|
540
489
|
}
|
|
541
490
|
export {
|
|
542
|
-
CHANGELOG_BODY_SLOT,
|
|
543
491
|
CHANGELOG_NOTE_ATTRIBUTE,
|
|
544
492
|
CHANGELOG_PLUGIN_NAME,
|
|
545
493
|
CHANGELOG_SITE_ATTRIBUTE,
|
|
494
|
+
changelog_default as Changelog,
|
|
546
495
|
DEFAULT_CHANGELOG_CLASS_NAME,
|
|
547
496
|
DEFAULT_CHANGELOG_HEADING,
|
|
548
497
|
DEFAULT_CHANGELOG_LOCALE,
|
|
@@ -558,6 +507,7 @@ export {
|
|
|
558
507
|
changelogPlugin,
|
|
559
508
|
filterChangelogCommits,
|
|
560
509
|
formatChangelogDate,
|
|
510
|
+
getChangelog,
|
|
561
511
|
renderNoteChangeHistory,
|
|
562
512
|
renderSiteChangelog,
|
|
563
513
|
resolveChangelogOptions,
|
package/dist/src/changelog.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare function formatChangelogDate(isoDate: string, options: ResolvedCh
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function filterChangelogCommits(commits: readonly ChangelogCommit[], options: ResolvedChangelogOptions): ChangelogCommit[];
|
|
16
16
|
/** Builds the per-note history for one entry. */
|
|
17
|
-
export declare function buildNoteChangeHistory(entry: ContentManifestEntry, commits: readonly ChangelogCommit[], options: ResolvedChangelogOptions): NoteChangeHistory;
|
|
17
|
+
export declare function buildNoteChangeHistory(entry: Pick<ContentManifestEntry, "slug" | "permalink" | "title">, commits: readonly ChangelogCommit[], options: ResolvedChangelogOptions): NoteChangeHistory;
|
|
18
18
|
/**
|
|
19
19
|
* Builds the site-wide changelog dataset. Each commit is linked to the notes
|
|
20
20
|
* it touched by matching changed paths against the manifest content index.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PluginDataHelper, PublicContentEntry, PublicContentView } from "@riebeckite/core";
|
|
2
|
+
import type { ChangelogCommit, ChangelogOptions, NoteChangeHistory } from "./types.js";
|
|
3
|
+
export type ChangelogHistoryLoader = (entry: PublicContentEntry) => Promise<readonly ChangelogCommit[]>;
|
|
4
|
+
export declare const getChangelog: PluginDataHelper<{
|
|
5
|
+
view: PublicContentView;
|
|
6
|
+
entry: PublicContentEntry;
|
|
7
|
+
loadCommits: ChangelogHistoryLoader;
|
|
8
|
+
options?: ChangelogOptions;
|
|
9
|
+
}, Promise<NoteChangeHistory | null>>;
|
|
@@ -25,6 +25,7 @@ export declare class GitChangelogReader {
|
|
|
25
25
|
resolveRepository(): Promise<RepositoryPathsResolution>;
|
|
26
26
|
/** True when the content directory lives inside a Git work tree. */
|
|
27
27
|
isAvailable(): Promise<boolean>;
|
|
28
|
+
getStateFingerprint(): Promise<string | null>;
|
|
28
29
|
/**
|
|
29
30
|
* Commit history for one file, newest first, following renames.
|
|
30
31
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-changelog",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,10 +12,17 @@
|
|
|
12
12
|
"source": "./index.ts",
|
|
13
13
|
"default": "./dist/index.js"
|
|
14
14
|
},
|
|
15
|
+
"./components": {
|
|
16
|
+
"types": "./dist/components/changelog.d.ts",
|
|
17
|
+
"import": "./dist/components/changelog.js",
|
|
18
|
+
"source": "./components/changelog.tsx",
|
|
19
|
+
"default": "./dist/components/changelog.js"
|
|
20
|
+
},
|
|
15
21
|
"./style.css": "./style.css"
|
|
16
22
|
},
|
|
17
23
|
"dependencies": {
|
|
18
|
-
"
|
|
24
|
+
"hono": "^4.12.25",
|
|
25
|
+
"@riebeckite/core": "0.0.24"
|
|
19
26
|
},
|
|
20
27
|
"devDependencies": {
|
|
21
28
|
"@types/node": "^24.5.2",
|