@riebeckite/plugin-related-posts 0.0.15 → 0.0.17
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 +22 -6
- package/README_ja.md +22 -6
- package/dist/components/related-posts.d.ts +7 -0
- package/dist/components/related-posts.js +21 -0
- package/dist/index.d.ts +1 -7
- package/dist/index.js +30 -12
- package/dist/src/render.d.ts +0 -4
- package/package.json +10 -3
- package/style.css +1 -6
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @riebeckite/plugin-related-posts
|
|
2
2
|
|
|
3
3
|
Build-time "related notes" navigation. For every published entry, the plugin
|
|
4
|
-
ranks the other entries in the content manifest and
|
|
5
|
-
section to the
|
|
4
|
+
ranks the other entries in the content manifest and contributes a related-posts
|
|
5
|
+
section to the `article.footer` Slot. No client-side JavaScript is required.
|
|
6
6
|
|
|
7
7
|
[日本語](./README_ja.md)
|
|
8
8
|
|
|
@@ -21,9 +21,9 @@ Candidates are sorted by score (descending), then by title, then by slug, and
|
|
|
21
21
|
clamped to `limit`. Entries that score below `minScore` are dropped. When no
|
|
22
22
|
candidate qualifies, the entry's HTML is left untouched.
|
|
23
23
|
|
|
24
|
-
The plugin
|
|
25
|
-
|
|
26
|
-
pages and in feeds.
|
|
24
|
+
The plugin contributes the section to each manifest entry's `article.footer`
|
|
25
|
+
Slot. The Site decides where to render that Slot, so the section appears on
|
|
26
|
+
generated pages and in feeds when the standard article footer is used.
|
|
27
27
|
|
|
28
28
|
## Usage
|
|
29
29
|
|
|
@@ -37,6 +37,22 @@ export default defineConfig({
|
|
|
37
37
|
});
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
## Component
|
|
41
|
+
|
|
42
|
+
In addition to the automatic `article.footer` Slot contribution, the navigation
|
|
43
|
+
is available as a server-rendered Hono JSX Component for placement in a Site
|
|
44
|
+
layout. Build the entries with the existing helper and pass resolved options:
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import RelatedPosts from "@riebeckite/plugin-related-posts/components";
|
|
48
|
+
|
|
49
|
+
<RelatedPosts entries={related} options={resolvedOptions} />;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`related` is the result of `buildRelatedPosts()` and `resolvedOptions` is the
|
|
53
|
+
result of `resolveRelatedPostsOptions()`. Import `style.css` when the Plugin is
|
|
54
|
+
not registered.
|
|
55
|
+
|
|
40
56
|
## Options
|
|
41
57
|
|
|
42
58
|
| Option | Type | Default | Description |
|
|
@@ -85,6 +101,7 @@ import "@riebeckite/plugin-related-posts/style.css";
|
|
|
85
101
|
- `resolveRelatedPostsOptions(options?)` — apply option defaults
|
|
86
102
|
- `buildRelatedPosts({ manifest, entry, options, config? })` — rank related entries
|
|
87
103
|
- `renderRelatedPosts(entries, options)` — render the navigation HTML
|
|
104
|
+
- `RelatedPosts` and `@riebeckite/plugin-related-posts/components` — Hono JSX Component
|
|
88
105
|
- Types: `RelatedPostsOptions`, `ResolvedRelatedPostsOptions`, `RelatedPostsEntry`
|
|
89
106
|
|
|
90
107
|
## Limitations
|
|
@@ -96,4 +113,3 @@ import "@riebeckite/plugin-related-posts/style.css";
|
|
|
96
113
|
## See also
|
|
97
114
|
|
|
98
115
|
- [Plugin guide](../../../docs/en/docs/reference/plugin-api.md)
|
|
99
|
-
|
package/README_ja.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
ビルド時に「関連ノート」ナビゲーションを生成するプラグインです。公開対象の
|
|
4
4
|
各エントリについて、コンテンツマニフェスト上の他のエントリをスコア順に並べ、
|
|
5
|
-
関連ノートのセクションを
|
|
5
|
+
関連ノートのセクションを `article.footer` Slot に追加します。クライアント側 JavaScript は
|
|
6
6
|
不要です。
|
|
7
7
|
|
|
8
8
|
[English](./README.md)
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
`limit` 件に絞ります。`minScore` 未満の候補は除外します。条件を満たす候補が
|
|
23
23
|
1 件もない場合、そのエントリの HTML は変更しません。
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
各マニフェストエントリの `article.footer` Slot にセクションを追加します。標準の
|
|
26
|
+
記事フッターでこの Slot を描画している場合、生成ページとフィードの両方に
|
|
27
27
|
セクションが反映されます。
|
|
28
28
|
|
|
29
29
|
## 使い方
|
|
@@ -38,6 +38,22 @@ export default defineConfig({
|
|
|
38
38
|
});
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
## Component として使う
|
|
42
|
+
|
|
43
|
+
自動の `article.footer` Slot への追加に加え、Site のレイアウト内で任意の位置に
|
|
44
|
+
配置できる、サーバー描画の Hono JSX Component も公開しています。既存の helper
|
|
45
|
+
で entries を計算し、解決済みの options を渡してください。
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import RelatedPosts from "@riebeckite/plugin-related-posts/components";
|
|
49
|
+
|
|
50
|
+
<RelatedPosts entries={related} options={resolvedOptions} />;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`related` には `buildRelatedPosts()` の戻り値を、`resolvedOptions` には
|
|
54
|
+
`resolveRelatedPostsOptions()` の戻り値を渡します。Plugin を登録しない場合は
|
|
55
|
+
`style.css` も読み込んでください。
|
|
56
|
+
|
|
41
57
|
## オプション
|
|
42
58
|
|
|
43
59
|
| オプション | 型 | 既定値 | 説明 |
|
|
@@ -86,6 +102,7 @@ import "@riebeckite/plugin-related-posts/style.css";
|
|
|
86
102
|
- `resolveRelatedPostsOptions(options?)` — オプションの既定値を適用する
|
|
87
103
|
- `buildRelatedPosts({ manifest, entry, options, config? })` — 関連エントリを採点・整列する
|
|
88
104
|
- `renderRelatedPosts(entries, options)` — ナビゲーション HTML を生成する
|
|
105
|
+
- `RelatedPosts` と `@riebeckite/plugin-related-posts/components` — Hono JSX Component
|
|
89
106
|
- 型: `RelatedPostsOptions`, `ResolvedRelatedPostsOptions`, `RelatedPostsEntry`
|
|
90
107
|
|
|
91
108
|
## 制約
|
|
@@ -94,7 +111,6 @@ import "@riebeckite/plugin-related-posts/style.css";
|
|
|
94
111
|
- 判定材料はタグ・直接リンク・共引用のみです。読了時間や新しさ、フォルダは
|
|
95
112
|
順位を決定的に保つため意図的に使いません。
|
|
96
113
|
|
|
97
|
-
##
|
|
98
|
-
|
|
99
|
-
- [?????????](../../../docs/ja/docs/reference/plugin-api.md)
|
|
114
|
+
## 関連リンク
|
|
100
115
|
|
|
116
|
+
- [プラグイン API](../../../docs/ja/docs/reference/plugin-api.md)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RelatedPostsEntry, ResolvedRelatedPostsOptions } from "../src/types.js";
|
|
2
|
+
type Props = {
|
|
3
|
+
entries: readonly RelatedPostsEntry[];
|
|
4
|
+
options: ResolvedRelatedPostsOptions;
|
|
5
|
+
};
|
|
6
|
+
export default function RelatedPosts(props: Props): import("hono/jsx/jsx-dev-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// components/related-posts.tsx
|
|
2
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
3
|
+
function RelatedPosts(props) {
|
|
4
|
+
if (props.entries.length === 0) return null;
|
|
5
|
+
const { className, heading, headingText } = props.options;
|
|
6
|
+
return /* @__PURE__ */ jsxs("nav", { class: className, "data-related-posts": "", children: [
|
|
7
|
+
heading ? /* @__PURE__ */ jsx("h2", { class: `${className}__heading`, children: headingText }) : null,
|
|
8
|
+
/* @__PURE__ */ jsx("ul", { children: props.entries.map((entry) => /* @__PURE__ */ jsx("li", { class: `${className}__item`, children: /* @__PURE__ */ jsx(
|
|
9
|
+
"a",
|
|
10
|
+
{
|
|
11
|
+
class: `${className}__link`,
|
|
12
|
+
href: entry.permalink,
|
|
13
|
+
"data-related-score": entry.score,
|
|
14
|
+
children: entry.title
|
|
15
|
+
}
|
|
16
|
+
) }, entry.slug)) })
|
|
17
|
+
] });
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
RelatedPosts as default
|
|
21
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import type { RelatedPostsOptions } from "./src/types.js";
|
|
2
|
+
export { default as RelatedPosts } from "./components/related-posts.js";
|
|
2
3
|
export { resolveRelatedPostsOptions } from "./src/options.js";
|
|
3
4
|
export { buildRelatedPosts, CO_CITATION_WEIGHT, DIRECT_LINK_WEIGHT, isEligibleRelatedEntry, SHARED_TAG_WEIGHT, } from "./src/related.js";
|
|
4
5
|
export { RELATED_POSTS_ATTRIBUTE, renderRelatedPosts, } from "./src/render.js";
|
|
5
6
|
export type { RelatedPostsEntry, RelatedPostsOptions, ResolvedRelatedPostsOptions, } from "./src/types.js";
|
|
6
7
|
export declare const RELATED_POSTS_PLUGIN_NAME = "related-posts";
|
|
7
|
-
/**
|
|
8
|
-
* Build-time "related notes" navigation.
|
|
9
|
-
*
|
|
10
|
-
* For every published entry it ranks the other entries in the manifest, then
|
|
11
|
-
* appends a `<nav>` section to the manifest entry HTML, which is the final
|
|
12
|
-
* rendering source. No client runtime is required.
|
|
13
|
-
*/
|
|
14
8
|
export declare function relatedPosts(options?: RelatedPostsOptions): import("@riebeckite/core").RiebeckitePlugin<RelatedPostsOptions>;
|
|
15
9
|
/** Alias matching the `*Plugin` suffix used by other plugin factories. */
|
|
16
10
|
export declare const relatedPostsPlugin: typeof relatedPosts;
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
// index.ts
|
|
2
2
|
import {
|
|
3
|
+
appendContentBodySlot,
|
|
3
4
|
createStyleAsset,
|
|
4
5
|
definePlugin
|
|
5
6
|
} from "@riebeckite/core";
|
|
7
|
+
import { renderToString as renderToString2 } from "hono/jsx/dom/server";
|
|
8
|
+
|
|
9
|
+
// components/related-posts.tsx
|
|
10
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
11
|
+
function RelatedPosts(props) {
|
|
12
|
+
if (props.entries.length === 0) return null;
|
|
13
|
+
const { className, heading, headingText } = props.options;
|
|
14
|
+
return /* @__PURE__ */ jsxs("nav", { class: className, "data-related-posts": "", children: [
|
|
15
|
+
heading ? /* @__PURE__ */ jsx("h2", { class: `${className}__heading`, children: headingText }) : null,
|
|
16
|
+
/* @__PURE__ */ jsx("ul", { children: props.entries.map((entry) => /* @__PURE__ */ jsx("li", { class: `${className}__item`, children: /* @__PURE__ */ jsx(
|
|
17
|
+
"a",
|
|
18
|
+
{
|
|
19
|
+
class: `${className}__link`,
|
|
20
|
+
href: entry.permalink,
|
|
21
|
+
"data-related-score": entry.score,
|
|
22
|
+
children: entry.title
|
|
23
|
+
}
|
|
24
|
+
) }, entry.slug)) })
|
|
25
|
+
] });
|
|
26
|
+
}
|
|
6
27
|
|
|
7
28
|
// src/options.ts
|
|
8
29
|
var DEFAULT_RELATED_POSTS_LIMIT = 5;
|
|
@@ -100,18 +121,10 @@ function addScore(scores, slug, weight) {
|
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
// src/render.ts
|
|
103
|
-
import {
|
|
124
|
+
import { renderToString } from "hono/jsx/dom/server";
|
|
104
125
|
var RELATED_POSTS_ATTRIBUTE = "data-related-posts";
|
|
105
126
|
function renderRelatedPosts(entries, options) {
|
|
106
|
-
|
|
107
|
-
const className = escapeHtmlAttribute(options.className);
|
|
108
|
-
const heading = options.heading ? `<h2 class="${className}__heading">${escapeHtml(options.headingText)}</h2>` : "";
|
|
109
|
-
const items = entries.map(
|
|
110
|
-
(entry) => `<li class="${className}__item"><a class="${className}__link" href="${escapeHtmlAttribute(
|
|
111
|
-
entry.permalink
|
|
112
|
-
)}" data-related-score="${entry.score}">${escapeHtml(entry.title)}</a></li>`
|
|
113
|
-
).join("");
|
|
114
|
-
return `<nav class="${className}" ${RELATED_POSTS_ATTRIBUTE}>${heading}<ul>${items}</ul></nav>`;
|
|
127
|
+
return renderToString(RelatedPosts({ entries, options }));
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
// index.ts
|
|
@@ -120,6 +133,7 @@ function relatedPosts(options = {}) {
|
|
|
120
133
|
const resolved = resolveRelatedPostsOptions(options);
|
|
121
134
|
return definePlugin({
|
|
122
135
|
name: RELATED_POSTS_PLUGIN_NAME,
|
|
136
|
+
order: 100,
|
|
123
137
|
processedContentCache: {
|
|
124
138
|
version: "related-posts-v1",
|
|
125
139
|
dependencyMode: "none"
|
|
@@ -130,7 +144,6 @@ function relatedPosts(options = {}) {
|
|
|
130
144
|
onManifestCreated: (context) => {
|
|
131
145
|
const { manifest } = context;
|
|
132
146
|
for (const entry of manifest.discoverableEntries) {
|
|
133
|
-
if (entry.html.includes(RELATED_POSTS_ATTRIBUTE)) continue;
|
|
134
147
|
if (!isEligibleRelatedEntry(entry, manifest, context.config)) continue;
|
|
135
148
|
const related = buildRelatedPosts({
|
|
136
149
|
manifest,
|
|
@@ -139,7 +152,11 @@ function relatedPosts(options = {}) {
|
|
|
139
152
|
config: context.config
|
|
140
153
|
});
|
|
141
154
|
if (related.length === 0) continue;
|
|
142
|
-
|
|
155
|
+
appendContentBodySlot(
|
|
156
|
+
entry,
|
|
157
|
+
"article.footer",
|
|
158
|
+
renderToString2(RelatedPosts({ entries: related, options: resolved }))
|
|
159
|
+
);
|
|
143
160
|
}
|
|
144
161
|
},
|
|
145
162
|
assets: [createStyleAsset(RELATED_POSTS_PLUGIN_NAME)]
|
|
@@ -183,6 +200,7 @@ export {
|
|
|
183
200
|
DIRECT_LINK_WEIGHT,
|
|
184
201
|
RELATED_POSTS_ATTRIBUTE,
|
|
185
202
|
RELATED_POSTS_PLUGIN_NAME,
|
|
203
|
+
RelatedPosts,
|
|
186
204
|
SHARED_TAG_WEIGHT,
|
|
187
205
|
buildRelatedPosts,
|
|
188
206
|
isEligibleRelatedEntry,
|
package/dist/src/render.d.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { RelatedPostsEntry, ResolvedRelatedPostsOptions } from "./types.js";
|
|
2
2
|
/** Boolean attribute that marks the generated navigation for tests and styling. */
|
|
3
3
|
export declare const RELATED_POSTS_ATTRIBUTE = "data-related-posts";
|
|
4
|
-
/**
|
|
5
|
-
* Renders the related-posts navigation. Returns an empty string when there is
|
|
6
|
-
* nothing to show, so callers can leave an entry's HTML untouched.
|
|
7
|
-
*/
|
|
8
4
|
export declare function renderRelatedPosts(entries: readonly RelatedPostsEntry[], options: ResolvedRelatedPostsOptions): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-related-posts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,14 +12,21 @@
|
|
|
12
12
|
"source": "./index.ts",
|
|
13
13
|
"default": "./dist/index.js"
|
|
14
14
|
},
|
|
15
|
+
"./components": {
|
|
16
|
+
"types": "./dist/components/related-posts.d.ts",
|
|
17
|
+
"import": "./dist/components/related-posts.js",
|
|
18
|
+
"source": "./components/related-posts.tsx",
|
|
19
|
+
"default": "./dist/components/related-posts.js"
|
|
20
|
+
},
|
|
15
21
|
"./style.css": "./style.css"
|
|
16
22
|
},
|
|
17
23
|
"dependencies": {
|
|
18
|
-
"
|
|
24
|
+
"hono": "^4.12.25",
|
|
25
|
+
"@riebeckite/core": "0.0.17"
|
|
19
26
|
},
|
|
20
27
|
"devDependencies": {
|
|
21
28
|
"tsx": "^4.22.4",
|
|
22
|
-
"@riebeckite/test": "0.0.
|
|
29
|
+
"@riebeckite/test": "0.0.17"
|
|
23
30
|
},
|
|
24
31
|
"publishConfig": {
|
|
25
32
|
"access": "public"
|
package/style.css
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
transparent
|
|
8
8
|
);
|
|
9
9
|
|
|
10
|
-
margin-
|
|
10
|
+
margin-block-start: var(--rb-space-6, 3rem);
|
|
11
11
|
padding-top: 1.5rem;
|
|
12
12
|
border-top: 1px solid var(--rb-related-posts-rule);
|
|
13
13
|
color: var(--rb-related-posts-ink);
|
|
@@ -44,8 +44,3 @@
|
|
|
44
44
|
.rb-related-posts__link:focus-visible {
|
|
45
45
|
text-decoration-color: var(--rb-related-posts-muted);
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
html[data-theme="dark"] .rb-related-posts,
|
|
49
|
-
html.dark .rb-related-posts {
|
|
50
|
-
--rb-related-posts-ink: var(--rb-color-paper, #ffffff);
|
|
51
|
-
}
|