@riebeckite/plugin-breadcrumbs 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 +7 -8
- package/README_ja.md +8 -10
- package/dist/index.d.ts +5 -5
- package/dist/index.js +47 -27
- package/dist/src/render.d.ts +0 -10
- package/dist/src/types.d.ts +1 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -32,11 +32,12 @@ render it in the document `<head>` (the reference Riebeckite app renders
|
|
|
32
32
|
`entry.headTags` in `_renderer.tsx`). Item URLs are made absolute against the
|
|
33
33
|
configured site `baseUrl`.
|
|
34
34
|
|
|
35
|
-
When the `seo` plugin is also enabled it
|
|
36
|
-
(`Home / Note`) inside
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
When the `seo` plugin is also enabled it would emit its own two-level
|
|
36
|
+
BreadcrumbList (`Home / Note`) inside the article JSON-LD. Pass the page's
|
|
37
|
+
`headTags` to the seo extension and it detects the BreadcrumbList this plugin
|
|
38
|
+
contributed and omits its own placeholder, so the page carries a single
|
|
39
|
+
BreadcrumbList entity without any Site-side coupling. Set `jsonLd: false` to
|
|
40
|
+
stop this plugin from emitting one instead.
|
|
40
41
|
|
|
41
42
|
## Usage
|
|
42
43
|
|
|
@@ -58,7 +59,7 @@ export default defineConfig({
|
|
|
58
59
|
| `className` | `string` | `"rb-breadcrumbs"` | Root CSS class |
|
|
59
60
|
| `ariaLabel` | `string` | `"Breadcrumbs"` | Accessible nav name |
|
|
60
61
|
| `separator` | `string` | `"/"` | Text between crumbs |
|
|
61
|
-
| `jsonLd` | `boolean` | `true` | Emit
|
|
62
|
+
| `jsonLd` | `boolean` | `true` | Emit the BreadcrumbList script |
|
|
62
63
|
|
|
63
64
|
```ts
|
|
64
65
|
breadcrumbs({
|
|
@@ -103,7 +104,6 @@ import "@riebeckite/plugin-breadcrumbs/style.css";
|
|
|
103
104
|
- `buildBreadcrumbItems({ manifest, entry, config, homeLabel })` — build the trail
|
|
104
105
|
- `renderBreadcrumbNav(items, options)` — render the navigation HTML
|
|
105
106
|
- `buildBreadcrumbJsonLd(config, items)` — build the JSON-LD object
|
|
106
|
-
- `injectBreadcrumbNav(html, nav)` / `injectBreadcrumbJsonLd(html, schema)` — HTML injection helpers
|
|
107
107
|
- Types: `BreadcrumbsOptions`, `ResolvedBreadcrumbsOptions`, `BreadcrumbItem`
|
|
108
108
|
|
|
109
109
|
## Limitations
|
|
@@ -115,4 +115,3 @@ import "@riebeckite/plugin-breadcrumbs/style.css";
|
|
|
115
115
|
## See also
|
|
116
116
|
|
|
117
117
|
- [Plugin guide](../../../docs/en/docs/reference/plugin-api.md)
|
|
118
|
-
|
package/README_ja.md
CHANGED
|
@@ -32,11 +32,11 @@ JavaScript は不要です。
|
|
|
32
32
|
`entry.headTags` を描画します)。アイテムの URL は設定の `baseUrl` に基づき
|
|
33
33
|
絶対 URL に変換されます。
|
|
34
34
|
|
|
35
|
-
`seo` プラグインも有効な場合、seo
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
`seo` プラグインも有効な場合、seo 側は独自の 2 階層の BreadcrumbList
|
|
36
|
+
(`ホーム / ノート`)を記事 JSON-LD に含めようとします。ページの `headTags`
|
|
37
|
+
を seo 拡張へ渡すと、本プラグインが提供した BreadcrumbList を検出して seo 側の
|
|
38
|
+
暫定版を省略するため、Site 側で調整しなくても BreadcrumbList は 1 つだけに
|
|
39
|
+
なります。本プラグインからの出力自体を止めたい場合は `jsonLd: false` を使います。
|
|
40
40
|
|
|
41
41
|
## 使い方
|
|
42
42
|
|
|
@@ -58,7 +58,7 @@ export default defineConfig({
|
|
|
58
58
|
| `className` | `string` | `"rb-breadcrumbs"` | ルート要素の CSS クラス |
|
|
59
59
|
| `ariaLabel` | `string` | `"Breadcrumbs"` | ナビゲーションのアクセシブル名 |
|
|
60
60
|
| `separator` | `string` | `"/"` | パンくず間の文字 |
|
|
61
|
-
| `jsonLd` | `boolean` | `true` | BreadcrumbList
|
|
61
|
+
| `jsonLd` | `boolean` | `true` | BreadcrumbList スクリプトを出力する |
|
|
62
62
|
|
|
63
63
|
```ts
|
|
64
64
|
breadcrumbs({
|
|
@@ -103,7 +103,6 @@ import "@riebeckite/plugin-breadcrumbs/style.css";
|
|
|
103
103
|
- `buildBreadcrumbItems({ manifest, entry, config, homeLabel })` — パンくず列を組み立てる
|
|
104
104
|
- `renderBreadcrumbNav(items, options)` — ナビゲーション HTML を生成する
|
|
105
105
|
- `buildBreadcrumbJsonLd(config, items)` — JSON-LD オブジェクトを生成する
|
|
106
|
-
- `injectBreadcrumbNav(html, nav)` / `injectBreadcrumbJsonLd(html, schema)` — HTML 注入ヘルパー
|
|
107
106
|
- 型: `BreadcrumbsOptions`, `ResolvedBreadcrumbsOptions`, `BreadcrumbItem`
|
|
108
107
|
|
|
109
108
|
## 制約
|
|
@@ -112,7 +111,6 @@ import "@riebeckite/plugin-breadcrumbs/style.css";
|
|
|
112
111
|
- スラッグ階層のみを参照します。frontmatter の並び順や series プラグインの
|
|
113
112
|
順序は意図的に考慮しません。
|
|
114
113
|
|
|
115
|
-
##
|
|
116
|
-
|
|
117
|
-
- [?????????](../../../docs/ja/docs/reference/plugin-api.md)
|
|
114
|
+
## 関連リンク
|
|
118
115
|
|
|
116
|
+
- [プラグイン API](../../../docs/ja/docs/reference/plugin-api.md)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { BreadcrumbsOptions } from "./src/types.js";
|
|
2
2
|
export { buildBreadcrumbItems } from "./src/breadcrumbs.js";
|
|
3
3
|
export { resolveBreadcrumbsOptions } from "./src/options.js";
|
|
4
|
-
export { BREADCRUMBS_ATTRIBUTE, buildBreadcrumbHeadTag, buildBreadcrumbJsonLd, hasBreadcrumbHeadTag,
|
|
4
|
+
export { BREADCRUMBS_ATTRIBUTE, buildBreadcrumbHeadTag, buildBreadcrumbJsonLd, hasBreadcrumbHeadTag, renderBreadcrumbNav, } from "./src/render.js";
|
|
5
5
|
export type { BreadcrumbItem, BreadcrumbsOptions, ResolvedBreadcrumbsOptions, } from "./src/types.js";
|
|
6
6
|
export declare const BREADCRUMBS_PLUGIN_NAME = "breadcrumbs";
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Public-location breadcrumbs for Riebeckite notes.
|
|
9
9
|
*
|
|
10
|
-
* For every published entry it derives a trail from the note's
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* For every published entry it derives a trail from the note's canonical public location and
|
|
11
|
+
* contributes a `<nav>` to the article header. The hierarchical BreadcrumbList
|
|
12
|
+
* JSON-LD is contributed
|
|
13
13
|
* through `entry.headTags` so the Site shell can render it in the document
|
|
14
14
|
* `<head>`. No client runtime is required.
|
|
15
15
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
// index.ts
|
|
2
2
|
import {
|
|
3
|
+
appendContentBodySlot,
|
|
3
4
|
createStyleAsset,
|
|
4
5
|
definePlugin
|
|
5
6
|
} from "@riebeckite/core";
|
|
6
7
|
|
|
7
8
|
// src/breadcrumbs.ts
|
|
8
|
-
import {
|
|
9
|
+
import { resolvePublicFolderLocation } from "@riebeckite/core";
|
|
9
10
|
function buildBreadcrumbItems(args) {
|
|
10
11
|
const { manifest, entry, config, homeLabel } = args;
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
if (!entry.slug) return [];
|
|
13
|
+
const segments = publicPathSegments(entry.publicLocation.permalink);
|
|
13
14
|
const items = [];
|
|
14
15
|
const homeName = homeLabel || config.site.title;
|
|
15
|
-
if (homeName) items
|
|
16
|
+
if (homeName) pushBreadcrumbItem(items, { name: homeName, url: "/" });
|
|
17
|
+
if (segments.length === 0) return items;
|
|
16
18
|
for (let index = 0; index < segments.length - 1; index++) {
|
|
17
|
-
const
|
|
18
|
-
const location =
|
|
19
|
-
items
|
|
19
|
+
const pathname = `/${segments.slice(0, index + 1).join("/")}`;
|
|
20
|
+
const location = resolvePublicFolderLocation(manifest, pathname);
|
|
21
|
+
pushBreadcrumbItem(items, {
|
|
20
22
|
name: location.type === "content" ? location.entry.title : titleCaseSegment(segments[index]),
|
|
21
23
|
...location.type === "content" ? { url: location.entry.publicLocation.permalink } : location.type === "generated" ? { url: location.pathname } : {}
|
|
22
24
|
});
|
|
23
25
|
}
|
|
24
|
-
items
|
|
26
|
+
pushBreadcrumbItem(items, {
|
|
27
|
+
name: entry.title,
|
|
28
|
+
url: entry.publicLocation.permalink
|
|
29
|
+
});
|
|
25
30
|
return items;
|
|
26
31
|
}
|
|
32
|
+
function pushBreadcrumbItem(items, item) {
|
|
33
|
+
const previous = items.at(-1);
|
|
34
|
+
if (previous?.name === item.name) {
|
|
35
|
+
items[items.length - 1] = item;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
items.push(item);
|
|
39
|
+
}
|
|
40
|
+
function publicPathSegments(permalink) {
|
|
41
|
+
return permalink.split("/").filter(Boolean);
|
|
42
|
+
}
|
|
27
43
|
function titleCaseSegment(segment) {
|
|
28
44
|
return segment.length === 0 ? segment : segment.charAt(0).toUpperCase() + segment.slice(1);
|
|
29
45
|
}
|
|
@@ -96,23 +112,22 @@ function buildBreadcrumbHeadTag(schema) {
|
|
|
96
112
|
}
|
|
97
113
|
function hasBreadcrumbHeadTag(headTags) {
|
|
98
114
|
return headTags?.some(
|
|
99
|
-
(tag) => tag.tag === "script" && typeof tag.children === "string" && tag.children
|
|
115
|
+
(tag) => tag.tag === "script" && typeof tag.children === "string" && hasBreadcrumbListJsonLd(tag.children)
|
|
100
116
|
) ?? false;
|
|
101
117
|
}
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return `${html.slice(0, end)}${nav}${html.slice(end)}`;
|
|
118
|
+
function hasBreadcrumbListJsonLd(value) {
|
|
119
|
+
try {
|
|
120
|
+
return hasBreadcrumbListType(JSON.parse(value));
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
108
123
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return
|
|
115
|
-
|
|
124
|
+
}
|
|
125
|
+
function hasBreadcrumbListType(value) {
|
|
126
|
+
if (Array.isArray(value)) return value.some(hasBreadcrumbListType);
|
|
127
|
+
if (!value || typeof value !== "object") return false;
|
|
128
|
+
const record = value;
|
|
129
|
+
if (record["@type"] === "BreadcrumbList") return true;
|
|
130
|
+
return hasBreadcrumbListType(record["@graph"]);
|
|
116
131
|
}
|
|
117
132
|
|
|
118
133
|
// index.ts
|
|
@@ -133,7 +148,6 @@ function breadcrumbs(options = {}) {
|
|
|
133
148
|
const { config, manifest } = context;
|
|
134
149
|
if (!config) return;
|
|
135
150
|
for (const entry of manifest.publicEntries) {
|
|
136
|
-
if (entry.html.includes(BREADCRUMBS_ATTRIBUTE)) continue;
|
|
137
151
|
const items = buildBreadcrumbItems({
|
|
138
152
|
manifest,
|
|
139
153
|
entry,
|
|
@@ -141,9 +155,10 @@ function breadcrumbs(options = {}) {
|
|
|
141
155
|
homeLabel: resolved.homeLabel
|
|
142
156
|
});
|
|
143
157
|
if (items.length === 0) continue;
|
|
144
|
-
let html = entry.html;
|
|
145
158
|
const nav = renderBreadcrumbNav(items, resolved);
|
|
146
|
-
|
|
159
|
+
if (!hasSlotFragment(entry.bodySlots?.["article.header"], nav)) {
|
|
160
|
+
appendContentBodySlot(entry, "article.header", nav);
|
|
161
|
+
}
|
|
147
162
|
if (resolved.jsonLd) {
|
|
148
163
|
const schema = buildBreadcrumbJsonLd(config, items);
|
|
149
164
|
if (!hasBreadcrumbHeadTag(entry.headTags)) {
|
|
@@ -153,13 +168,19 @@ function breadcrumbs(options = {}) {
|
|
|
153
168
|
];
|
|
154
169
|
}
|
|
155
170
|
}
|
|
156
|
-
entry.html = html;
|
|
157
171
|
}
|
|
158
172
|
},
|
|
159
173
|
assets: [createStyleAsset(BREADCRUMBS_PLUGIN_NAME)]
|
|
160
174
|
});
|
|
161
175
|
}
|
|
162
176
|
var breadcrumbsPlugin = breadcrumbs;
|
|
177
|
+
function hasSlotFragment(slot, fragment) {
|
|
178
|
+
return slot === fragment || slot?.startsWith(`${fragment}
|
|
179
|
+
`) || slot?.endsWith(`
|
|
180
|
+
${fragment}`) || slot?.includes(`
|
|
181
|
+
${fragment}
|
|
182
|
+
`) || false;
|
|
183
|
+
}
|
|
163
184
|
function validateBreadcrumbsOptions(options) {
|
|
164
185
|
if (!options) return [];
|
|
165
186
|
const issues = [];
|
|
@@ -187,7 +208,6 @@ export {
|
|
|
187
208
|
buildBreadcrumbItems,
|
|
188
209
|
buildBreadcrumbJsonLd,
|
|
189
210
|
hasBreadcrumbHeadTag,
|
|
190
|
-
injectBreadcrumbNav,
|
|
191
211
|
renderBreadcrumbNav,
|
|
192
212
|
resolveBreadcrumbsOptions
|
|
193
213
|
};
|
package/dist/src/render.d.ts
CHANGED
|
@@ -22,13 +22,3 @@ export declare function buildBreadcrumbHeadTag(schema: Record<string, unknown>):
|
|
|
22
22
|
* repeated plugin passes do not emit duplicates.
|
|
23
23
|
*/
|
|
24
24
|
export declare function hasBreadcrumbHeadTag(headTags: readonly PluginHeadTag[] | undefined): boolean;
|
|
25
|
-
/**
|
|
26
|
-
* Places the completed navigation at the top of the note.
|
|
27
|
-
*
|
|
28
|
-
* When the HTML is a full page (it contains an `<article>` or `<body>` tag)
|
|
29
|
-
* the nav is inserted right after that opening tag. The manifest `html` we
|
|
30
|
-
* operate on is normally a content fragment the Site wraps in its own
|
|
31
|
-
* `<article>`, in which case the nav is simply prepended so it renders above
|
|
32
|
-
* the note's title.
|
|
33
|
-
*/
|
|
34
|
-
export declare function injectBreadcrumbNav(html: string, nav: string): string;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -16,9 +16,7 @@ export type BreadcrumbsOptions = {
|
|
|
16
16
|
/** Text placed between siblings (defaults to "/"). */
|
|
17
17
|
separator?: string;
|
|
18
18
|
/**
|
|
19
|
-
* Emit a BreadcrumbList JSON-LD script
|
|
20
|
-
* rendered one, its list is replaced with the hierarchical trail
|
|
21
|
-
* (defaults to `true`).
|
|
19
|
+
* Emit a hierarchical BreadcrumbList JSON-LD script (defaults to `true`).
|
|
22
20
|
*/
|
|
23
21
|
jsonLd?: boolean;
|
|
24
22
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-breadcrumbs",
|
|
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",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"./style.css": "./style.css"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@riebeckite/core": "0.0.
|
|
18
|
+
"@riebeckite/core": "0.0.17"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"tsx": "^4.22.4",
|
|
22
|
-
"@riebeckite/test": "0.0.
|
|
22
|
+
"@riebeckite/test": "0.0.17"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|