@sugarat/theme 0.1.40 → 0.1.42

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/node.d.ts CHANGED
@@ -260,22 +260,38 @@ declare namespace Theme {
260
260
  */
261
261
  RSS?: RSSOptions;
262
262
  }
263
- type RSSOptions = FeedOptions & {
263
+ type RSSOptions = Omit<FeedOptions, 'id'> & {
264
+ id?: string;
265
+ /**
266
+ * 你的站点地址
267
+ * @example 'https://sugarat.top'
268
+ */
264
269
  baseUrl: string;
265
270
  /**
266
271
  * 线上访问的RSS地址
272
+ * @default
273
+ * @example https://sugarat.top/feed.rss
274
+ * ```ts
275
+ * `${baseUrl + VPConfig.site.base + (filename || 'feed.rss'}`
276
+ * ```
267
277
  */
268
- url: string;
278
+ url?: string;
269
279
  /**
270
280
  * 输出的RSS文件名
271
281
  * @default 'feed.rss'
272
282
  */
273
283
  filename?: string;
274
284
  /**
275
- * 是否展示RSS的图标
285
+ * RSS的图标展示
276
286
  * @default true
277
287
  */
278
- showIcon?: boolean;
288
+ icon?: boolean;
289
+ /**
290
+ * 限制输出文件包含的文章数量
291
+ * @default 0
292
+ * @description (0 不限制;> 1 会按照日期排序对输出内容进行调整)
293
+ */
294
+ limit?: number;
279
295
  };
280
296
  interface Config extends DefaultTheme.Config {
281
297
  blog?: BlogConfig;
package/node.js CHANGED
@@ -352,6 +352,11 @@ function supportRunExtendsPlugin(config) {
352
352
  markdownExtendsConfigOriginal?.(...rest);
353
353
  };
354
354
  }
355
+ const inlineConfig = config.extends;
356
+ if (inlineConfig.themeConfig?.blog?.RSS && inlineConfig.themeConfig?.blog?.RSS?.icon !== false && inlineConfig.themeConfig?.socialLinks?.length && !inlineConfig.themeConfig?.socialLinks?.[0].link) {
357
+ const { RSS } = inlineConfig.themeConfig?.blog;
358
+ inlineConfig.themeConfig.socialLinks[0].link = `${RSS.baseUrl}${(config.base || "/") + (RSS.filename || "feed.rss")}`;
359
+ }
355
360
  }
356
361
 
357
362
  // src/utils/node/theme.ts
@@ -369,6 +374,7 @@ function patchDefaultThemeSideBar(cfg) {
369
374
  ]
370
375
  } : void 0;
371
376
  }
377
+ var pageMap = /* @__PURE__ */ new Map();
372
378
  function getArticles(cfg) {
373
379
  const srcDir = cfg?.srcDir || process.argv.slice(2)?.[1] || ".";
374
380
  const files = import_fast_glob.default.sync(`${srcDir}/**/*.md`, { ignore: ["node_modules"] });
@@ -389,9 +395,13 @@ function getArticles(cfg) {
389
395
  ""
390
396
  );
391
397
  }
398
+ pageMap.set(`/${route}`, v);
392
399
  const fileContent = import_fs.default.readFileSync(v, "utf-8");
400
+ const { data: frontmatter, excerpt } = (0, import_gray_matter.default)(fileContent, {
401
+ excerpt: true
402
+ });
393
403
  const meta = {
394
- ...(0, import_gray_matter.default)(fileContent).data
404
+ ...frontmatter
395
405
  };
396
406
  if (!meta.title) {
397
407
  meta.title = getDefaultTitle(fileContent);
@@ -425,13 +435,13 @@ function getArticles(cfg) {
425
435
  }
426
436
  function patchVPThemeConfig(cfg, vpThemeConfig = {}) {
427
437
  const RSS = cfg?.RSS;
428
- if (RSS && RSS.showIcon !== false) {
438
+ if (RSS && RSS.icon !== false) {
429
439
  vpThemeConfig.socialLinks = [
430
440
  {
431
441
  icon: {
432
442
  svg: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 448 512"><path d="M400 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zM112 416c-26.51 0-48-21.49-48-48s21.49-48 48-48s48 21.49 48 48s-21.49 48-48 48zm157.533 0h-34.335c-6.011 0-11.051-4.636-11.442-10.634c-5.214-80.05-69.243-143.92-149.123-149.123c-5.997-.39-10.633-5.431-10.633-11.441v-34.335c0-6.535 5.468-11.777 11.994-11.425c110.546 5.974 198.997 94.536 204.964 204.964c.352 6.526-4.89 11.994-11.425 11.994zm103.027 0h-34.334c-6.161 0-11.175-4.882-11.427-11.038c-5.598-136.535-115.204-246.161-251.76-251.76C68.882 152.949 64 147.935 64 141.774V107.44c0-6.454 5.338-11.664 11.787-11.432c167.83 6.025 302.21 141.191 308.205 308.205c.232 6.449-4.978 11.787-11.432 11.787z" fill="currentColor"></path></svg>'
433
443
  },
434
- link: RSS.url
444
+ link: RSS?.url
435
445
  }
436
446
  ];
437
447
  }
@@ -445,34 +455,58 @@ var import_child_process2 = require("child_process");
445
455
 
446
456
  // src/utils/node/genFeed.ts
447
457
  var import_path2 = __toESM(require("path"));
448
- var import_fs2 = require("fs");
458
+ var import_fs2 = __toESM(require("fs"));
449
459
  var import_feed = require("feed");
450
- function genFeed(config) {
460
+ async function genFeed(config) {
451
461
  const blogCfg = config.userConfig.themeConfig.blog;
452
- const posts = blogCfg.pagesData;
462
+ let posts = blogCfg.pagesData;
453
463
  const { RSS, authorList = [] } = blogCfg;
454
464
  if (!RSS)
455
465
  return;
466
+ const { createMarkdownRenderer } = await import("vitepress");
467
+ const mdRender = await createMarkdownRenderer(
468
+ config.srcDir,
469
+ config.markdown,
470
+ config.site.base,
471
+ config.logger
472
+ );
456
473
  console.log();
457
474
  console.log("=== feed: https://github.com/jpmonette/feed ===");
458
475
  const { base } = config.userConfig;
459
476
  const { baseUrl, filename } = RSS;
460
- const feed = new import_feed.Feed(RSS);
477
+ const feed = new import_feed.Feed({
478
+ id: baseUrl,
479
+ link: baseUrl,
480
+ ...RSS
481
+ });
461
482
  posts.sort(
462
483
  (a, b) => +new Date(b.meta.date) - +new Date(a.meta.date)
463
484
  );
485
+ posts = posts.filter((v) => v.meta.layout !== "home").filter((v) => v.meta.hidden !== true);
486
+ if (void 0 !== RSS?.limit && RSS?.limit > 0) {
487
+ posts.splice(RSS.limit);
488
+ }
464
489
  for (const { route, meta } of posts) {
465
- const { title, description, date, hidden } = meta;
466
- if (hidden)
467
- continue;
490
+ const { title, description, date } = meta;
468
491
  const author = meta.author ?? blogCfg.author;
469
- const link = `${baseUrl}${withBase(base || "", route)}.html`;
492
+ let link = `${baseUrl}${withBase(
493
+ base || "",
494
+ route.replace(/(^|\/)index$/, "$1")
495
+ )}`;
496
+ link = link.endsWith("/") ? link : `${link}${config?.cleanUrls ? "" : ".html"}`;
470
497
  const authorLink = authorList.find((v) => v.nickname === author)?.url;
498
+ let html;
499
+ const filepath = pageMap.get(route);
500
+ if (filepath) {
501
+ const fileContent = import_fs2.default.readFileSync(filepath, "utf-8");
502
+ html = mdRender.render(fileContent);
503
+ }
471
504
  feed.addItem({
472
505
  title,
473
506
  id: link,
474
507
  link,
475
508
  description,
509
+ content: html,
476
510
  author: [
477
511
  {
478
512
  name: author,
@@ -482,9 +516,14 @@ function genFeed(config) {
482
516
  date: new Date(date)
483
517
  });
484
518
  }
485
- const RSSFile = import_path2.default.join(config.outDir, filename || "feed.rss");
519
+ const RSSFilename = filename || "feed.rss";
520
+ const RSSFile = import_path2.default.join(config.outDir, RSSFilename);
486
521
  (0, import_fs2.writeFileSync)(RSSFile, feed.rss2());
487
- console.log("\u{1F389} RSS generated", filename || "feed.rss");
522
+ console.log("\u{1F389} RSS generated", RSSFilename);
523
+ console.log("rss filepath:", RSSFile);
524
+ console.log("rss url:", `${baseUrl}${config.site.base + RSSFilename}`);
525
+ console.log("include", posts.length, "posts");
526
+ console.log();
488
527
  console.log();
489
528
  }
490
529
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -58,10 +58,10 @@
58
58
  "scripts": {
59
59
  "dev": "npm run build:node && npm run dev:docs",
60
60
  "dev:docs": "vitepress dev docs",
61
- "dev:node": "npx tsup src/node.ts --dts --out-dir=./ --watch",
61
+ "dev:node": "npx tsup src/node.ts --dts --out-dir=./ --watch --external vitepress",
62
62
  "build": "npm run build:node && npm run build:docs",
63
63
  "build:docs": "vitepress build docs",
64
- "build:node": "npx tsup src/node.ts --dts --out-dir=./",
64
+ "build:node": "npx tsup src/node.ts --dts --out-dir=./ --external vitepress",
65
65
  "serve": "vitepress serve docs"
66
66
  }
67
67
  }
@@ -141,7 +141,7 @@ export function useActiveAnchor() {
141
141
  onMounted(() => {
142
142
  const { hash } = window.location
143
143
  if (hash) {
144
- el.value = document.querySelector(decodeURIComponent(hash))
144
+ el.value = document.querySelector(decodeURIComponent(hash)) as any
145
145
  }
146
146
  })
147
147
  return el
@@ -281,22 +281,38 @@ export namespace Theme {
281
281
  RSS?: RSSOptions
282
282
  }
283
283
 
284
- export type RSSOptions = FeedOptions & {
284
+ export type RSSOptions = Omit<FeedOptions, 'id'> & {
285
+ id?: string
286
+ /**
287
+ * 你的站点地址
288
+ * @example 'https://sugarat.top'
289
+ */
285
290
  baseUrl: string
286
291
  /**
287
292
  * 线上访问的RSS地址
293
+ * @default
294
+ * @example https://sugarat.top/feed.rss
295
+ * ```ts
296
+ * `${baseUrl + VPConfig.site.base + (filename || 'feed.rss'}`
297
+ * ```
288
298
  */
289
- url: string
299
+ url?: string
290
300
  /**
291
301
  * 输出的RSS文件名
292
302
  * @default 'feed.rss'
293
303
  */
294
304
  filename?: string
295
305
  /**
296
- * 是否展示RSS的图标
306
+ * RSS的图标展示
297
307
  * @default true
298
308
  */
299
- showIcon?: boolean
309
+ icon?: boolean
310
+ /**
311
+ * 限制输出文件包含的文章数量
312
+ * @default 0
313
+ * @description (0 不限制;> 1 会按照日期排序对输出内容进行调整)
314
+ */
315
+ limit?: number
300
316
  }
301
317
  export interface Config extends DefaultTheme.Config {
302
318
  blog?: BlogConfig
@@ -1,42 +1,78 @@
1
1
  /* eslint-disable no-console */
2
2
  import path from 'path'
3
- import { writeFileSync } from 'fs'
3
+ import fs, { writeFileSync } from 'fs'
4
4
  import { Feed } from 'feed'
5
5
  import type { SiteConfig } from 'vitepress'
6
6
  import type { Theme } from '../../composables/config/index'
7
7
  import { withBase } from './index'
8
+ import { pageMap } from './theme'
8
9
 
9
- export function genFeed(config: SiteConfig) {
10
+ export async function genFeed(config: SiteConfig) {
10
11
  const blogCfg: Theme.BlogConfig = config.userConfig.themeConfig.blog
11
- const posts: Theme.PageData[] = blogCfg.pagesData
12
+ let posts: Theme.PageData[] = blogCfg.pagesData
12
13
  const { RSS, authorList = [] } = blogCfg
13
14
  if (!RSS) return
15
+ const { createMarkdownRenderer } = await import('vitepress')
16
+
17
+ const mdRender = await createMarkdownRenderer(
18
+ config.srcDir,
19
+ config.markdown,
20
+ config.site.base,
21
+ config.logger
22
+ )
14
23
  console.log()
15
24
  console.log('=== feed: https://github.com/jpmonette/feed ===')
16
25
  const { base } = config.userConfig
17
26
 
18
27
  const { baseUrl, filename } = RSS
19
- const feed = new Feed(RSS)
28
+ const feed = new Feed({
29
+ id: baseUrl,
30
+ link: baseUrl,
31
+ ...RSS
32
+ })
20
33
 
21
34
  posts.sort(
22
35
  (a, b) =>
23
36
  +new Date(b.meta.date as string) - +new Date(a.meta.date as string)
24
37
  )
25
38
 
39
+ posts = posts
40
+ // 过滤掉 layout:home
41
+ .filter((v) => v.meta.layout !== 'home')
42
+ // 过滤掉不展示的
43
+ .filter((v) => v.meta.hidden !== true)
44
+
45
+ if (undefined !== RSS?.limit && RSS?.limit > 0) {
46
+ posts.splice(RSS.limit)
47
+ }
48
+
26
49
  for (const { route, meta } of posts) {
27
- const { title, description, date, hidden } = meta
28
- if (hidden) continue
50
+ const { title, description, date } = meta
51
+
29
52
  const author = meta.author ?? blogCfg.author
30
- const link = `${baseUrl}${withBase(base || '', route)}.html`
53
+ let link = `${baseUrl}${withBase(
54
+ base || '',
55
+ // 移除末尾的index
56
+ route.replace(/(^|\/)index$/, '$1')
57
+ )}`
58
+ // 补全后缀
59
+ link = link.endsWith('/')
60
+ ? link
61
+ : `${link}${config?.cleanUrls ? '' : '.html'}`
31
62
  const authorLink = authorList.find((v) => v.nickname === author)?.url
63
+ let html
64
+ const filepath = pageMap.get(route)
65
+ if (filepath) {
66
+ const fileContent = fs.readFileSync(filepath, 'utf-8')
67
+ html = mdRender.render(fileContent)
68
+ }
69
+
32
70
  feed.addItem({
33
71
  title,
34
72
  id: link,
35
- // TODO: 待定,添加transform
36
73
  link,
37
74
  description,
38
- // TODO: 待定,文章多的时候,会导致 RSS 文件过大
39
- // content: html,
75
+ content: html,
40
76
  author: [
41
77
  {
42
78
  name: author,
@@ -46,8 +82,13 @@ export function genFeed(config: SiteConfig) {
46
82
  date: new Date(date)
47
83
  })
48
84
  }
49
- const RSSFile = path.join(config.outDir, filename || 'feed.rss')
85
+ const RSSFilename = filename || 'feed.rss'
86
+ const RSSFile = path.join(config.outDir, RSSFilename)
50
87
  writeFileSync(RSSFile, feed.rss2())
51
- console.log('🎉 RSS generated', filename || 'feed.rss')
88
+ console.log('🎉 RSS generated', RSSFilename)
89
+ console.log('rss filepath:', RSSFile)
90
+ console.log('rss url:', `${baseUrl}${config.site.base + RSSFilename}`)
91
+ console.log('include', posts.length, 'posts')
92
+ console.log()
52
93
  console.log()
53
94
  }
@@ -92,4 +92,19 @@ export function supportRunExtendsPlugin(config: UserConfig<Theme.Config>) {
92
92
  markdownExtendsConfigOriginal?.(...rest)
93
93
  }
94
94
  }
95
+
96
+ // 特殊处理RSS,自动生成url(未来统一维护到 RSS插件里,待下一版主题架构升级)
97
+ const inlineConfig = config.extends as UserConfig<Theme.Config>
98
+
99
+ if (
100
+ inlineConfig.themeConfig?.blog?.RSS &&
101
+ inlineConfig.themeConfig?.blog?.RSS?.icon !== false &&
102
+ inlineConfig.themeConfig?.socialLinks?.length &&
103
+ !inlineConfig.themeConfig?.socialLinks?.[0].link
104
+ ) {
105
+ const { RSS } = inlineConfig.themeConfig?.blog
106
+ inlineConfig.themeConfig.socialLinks[0].link = `${RSS.baseUrl}${
107
+ (config.base || '/') + (RSS.filename || 'feed.rss')
108
+ }`
109
+ }
95
110
  }
@@ -20,6 +20,9 @@ export function patchDefaultThemeSideBar(cfg?: Partial<Theme.BlogConfig>) {
20
20
  : undefined
21
21
  }
22
22
 
23
+ // hack:RSS用
24
+ export const pageMap = new Map<string, string>()
25
+
23
26
  export function getArticles(cfg?: Partial<Theme.BlogConfig>) {
24
27
  const srcDir = cfg?.srcDir || process.argv.slice(2)?.[1] || '.'
25
28
  const files = glob.sync(`${srcDir}/**/*.md`, { ignore: ['node_modules'] })
@@ -51,11 +54,17 @@ export function getArticles(cfg?: Partial<Theme.BlogConfig>) {
51
54
  ''
52
55
  )
53
56
  }
57
+ // hack:RSS使用
58
+ pageMap.set(`/${route}`, v)
54
59
 
55
60
  const fileContent = fs.readFileSync(v, 'utf-8')
61
+ // TODO:摘要生成优化
62
+ const { data: frontmatter, excerpt } = matter(fileContent, {
63
+ excerpt: true
64
+ })
56
65
 
57
66
  const meta: Partial<Theme.PageMeta> = {
58
- ...matter(fileContent).data
67
+ ...frontmatter
59
68
  }
60
69
 
61
70
  if (!meta.title) {
@@ -118,15 +127,15 @@ export function patchVPThemeConfig(
118
127
  cfg?: Partial<Theme.BlogConfig>,
119
128
  vpThemeConfig: any = {}
120
129
  ) {
121
- // 添加 icon
130
+ // 添加 RSS icon
122
131
  const RSS = cfg?.RSS
123
- if (RSS && RSS.showIcon !== false) {
132
+ if (RSS && RSS.icon !== false) {
124
133
  vpThemeConfig.socialLinks = [
125
134
  {
126
135
  icon: {
127
136
  svg: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 448 512"><path d="M400 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zM112 416c-26.51 0-48-21.49-48-48s21.49-48 48-48s48 21.49 48 48s-21.49 48-48 48zm157.533 0h-34.335c-6.011 0-11.051-4.636-11.442-10.634c-5.214-80.05-69.243-143.92-149.123-149.123c-5.997-.39-10.633-5.431-10.633-11.441v-34.335c0-6.535 5.468-11.777 11.994-11.425c110.546 5.974 198.997 94.536 204.964 204.964c.352 6.526-4.89 11.994-11.425 11.994zm103.027 0h-34.334c-6.161 0-11.175-4.882-11.427-11.038c-5.598-136.535-115.204-246.161-251.76-251.76C68.882 152.949 64 147.935 64 141.774V107.44c0-6.454 5.338-11.664 11.787-11.432c167.83 6.025 302.21 141.191 308.205 308.205c.232 6.449-4.978 11.787-11.432 11.787z" fill="currentColor"></path></svg>'
128
137
  },
129
- link: RSS.url
138
+ link: RSS?.url
130
139
  }
131
140
  ]
132
141
  }