@riebeckite/plugin-breadcrumbs 0.0.14 → 0.0.16

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
@@ -20,8 +20,9 @@ has an index note of its own (a note with the folder slug), that note's title
20
20
  is used for the crumb, otherwise the segment is title-cased. The final crumb is
21
21
  the note itself and links to its permalink.
22
22
 
23
- The plugin updates both `entry.html` and the cached `PostContent.html` that the
24
- content route renders, so the nav appears on generated pages and in feeds.
23
+ The plugin injects the nav into the manifest entry's HTML. Core synchronizes
24
+ that HTML with the content the route renders, so the nav appears on generated
25
+ pages and in feeds.
25
26
 
26
27
  ## JSON-LD
27
28
 
@@ -113,4 +114,5 @@ import "@riebeckite/plugin-breadcrumbs/style.css";
113
114
 
114
115
  ## See also
115
116
 
116
- - [Plugin guide](../../../docs/en/reference/plugin-api.md)
117
+ - [Plugin guide](../../../docs/en/docs/reference/plugin-api.md)
118
+
package/README_ja.md CHANGED
@@ -20,8 +20,8 @@ JavaScript は不要です。
20
20
  タイトルを使い、なければセグメントをタイトルケース(先頭大文字)にして
21
21
  代用します。最後のパンくずはそのノート自身で、パーマリンクへリンクします。
22
22
 
23
- `entry.html` と、コンテンツルートが描画するキャッシュ済みの
24
- `PostContent.html` の両方を更新するため、生成ページとフィードの両方に
23
+ マニフェストエントリの HTML にナビゲーションを挿入します。Core がその HTML を
24
+ コンテンツルートの描画 HTML と同期するため、生成ページとフィードの両方に
25
25
  ナビゲーションが反映されます。
26
26
 
27
27
  ## JSON-LD
@@ -112,6 +112,6 @@ import "@riebeckite/plugin-breadcrumbs/style.css";
112
112
  - スラッグ階層のみを参照します。frontmatter の並び順や series プラグインの
113
113
  順序は意図的に考慮しません。
114
114
 
115
- ## ????
115
+ ## 関連リンク
116
116
 
117
- - [?????????](../../../docs/ja/reference/plugin-api.md)
117
+ - [プラグイン API](../../../docs/ja/docs/reference/plugin-api.md)
package/dist/index.d.ts CHANGED
@@ -5,13 +5,13 @@ export { BREADCRUMBS_ATTRIBUTE, buildBreadcrumbHeadTag, buildBreadcrumbJsonLd, h
5
5
  export type { BreadcrumbItem, BreadcrumbsOptions, ResolvedBreadcrumbsOptions, } from "./src/types.js";
6
6
  export declare const BREADCRUMBS_PLUGIN_NAME = "breadcrumbs";
7
7
  /**
8
- * Slug-hierarchy breadcrumbs for Riebeckite notes.
8
+ * Public-location breadcrumbs for Riebeckite notes.
9
9
  *
10
- * For every published entry it derives a trail from the note's slug and
11
- * inserts a `<nav>` at the top of both `entry.html` and the cached
12
- * `PostContent.html` that the content route renders. The hierarchical
13
- * BreadcrumbList JSON-LD is contributed through `entry.headTags` so the Site
14
- * shell can render it in the document `<head>`. No client runtime is required.
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
13
+ * through `entry.headTags` so the Site shell can render it in the document
14
+ * `<head>`. No client runtime is required.
15
15
  */
16
16
  export declare function breadcrumbs(options?: BreadcrumbsOptions): import("@riebeckite/core").RiebeckitePlugin<BreadcrumbsOptions>;
17
17
  /** Alias matching the `*Plugin` suffix used by other plugin factories. */
package/dist/index.js CHANGED
@@ -5,24 +5,40 @@ import {
5
5
  } from "@riebeckite/core";
6
6
 
7
7
  // src/breadcrumbs.ts
8
+ import { resolvePublicFolderLocation } from "@riebeckite/core";
8
9
  function buildBreadcrumbItems(args) {
9
10
  const { manifest, entry, config, homeLabel } = args;
10
- const segments = entry.slug.split("/").filter(Boolean);
11
- if (segments.length === 0) return [];
11
+ if (!entry.slug) return [];
12
+ const segments = publicPathSegments(entry.publicLocation.permalink);
12
13
  const items = [];
13
14
  const homeName = homeLabel || config.site.title;
14
- if (homeName) items.push({ name: homeName, url: "/" });
15
+ if (homeName) pushBreadcrumbItem(items, { name: homeName, url: "/" });
16
+ if (segments.length === 0) return items;
15
17
  for (let index = 0; index < segments.length - 1; index++) {
16
- const folderSlug = segments.slice(0, index + 1).join("/");
17
- const folderEntry = manifest.bySlug.get(folderSlug);
18
- items.push({
19
- name: folderEntry?.title ?? titleCaseSegment(segments[index]),
20
- url: `/${folderSlug}`
18
+ const pathname = `/${segments.slice(0, index + 1).join("/")}`;
19
+ const location = resolvePublicFolderLocation(manifest, pathname);
20
+ pushBreadcrumbItem(items, {
21
+ name: location.type === "content" ? location.entry.title : titleCaseSegment(segments[index]),
22
+ ...location.type === "content" ? { url: location.entry.publicLocation.permalink } : location.type === "generated" ? { url: location.pathname } : {}
21
23
  });
22
24
  }
23
- items.push({ name: entry.title, url: entry.permalink });
25
+ pushBreadcrumbItem(items, {
26
+ name: entry.title,
27
+ url: entry.publicLocation.permalink
28
+ });
24
29
  return items;
25
30
  }
31
+ function pushBreadcrumbItem(items, item) {
32
+ const previous = items.at(-1);
33
+ if (previous?.name === item.name) {
34
+ items[items.length - 1] = item;
35
+ return;
36
+ }
37
+ items.push(item);
38
+ }
39
+ function publicPathSegments(permalink) {
40
+ return permalink.split("/").filter(Boolean);
41
+ }
26
42
  function titleCaseSegment(segment) {
27
43
  return segment.length === 0 ? segment : segment.charAt(0).toUpperCase() + segment.slice(1);
28
44
  }
@@ -66,7 +82,7 @@ function renderBreadcrumbNav(items, options) {
66
82
  const isLast = index === items.length - 1;
67
83
  const crumb = isLast ? `<span class="${className}__current" aria-current="page">${escapeHtml(
68
84
  item.name
69
- )}</span>` : `<a class="${className}__link" href="${escapeHtmlAttribute(
85
+ )}</span>` : !item.url ? `<span class="${className}__text">${escapeHtml(item.name)}</span>` : `<a class="${className}__link" href="${escapeHtmlAttribute(
70
86
  item.url
71
87
  )}">${escapeHtml(item.name)}</a>`;
72
88
  const trailing = isLast ? "" : `<span class="${className}__separator" aria-hidden="true">${separator}</span>`;
@@ -82,7 +98,7 @@ function buildBreadcrumbJsonLd(config, items) {
82
98
  "@type": "ListItem",
83
99
  position: index + 1,
84
100
  name: item.name,
85
- item: buildAbsoluteUrl(config, item.url)
101
+ ...item.url ? { item: buildAbsoluteUrl(config, item.url) } : {}
86
102
  }))
87
103
  };
88
104
  }
@@ -118,18 +134,20 @@ ${html}`;
118
134
  var BREADCRUMBS_PLUGIN_NAME = "breadcrumbs";
119
135
  function breadcrumbs(options = {}) {
120
136
  const resolved = resolveBreadcrumbsOptions(options);
121
- const processed = /* @__PURE__ */ new Map();
122
137
  return definePlugin({
123
138
  name: BREADCRUMBS_PLUGIN_NAME,
139
+ optional: ["content.folder-pages"],
140
+ processedContentCache: {
141
+ version: "breadcrumbs-v1",
142
+ dependencyMode: "none"
143
+ },
144
+ outputDependencies: [{ type: "global" }],
124
145
  options,
125
146
  validateOptions: validateBreadcrumbsOptions,
126
- onPostProcessed: (context) => {
127
- processed.set(context.slug, context.content);
128
- },
129
147
  onManifestCreated: (context) => {
130
148
  const { config, manifest } = context;
131
149
  if (!config) return;
132
- for (const entry of manifest.entries) {
150
+ for (const entry of manifest.publicEntries) {
133
151
  if (entry.html.includes(BREADCRUMBS_ATTRIBUTE)) continue;
134
152
  const items = buildBreadcrumbItems({
135
153
  manifest,
@@ -151,10 +169,7 @@ function breadcrumbs(options = {}) {
151
169
  }
152
170
  }
153
171
  entry.html = html;
154
- const content = processed.get(entry.slug);
155
- if (content) content.html = html;
156
172
  }
157
- processed.clear();
158
173
  },
159
174
  assets: [createStyleAsset(BREADCRUMBS_PLUGIN_NAME)]
160
175
  });
@@ -6,13 +6,4 @@ export type BuildBreadcrumbItemsArgs = {
6
6
  config: ResolvedRiebeckiteConfig;
7
7
  homeLabel: string;
8
8
  };
9
- /**
10
- * Builds the breadcrumb trail for a note from its slug hierarchy.
11
- *
12
- * The first crumb is always the site home (the site title unless the consumer
13
- * provided a `homeLabel`); each intermediate slug segment becomes a crumb
14
- * pointing at the folder's own URL. When the folder has an index note of its
15
- * own, that note's title is used for the crumb instead of the raw segment.
16
- * The final crumb is the note itself and points at its permalink.
17
- */
18
9
  export declare function buildBreadcrumbItems(args: BuildBreadcrumbItemsArgs): BreadcrumbItem[];
@@ -1,7 +1,7 @@
1
1
  /** One crumb in the generated breadcrumb trail. */
2
2
  export type BreadcrumbItem = {
3
3
  name: string;
4
- url: string;
4
+ url?: string;
5
5
  };
6
6
  export type BreadcrumbsOptions = {
7
7
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-breadcrumbs",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
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.14"
18
+ "@riebeckite/core": "0.0.16"
19
19
  },
20
20
  "devDependencies": {
21
21
  "tsx": "^4.22.4",
22
- "@riebeckite/test": "0.0.14"
22
+ "@riebeckite/test": "0.0.16"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"