@vdaluz/astro-blog 1.1.0 → 1.2.0

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
@@ -71,7 +71,7 @@ Peer dependency: `astro` >= 6. For post body styling you'll also want `@tailwind
71
71
 
72
72
  | Import | What |
73
73
  | --- | --- |
74
- | `@vdaluz/astro-blog` | `blogSchema`, `buildBlogPostingSchema`, `scoreRelated`, `normalizeTag`, `filterPostsByTag`, `shikiConfig`, `buildRssItems`, `t`, `formatDate`, types |
74
+ | `@vdaluz/astro-blog` | `blogSchema`, `buildBlogPostingSchema`, `scoreRelated`, `normalizeTag`, `filterPostsByTag`, `shikiConfig`, `buildRssItems`, `t`, `formatDate`, `postHref`, `serializeForScriptTag`, types |
75
75
  | `@vdaluz/astro-blog/PostCard.astro` | Post card for listings |
76
76
  | `@vdaluz/astro-blog/RelatedPosts.astro` | Related-posts grid |
77
77
  | `@vdaluz/astro-blog/Pagination.astro` | Paginated listing nav |
@@ -112,6 +112,21 @@ Set `updatedDate` in a post's frontmatter when you substantively edit it after p
112
112
 
113
113
  Every URL-building surface in this package defaults to no trailing slash and accepts a `trailingSlash` prop/option to opt in: `BlogPostMeta` (JSON-LD's `url`/`mainEntityOfPage.@id`), `PostCard`, `RelatedPosts`, `Pagination` (its numbered page links, including page 1's own href), and `buildRssItems` (each item's `link`). If your site's actual canonical post URL is slash-terminated, pass `trailingSlash={true}` (or `{ trailingSlash: true }` for `buildRssItems`) to **every one of these** - passing it to only one (e.g. just `BlogPostMeta`) leaves the rest emitting slash-less URLs that disagree with JSON-LD and your page's own `<link rel="canonical">`, which can cause search engines to pick the wrong canonical form. Check your real canonical output before setting this, not just your app's `trailingSlash` config: prerendered routes on some hosts are served slash-terminated regardless of that config (confirm with `curl -sI` on a bare post URL - a `307`/`308` to the slash form means you need `trailingSlash={true}`).
114
114
 
115
+ ### Building your own URLs or JSON-LD
116
+
117
+ `postHref(base, id, trailingSlash)` is the exact helper `PostCard`, `RelatedPosts`, and `Pagination` use internally to build a post's href. Reach for it directly when building your own card, archive list, or sitemap entry, instead of reimplementing the same `${base}/${id}` concatenation - that duplication is how the original trailing-slash drift covered above happened.
118
+
119
+ `serializeForScriptTag(value)` safely serializes a value for a `<script type="application/ld+json">` tag - `JSON.stringify` alone can emit a literal `</script>` inside a string field, which breaks the page. `BlogPostMeta` already uses it internally; call it yourself when embedding a second JSON-LD block at the layout level (e.g. `WebSite`/`Organization` schema next to `BlogPostMeta`'s `BlogPosting`):
120
+
121
+ ```astro
122
+ ---
123
+ import { serializeForScriptTag } from '@vdaluz/astro-blog';
124
+
125
+ const websiteSchema = { '@context': 'https://schema.org', '@type': 'WebSite', name: 'My Site' };
126
+ ---
127
+ <script type="application/ld+json" set:html={serializeForScriptTag(websiteSchema)} />
128
+ ```
129
+
115
130
  ### Table of contents + reading time
116
131
 
117
132
  `TableOfContents` reads the `headings` array Astro's own `render()` already returns - no separate parsing step. It renders nothing if the post has fewer than `minHeadings` (default 3) h2/h3 headings.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vdaluz/astro-blog",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Token-driven Astro blog components, related-posts scoring, a schema factory, and Shiki config - proven in production on vdaluz.com and imperfectsystems.com.",
5
5
  "keywords": [
6
6
  "astro",
@@ -33,7 +33,7 @@ const heroImageWebp = post.data.heroImage?.replace(/\.(jpe?g|png|gif)$/i, '.webp
33
33
  >
34
34
  {
35
35
  heroImageWebp && (
36
- <a href={href}>
36
+ <a href={href} aria-hidden="true" tabindex="-1">
37
37
  <img
38
38
  src={heroImageWebp}
39
39
  alt=""
@@ -34,7 +34,7 @@ const heroImageWebp = (heroImage?: string) => heroImage?.replace(/\.(jpe?g|png|g
34
34
  posts.map((post) => (
35
35
  <article class="bg-surface rounded-xl shadow-[0_1px_3px_0_rgb(0_0_0_/_0.1),0_1px_2px_-1px_rgb(0_0_0_/_0.1)] overflow-hidden hover:shadow-lg transition-all duration-300 flex flex-col">
36
36
  {heroImageWebp(post.data.heroImage) && (
37
- <a href={postHref(base, post.id, trailingSlash)}>
37
+ <a href={postHref(base, post.id, trailingSlash)} aria-hidden="true" tabindex="-1">
38
38
  <img
39
39
  src={heroImageWebp(post.data.heroImage)}
40
40
  alt=""
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
- export { blogSchema, buildBlogPostingSchema } from './lib/schema.ts';
1
+ export { blogSchema, buildBlogPostingSchema, serializeForScriptTag } from './lib/schema.ts';
2
2
  export type { BlogPostingSchemaOptions } from './lib/schema.ts';
3
+ export { postHref } from './lib/post-href.ts';
3
4
  export { scoreRelated, normalizeTag } from './lib/relatedPosts.ts';
4
5
  export type { ScoreRelatedOptions } from './lib/relatedPosts.ts';
5
6
  export { filterPostsByTag } from './lib/filterPosts.ts';