@riebeckite/plugin-breadcrumbs 0.0.16 → 0.0.18

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 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 emits a two-level BreadcrumbList
36
- (`Home / Note`) inside its own article JSON-LD. The two blocks coexist; to
37
- avoid duplicate BreadcrumbList entities a Site can drop the seo plugin's
38
- placeholder for article pages (shown in the reference app's content route) or
39
- disable `jsonLd` on this plugin (`jsonLd: false`).
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/replace the BreadcrumbList script |
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
@@ -114,5 +114,4 @@ import "@riebeckite/plugin-breadcrumbs/style.css";
114
114
 
115
115
  ## See also
116
116
 
117
- - [Plugin guide](../../../docs/en/docs/reference/plugin-api.md)
118
-
117
+ - [Plugin guide](../../../docs/docs/reference/plugin-api.en.md)
package/README_ja.md CHANGED
@@ -32,11 +32,11 @@ JavaScript は不要です。
32
32
  `entry.headTags` を描画します)。アイテムの URL は設定の `baseUrl` に基づき
33
33
  絶対 URL に変換されます。
34
34
 
35
- `seo` プラグインも有効な場合、seo 側の記事 JSON-LD に 2 階層の
36
- BreadcrumbList(`ホーム / ノート`)が含まれます。この 2 つは共存するため、
37
- BreadcrumbList の重複を避けたい Site は記事ページで seo の暫定版を除去して
38
- ください(リファレンスアプリのコンテンツルートで実施例を確認できます)。
39
- 無効にする場合は本プラグインの `jsonLd: false` も利用できます。
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
  ## 制約
@@ -114,4 +113,4 @@ import "@riebeckite/plugin-breadcrumbs/style.css";
114
113
 
115
114
  ## 関連リンク
116
115
 
117
- - [プラグイン API](../../../docs/ja/docs/reference/plugin-api.md)
116
+ - [プラグイン API](../../../docs/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, injectBreadcrumbNav, renderBreadcrumbNav, } from "./src/render.js";
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
10
  * For every published entry it derives a trail from the note's canonical public location and
11
- * inserts a `<nav>` at the top of the manifest entry HTML, which is the final
12
- * rendering source. The hierarchical BreadcrumbList JSON-LD is contributed
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,5 +1,6 @@
1
1
  // index.ts
2
2
  import {
3
+ appendContentBodySlot,
3
4
  createStyleAsset,
4
5
  definePlugin
5
6
  } from "@riebeckite/core";
@@ -111,23 +112,22 @@ function buildBreadcrumbHeadTag(schema) {
111
112
  }
112
113
  function hasBreadcrumbHeadTag(headTags) {
113
114
  return headTags?.some(
114
- (tag) => tag.tag === "script" && typeof tag.children === "string" && tag.children.includes('"@type":"BreadcrumbList"')
115
+ (tag) => tag.tag === "script" && typeof tag.children === "string" && hasBreadcrumbListJsonLd(tag.children)
115
116
  ) ?? false;
116
117
  }
117
- function injectBreadcrumbNav(html, nav) {
118
- if (nav === "" || html.includes(BREADCRUMBS_ATTRIBUTE)) return html;
119
- const article = /<article\b[^>]*>/i.exec(html);
120
- if (article) {
121
- const end = article.index + article[0].length;
122
- 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;
123
123
  }
124
- const body = /<body\b[^>]*>/i.exec(html);
125
- if (body) {
126
- const end = body.index + body[0].length;
127
- return `${html.slice(0, end)}${nav}${html.slice(end)}`;
128
- }
129
- return `${nav}
130
- ${html}`;
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"]);
131
131
  }
132
132
 
133
133
  // index.ts
@@ -148,7 +148,6 @@ function breadcrumbs(options = {}) {
148
148
  const { config, manifest } = context;
149
149
  if (!config) return;
150
150
  for (const entry of manifest.publicEntries) {
151
- if (entry.html.includes(BREADCRUMBS_ATTRIBUTE)) continue;
152
151
  const items = buildBreadcrumbItems({
153
152
  manifest,
154
153
  entry,
@@ -156,9 +155,10 @@ function breadcrumbs(options = {}) {
156
155
  homeLabel: resolved.homeLabel
157
156
  });
158
157
  if (items.length === 0) continue;
159
- let html = entry.html;
160
158
  const nav = renderBreadcrumbNav(items, resolved);
161
- html = injectBreadcrumbNav(html, nav);
159
+ if (!hasSlotFragment(entry.bodySlots?.["article.header"], nav)) {
160
+ appendContentBodySlot(entry, "article.header", nav);
161
+ }
162
162
  if (resolved.jsonLd) {
163
163
  const schema = buildBreadcrumbJsonLd(config, items);
164
164
  if (!hasBreadcrumbHeadTag(entry.headTags)) {
@@ -168,13 +168,19 @@ function breadcrumbs(options = {}) {
168
168
  ];
169
169
  }
170
170
  }
171
- entry.html = html;
172
171
  }
173
172
  },
174
173
  assets: [createStyleAsset(BREADCRUMBS_PLUGIN_NAME)]
175
174
  });
176
175
  }
177
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
+ }
178
184
  function validateBreadcrumbsOptions(options) {
179
185
  if (!options) return [];
180
186
  const issues = [];
@@ -202,7 +208,6 @@ export {
202
208
  buildBreadcrumbItems,
203
209
  buildBreadcrumbJsonLd,
204
210
  hasBreadcrumbHeadTag,
205
- injectBreadcrumbNav,
206
211
  renderBreadcrumbNav,
207
212
  resolveBreadcrumbsOptions
208
213
  };
@@ -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;
@@ -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. When the seo plugin already
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.16",
3
+ "version": "0.0.18",
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.16"
18
+ "@riebeckite/core": "0.0.18"
19
19
  },
20
20
  "devDependencies": {
21
21
  "tsx": "^4.22.4",
22
- "@riebeckite/test": "0.0.16"
22
+ "@riebeckite/test": "0.0.18"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"