valaxy 0.1.2 → 0.2.2

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.
Files changed (55) hide show
  1. package/dist/chunk-2K6ROMWQ.mjs +88 -0
  2. package/dist/chunk-NBGVYWSW.js +88 -0
  3. package/dist/chunk-TSLYS2VY.js +1 -0
  4. package/dist/chunk-U4ZDCBEC.mjs +1 -0
  5. package/dist/{config-d6527c8c.d.ts → config-24b4f209.d.ts} +16 -2
  6. package/dist/index.d.ts +37 -7
  7. package/dist/index.js +1 -1
  8. package/dist/index.mjs +1 -1
  9. package/dist/node/cli.js +44 -5
  10. package/dist/node/cli.mjs +44 -5
  11. package/dist/node/index.d.ts +1 -1
  12. package/dist/node/index.js +1 -1
  13. package/dist/node/index.mjs +1 -1
  14. package/package.json +15 -13
  15. package/src/client/components/PostCard.vue +15 -16
  16. package/src/client/components/ValaxyMd.vue +1 -1
  17. package/src/client/components/ValaxySidebar.vue +0 -2
  18. package/src/client/components/ValaxyToc.vue +1 -1
  19. package/src/client/composables/category.ts +24 -5
  20. package/src/client/composables/comments/index.ts +1 -0
  21. package/src/client/composables/comments/twikoo.ts +37 -0
  22. package/src/client/composables/comments/waline.ts +0 -1
  23. package/src/client/composables/common.ts +2 -1
  24. package/src/client/composables/post.ts +26 -1
  25. package/src/client/composables/tag.ts +9 -2
  26. package/src/client/index.html +5 -0
  27. package/src/client/layouts/404.vue +5 -1
  28. package/src/client/main.ts +1 -3
  29. package/src/client/modules/valaxy.ts +3 -2
  30. package/src/client/shims.d.ts +0 -5
  31. package/src/client/styles/common/code.scss +181 -9
  32. package/src/client/styles/common/custom-blocks.scss +84 -0
  33. package/src/client/styles/common/markdown.scss +1 -4
  34. package/src/client/styles/css-vars.scss +12 -5
  35. package/src/client/styles/global/i18n.scss +20 -0
  36. package/src/client/styles/index.scss +21 -8
  37. package/src/client/styles/palette.scss +57 -48
  38. package/src/node/cli.ts +15 -0
  39. package/src/node/markdown/headings.ts +3 -2
  40. package/src/node/markdown/highlight.ts +50 -0
  41. package/src/node/markdown/highlightLines.ts +96 -0
  42. package/src/node/markdown/index.ts +20 -11
  43. package/src/node/markdown/markdown-it-container.ts +9 -1
  44. package/src/node/plugins/index.ts +1 -1
  45. package/src/node/plugins/preset.ts +2 -2
  46. package/src/node/plugins/unocss.ts +9 -12
  47. package/src/node/rss.ts +127 -0
  48. package/src/node/shims.d.ts +10 -0
  49. package/src/types/config.ts +28 -2
  50. package/src/types/posts.ts +6 -1
  51. package/dist/chunk-5D7M5SQP.js +0 -1
  52. package/dist/chunk-CF6MGLH2.mjs +0 -84
  53. package/dist/chunk-L22LX2G6.mjs +0 -1
  54. package/dist/chunk-W5MJCUNY.js +0 -84
  55. package/src/client/pages/posts/index.md +0 -5
@@ -1,18 +1,31 @@
1
1
  // import common and light/dark css vars in main.ts
2
2
 
3
+ $c-primary: #0078e7 !default;
4
+
3
5
  // global css
4
- @use './global/reset.scss' as *;
5
- @use './global/helper.scss' as *;
6
- @use './global/index.scss' as *;
7
- @use './global/nprogress.scss' as *;
6
+ @use "./global/reset.scss" as *;
7
+ @use "./global/helper.scss" as *;
8
+ @use "./global/index.scss" as *;
9
+ @use "./global/i18n.scss" as *;
10
+ @use "./global/nprogress.scss" as *;
8
11
 
9
12
  // common
10
- @use './common/button.scss' as *;
11
- @use './common/hamburger.scss' as *;
12
- @use './common/markdown.scss' as *;
13
+ @use "./common/button.scss" as *;
14
+ @use "./common/code.scss" as *;
15
+ @use "./common/custom-blocks.scss" as *;
16
+ @use "./common/hamburger.scss" as *;
17
+
13
18
  @use "./common/scrollbar.scss" as *;
14
19
  @use "./common/sidebar.scss" as *;
15
20
  @use "./common/transition.scss" as *;
16
21
 
17
22
  // banner
18
- @use './widgets/banner.scss' as *;
23
+ @use "./widgets/banner.scss" as *;
24
+
25
+ // markdown
26
+ @use "./common/markdown.scss";
27
+ @forward "star-markdown-css/src/scss/theme/yun.scss" with (
28
+ $colors: (
29
+ "primary": $c-primary,
30
+ )
31
+ );
@@ -4,64 +4,73 @@
4
4
  @use "./mixins" as *;
5
5
 
6
6
  $palette: () !default;
7
- $palette: map.merge((
8
- 'white': #ffffff,
9
- 'black': #1a1a1a,
10
- 'gray': #8e8e8e,
11
- 'danger': #db2828,
12
- 'warning': #f2711c
13
- ), $palette);
7
+ $palette: map.merge(
8
+ (
9
+ "white": #ffffff,
10
+ "black": #1a1a1a,
11
+ "gray": #8e8e8e,
12
+ "danger": #db2828,
13
+ "warning": #f2711c,
14
+ ),
15
+ $palette
16
+ );
14
17
 
15
18
  $colors: () !default;
16
- $colors: map.merge((
17
- 'primary': #0078E7,
18
- ), $colors);
19
+ $colors: map.merge(
20
+ (
21
+ "primary": #0078e7,
22
+ ),
23
+ $colors
24
+ );
19
25
 
20
- // @debug $yun-c-primary;
21
- // $yun-c-primary: #0078E7 !default;
22
- // @if meta.variable-exists('yun-c-primary') {
23
- // $colors: map.merge((
24
- // 'primary': $yun-c-primary,
25
- // ), $colors);
26
- // }
27
-
28
- $c-primary: map.get($colors, 'primary') !default;
29
-
30
- $colors: map.merge((
31
- 'primary-light': lighten($c-primary, 15%),
32
- 'primary-lighter': lighten($c-primary, 30%),
33
- 'primary-dark': darken($c-primary, 5%),
34
- ), $colors);
26
+ $c-primary: map.get($colors, "primary") !default;
35
27
 
28
+ $colors: map.merge(
29
+ (
30
+ "primary-light": lighten($c-primary, 15%),
31
+ "primary-lighter": lighten($c-primary, 30%),
32
+ "primary-dark": darken($c-primary, 5%),
33
+ ),
34
+ $colors
35
+ );
36
36
 
37
37
  $light: () !default;
38
- $light: map.merge((
39
- 'border-color': #222,
38
+ $light: map.merge(
39
+ (
40
+ "border-color": #222,
40
41
 
41
- 'c-bg': white,
42
- 'c-bg-light': white,
43
- 'c-bg-dark': #fafafa,
44
- 'c-text': #333,
45
- 'c-text-light': #555,
46
- 'c-text-dark': #111,
42
+ "c-bg": white,
43
+ "c-bg-light": white,
44
+ "c-bg-dark": #fafafa,
45
+ "c-text": #333,
46
+ "c-text-light": #555,
47
+ "c-text-dark": #111,
47
48
 
48
- 'c-primary-rgb': #{red($c-primary), green($c-primary), blue($c-primary)},
49
+ "c-primary-rgb": #{red($c-primary),
50
+ green(
51
+ $c-primary,
52
+ ),
53
+ blue($c-primary)},
49
54
 
50
- 'c-link': get-css-var('c-primary-dark'),
51
- ), $light);
55
+ "c-link": get-css-var("c-primary-dark"),
56
+ ),
57
+ $light
58
+ );
52
59
 
53
60
  $dark: () !default;
54
- $dark: map.merge((
55
- 'border-color': #e6e6e6,
56
-
57
- 'c-bg': #1a1a1a,
58
- 'c-bg-light': #22252e,
59
- 'c-bg-dark': #1a1a1a,
61
+ $dark: map.merge(
62
+ (
63
+ "border-color": #e6e6e6,
60
64
 
61
- 'c-text': #f2f2f2,
62
- 'c-text-light': #eee,
63
- 'c-text-lighter': #ddd,
64
- 'c-text-dark': rgba(#ebebeb, 0.8),
65
+ "c-bg": #1a1a1a,
66
+ "c-bg-light": #22252e,
67
+ "c-bg-dark": #1a1a1a,
65
68
 
66
- 'c-link': map.get($colors, 'primary-light'),
67
- ), $dark);
69
+ "c-text": #f2f2f2,
70
+ "c-text-light": #eee,
71
+ "c-text-lighter": #ddd,
72
+ "c-text-dark": rgba(#ebebeb, 0.8),
73
+ "c-link": map.get($colors, "primary-light"),
74
+ ),
75
+ $dark
76
+ );
package/src/node/cli.ts CHANGED
@@ -14,6 +14,8 @@ import { bindShortcut, initServer, printInfo } from './utils/cli'
14
14
 
15
15
  // build
16
16
  import { build, ssgBuild } from './build'
17
+ // rss
18
+ import { build as rssBuild } from './rss'
17
19
 
18
20
  const cli = yargs.scriptName('valaxy')
19
21
  .usage('$0 [args]')
@@ -157,6 +159,19 @@ cli.command(
157
159
  },
158
160
  )
159
161
 
162
+ cli.command(
163
+ 'rss [root]',
164
+ 'generate rss feed',
165
+ args => commonOptions(args)
166
+ .strict()
167
+ .help(),
168
+ async({ root }) => {
169
+ consola.info('Generate RSS ...')
170
+ const options = await resolveOptions({ userRoot: root }, 'build')
171
+ await rssBuild(options)
172
+ },
173
+ )
174
+
160
175
  /**
161
176
  * set common options for cli
162
177
  * @param args
@@ -4,10 +4,11 @@ import type { MarkdownRenderer } from '../markdown'
4
4
  import { deeplyParseHeader } from './parseHeader'
5
5
  import { slugify } from './slugify'
6
6
 
7
- export const headingPlugin = (md: MarkdownIt, include = ['h2', 'h3']) => {
7
+ export const headingPlugin = (md: MarkdownIt, include = [1, 2, 3, 4]) => {
8
8
  md.renderer.rules.heading_open = (tokens, i, options, env, self) => {
9
9
  const token = tokens[i]
10
- if (include.includes(token.tag)) {
10
+ const tags = include.map(item => `h${item}`)
11
+ if (tags.includes(token.tag)) {
11
12
  const title = tokens[i + 1].content
12
13
  const idAttr = token.attrs!.find(([name]) => name === 'id')
13
14
  const slug = idAttr && idAttr[1]
@@ -0,0 +1,50 @@
1
+ import consola from 'consola'
2
+ import escapeHtml from 'escape-html'
3
+ import prism from 'prismjs'
4
+
5
+ // prism is listed as actual dep so it's ok to require
6
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
7
+ const loadLanguages = require('prismjs/components/index')
8
+
9
+ // required to make embedded highlighting work...
10
+ loadLanguages(['markup', 'css', 'javascript'])
11
+
12
+ function wrap(code: string, lang: string): string {
13
+ if (lang === 'text')
14
+ code = escapeHtml(code)
15
+
16
+ return `<pre v-pre><code>${code}</code></pre>`
17
+ }
18
+
19
+ export const highlight = (str: string, lang: string) => {
20
+ if (!lang)
21
+ return wrap(str, 'text')
22
+
23
+ lang = lang.toLowerCase()
24
+ const rawLang = lang
25
+ if (lang === 'vue' || lang === 'html')
26
+ lang = 'markup'
27
+
28
+ if (lang === 'md')
29
+ lang = 'markdown'
30
+
31
+ if (lang === 'ts')
32
+ lang = 'typescript'
33
+
34
+ if (lang === 'py')
35
+ lang = 'python'
36
+
37
+ if (!prism.languages[lang]) {
38
+ try {
39
+ loadLanguages([lang])
40
+ }
41
+ catch (e) {
42
+ consola.warn(`[valaxy] Syntax highlight for language "${lang}" is not supported.`)
43
+ }
44
+ }
45
+ if (prism.languages[lang]) {
46
+ const code = prism.highlight(str, prism.languages[lang], lang)
47
+ return wrap(code, rawLang)
48
+ }
49
+ return wrap(str, 'text')
50
+ }
@@ -0,0 +1,96 @@
1
+ // Modified from https://github.com/egoist/markdown-it-highlight-lines
2
+ import type MarkdownIt from 'markdown-it'
3
+
4
+ const wrapperRE = /^<pre .*?><code>/
5
+
6
+ export const highlightLinePlugin = (md: MarkdownIt) => {
7
+ const fence = md.renderer.rules.fence!
8
+ md.renderer.rules.fence = (...args) => {
9
+ const [tokens, idx, options] = args
10
+ const token = tokens[idx]
11
+
12
+ // due to use of markdown-it-attrs, the {0} syntax would have been converted
13
+ // to attrs on the token
14
+ const attr = token.attrs && token.attrs[0]
15
+ if (!attr)
16
+ return fence(...args)
17
+
18
+ const lines = attr[0]
19
+ if (!lines || !/[\d,-]+/.test(lines))
20
+ return fence(...args)
21
+
22
+ const lineNumbers = lines
23
+ .split(',')
24
+ .map(v => v.split('-').map(v => parseInt(v, 10)))
25
+
26
+ const code = options.highlight
27
+ ? options.highlight(token.content, token.info, '')
28
+ : token.content
29
+
30
+ const rawCode = code.replace(wrapperRE, '')
31
+ const highlightLinesCode = rawCode
32
+ .split('\n')
33
+ .map((split, index) => {
34
+ const lineNumber = index + 1
35
+ const inRange = lineNumbers.some(([start, end]) => {
36
+ if (start && end)
37
+ return lineNumber >= start && lineNumber <= end
38
+
39
+ return lineNumber === start
40
+ })
41
+ if (inRange)
42
+ return '<div class="highlighted">&nbsp;</div>'
43
+
44
+ return '<br>'
45
+ })
46
+ .join('')
47
+
48
+ const highlightLinesWrapperCode = `<div class="highlight-lines">${highlightLinesCode}</div>`
49
+
50
+ return highlightLinesWrapperCode + code
51
+ }
52
+ }
53
+
54
+ // markdown-it plugin for wrapping <pre> ... </pre>.
55
+ //
56
+ // If your plugin was chained before preWrapper, you can add additional element directly.
57
+ // If your plugin was chained after preWrapper, you can use these slots:
58
+ // 1. <!--beforebegin-->
59
+ // 2. <!--afterbegin-->
60
+ // 3. <!--beforeend-->
61
+ // 4. <!--afterend-->
62
+ export const preWrapperPlugin = (md: MarkdownIt) => {
63
+ const fence = md.renderer.rules.fence!
64
+ md.renderer.rules.fence = (...args) => {
65
+ const [tokens, idx] = args
66
+ const token = tokens[idx]
67
+ const rawCode = fence(...args)
68
+ return `<div class="language-${token.info.trim()}">${rawCode}</div>`
69
+ }
70
+ }
71
+
72
+ // markdown-it plugin for generating line numbers.
73
+ // It depends on preWrapper plugin.
74
+ export const lineNumberPlugin = (md: MarkdownIt) => {
75
+ const fence = md.renderer.rules.fence!
76
+ md.renderer.rules.fence = (...args) => {
77
+ const rawCode = fence(...args)
78
+ const code = rawCode.slice(
79
+ rawCode.indexOf('<code>'),
80
+ rawCode.indexOf('</code>'),
81
+ )
82
+
83
+ const lines = code.split('\n')
84
+ const lineNumbersCode = [...Array(lines.length - 1)]
85
+ .map((line, index) => `<span class="line-number">${index + 1}</span><br>`)
86
+ .join('')
87
+
88
+ const lineNumbersWrapperCode = `<div class="line-numbers-wrapper">${lineNumbersCode}</div>`
89
+
90
+ const finalCode = rawCode
91
+ .replace(/<\/div>$/, `${lineNumbersWrapperCode}</div>`)
92
+ .replace(/"(language-\w+)"/, '"$1 line-numbers-mode"')
93
+
94
+ return finalCode
95
+ }
96
+ }
@@ -2,10 +2,10 @@ import type MarkdownIt from 'markdown-it'
2
2
 
3
3
  import Anchor from 'markdown-it-anchor'
4
4
  import Emoji from 'markdown-it-emoji'
5
- import Prism from 'markdown-it-prism'
6
5
  import LinkAttributes from 'markdown-it-link-attributes'
7
6
  import TOC from 'markdown-it-table-of-contents'
8
7
  import TaskLists from 'markdown-it-task-lists'
8
+ import attrs from 'markdown-it-attrs'
9
9
 
10
10
  import type { KatexOptions } from 'katex'
11
11
  import Katex from '../markdown/markdown-it-katex'
@@ -13,6 +13,8 @@ import { containerPlugin } from '../markdown/markdown-it-container'
13
13
  import { headingPlugin } from '../markdown/headings'
14
14
  import { slugify } from './slugify'
15
15
  import { parseHeader } from './parseHeader'
16
+ import { highlight } from './highlight'
17
+ import { highlightLinePlugin, preWrapperPlugin } from './highlightLines'
16
18
 
17
19
  export interface Header {
18
20
  level: number
@@ -40,18 +42,25 @@ export interface MarkdownOptions extends MarkdownIt.Options {
40
42
  }
41
43
 
42
44
  export function setupMarkdownPlugins(md: MarkdownIt, mdOptions: MarkdownOptions = {}) {
45
+ md.set({
46
+ highlight,
47
+ })
43
48
  md
49
+ .use(highlightLinePlugin)
50
+ .use(preWrapperPlugin)
44
51
  .use(containerPlugin)
45
- .use(headingPlugin)
46
- // https://prismjs.com/
47
- md.use(Prism)
48
- md.use(LinkAttributes, {
49
- matcher: (link: string) => /^https?:\/\//.test(link),
50
- attrs: {
51
- target: '_blank',
52
- rel: 'noopener',
53
- },
54
- })
52
+ .use(headingPlugin, mdOptions?.toc?.includeLevel)
53
+ // .use(lineNumberPlugin)
54
+ // https://github.com/arve0/markdown-it-attrs
55
+ // add classes
56
+ md.use(attrs)
57
+ .use(LinkAttributes, {
58
+ matcher: (link: string) => /^https?:\/\//.test(link),
59
+ attrs: {
60
+ target: '_blank',
61
+ rel: 'noopener',
62
+ },
63
+ })
55
64
  md.use(Katex, mdOptions.katex)
56
65
  .use(Anchor, {
57
66
  slugify,
@@ -24,7 +24,7 @@ function createContainer(classes: string, defaultTitle: string): ContainerArgs {
24
24
  if (token.nesting === 1) {
25
25
  if (classes === 'details') {
26
26
  return `<details class="${classes} custom-block">${
27
- info ? `<summary>${info}</summary>` : ''
27
+ `<summary>${info}</summary>`
28
28
  }\n`
29
29
  }
30
30
  return `<div class="${classes} custom-block"><p class="custom-block-title">${
@@ -50,4 +50,12 @@ export const containerPlugin = (md: MarkdownIt) => {
50
50
  render: (tokens: Token[], idx: number) =>
51
51
  tokens[idx].nesting === 1 ? '<div v-pre>\n' : '</div>\n',
52
52
  })
53
+
54
+ const languages = ['zh-CN', 'en']
55
+ languages.forEach((lang) => {
56
+ md.use(container, lang, {
57
+ render: (tokens: Token[], idx: number) =>
58
+ tokens[idx].nesting === 1 ? `<div lang="${lang}">\n` : '</div>\n',
59
+ })
60
+ })
53
61
  }
@@ -42,7 +42,7 @@ function generateLocales(roots: string[]) {
42
42
  roots.forEach((root, i) => {
43
43
  languages.forEach((lang) => {
44
44
  const langYml = `${root}/locales/${lang}.yml`
45
- if (fs.existsSync(langYml)) {
45
+ if (fs.existsSync(langYml) && fs.readFileSync(langYml, 'utf-8')) {
46
46
  const varName = lang.replace('-', '') + i
47
47
  imports.push(`import ${varName} from "${langYml}"`)
48
48
  imports.push(`Object.assign(messages['${lang}'], ${varName})`)
@@ -89,7 +89,7 @@ export async function ViteValaxyPlugins(
89
89
  },
90
90
  },
91
91
  },
92
- }) as PluginOption,
92
+ }),
93
93
 
94
94
  ValaxyPlugin,
95
95
  MarkdownPlugin,
@@ -210,7 +210,7 @@ export async function ViteValaxyPlugins(
210
210
  runtimeOnly: true,
211
211
  compositionOnly: true,
212
212
  include: roots.map(root => `${root}/locales/**`),
213
- }) as PluginOption,
213
+ }),
214
214
 
215
215
  // https://github.com/antfu/vite-plugin-inspect
216
216
  // Visit http://localhost:3333/__inspect/ to see the inspector
@@ -12,7 +12,8 @@ import {
12
12
  transformerVariantGroup,
13
13
  } from 'unocss'
14
14
  import type { ValaxyConfig } from 'valaxy'
15
- import type { ThemeUserConfig } from 'valaxy-theme-yun/config'
15
+ import type { ThemeUserConfig } from 'valaxy-theme-yun'
16
+ import { generateSafelist } from 'valaxy-theme-yun'
16
17
  import type { ResolvedValaxyOptions } from '../options'
17
18
 
18
19
  export const createSafelist = (config: ValaxyConfig<ThemeUserConfig>) => {
@@ -24,26 +25,22 @@ export const createSafelist = (config: ValaxyConfig<ThemeUserConfig>) => {
24
25
  'i-ri-cloud-line',
25
26
  ]
26
27
 
27
- const safelist = 'animate-fade-in prose prose-sm m-auto text-left'.split(' ').concat([
28
+ let themeSafelist: string[] = []
29
+ if (typeof generateSafelist === 'function')
30
+ themeSafelist = generateSafelist(config.themeConfig)
31
+
32
+ const safelist = 'animate-fade-in m-auto text-left'.split(' ').concat([
28
33
  'rotate-y-180',
29
- ]).concat(safeIcons)
34
+ ]).concat(safeIcons).concat(themeSafelist)
35
+
30
36
  // generate icon safelist
31
37
  if (config.social.length)
32
38
  config.social.forEach(item => safelist.push(item.icon))
33
39
 
34
- if (config.themeConfig.footer?.icon?.name)
35
- safelist.push(config.themeConfig.footer?.icon?.name)
36
-
37
40
  // sponsor icon
38
41
  if (config.sponsor.methods.length)
39
42
  config.sponsor.methods.forEach(item => safelist.push(item.icon))
40
43
 
41
- const types = config.themeConfig.types
42
- if (types) {
43
- for (const type in types)
44
- safelist.push(types[type].icon)
45
- }
46
-
47
44
  return safelist
48
45
  }
49
46
 
@@ -0,0 +1,127 @@
1
+ import { dirname } from 'path'
2
+ import chalk from 'chalk'
3
+
4
+ import fg from 'fast-glob'
5
+ import fs from 'fs-extra'
6
+ import matter from 'gray-matter'
7
+ import MarkdownIt from 'markdown-it'
8
+ import type { Author, FeedOptions, Item } from 'feed'
9
+ import { Feed } from 'feed'
10
+ import consola from 'consola'
11
+ import type { ResolvedValaxyOptions } from './options'
12
+
13
+ const markdown = MarkdownIt({
14
+ html: true,
15
+ breaks: true,
16
+ linkify: true,
17
+ })
18
+
19
+ /**
20
+ * generate rss
21
+ * @param options
22
+ * @returns
23
+ */
24
+ export async function build(options: ResolvedValaxyOptions) {
25
+ const { config } = options
26
+
27
+ if (!config.url) {
28
+ consola.error('You must set "config.url" to generate rss.')
29
+ return
30
+ }
31
+
32
+ const siteUrl = config.url.endsWith('/') ? config.url : `${config.url}/`
33
+ const DOMAIN = config.url.endsWith('/') ? config.url.slice(0, -1) : config.url
34
+ const author: Author = {
35
+ name: options.config.author.name,
36
+ email: options.config.author.email,
37
+ link: options.config.author.link,
38
+ }
39
+
40
+ consola.info(`RSS Site Url: ${chalk.cyan(siteUrl)}`)
41
+
42
+ const ccVersion = (config.license.type === 'zero') ? '1.0' : '4.0'
43
+ const feedOptions: FeedOptions = {
44
+ title: config.title,
45
+ description: config.description,
46
+ id: siteUrl || 'valaxy',
47
+ link: siteUrl,
48
+ copyright: `CC ${config.license.type.toUpperCase()} ${ccVersion} ${new Date().getFullYear()} © ${config.author.name}`,
49
+ feedLinks: {
50
+ json: `${siteUrl}feed.json`,
51
+ atom: `${siteUrl}feed.atom`,
52
+ rss: `${siteUrl}feed.xml`,
53
+ },
54
+ }
55
+
56
+ // generate
57
+ const files = await fg(`${options.userRoot}/pages/posts/**/*.md`)
58
+
59
+ const posts: Item[] = []
60
+ files
61
+ .forEach((i) => {
62
+ const raw = fs.readFileSync(i, 'utf-8')
63
+ const { data, content, excerpt } = matter(raw)
64
+
65
+ // not add to posts
66
+ if (!data.date) {
67
+ consola.warn(`Do you forget to write date for ${chalk.dim(i)}`)
68
+ return false
69
+ }
70
+
71
+ if (data.draft) {
72
+ consola.warn(`Ignore draft post: ${chalk.dim(i)}`)
73
+ return false
74
+ }
75
+
76
+ // todo i18n
77
+
78
+ // render excerpt
79
+ const html = markdown.render(excerpt || content)
80
+ .replace('src="/', `src="${DOMAIN}/`)
81
+
82
+ if (data.image?.startsWith('/'))
83
+ data.image = DOMAIN + data.image
84
+
85
+ posts.push({
86
+ title: '',
87
+ ...data,
88
+ id: (data.id || '').toString(),
89
+ date: new Date(data.date),
90
+ published: new Date(data.updated || data.date),
91
+ content: html,
92
+ author: [author],
93
+ link: DOMAIN + i.replace(`${options.userRoot}/pages`, '').replace(/\.md$/, ''),
94
+ })
95
+ })
96
+
97
+ // sort by updated
98
+ posts.sort((a, b) => +new Date(b.published || b.date) - +new Date(a.published || a.date))
99
+ // await writeFeed('feed', feedOptions, posts)
100
+
101
+ // write
102
+ feedOptions.author = author
103
+ feedOptions.image = config.author.avatar.startsWith('http') ? config.author.avatar : `${DOMAIN}${config.author.avatar}`
104
+ feedOptions.favicon = `${DOMAIN}/${config.feed.favicon}`
105
+
106
+ const feed = new Feed(feedOptions)
107
+ posts.forEach(item => feed.addItem(item))
108
+ // items.forEach(i=> console.log(i.title, i.date))
109
+
110
+ await fs.ensureDir(dirname(`./dist/${config.feed.name}`))
111
+ const path = './dist'
112
+
113
+ const types = ['xml', 'atom', 'json']
114
+ types.forEach((type) => {
115
+ let data = ''
116
+ let name = `${path}/${config.feed.name || 'feed'}.${type}`
117
+ if (type === 'xml') { data = feed.rss2() }
118
+ else if (type === 'atom') {
119
+ if (!config.feed.name)
120
+ name = `${path}/atom.xml`
121
+ data = feed.atom1()
122
+ }
123
+ else if (type === 'json') { data = feed.json1() }
124
+ fs.writeFileSync(name, data, 'utf-8')
125
+ consola.info(`${type}: ${name}`)
126
+ })
127
+ }
@@ -1,3 +1,13 @@
1
+ declare module 'escape-html' {
2
+ const def: (str: string) => string
3
+ export default def
4
+ }
5
+
6
+ declare module 'prismjs' {
7
+ const def: any
8
+ export default def
9
+ }
10
+
1
11
  declare module 'markdown-it-attrs' {
2
12
  const def: any
3
13
  export default def