@binarynoir/vitepress-blog 0.1.1 → 0.3.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 +158 -33
- package/dist/chunk-42Z6H7IN.js +52 -0
- package/dist/chunk-42Z6H7IN.js.map +1 -0
- package/dist/{chunk-DICBXOTS.js → chunk-LYTJBMVK.js} +182 -66
- package/dist/chunk-LYTJBMVK.js.map +1 -0
- package/dist/client.cjs +140 -28
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +35 -4
- package/dist/client.d.ts +35 -4
- package/dist/client.js +136 -26
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +157 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/{plugin-BjbV6nDK.d.cts → plugin-Bb4rRwO8.d.cts} +55 -2
- package/dist/{plugin-BjbV6nDK.d.ts → plugin-Bb4rRwO8.d.ts} +55 -2
- package/dist/style.css +113 -0
- package/dist/vitepress.cjs +294 -98
- package/dist/vitepress.cjs.map +1 -1
- package/dist/vitepress.d.cts +17 -7
- package/dist/vitepress.d.ts +17 -7
- package/dist/vitepress.js +75 -7
- package/dist/vitepress.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-DICBXOTS.js.map +0 -1
package/dist/vitepress.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/vitepress.ts
|
|
31
31
|
var vitepress_exports = {};
|
|
32
32
|
__export(vitepress_exports, {
|
|
33
|
+
attachAnnouncements: () => attachAnnouncements,
|
|
33
34
|
blogHead: () => blogHead,
|
|
34
35
|
blogMarkdown: () => blogMarkdown,
|
|
35
36
|
blogPageData: () => blogPageData,
|
|
@@ -113,10 +114,213 @@ function findBlogsListing(pages, post) {
|
|
|
113
114
|
(index) => listBlogPosts(pages, index, { drafts: false, stickyFirst: false, applyLimit: false }).includes(post)
|
|
114
115
|
);
|
|
115
116
|
}
|
|
117
|
+
function listAnnouncements(pages, drafts) {
|
|
118
|
+
return pages.filter(isPost).filter((post) => post.post.announcement && (drafts || !post.post.draft)).sort(compareByDate);
|
|
119
|
+
}
|
|
120
|
+
function findAnnouncementBlog(pages, announcements, drafts) {
|
|
121
|
+
if (announcements.length === 0) return null;
|
|
122
|
+
const listingAll = pages.filter(isIndex).filter((index) => {
|
|
123
|
+
const listed = listBlogPosts(pages, index, { drafts, stickyFirst: false, applyLimit: false });
|
|
124
|
+
return announcements.every((post) => listed.includes(post));
|
|
125
|
+
});
|
|
126
|
+
return listingAll.find((index) => index.index.scope === "site") ?? listingAll[0] ?? findHomeBlog(pages, announcements[0], drafts);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/types.ts
|
|
130
|
+
var DEFAULT_LABELS = {
|
|
131
|
+
by: "By",
|
|
132
|
+
published: "Published",
|
|
133
|
+
updated: "Updated",
|
|
134
|
+
dateTime: "{date} at {time}",
|
|
135
|
+
readingTime: "{n} min read",
|
|
136
|
+
newer: "Newer post",
|
|
137
|
+
older: "Older post",
|
|
138
|
+
allPosts: "All posts in {blog}",
|
|
139
|
+
draft: "Draft",
|
|
140
|
+
sticky: "Pinned",
|
|
141
|
+
announcement: "Announcement",
|
|
142
|
+
announcements: "Announcements",
|
|
143
|
+
noAnnouncements: "No announcements.",
|
|
144
|
+
announcementCount: "{n} announcements",
|
|
145
|
+
dismiss: "Dismiss",
|
|
146
|
+
noPosts: "No posts yet.",
|
|
147
|
+
undated: "Undated",
|
|
148
|
+
tags: "Keywords",
|
|
149
|
+
tagged: "Posts tagged {tag}",
|
|
150
|
+
showAll: "Show all posts",
|
|
151
|
+
noTagged: "No posts tagged {tag}.",
|
|
152
|
+
previous: "Previous",
|
|
153
|
+
next: "Next",
|
|
154
|
+
pages: "Pages",
|
|
155
|
+
subscribe: "Subscribe",
|
|
156
|
+
rssFeed: "RSS feed for {blog}"
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// src/util.ts
|
|
160
|
+
var HTML_ESCAPES = {
|
|
161
|
+
"&": "&",
|
|
162
|
+
"<": "<",
|
|
163
|
+
">": ">",
|
|
164
|
+
'"': """,
|
|
165
|
+
"'": "'"
|
|
166
|
+
};
|
|
167
|
+
function escapeHtml(value) {
|
|
168
|
+
return value.replace(/[&<>"']/g, (character) => HTML_ESCAPES[character]);
|
|
169
|
+
}
|
|
170
|
+
function escapeMarkdown(text) {
|
|
171
|
+
const escaped = text.replace(/[\\`*_[\]#|~]/g, "\\$&").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
172
|
+
return /[{}]/.test(escaped) ? `<span v-pre>${escaped}</span>` : escaped;
|
|
173
|
+
}
|
|
174
|
+
function escapeTemplateText(text) {
|
|
175
|
+
const escaped = escapeHtml(text);
|
|
176
|
+
return /[{}]/.test(escaped) ? `<span v-pre>${escaped}</span>` : escaped;
|
|
177
|
+
}
|
|
178
|
+
function isExternalUrl(url) {
|
|
179
|
+
return /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(url);
|
|
180
|
+
}
|
|
181
|
+
function toMarkdownUrl(url) {
|
|
182
|
+
return encodeURI(url).replace(/[()]/g, (character) => character === "(" ? "%28" : "%29");
|
|
183
|
+
}
|
|
184
|
+
function toPageLink(text, routePath) {
|
|
185
|
+
return `[${escapeMarkdown(text)}](${toMarkdownUrl(`/${routePath}`)})`;
|
|
186
|
+
}
|
|
187
|
+
function toUrlPath(routePath, cleanUrls) {
|
|
188
|
+
return `/${routePath.replace(/(^|\/)index\.md$/, "$1").replace(/\.md$/, cleanUrls ? "" : ".html")}`;
|
|
189
|
+
}
|
|
190
|
+
function joinUrl(base, urlPath) {
|
|
191
|
+
return `${base.replace(/\/+$/, "")}/${urlPath.replace(/^\/+/, "")}`;
|
|
192
|
+
}
|
|
193
|
+
var ANNOUNCEMENTS_KEY = "blogAnnouncements";
|
|
194
|
+
var DISMISSED_STORAGE_KEY = "vp-blog-announcements-dismissed";
|
|
195
|
+
var DISMISSED_CLASS = "vp-blog-announcement-dismissed";
|
|
196
|
+
|
|
197
|
+
// src/announcements.ts
|
|
198
|
+
function resolveAnnouncements(pages, options, cleanUrls) {
|
|
199
|
+
const drafts = options.drafts ?? false;
|
|
200
|
+
const announcements = listAnnouncements(pages, drafts);
|
|
201
|
+
if (announcements.length === 0) return null;
|
|
202
|
+
const blog = findAnnouncementBlog(pages, announcements, drafts);
|
|
203
|
+
const labels = { ...DEFAULT_LABELS, ...options.labels };
|
|
204
|
+
return {
|
|
205
|
+
items: announcements.map((post) => ({
|
|
206
|
+
id: `${post.routePath}@${post.post.date?.toISOString() ?? ""}`,
|
|
207
|
+
text: post.post.announcementText || post.post.title,
|
|
208
|
+
link: toUrlPath(post.routePath, cleanUrls)
|
|
209
|
+
})),
|
|
210
|
+
listLink: blog ? `${toUrlPath(blog.routePath, cleanUrls)}?announcements` : "",
|
|
211
|
+
labels: {
|
|
212
|
+
announcement: labels.announcement,
|
|
213
|
+
announcementCount: labels.announcementCount,
|
|
214
|
+
dismiss: labels.dismiss
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function renderDismissedScript(data) {
|
|
219
|
+
const id = JSON.stringify(data.items[0].id).replace(/</g, "\\u003c");
|
|
220
|
+
return `try{var d=JSON.parse(localStorage.getItem(${JSON.stringify(DISMISSED_STORAGE_KEY)})||'[]');if(Array.isArray(d)&&d.indexOf(${id})>-1)document.documentElement.classList.add(${JSON.stringify(DISMISSED_CLASS)})}catch(e){}`;
|
|
221
|
+
}
|
|
116
222
|
|
|
117
223
|
// src/frontmatter.ts
|
|
118
224
|
var import_node_path = __toESM(require("path"), 1);
|
|
119
225
|
var import_yaml = require("yaml");
|
|
226
|
+
|
|
227
|
+
// src/dates.ts
|
|
228
|
+
var DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3})\d*)?)?)?\s*(Z|[+-]\d{2}:?\d{2})?$/i;
|
|
229
|
+
function isTimeZone(timeZone) {
|
|
230
|
+
try {
|
|
231
|
+
new Intl.DateTimeFormat("en-US", { timeZone });
|
|
232
|
+
return true;
|
|
233
|
+
} catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
var warnedZones = /* @__PURE__ */ new Set();
|
|
238
|
+
function resolveTimeZone(timeZone) {
|
|
239
|
+
if (!timeZone) return "UTC";
|
|
240
|
+
if (isTimeZone(timeZone)) return timeZone;
|
|
241
|
+
if (!warnedZones.has(timeZone)) {
|
|
242
|
+
warnedZones.add(timeZone);
|
|
243
|
+
console.warn(
|
|
244
|
+
`[vitepress-blog] "${timeZone}" is not a time zone; using UTC. Use an IANA name such as "America/New_York".`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return "UTC";
|
|
248
|
+
}
|
|
249
|
+
var offsetFormats = /* @__PURE__ */ new Map();
|
|
250
|
+
function zoneOffset(instant, timeZone) {
|
|
251
|
+
let format = offsetFormats.get(timeZone);
|
|
252
|
+
if (!format) {
|
|
253
|
+
format = new Intl.DateTimeFormat("en-US", {
|
|
254
|
+
timeZone,
|
|
255
|
+
hourCycle: "h23",
|
|
256
|
+
year: "numeric",
|
|
257
|
+
month: "2-digit",
|
|
258
|
+
day: "2-digit",
|
|
259
|
+
hour: "2-digit",
|
|
260
|
+
minute: "2-digit",
|
|
261
|
+
second: "2-digit"
|
|
262
|
+
});
|
|
263
|
+
offsetFormats.set(timeZone, format);
|
|
264
|
+
}
|
|
265
|
+
const parts = Object.fromEntries(format.formatToParts(instant).map(({ type, value }) => [type, Number(value)]));
|
|
266
|
+
const wall = Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second);
|
|
267
|
+
return wall - Math.floor(instant / 1e3) * 1e3;
|
|
268
|
+
}
|
|
269
|
+
function fromZonedTime(wall, timeZone) {
|
|
270
|
+
const first = wall - zoneOffset(wall, timeZone);
|
|
271
|
+
const second = wall - zoneOffset(first, timeZone);
|
|
272
|
+
return second;
|
|
273
|
+
}
|
|
274
|
+
function parsePostDate(value, timeZone = "UTC") {
|
|
275
|
+
if (value instanceof Date) {
|
|
276
|
+
if (Number.isNaN(value.getTime())) return null;
|
|
277
|
+
const midnight = value.getUTCHours() === 0 && value.getUTCMinutes() === 0 && value.getUTCSeconds() === 0;
|
|
278
|
+
return { date: value, hasTime: !midnight };
|
|
279
|
+
}
|
|
280
|
+
if (typeof value !== "string") return null;
|
|
281
|
+
const match = DATE_TIME.exec(value.trim());
|
|
282
|
+
if (!match) return null;
|
|
283
|
+
const [, year, month, day, hour, minute, second = "0", millisecond = "0", offset] = match;
|
|
284
|
+
const hasTime = hour !== void 0;
|
|
285
|
+
const wall = Date.UTC(
|
|
286
|
+
Number(year),
|
|
287
|
+
Number(month) - 1,
|
|
288
|
+
Number(day),
|
|
289
|
+
hasTime ? Number(hour) : 0,
|
|
290
|
+
hasTime ? Number(minute) : 0,
|
|
291
|
+
Number(second),
|
|
292
|
+
Number(millisecond.padEnd(3, "0"))
|
|
293
|
+
);
|
|
294
|
+
const check = new Date(wall);
|
|
295
|
+
if (check.getUTCMonth() !== Number(month) - 1 || check.getUTCDate() !== Number(day)) return null;
|
|
296
|
+
if (!offset) return { date: new Date(fromZonedTime(wall, timeZone)), hasTime };
|
|
297
|
+
if (offset.toUpperCase() === "Z") return { date: new Date(wall), hasTime };
|
|
298
|
+
const sign = offset.startsWith("-") ? -1 : 1;
|
|
299
|
+
const digits = offset.slice(1).replace(":", "");
|
|
300
|
+
const minutes = Number(digits.slice(0, 2)) * 60 + Number(digits.slice(2));
|
|
301
|
+
return { date: new Date(wall - sign * minutes * 6e4), hasTime };
|
|
302
|
+
}
|
|
303
|
+
function formatPostDate({ date, hasTime }, display) {
|
|
304
|
+
const { locale, timeZone } = display;
|
|
305
|
+
const day = new Intl.DateTimeFormat(locale, { timeZone, ...display.dateFormat }).format(date);
|
|
306
|
+
if (!hasTime) return day;
|
|
307
|
+
const time = new Intl.DateTimeFormat(locale, { timeZone, ...display.timeFormat }).format(date);
|
|
308
|
+
return display.dateTime.replace("{date}", day).replace("{time}", time);
|
|
309
|
+
}
|
|
310
|
+
function toZonedIsoDate(date, timeZone) {
|
|
311
|
+
const parts = Object.fromEntries(
|
|
312
|
+
new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric", month: "2-digit", day: "2-digit" }).formatToParts(date).map(({ type, value }) => [type, value])
|
|
313
|
+
);
|
|
314
|
+
return `${parts.year}-${parts.month}-${parts.day}`;
|
|
315
|
+
}
|
|
316
|
+
function toDateTimeAttribute({ date, hasTime }, timeZone) {
|
|
317
|
+
return hasTime ? date.toISOString() : toZonedIsoDate(date, timeZone);
|
|
318
|
+
}
|
|
319
|
+
function yearIn(date, timeZone) {
|
|
320
|
+
return Number(toZonedIsoDate(date, timeZone).slice(0, 4));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/frontmatter.ts
|
|
120
324
|
var POST_FRONTMATTER_KEY = "blog";
|
|
121
325
|
var INDEX_FRONTMATTER_KEY = "blog-index";
|
|
122
326
|
var FRONTMATTER = /^---[ \t]*\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)/;
|
|
@@ -155,12 +359,6 @@ function toStringList(value) {
|
|
|
155
359
|
const values = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
156
360
|
return values.map(toText).filter(Boolean);
|
|
157
361
|
}
|
|
158
|
-
function toDate(value) {
|
|
159
|
-
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
|
|
160
|
-
if (typeof value !== "string" && typeof value !== "number") return null;
|
|
161
|
-
const date = new Date(value);
|
|
162
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
163
|
-
}
|
|
164
362
|
function toAuthor(value, options) {
|
|
165
363
|
if (typeof value === "string") {
|
|
166
364
|
const name2 = value.trim();
|
|
@@ -225,11 +423,17 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
225
423
|
const stripTags = options.stripTags ?? true;
|
|
226
424
|
const clean = (text) => stripTags ? stripTagSyntax(text) : text;
|
|
227
425
|
const image = toRecord(pick("image"));
|
|
426
|
+
const timeZone = resolveTimeZone(options.timeZone);
|
|
427
|
+
const date = parsePostDate(pick("date"), timeZone);
|
|
428
|
+
const updated = parsePostDate(pick("updated"), timeZone);
|
|
429
|
+
const announcementText = typeof post.announcement === "string" ? clean(post.announcement.trim()) : "";
|
|
228
430
|
return {
|
|
229
431
|
title: clean(toText(post.title) || toText(data.title) || readHeading(content)) || fallbackTitle,
|
|
230
432
|
summary: clean(toText(post.summary) || toText(data.description) || readExcerpt(content)),
|
|
231
|
-
date:
|
|
232
|
-
|
|
433
|
+
date: date?.date ?? null,
|
|
434
|
+
dateHasTime: date?.hasTime ?? false,
|
|
435
|
+
updated: updated?.date ?? null,
|
|
436
|
+
updatedHasTime: updated?.hasTime ?? false,
|
|
233
437
|
image: toText(image.src) || toText(pick("image")),
|
|
234
438
|
imageAlt: toText(image.alt) || toText(post.imageAlt) || toText(data.imageAlt),
|
|
235
439
|
imageFit: (toText(image.fit) || toText(post.imageFit)) === "contain" ? "contain" : "cover",
|
|
@@ -240,6 +444,8 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
240
444
|
scope: toScope(post.scope),
|
|
241
445
|
sticky: post.sticky === true,
|
|
242
446
|
draft: post.draft === true,
|
|
447
|
+
announcement: post.announcement === true || !!announcementText,
|
|
448
|
+
announcementText,
|
|
243
449
|
byline: toBoolean(post.byline),
|
|
244
450
|
cover: toBoolean(post.cover),
|
|
245
451
|
postNav: toBoolean(post.postNav),
|
|
@@ -298,49 +504,6 @@ function resolveIndexConfig(data, relativePath, defaults = {}) {
|
|
|
298
504
|
var import_node_crypto = require("crypto");
|
|
299
505
|
var import_node_fs = require("fs");
|
|
300
506
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
301
|
-
|
|
302
|
-
// src/util.ts
|
|
303
|
-
var HTML_ESCAPES = {
|
|
304
|
-
"&": "&",
|
|
305
|
-
"<": "<",
|
|
306
|
-
">": ">",
|
|
307
|
-
'"': """,
|
|
308
|
-
"'": "'"
|
|
309
|
-
};
|
|
310
|
-
function escapeHtml(value) {
|
|
311
|
-
return value.replace(/[&<>"']/g, (character) => HTML_ESCAPES[character]);
|
|
312
|
-
}
|
|
313
|
-
function escapeMarkdown(text) {
|
|
314
|
-
const escaped = text.replace(/[\\`*_[\]#|~]/g, "\\$&").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
315
|
-
return /[{}]/.test(escaped) ? `<span v-pre>${escaped}</span>` : escaped;
|
|
316
|
-
}
|
|
317
|
-
function escapeTemplateText(text) {
|
|
318
|
-
const escaped = escapeHtml(text);
|
|
319
|
-
return /[{}]/.test(escaped) ? `<span v-pre>${escaped}</span>` : escaped;
|
|
320
|
-
}
|
|
321
|
-
function isExternalUrl(url) {
|
|
322
|
-
return /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(url);
|
|
323
|
-
}
|
|
324
|
-
function toMarkdownUrl(url) {
|
|
325
|
-
return encodeURI(url).replace(/[()]/g, (character) => character === "(" ? "%28" : "%29");
|
|
326
|
-
}
|
|
327
|
-
function toPageLink(text, routePath) {
|
|
328
|
-
return `[${escapeMarkdown(text)}](${toMarkdownUrl(`/${routePath}`)})`;
|
|
329
|
-
}
|
|
330
|
-
function toUrlPath(routePath, cleanUrls) {
|
|
331
|
-
return `/${routePath.replace(/(^|\/)index\.md$/, "$1").replace(/\.md$/, cleanUrls ? "" : ".html")}`;
|
|
332
|
-
}
|
|
333
|
-
function joinUrl(base, urlPath) {
|
|
334
|
-
return `${base.replace(/\/+$/, "")}/${urlPath.replace(/^\/+/, "")}`;
|
|
335
|
-
}
|
|
336
|
-
function toIsoDate(date) {
|
|
337
|
-
return date.toISOString().slice(0, 10);
|
|
338
|
-
}
|
|
339
|
-
function formatDate(date, locale, format) {
|
|
340
|
-
return new Intl.DateTimeFormat(locale, { timeZone: "UTC", ...format }).format(date);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// src/feed.ts
|
|
344
507
|
function toAbsoluteUrl(urlPath, site) {
|
|
345
508
|
return joinUrl(site.siteUrl, joinUrl(site.base, urlPath));
|
|
346
509
|
}
|
|
@@ -436,37 +599,20 @@ var import_node_path6 = __toESM(require("path"), 1);
|
|
|
436
599
|
|
|
437
600
|
// src/render.ts
|
|
438
601
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
439
|
-
|
|
440
|
-
// src/types.ts
|
|
441
|
-
var DEFAULT_LABELS = {
|
|
442
|
-
by: "By",
|
|
443
|
-
updated: "Updated",
|
|
444
|
-
readingTime: "{n} min read",
|
|
445
|
-
newer: "Newer post",
|
|
446
|
-
older: "Older post",
|
|
447
|
-
allPosts: "All posts in {blog}",
|
|
448
|
-
draft: "Draft",
|
|
449
|
-
sticky: "Pinned",
|
|
450
|
-
noPosts: "No posts yet.",
|
|
451
|
-
undated: "Undated",
|
|
452
|
-
tags: "Keywords",
|
|
453
|
-
tagged: "Posts tagged {tag}",
|
|
454
|
-
showAll: "Show all posts",
|
|
455
|
-
noTagged: "No posts tagged {tag}.",
|
|
456
|
-
previous: "Previous",
|
|
457
|
-
next: "Next",
|
|
458
|
-
pages: "Pages",
|
|
459
|
-
subscribe: "Subscribe",
|
|
460
|
-
rssFeed: "RSS feed for {blog}"
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
// src/render.ts
|
|
464
602
|
var BLOG_POSTS_PLACEHOLDER = "<!-- blog-posts -->";
|
|
465
603
|
function createRenderContext(options, pageDirectory, feeds = !!options.siteUrl) {
|
|
604
|
+
const locale = options.locale ?? "en-US";
|
|
605
|
+
const labels = { ...DEFAULT_LABELS, ...options.labels };
|
|
466
606
|
return {
|
|
467
|
-
locale
|
|
468
|
-
|
|
469
|
-
|
|
607
|
+
locale,
|
|
608
|
+
dates: {
|
|
609
|
+
locale,
|
|
610
|
+
timeZone: resolveTimeZone(options.timeZone),
|
|
611
|
+
dateFormat: options.dateFormat ?? { dateStyle: "long" },
|
|
612
|
+
timeFormat: options.timeFormat ?? { hour: "numeric", minute: "2-digit", timeZoneName: "short" },
|
|
613
|
+
dateTime: labels.dateTime
|
|
614
|
+
},
|
|
615
|
+
labels,
|
|
470
616
|
pageDirectory,
|
|
471
617
|
feeds
|
|
472
618
|
};
|
|
@@ -488,8 +634,10 @@ function toImageSource(post, pageDirectory) {
|
|
|
488
634
|
const relative = import_node_path3.default.relative(pageDirectory, absolute).split(import_node_path3.default.sep).join("/");
|
|
489
635
|
return relative.startsWith(".") ? relative : `./${relative}`;
|
|
490
636
|
}
|
|
491
|
-
function renderTime(date, context) {
|
|
492
|
-
|
|
637
|
+
function renderTime(date, hasTime, context) {
|
|
638
|
+
const value = { date, hasTime };
|
|
639
|
+
const attribute = toDateTimeAttribute(value, context.dates.timeZone);
|
|
640
|
+
return `<time datetime="${attribute}">${escapeTemplateText(formatPostDate(value, context.dates))}</time>`;
|
|
493
641
|
}
|
|
494
642
|
function renderAuthor(author) {
|
|
495
643
|
return author.url ? `[${escapeMarkdown(author.name)}](${toMarkdownUrl(author.url)})` : escapeMarkdown(author.name);
|
|
@@ -517,17 +665,30 @@ function renderTags(tags, blogRoutePath) {
|
|
|
517
665
|
function toTagsAttribute(post) {
|
|
518
666
|
return escapeHtml(post.post.tags.map((tag) => tag.toLowerCase()).join("|"));
|
|
519
667
|
}
|
|
668
|
+
function toItemAttributes(post) {
|
|
669
|
+
return `data-vp-blog-item data-tags="${toTagsAttribute(post)}"${post.post.announcement ? " data-announcement" : ""}`;
|
|
670
|
+
}
|
|
671
|
+
function renderBadges(post, context) {
|
|
672
|
+
const { draft, sticky, announcement } = post.post;
|
|
673
|
+
return [
|
|
674
|
+
draft && renderBadge("draft", context),
|
|
675
|
+
sticky && renderBadge("sticky", context),
|
|
676
|
+
announcement && renderBadge("announcement", context)
|
|
677
|
+
].filter(Boolean).join(" ");
|
|
678
|
+
}
|
|
520
679
|
function renderMeta(post, context, include) {
|
|
521
|
-
const { date,
|
|
680
|
+
const { date, dateHasTime, updated, updatedHasTime, readingMinutes, authors } = post.post;
|
|
522
681
|
const parts = [];
|
|
523
682
|
if (include.badges) {
|
|
524
|
-
const
|
|
525
|
-
const shown = badges.filter(Boolean).join(" ");
|
|
683
|
+
const shown = renderBadges(post, context);
|
|
526
684
|
if (shown) parts.push(shown);
|
|
527
685
|
}
|
|
528
|
-
if (date)
|
|
686
|
+
if (date) {
|
|
687
|
+
const time = renderTime(date, dateHasTime, context);
|
|
688
|
+
parts.push(include.updated ? `${escapeMarkdown(context.labels.published)} ${time}` : time);
|
|
689
|
+
}
|
|
529
690
|
if (include.updated && updated && (!date || updated.getTime() !== date.getTime())) {
|
|
530
|
-
parts.push(`${escapeMarkdown(context.labels.updated)} ${renderTime(updated, context)}`);
|
|
691
|
+
parts.push(`${escapeMarkdown(context.labels.updated)} ${renderTime(updated, updatedHasTime, context)}`);
|
|
531
692
|
}
|
|
532
693
|
if (readingMinutes) parts.push(escapeMarkdown(fill(context.labels.readingTime, { n: String(readingMinutes) })));
|
|
533
694
|
if (include.authors) {
|
|
@@ -548,12 +709,13 @@ function renderCard(post, context, index) {
|
|
|
548
709
|
const modifiers = [
|
|
549
710
|
post.post.sticky && "sticky",
|
|
550
711
|
post.post.draft && "draft",
|
|
712
|
+
post.post.announcement && "announcement",
|
|
551
713
|
image && "with-image",
|
|
552
714
|
image && post.post.imageFit === "contain" && "contain"
|
|
553
715
|
].filter(Boolean).map((modifier) => ` vp-blog-card--${modifier}`).join("");
|
|
554
716
|
const alt = escapeMarkdown(post.post.imageAlt);
|
|
555
717
|
return [
|
|
556
|
-
`<article class="vp-blog-card${modifiers}"${imageStyle(post)}
|
|
718
|
+
`<article class="vp-blog-card${modifiers}"${imageStyle(post)} ${toItemAttributes(post)}>`,
|
|
557
719
|
...image ? block("vp-blog-card__image", `[})](${toMarkdownUrl(link)})`) : [],
|
|
558
720
|
'<div class="vp-blog-card__body">',
|
|
559
721
|
...block("vp-blog-card__title", toPageLink(post.post.title, post.routePath)),
|
|
@@ -565,9 +727,9 @@ function renderCard(post, context, index) {
|
|
|
565
727
|
];
|
|
566
728
|
}
|
|
567
729
|
function renderCompactItem(post, context) {
|
|
568
|
-
const date = post.post.date ? `${renderTime(post.post.date, context)} ` : "";
|
|
569
|
-
const badges =
|
|
570
|
-
const marker = `<span hidden
|
|
730
|
+
const date = post.post.date ? `${renderTime(post.post.date, post.post.dateHasTime, context)} ` : "";
|
|
731
|
+
const badges = renderBadges(post, context);
|
|
732
|
+
const marker = `<span hidden ${toItemAttributes(post)}></span>`;
|
|
571
733
|
return `- ${marker}${date}${toPageLink(post.post.title, post.routePath)}${badges ? ` ${badges}` : ""}`;
|
|
572
734
|
}
|
|
573
735
|
function renderGroup(posts, index, context) {
|
|
@@ -590,7 +752,7 @@ function renderGroup(posts, index, context) {
|
|
|
590
752
|
function renderYears(posts, index, context) {
|
|
591
753
|
const years = /* @__PURE__ */ new Map();
|
|
592
754
|
for (const post of posts) {
|
|
593
|
-
const year = post.post.date ? String(post.post.date.
|
|
755
|
+
const year = post.post.date ? String(yearIn(post.post.date, context.dates.timeZone)) : context.labels.undated;
|
|
594
756
|
years.set(year, [...years.get(year) ?? [], post]);
|
|
595
757
|
}
|
|
596
758
|
const lines = [];
|
|
@@ -642,7 +804,9 @@ function renderClientAttributes(index, context) {
|
|
|
642
804
|
"data-label-no-tagged": labels.noTagged,
|
|
643
805
|
"data-label-previous": labels.previous,
|
|
644
806
|
"data-label-next": labels.next,
|
|
645
|
-
"data-label-pages": labels.pages
|
|
807
|
+
"data-label-pages": labels.pages,
|
|
808
|
+
"data-label-announcements": labels.announcements,
|
|
809
|
+
"data-label-no-announcements": labels.noAnnouncements
|
|
646
810
|
};
|
|
647
811
|
return Object.entries(attributes).map(([name, value]) => `${name}="${escapeHtml(value)}"`).join(" ");
|
|
648
812
|
}
|
|
@@ -765,7 +929,8 @@ function toOptionsKey(options) {
|
|
|
765
929
|
options.stripTags,
|
|
766
930
|
options.pageSize,
|
|
767
931
|
options.stickyLimit,
|
|
768
|
-
options.rssLink
|
|
932
|
+
options.rssLink,
|
|
933
|
+
options.timeZone
|
|
769
934
|
]);
|
|
770
935
|
}
|
|
771
936
|
function indexBlogSource(sourceDirectory, options = {}, rewrites = {}) {
|
|
@@ -1028,6 +1193,9 @@ function blogMarkdown(md, options = {}) {
|
|
|
1028
1193
|
|
|
1029
1194
|
// src/vitepress.ts
|
|
1030
1195
|
var DEFAULT_FEED_LIMIT = 20;
|
|
1196
|
+
var PACKAGE_NAME = "@binarynoir/vitepress-blog";
|
|
1197
|
+
var SITE_DATA_MODULE = "/@siteData";
|
|
1198
|
+
var SCRIPT_MARKER = "data-vp-blog-announcement";
|
|
1031
1199
|
function resolveSiteLocation(siteConfig, options) {
|
|
1032
1200
|
const siteUrl = options.siteUrl ?? siteConfig.userConfig?.sitemap?.hostname ?? siteConfig.sitemap?.hostname;
|
|
1033
1201
|
if (!siteUrl) return null;
|
|
@@ -1042,6 +1210,16 @@ function resolveSiteLocation(siteConfig, options) {
|
|
|
1042
1210
|
function loadPages(siteConfig, options) {
|
|
1043
1211
|
return loadBlogPages(siteConfig.srcDir, options, siteConfig);
|
|
1044
1212
|
}
|
|
1213
|
+
function attachAnnouncements(siteConfig, options = {}) {
|
|
1214
|
+
const site = siteConfig.site;
|
|
1215
|
+
const data = resolveAnnouncements(loadPages(siteConfig, options), options, !!siteConfig.cleanUrls);
|
|
1216
|
+
const changed = JSON.stringify(site[ANNOUNCEMENTS_KEY] ?? null) !== JSON.stringify(data);
|
|
1217
|
+
if (data) site[ANNOUNCEMENTS_KEY] = data;
|
|
1218
|
+
else delete site[ANNOUNCEMENTS_KEY];
|
|
1219
|
+
site.head = (site.head ?? []).filter(([, attributes]) => !(SCRIPT_MARKER in (attributes ?? {})));
|
|
1220
|
+
if (data) site.head.push(["script", { [SCRIPT_MARKER]: "" }, renderDismissedScript(data)]);
|
|
1221
|
+
return changed;
|
|
1222
|
+
}
|
|
1045
1223
|
function listFeedPosts(pages, index, options) {
|
|
1046
1224
|
const limit = index.index.feed?.limit ?? options.feedLimit ?? DEFAULT_FEED_LIMIT;
|
|
1047
1225
|
return listBlogPosts(pages, index, { drafts: false, stickyFirst: false, applyLimit: false }).slice(0, limit);
|
|
@@ -1146,11 +1324,26 @@ function blogVitePlugin(options = {}) {
|
|
|
1146
1324
|
let siteConfig;
|
|
1147
1325
|
const currentPages = () => siteConfig ? loadPages(siteConfig, options) : [];
|
|
1148
1326
|
const dependentFiles = () => [...currentPages().map((page) => page.absolutePath), ...widgetPages];
|
|
1327
|
+
const invalidate = (server, files) => {
|
|
1328
|
+
for (const file of files) {
|
|
1329
|
+
for (const module2 of server.moduleGraph.getModulesByFile(file) ?? []) server.moduleGraph.invalidateModule(module2);
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
const refreshAnnouncements = (server) => {
|
|
1333
|
+
if (!siteConfig || !attachAnnouncements(siteConfig, options)) return false;
|
|
1334
|
+
const module2 = server.moduleGraph.getModuleById(SITE_DATA_MODULE);
|
|
1335
|
+
if (module2) server.moduleGraph.invalidateModule(module2);
|
|
1336
|
+
return true;
|
|
1337
|
+
};
|
|
1149
1338
|
return {
|
|
1150
1339
|
name: "vitepress-blog",
|
|
1151
1340
|
enforce: "pre",
|
|
1341
|
+
config() {
|
|
1342
|
+
return { ssr: { noExternal: [PACKAGE_NAME] } };
|
|
1343
|
+
},
|
|
1152
1344
|
configResolved(config) {
|
|
1153
1345
|
siteConfig = config.vitepress;
|
|
1346
|
+
if (siteConfig) attachAnnouncements(siteConfig, options);
|
|
1154
1347
|
},
|
|
1155
1348
|
transform(code, id) {
|
|
1156
1349
|
if (!siteConfig || !id.endsWith(".md") || !(mayHaveBlogFrontmatter(code) || hasWidget(code))) return null;
|
|
@@ -1161,11 +1354,8 @@ function blogVitePlugin(options = {}) {
|
|
|
1161
1354
|
configureServer(server) {
|
|
1162
1355
|
const refresh = (file) => {
|
|
1163
1356
|
if (!siteConfig || !file.endsWith(".md")) return;
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
server.moduleGraph.invalidateModule(module2);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1357
|
+
invalidate(server, dependentFiles());
|
|
1358
|
+
refreshAnnouncements(server);
|
|
1169
1359
|
server.ws.send({ type: "full-reload" });
|
|
1170
1360
|
};
|
|
1171
1361
|
server.watcher.on("add", refresh).on("unlink", refresh);
|
|
@@ -1196,6 +1386,11 @@ function blogVitePlugin(options = {}) {
|
|
|
1196
1386
|
},
|
|
1197
1387
|
handleHotUpdate({ file, modules, server }) {
|
|
1198
1388
|
if (!siteConfig || !file.endsWith(".md")) return;
|
|
1389
|
+
if (refreshAnnouncements(server)) {
|
|
1390
|
+
invalidate(server, dependentFiles());
|
|
1391
|
+
server.ws.send({ type: "full-reload" });
|
|
1392
|
+
return [];
|
|
1393
|
+
}
|
|
1199
1394
|
const pages = currentPages();
|
|
1200
1395
|
if (!pages.some((page) => page.absolutePath === file)) return;
|
|
1201
1396
|
const affected = dependentFiles().filter((dependent) => dependent !== file).flatMap((dependent) => [...server.moduleGraph.getModulesByFile(dependent) ?? []]);
|
|
@@ -1232,6 +1427,7 @@ function withBlog(config, options = {}) {
|
|
|
1232
1427
|
}
|
|
1233
1428
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1234
1429
|
0 && (module.exports = {
|
|
1430
|
+
attachAnnouncements,
|
|
1235
1431
|
blogHead,
|
|
1236
1432
|
blogMarkdown,
|
|
1237
1433
|
blogPageData,
|