@binarynoir/vitepress-blog 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +491 -2
- package/dist/chunk-DICBXOTS.js +1001 -0
- package/dist/chunk-DICBXOTS.js.map +1 -0
- package/dist/client.cjs +176 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +51 -0
- package/dist/client.d.ts +51 -0
- package/dist/client.js +139 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +930 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin-BjbV6nDK.d.cts +190 -0
- package/dist/plugin-BjbV6nDK.d.ts +190 -0
- package/dist/style.css +418 -0
- package/dist/vitepress.cjs +1243 -0
- package/dist/vitepress.cjs.map +1 -0
- package/dist/vitepress.d.cts +81 -0
- package/dist/vitepress.d.ts +81 -0
- package/dist/vitepress.js +234 -0
- package/dist/vitepress.js.map +1 -0
- package/package.json +108 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BinaryNoir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,492 @@
|
|
|
1
|
-
#
|
|
1
|
+
# vitepress-blog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@binarynoir/vitepress-blog)
|
|
4
|
+
[](https://github.com/binarynoir/vitepress-blog/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
A [VitePress 2](https://vitepress.dev) plugin that turns pages into a blog with
|
|
8
|
+
frontmatter alone. Any page anywhere in your docs can be a post. A main blog
|
|
9
|
+
lists posts from the whole site, and **section blogs** list the posts of one
|
|
10
|
+
folder, the way section glossaries and tagged pages work. Every blog gets its
|
|
11
|
+
own RSS feed.
|
|
12
|
+
|
|
13
|
+
[Documentation and live demo](https://binarynoir.tech/plugins/blog/)
|
|
14
|
+
|
|
15
|
+
[](https://buymeacoffee.com/binarynoir)
|
|
16
|
+
[](https://ko-fi.com/binarynoir)
|
|
17
|
+
[](https://binarynoir.tech)
|
|
18
|
+
|
|
19
|
+
## What this does
|
|
20
|
+
|
|
21
|
+
```md
|
|
22
|
+
---
|
|
23
|
+
blog:
|
|
24
|
+
date: 2026-10-01
|
|
25
|
+
summary: Team A's first release is out.
|
|
26
|
+
image: ./launch.png
|
|
27
|
+
author: jane
|
|
28
|
+
tags: [release]
|
|
29
|
+
scope: site # also list it on the main blog
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
# We launched
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- **Blog pages** (`blog-index` in the frontmatter) get a list of posts: cards
|
|
36
|
+
with image, title, date, reading time, authors, summary and tags. Sticky
|
|
37
|
+
posts are pinned to the top, the rest are split into pages, and a keyword
|
|
38
|
+
list above them filters the posts by tag.
|
|
39
|
+
- **Posts** (`blog` in the frontmatter) get a byline under their title, links to
|
|
40
|
+
the newer and older posts, and Open Graph and Twitter card tags.
|
|
41
|
+
- **Every blog page gets an RSS feed**, written beside it at build time and
|
|
42
|
+
linked from its `<head>` and from each post it carries.
|
|
43
|
+
- Lists are plain Markdown generated at build time, with every post in the
|
|
44
|
+
HTML, so they show up in search and work without JavaScript. Links go
|
|
45
|
+
through VitePress, so they get your `base` and `cleanUrls` and are checked
|
|
46
|
+
for dead links. A small composable, `useBlog()`, adds pages and keyword
|
|
47
|
+
filtering in the browser.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npm install --save-dev @binarynoir/vitepress-blog
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Needs Node 22 or later and VitePress 2.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
Wrap your config, and in your theme import the stylesheet and call `useBlog()`:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// .vitepress/config.mts
|
|
63
|
+
import { defineConfig } from 'vitepress';
|
|
64
|
+
import { withBlog } from '@binarynoir/vitepress-blog/vitepress';
|
|
65
|
+
|
|
66
|
+
export default withBlog(
|
|
67
|
+
defineConfig({
|
|
68
|
+
sitemap: { hostname: 'https://example.com' },
|
|
69
|
+
// ...your config
|
|
70
|
+
}),
|
|
71
|
+
{
|
|
72
|
+
authors: { jane: { name: 'Jane Doe', url: 'https://example.com/jane' } },
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// .vitepress/theme/index.ts
|
|
79
|
+
import DefaultTheme from 'vitepress/theme';
|
|
80
|
+
import { useBlog } from '@binarynoir/vitepress-blog/client';
|
|
81
|
+
import '@binarynoir/vitepress-blog/style.css';
|
|
82
|
+
|
|
83
|
+
export default {
|
|
84
|
+
extends: DefaultTheme,
|
|
85
|
+
setup() {
|
|
86
|
+
// Pages (?page=2) and keyword filtering (?tag=release) on blog pages.
|
|
87
|
+
useBlog();
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`withBlog` keeps any `markdown.config`, `transformHead`, `transformPageData` and
|
|
93
|
+
`buildEnd` you already have: the blog's hook runs and then yours, so it
|
|
94
|
+
composes with other `withX()` wrappers in any order.
|
|
95
|
+
|
|
96
|
+
### Wiring it by hand
|
|
97
|
+
|
|
98
|
+
If you'd rather see each piece in your config, `withBlog` is these five:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { blogMarkdown } from '@binarynoir/vitepress-blog';
|
|
102
|
+
import { blogHead, blogPageData, blogVitePlugin, writeBlogFeeds } from '@binarynoir/vitepress-blog/vitepress';
|
|
103
|
+
|
|
104
|
+
const blogOptions = { authors: { jane: 'Jane Doe' } };
|
|
105
|
+
|
|
106
|
+
export default defineConfig({
|
|
107
|
+
markdown: {
|
|
108
|
+
config(md) {
|
|
109
|
+
md.use(blogMarkdown, blogOptions); // lists, bylines, newer / older links
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
transformHead: (context) => blogHead(context, blogOptions), // social tags, feed links
|
|
113
|
+
transformPageData: (pageData, context) => blogPageData(pageData, context, blogOptions), // descriptions
|
|
114
|
+
buildEnd: (siteConfig) => writeBlogFeeds(siteConfig, blogOptions), // RSS feeds, images
|
|
115
|
+
vite: { plugins: [blogVitePlugin(blogOptions)] }, // fresh lists and feeds in `vitepress dev`
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Pass the same options to all five.
|
|
120
|
+
|
|
121
|
+
## Posts
|
|
122
|
+
|
|
123
|
+
A page is a post when its frontmatter has a `blog` key. `blog: true` is a post
|
|
124
|
+
with every default; an object sets its details.
|
|
125
|
+
|
|
126
|
+
```md
|
|
127
|
+
---
|
|
128
|
+
title: We launched
|
|
129
|
+
blog:
|
|
130
|
+
date: 2026-10-01
|
|
131
|
+
updated: 2026-10-05
|
|
132
|
+
summary: Team A's first release is out.
|
|
133
|
+
image: ./launch.png
|
|
134
|
+
imageAlt: The release dashboard
|
|
135
|
+
author: [jane, Sam Lee]
|
|
136
|
+
tags: [release, team-a]
|
|
137
|
+
category: Engineering
|
|
138
|
+
scope: site
|
|
139
|
+
sticky: true
|
|
140
|
+
---
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
| Key | Default | What it does |
|
|
144
|
+
| --------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
145
|
+
| `title` | the page's `title`, then its `#` heading | Title in lists, feeds and social cards. |
|
|
146
|
+
| `summary` | the page's `description`, then first prose | One or two sentences for cards, feeds and `<meta name="description">`. |
|
|
147
|
+
| `date` | none | Publication date. Posts are sorted newest first; undated posts go last. |
|
|
148
|
+
| `updated` | none | Shown in the byline and used as the feed's last-updated time. |
|
|
149
|
+
| `image` | none | Card, social and feed image: a path relative to the post, a public path (`/cover.png`) or a URL. `{ src, alt, fit, position }` also works. |
|
|
150
|
+
| `imageAlt` | `''` | Alt text for the image. |
|
|
151
|
+
| `imageFit` | `cover` | How cards fit the image in their frame: `cover` crops it to fill the frame, `contain` shows all of it, letterboxed. Use `contain` for logos and diagrams. |
|
|
152
|
+
| `imagePosition` | the centre | Which part of a cropped image stays in view, as CSS `object-position`: `top`, `left`, `30% 20%`. |
|
|
153
|
+
| `author` | the `defaultAuthor` option | A name, a key from the `authors` option, `{ name, url, email }`, or a list of these. `authors` works too. |
|
|
154
|
+
| `tags` | none | A list or a comma-separated string. Shown as chips and written to the feed as categories. |
|
|
155
|
+
| `category` | none | Written to the feed and `article:section`. |
|
|
156
|
+
| `scope` | `section` | `site` (or `global`) also lists the post on the main blog. See [Scopes](#scopes). |
|
|
157
|
+
| `sticky` | `false` | Pinned to the top of its blog pages, above the paginated posts, with a Pinned badge. See [Sticky posts](#sticky-posts). |
|
|
158
|
+
| `draft` | `false` | Left out of lists and feeds. The page itself is still built: use `srcExclude` to hide it. |
|
|
159
|
+
| `byline` | the `byline` option | `false` hides this post's byline. |
|
|
160
|
+
| `cover` | the `cover` option | `true` shows this post's image under its byline. |
|
|
161
|
+
| `postNav` | the `postNav` option | `false` hides this post's newer / older links. |
|
|
162
|
+
|
|
163
|
+
Title, description, date, author(s), image and tags may also be written at the
|
|
164
|
+
top level of the frontmatter, as other blog tools expect. A value under `blog`
|
|
165
|
+
wins.
|
|
166
|
+
|
|
167
|
+
## Blog pages
|
|
168
|
+
|
|
169
|
+
A page with `blog-index` in its frontmatter lists posts. `blog-index: true` is a
|
|
170
|
+
**section blog** of the page's own folder.
|
|
171
|
+
|
|
172
|
+
```md
|
|
173
|
+
---
|
|
174
|
+
title: Team A blog
|
|
175
|
+
blog-index:
|
|
176
|
+
layout: grid
|
|
177
|
+
groupBy: year
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
# Team A blog
|
|
181
|
+
|
|
182
|
+
Notes from Team A.
|
|
183
|
+
|
|
184
|
+
<!-- blog-posts -->
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The list goes where `<!-- blog-posts -->` is, or after the page's content.
|
|
188
|
+
|
|
189
|
+
| Key | Default | What it does |
|
|
190
|
+
| ------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
191
|
+
| `scope` | `section` | `site` makes this the main blog. See [Scopes](#scopes). |
|
|
192
|
+
| `folders` | the page's folder | Folders to list posts from, relative to the source directory; `./` and `../` are page-relative. |
|
|
193
|
+
| `tags` | all | Only list posts with at least one of these tags. |
|
|
194
|
+
| `limit` | all | Most posts to list. The feed has its own limit. |
|
|
195
|
+
| `layout` | `list` | `list` (image beside text), `grid` (cards) or `compact` (dates and titles). |
|
|
196
|
+
| `groupBy` | none | `year` puts a heading above each year's posts. |
|
|
197
|
+
| `showTags` | `true` | `false` hides tag chips on cards. |
|
|
198
|
+
| `tagList` | `true` | A keyword list above the posts: every tag with its count, each a filter. `false` leaves it out. |
|
|
199
|
+
| `pageSize` | `10` (the `pageSize` option) | Posts per page. `0` shows them all on one page. |
|
|
200
|
+
| `stickyLimit` | all (the `stickyLimit` option) | Most sticky posts pinned to the top. Extra ones take their place by date. |
|
|
201
|
+
| `rssLink` | `true` (the `rssLink` option) | A "Subscribe" link with the RSS icon to this blog's feed. `false` leaves it out. |
|
|
202
|
+
| `feed` | on | `false` turns the feed off, or `{ title, description, limit, path }` to set it up. |
|
|
203
|
+
|
|
204
|
+
### Sticky posts
|
|
205
|
+
|
|
206
|
+
`sticky: true` pins a post to the top of every blog page that lists it, in its
|
|
207
|
+
own section above the other posts, with a Pinned badge. Pinned posts show on
|
|
208
|
+
the first page only and don't count toward `pageSize`. When more posts are
|
|
209
|
+
sticky than a page's `stickyLimit`, the newest ones are pinned and the others
|
|
210
|
+
take their place by date. Feeds ignore stickiness and stay in date order.
|
|
211
|
+
|
|
212
|
+
To keep sticky posts on top of an [Auto Sidebar](https://binarynoir.tech/plugins/auto-sidebar/)
|
|
213
|
+
sidebar too, sort the blog folder by stickiness, then date:
|
|
214
|
+
|
|
215
|
+
```txt
|
|
216
|
+
# docs/blog/.sidebar
|
|
217
|
+
index.md:All posts
|
|
218
|
+
.sort: blog.sticky desc, date desc
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The sidebar pins every sticky post; `stickyLimit` applies to blog pages.
|
|
222
|
+
|
|
223
|
+
### Pages and keywords
|
|
224
|
+
|
|
225
|
+
Every post is written into the blog page's HTML, so search engines, local
|
|
226
|
+
search and readers without JavaScript see them all. With `useBlog()` in your
|
|
227
|
+
theme (see [Usage](#usage)), the browser then shows one page at a time and
|
|
228
|
+
filters by keyword, both from the URL:
|
|
229
|
+
|
|
230
|
+
- `?page=2` shows the second page. Previous, page number and Next links are
|
|
231
|
+
added below the posts when there is more than one page.
|
|
232
|
+
- `?tag=release` shows only posts tagged `release` (in any case), with a
|
|
233
|
+
"Posts tagged release · Show all posts" line above them. Pages then count
|
|
234
|
+
the matching posts only.
|
|
235
|
+
|
|
236
|
+
Keywords are links everywhere they appear: in the keyword list at the top of
|
|
237
|
+
a blog page, on each card, and in a post's byline, where they link to the
|
|
238
|
+
post's own blog, filtered. They are ordinary links, so VitePress follows them
|
|
239
|
+
without reloading the page, and each filtered view has its own URL to share.
|
|
240
|
+
|
|
241
|
+
## Latest posts on any page
|
|
242
|
+
|
|
243
|
+
A `::: blog-posts` block shows a blog's latest posts anywhere: on the home
|
|
244
|
+
page, or on a section's landing page next to its own blog. Each block has its
|
|
245
|
+
own settings:
|
|
246
|
+
|
|
247
|
+
```md
|
|
248
|
+
## Latest from the blog
|
|
249
|
+
|
|
250
|
+
::: blog-posts
|
|
251
|
+
blog: /blog/
|
|
252
|
+
limit: 3
|
|
253
|
+
stickyLimit: 1
|
|
254
|
+
layout: grid
|
|
255
|
+
more: All posts →
|
|
256
|
+
:::
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
| Key | Default | What it does |
|
|
260
|
+
| ------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
261
|
+
| `blog` | the page's own blog | The blog to show: its URL (`/blog/`) or file (`blog/index.md`); `./` and `../` are page-relative. |
|
|
262
|
+
| `limit` | `5` | Most posts shown, pinned ones included. `0` shows them all. |
|
|
263
|
+
| `stickyLimit` | the blog page's | Most sticky posts pinned to the top. `0` pins them all. |
|
|
264
|
+
| `layout` | the blog page's | `list`, `grid` or `compact`. |
|
|
265
|
+
| `showTags` | the blog page's | `false` hides keyword chips. |
|
|
266
|
+
| `tags` | all | Only show posts with at least one of these keywords. |
|
|
267
|
+
| `more` | `All posts in {blog}` | Text of the link to the blog page below the posts. `false` leaves the link out. |
|
|
268
|
+
| `rssLink` | the blog page's | A "Subscribe" link with the RSS icon to the blog's feed, beside the link to the blog page. `false` leaves it out. |
|
|
269
|
+
|
|
270
|
+
- The page's own blog is the deepest section blog covering its folder, else
|
|
271
|
+
the main blog, so a block on the home page shows the main blog and one on
|
|
272
|
+
a section's page shows that section's blog.
|
|
273
|
+
- It shows the posts its blog lists, newest first, with no drafts unless the
|
|
274
|
+
`drafts` option is on. Keyword chips link to the blog page, filtered.
|
|
275
|
+
- It is written into the page at build time, like blog pages, and refreshes in
|
|
276
|
+
`vitepress dev` when a post changes. A page can hold several blocks. A
|
|
277
|
+
block inside a code fence is left as written.
|
|
278
|
+
- A blog it can't find, or settings that aren't valid YAML, log a warning
|
|
279
|
+
naming the page and line.
|
|
280
|
+
|
|
281
|
+
## Scopes
|
|
282
|
+
|
|
283
|
+
Scopes decide which blog lists a post, the same way section glossaries decide
|
|
284
|
+
which glossary a page uses.
|
|
285
|
+
|
|
286
|
+
- **A section blog** lists every post in its folder and the folders below it,
|
|
287
|
+
whatever the post's scope. Section blogs can be nested: a post in
|
|
288
|
+
`team-a/guides/` is listed by both `team-a/` and `team-a/guides/`.
|
|
289
|
+
- **The main blog** (`blog-index: { scope: site }`) lists:
|
|
290
|
+
- posts from any folder that are marked `scope: site`
|
|
291
|
+
- posts in folders that don't have a section blog of their own
|
|
292
|
+
|
|
293
|
+
So a site with only a main blog needs no scope settings at all, and a post
|
|
294
|
+
inside a section only reaches the main blog when it says `scope: site`.
|
|
295
|
+
|
|
296
|
+
```text
|
|
297
|
+
docs/
|
|
298
|
+
├─ blog/
|
|
299
|
+
│ ├─ index.md blog-index: { scope: site } → hello, launch, notes
|
|
300
|
+
│ └─ hello.md blog: true
|
|
301
|
+
├─ team-a/
|
|
302
|
+
│ ├─ index.md blog-index: true → launch, internal
|
|
303
|
+
│ ├─ launch.md blog: { scope: site }
|
|
304
|
+
│ └─ internal.md blog: true
|
|
305
|
+
└─ notes.md blog: true (no section blog covers it)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
A post's newer and older links stay within its own blog: the deepest section
|
|
309
|
+
blog that lists it, otherwise the main blog.
|
|
310
|
+
|
|
311
|
+
## Feeds
|
|
312
|
+
|
|
313
|
+
Each blog page's RSS 2.0 feed is written next to it at the end of the build:
|
|
314
|
+
`news/index.md` gets `news/feed.xml`, and `news/archive.md` gets
|
|
315
|
+
`news/archive-feed.xml`. Set `feed.path` to name it yourself. Feeds hold the
|
|
316
|
+
newest 20 posts (the `feedLimit` option or `feed.limit`), never drafts, and
|
|
317
|
+
each item has its title, link, date, summary, authors, tags and image.
|
|
318
|
+
|
|
319
|
+
Feeds need absolute links, so they use the `siteUrl` option or VitePress's
|
|
320
|
+
`sitemap.hostname`. Without either, no feeds are written and the build says so.
|
|
321
|
+
|
|
322
|
+
Every blog page and `::: blog-posts` block shows a "Subscribe" link with the RSS icon to its own blog's feed, so readers can follow the whole site or just one section (`rssLink: false` hides it). It only appears when feeds are written, that is, with a site URL. The blog page also links its feed with `<link rel="alternate">`, and so does every
|
|
323
|
+
post the feed carries, so feed readers find it from any of them. In
|
|
324
|
+
`vitepress dev` the feeds are served from memory, with links to the dev server
|
|
325
|
+
if you have no site URL.
|
|
326
|
+
|
|
327
|
+
### Images in feeds and social cards
|
|
328
|
+
|
|
329
|
+
Vite renames images it bundles, and the new name isn't known until the build
|
|
330
|
+
is done. So a post image given as a relative path is also copied to
|
|
331
|
+
`blog-images/` under a name made from its content, and feeds and social cards
|
|
332
|
+
link that copy. Public paths (`/cover.png`) and URLs are linked as they are.
|
|
333
|
+
|
|
334
|
+
## Options
|
|
335
|
+
|
|
336
|
+
| Option | Default | What it does |
|
|
337
|
+
| ---------------- | ----------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
338
|
+
| `siteUrl` | `sitemap.hostname` | Absolute URL of the published site, for feeds and social tags. |
|
|
339
|
+
| `authors` | `{}` | Authors posts can name by key: `{ jane: { name, url, email } }` or `{ jane: 'Jane Doe' }`. |
|
|
340
|
+
| `defaultAuthor` | none | Author for posts that don't name one. |
|
|
341
|
+
| `locale` | `'en-US'` | Locale for dates and author lists ("Jane and Sam"). |
|
|
342
|
+
| `dateFormat` | `{ dateStyle: 'long' }` | `Intl.DateTimeFormat` options. Dates are shown in UTC. |
|
|
343
|
+
| `wordsPerMinute` | `200` | For "5 min read". `0` hides reading time. |
|
|
344
|
+
| `byline` | `true` | Author, date and tags under each post's title. |
|
|
345
|
+
| `cover` | `false` | Each post's image under its byline. |
|
|
346
|
+
| `postNav` | `true` | Newer and older post links at the end of each post. |
|
|
347
|
+
| `drafts` | `false` | List drafts, marked. Pass `process.env.NODE_ENV !== 'production'` for dev only. |
|
|
348
|
+
| `stripTags` | `true` | Remove [Markdown Tags](https://binarynoir.tech/plugins/markdown-tags/) syntax from titles and summaries. |
|
|
349
|
+
| `feedLimit` | `20` | Most posts in each feed. |
|
|
350
|
+
| `imagesDir` | `'blog-images'` | Output folder for copied post images. |
|
|
351
|
+
| `pageSize` | `10` | Posts per page on blog pages that don't set `pageSize`. `0` turns pages off. |
|
|
352
|
+
| `stickyLimit` | `0` (all) | Most pinned sticky posts on blog pages that don't set `stickyLimit`. |
|
|
353
|
+
| `rssLink` | `true` | A "Subscribe" RSS link on blog pages and blocks that don't set `rssLink`. |
|
|
354
|
+
| `labels` | English | Words the plugin writes, for translating. See below. |
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
withBlog(config, {
|
|
358
|
+
locale: 'de-DE',
|
|
359
|
+
labels: {
|
|
360
|
+
by: 'Von',
|
|
361
|
+
updated: 'Aktualisiert',
|
|
362
|
+
readingTime: '{n} Min. Lesezeit',
|
|
363
|
+
newer: 'Neuerer Beitrag',
|
|
364
|
+
older: 'Älterer Beitrag',
|
|
365
|
+
allPosts: 'Alle Beiträge in {blog}',
|
|
366
|
+
draft: 'Entwurf',
|
|
367
|
+
sticky: 'Angeheftet',
|
|
368
|
+
noPosts: 'Noch keine Beiträge.',
|
|
369
|
+
undated: 'Ohne Datum',
|
|
370
|
+
tags: 'Schlagwörter',
|
|
371
|
+
tagged: 'Beiträge mit {tag}',
|
|
372
|
+
showAll: 'Alle Beiträge zeigen',
|
|
373
|
+
noTagged: 'Keine Beiträge mit {tag}.',
|
|
374
|
+
previous: 'Zurück',
|
|
375
|
+
next: 'Weiter',
|
|
376
|
+
pages: 'Seiten',
|
|
377
|
+
subscribe: 'Abonnieren',
|
|
378
|
+
rssFeed: 'RSS-Feed für {blog}',
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Styling
|
|
384
|
+
|
|
385
|
+
`style.css` uses VitePress's theme variables, so it follows light and dark
|
|
386
|
+
mode. Override these to restyle it without new rules:
|
|
387
|
+
|
|
388
|
+
| Variable | Default |
|
|
389
|
+
| ----------------------- | -------------------------- |
|
|
390
|
+
| `--vp-blog-card-bg` | `var(--vp-c-bg-soft)` |
|
|
391
|
+
| `--vp-blog-card-border` | `var(--vp-c-divider)` |
|
|
392
|
+
| `--vp-blog-card-radius` | `12px` |
|
|
393
|
+
| `--vp-blog-meta-color` | `var(--vp-c-text-2)` |
|
|
394
|
+
| `--vp-blog-tag-bg` | `var(--vp-c-default-soft)` |
|
|
395
|
+
| `--vp-blog-tag-color` | `var(--vp-c-text-2)` |
|
|
396
|
+
| `--vp-blog-accent` | `var(--vp-c-brand-1)` |
|
|
397
|
+
|
|
398
|
+
Every generated element has a `vp-blog-` class (`vp-blog-card`,
|
|
399
|
+
`vp-blog-byline`, `vp-blog-post-nav`, …) for anything more.
|
|
400
|
+
|
|
401
|
+
## Notes
|
|
402
|
+
|
|
403
|
+
- **Lists stay fresh in `vitepress dev`.** VitePress caches each page's
|
|
404
|
+
output by its own source, so the dev plugin adds a fingerprint of every post
|
|
405
|
+
to blog pages and posts, and reloads them when any post is added, edited or
|
|
406
|
+
deleted.
|
|
407
|
+
- **Pages kept out of the build aren't listed.** Anything `srcExclude` leaves
|
|
408
|
+
out (including [Exclude](https://binarynoir.tech/plugins/exclude/) rules) is
|
|
409
|
+
skipped, so a list never links to a missing page.
|
|
410
|
+
- **Rewrites are followed.** Links and feeds use the path a page is served at.
|
|
411
|
+
- **Plain markdown-it**: `blogMarkdown` needs VitePress's `env.path`,
|
|
412
|
+
`env.relativePath` and `env.frontmatter`, so it does nothing elsewhere.
|
|
413
|
+
|
|
414
|
+
## Releasing
|
|
415
|
+
|
|
416
|
+
Releases are tag-triggered. From a clean `main` that's in sync with
|
|
417
|
+
`origin/main`:
|
|
418
|
+
|
|
419
|
+
```sh
|
|
420
|
+
npm run release:patch # or release:minor / release:major
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
This runs typecheck/lint/test/build locally, then `npm version <bump>` and
|
|
424
|
+
`git push --follow-tags`. Pushing the tag triggers
|
|
425
|
+
[`.github/workflows/release.yml`](.github/workflows/release.yml), which re-runs
|
|
426
|
+
the checks, publishes to npm, and creates a GitHub release.
|
|
427
|
+
|
|
428
|
+
## License
|
|
429
|
+
|
|
430
|
+
MIT
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Feedback and support
|
|
435
|
+
|
|
436
|
+
Bug reports, feature requests and questions are all welcome. Please open them on this project's [GitHub issue tracker](https://github.com/binarynoir/vitepress-blog/issues):
|
|
437
|
+
|
|
438
|
+
- **Found a bug?** [Report it](https://github.com/binarynoir/vitepress-blog/issues/new?labels=bug&title=Bug%3A%20). Include your version and the steps to reproduce it.
|
|
439
|
+
- **Have an idea?** [Request a feature](https://github.com/binarynoir/vitepress-blog/issues/new?labels=enhancement&title=Feature%20request%3A%20). Tell us what you are trying to do.
|
|
440
|
+
- **Something else?** [Browse open issues](https://github.com/binarynoir/vitepress-blog/issues) to see if it has already been raised.
|
|
441
|
+
|
|
442
|
+
Pull requests are welcome too.
|
|
443
|
+
|
|
444
|
+
## More from BinaryNoir
|
|
445
|
+
|
|
446
|
+
<p align="center">
|
|
447
|
+
<a href="https://binarynoir.tech">
|
|
448
|
+
<picture>
|
|
449
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://binarynoir.tech/logo-wide-dark.svg">
|
|
450
|
+
<img alt="BinaryNoir" src="https://binarynoir.tech/logo-wide-light.svg" width="320">
|
|
451
|
+
</picture>
|
|
452
|
+
</a>
|
|
453
|
+
</p>
|
|
454
|
+
|
|
455
|
+
Blog is one of a family of open-source VitePress plugins, editor extensions and command-line tools from BinaryNoir. Browse them all, with documentation and live demos, at **[binarynoir.tech](https://binarynoir.tech)**.
|
|
456
|
+
|
|
457
|
+
**VitePress plugins**
|
|
458
|
+
|
|
459
|
+
- [Auto Sidebar](https://binarynoir.tech/plugins/auto-sidebar): sidebar built from your folders
|
|
460
|
+
- [Auto Navbar](https://binarynoir.tech/plugins/auto-navbar): navbar built from your folders
|
|
461
|
+
- [Sidebar Accordion](https://binarynoir.tech/plugins/sidebar-accordion): one open sidebar group at a time
|
|
462
|
+
- [Markdown Tags](https://binarynoir.tech/plugins/markdown-tags/): inline status badges and tagged-pages lists
|
|
463
|
+
- [Reading Time Tag](https://binarynoir.tech/plugins/reading-time-tag): reading-time estimates
|
|
464
|
+
- [Glossary Tooltips](https://binarynoir.tech/plugins/glossary/): hover definitions from one glossary file
|
|
465
|
+
- [Text Style](https://binarynoir.tech/plugins/text-style): colored and sized text inline
|
|
466
|
+
- [Downloads](https://binarynoir.tech/plugins/downloads): downloadable files next to your pages
|
|
467
|
+
- [Exclude](https://binarynoir.tech/plugins/exclude/): keep pages out of the build with a `.exclude` file
|
|
468
|
+
- [README Index](https://binarynoir.tech/plugins/readme-index/): `README.md` index pages for VuePress migrations
|
|
469
|
+
- [HTML Escape](https://binarynoir.tech/plugins/html-escape/): stray tags and braces shown as text, not build errors
|
|
470
|
+
- [Broken Links](https://binarynoir.tech/plugins/broken-links): dead links shown as text, not build errors
|
|
471
|
+
|
|
472
|
+
**Vite plugins**
|
|
473
|
+
|
|
474
|
+
- [Optimize Images](https://binarynoir.tech/plugins/optimize-images): smaller images in the build output
|
|
475
|
+
- [Image Fallback](https://binarynoir.tech/plugins/image-fallback): a placeholder instead of a failed build
|
|
476
|
+
|
|
477
|
+
**Editor extensions**
|
|
478
|
+
|
|
479
|
+
- [Tags for Markdown for VS Code](https://binarynoir.tech/plugins/markdown-tags/vscode): status badges in the VS Code Markdown preview
|
|
480
|
+
- [Tags for Markdown for Obsidian](https://binarynoir.tech/plugins/markdown-tags/obsidian): status badges in Obsidian
|
|
481
|
+
|
|
482
|
+
**Command-line tools**
|
|
483
|
+
|
|
484
|
+
- [NoirWatch](https://binarynoir.tech/apps/noirwatch): get notified when a web page changes
|
|
485
|
+
- [NoirCon](https://binarynoir.tech/apps/noircon): get notified when a site, server or port goes down
|
|
486
|
+
- [callrx](https://binarynoir.tech/apps/callrx): amateur radio callsign lookup
|
|
487
|
+
|
|
488
|
+
Website, docs and demos: **[binarynoir.tech](https://binarynoir.tech)**
|
|
489
|
+
|
|
490
|
+
## Author
|
|
491
|
+
|
|
492
|
+
John Smith III, [BinaryNoir](https://binarynoir.tech). Thanks to all contributors and users for their support and feedback.
|