valaxy 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/valaxy.js +1 -1
- package/dist/chunk-4LP5LOFC.js +86 -0
- package/dist/chunk-BDKTP6RW.js +1 -0
- package/dist/chunk-UVMBC7I3.mjs +1 -0
- package/dist/chunk-VLZSM7W4.mjs +86 -0
- package/dist/config-24b4f209.d.ts +188 -0
- package/dist/index.d.ts +316 -151
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{cli.d.ts → node/cli.d.ts} +0 -0
- package/dist/node/cli.js +45 -0
- package/dist/node/cli.mjs +45 -0
- package/dist/node/index.d.ts +46 -0
- package/dist/node/index.js +1 -0
- package/dist/node/index.mjs +1 -0
- package/package.json +22 -18
- package/src/client/components/PostCard.vue +1 -1
- package/src/client/components/PostList.vue +5 -5
- package/src/client/components/ValaxyCopyright.vue +1 -1
- package/src/client/components/ValaxyFooter.vue +1 -1
- package/src/client/components/ValaxyOverlay.vue +4 -4
- package/src/client/components/ValaxyPagination.vue +16 -14
- package/src/client/components/ValaxyRightSidebar.vue +4 -4
- package/src/client/components/ValaxySidebar.vue +4 -8
- package/src/client/components/ValaxyToc.vue +11 -9
- package/src/client/composables/comments/index.ts +1 -0
- package/src/client/composables/comments/twikoo.ts +37 -0
- package/src/client/composables/comments/waline.ts +8 -5
- package/src/client/composables/common.ts +3 -4
- package/src/client/composables/search/algolia.ts +2 -1
- package/src/client/composables/sidebar.ts +2 -2
- package/src/client/composables/tag.ts +9 -2
- package/src/client/composables/widgets/backToTop.ts +10 -4
- package/src/client/main.ts +1 -8
- package/src/client/modules/valaxy.ts +36 -15
- package/src/client/shims.d.ts +5 -5
- package/src/client/styles/common/button.scss +3 -5
- package/src/client/styles/common/code.scss +157 -0
- package/src/client/styles/common/hamburger.scss +2 -2
- package/src/client/styles/common/markdown.scss +6 -5
- package/src/client/styles/common/sidebar.scss +3 -3
- package/src/client/styles/common/transition.scss +2 -2
- package/src/client/styles/css-vars.scss +39 -0
- package/src/client/styles/global/helper.scss +2 -2
- package/src/client/styles/global/index.scss +1 -1
- package/src/client/styles/global/nprogress.scss +1 -1
- package/src/client/styles/global/reset.scss +2 -1
- package/src/client/styles/index.scss +12 -1
- package/src/client/styles/mixins/config.scss +1 -1
- package/src/client/styles/mixins/index.scss +1 -0
- package/src/client/styles/palette.scss +6 -8
- package/src/client/styles/vars.scss +1 -13
- package/src/client/styles/widgets/banner.scss +2 -2
- package/src/client/utils/index.ts +0 -2
- package/src/node/build.ts +20 -1
- package/src/node/cli.ts +32 -19
- package/src/node/markdown/highlight.ts +50 -0
- package/src/node/markdown/highlightLines.ts +96 -0
- package/src/node/markdown/index.ts +22 -11
- package/src/node/markdown/markdown-it-katex.ts +20 -10
- package/src/node/markdown/parseHeader.ts +4 -2
- package/src/node/options.ts +3 -1
- package/src/node/plugins/index.ts +53 -24
- package/src/node/plugins/markdown.ts +1 -1
- package/src/node/plugins/preset.ts +46 -10
- package/src/node/plugins/unocss.ts +12 -7
- package/src/node/rss.ts +121 -0
- package/src/node/server.ts +1 -1
- package/src/node/shims.d.ts +15 -0
- package/src/node/utils/cli.ts +0 -1
- package/src/node/vite.ts +7 -15
- package/src/types/config.ts +27 -2
- package/tsup.config.ts +1 -0
- package/dist/build-CLF7GOPQ.mjs +0 -1
- package/dist/build-F47TGGER.js +0 -1
- package/dist/chunk-5S6S3FLN.mjs +0 -83
- package/dist/chunk-JORQSKHF.mjs +0 -1
- package/dist/chunk-TOMJSB6R.js +0 -83
- package/dist/chunk-VJNIZVTJ.js +0 -1
- package/dist/cli.js +0 -6
- package/dist/cli.mjs +0 -6
- package/src/client/pages/about/index.md +0 -5
- package/src/client/pages/posts/index.md +0 -5
- package/src/client/styles/css-vars/dark.scss +0 -17
- package/src/client/styles/css-vars/index.scss +0 -18
- package/src/client/styles/css-vars/light.scss +0 -9
|
@@ -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"> </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,9 +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'
|
|
7
|
+
import TaskLists from 'markdown-it-task-lists'
|
|
8
|
+
import attrs from 'markdown-it-attrs'
|
|
8
9
|
|
|
9
10
|
import type { KatexOptions } from 'katex'
|
|
10
11
|
import Katex from '../markdown/markdown-it-katex'
|
|
@@ -12,6 +13,8 @@ import { containerPlugin } from '../markdown/markdown-it-container'
|
|
|
12
13
|
import { headingPlugin } from '../markdown/headings'
|
|
13
14
|
import { slugify } from './slugify'
|
|
14
15
|
import { parseHeader } from './parseHeader'
|
|
16
|
+
import { highlight } from './highlight'
|
|
17
|
+
import { highlightLinePlugin, preWrapperPlugin } from './highlightLines'
|
|
15
18
|
|
|
16
19
|
export interface Header {
|
|
17
20
|
level: number
|
|
@@ -39,18 +42,25 @@ export interface MarkdownOptions extends MarkdownIt.Options {
|
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
export function setupMarkdownPlugins(md: MarkdownIt, mdOptions: MarkdownOptions = {}) {
|
|
45
|
+
md.set({
|
|
46
|
+
highlight,
|
|
47
|
+
})
|
|
42
48
|
md
|
|
49
|
+
.use(highlightLinePlugin)
|
|
50
|
+
.use(preWrapperPlugin)
|
|
43
51
|
.use(containerPlugin)
|
|
44
52
|
.use(headingPlugin)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
+
})
|
|
54
64
|
md.use(Katex, mdOptions.katex)
|
|
55
65
|
.use(Anchor, {
|
|
56
66
|
slugify,
|
|
@@ -58,11 +68,12 @@ export function setupMarkdownPlugins(md: MarkdownIt, mdOptions: MarkdownOptions
|
|
|
58
68
|
})
|
|
59
69
|
.use(TOC, {
|
|
60
70
|
slugify,
|
|
61
|
-
includeLevel: [2, 3],
|
|
71
|
+
includeLevel: [2, 3, 4],
|
|
62
72
|
format: parseHeader,
|
|
63
73
|
...mdOptions.toc,
|
|
64
74
|
})
|
|
65
75
|
.use(Emoji)
|
|
76
|
+
.use(TaskLists)
|
|
66
77
|
|
|
67
78
|
const originalRender = md.render
|
|
68
79
|
md.render = (...args) => {
|
|
@@ -41,11 +41,13 @@ function isValidDelim(state: any, pos: number) {
|
|
|
41
41
|
function math_inline(state: any, silent: boolean) {
|
|
42
42
|
let match, token, res, pos
|
|
43
43
|
|
|
44
|
-
if (state.src[state.pos] !== '$')
|
|
44
|
+
if (state.src[state.pos] !== '$')
|
|
45
|
+
return false
|
|
45
46
|
|
|
46
47
|
res = isValidDelim(state, state.pos)
|
|
47
48
|
if (!res.can_open) {
|
|
48
|
-
if (!silent)
|
|
49
|
+
if (!silent)
|
|
50
|
+
state.pending += '$'
|
|
49
51
|
state.pos += 1
|
|
50
52
|
return true
|
|
51
53
|
}
|
|
@@ -64,20 +66,23 @@ function math_inline(state: any, silent: boolean) {
|
|
|
64
66
|
while (state.src[pos] === '\\') pos -= 1
|
|
65
67
|
|
|
66
68
|
// Even number of escapes, potential closing delimiter found
|
|
67
|
-
if (((match - pos) % 2) === 1)
|
|
69
|
+
if (((match - pos) % 2) === 1)
|
|
70
|
+
break
|
|
68
71
|
match += 1
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
// No closing delimter found. Consume $ and continue.
|
|
72
75
|
if (match === -1) {
|
|
73
|
-
if (!silent)
|
|
76
|
+
if (!silent)
|
|
77
|
+
state.pending += '$'
|
|
74
78
|
state.pos = start
|
|
75
79
|
return true
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
// Check if we have empty content, ie: $$. Do not parse.
|
|
79
83
|
if (match - start === 0) {
|
|
80
|
-
if (!silent)
|
|
84
|
+
if (!silent)
|
|
85
|
+
state.pending += '$$'
|
|
81
86
|
state.pos = start + 1
|
|
82
87
|
return true
|
|
83
88
|
}
|
|
@@ -85,7 +90,8 @@ function math_inline(state: any, silent: boolean) {
|
|
|
85
90
|
// Check for valid closing delimiter
|
|
86
91
|
res = isValidDelim(state, match)
|
|
87
92
|
if (!res.can_close) {
|
|
88
|
-
if (!silent)
|
|
93
|
+
if (!silent)
|
|
94
|
+
state.pending += '$'
|
|
89
95
|
state.pos = start
|
|
90
96
|
return true
|
|
91
97
|
}
|
|
@@ -106,13 +112,16 @@ function math_block(state: any, start: number, end: number, silent: boolean) {
|
|
|
106
112
|
let pos = state.bMarks[start] + state.tShift[start]
|
|
107
113
|
let max = state.eMarks[start]
|
|
108
114
|
|
|
109
|
-
if (pos + 2 > max)
|
|
110
|
-
|
|
115
|
+
if (pos + 2 > max)
|
|
116
|
+
return false
|
|
117
|
+
if (state.src.slice(pos, pos + 2) !== '$$')
|
|
118
|
+
return false
|
|
111
119
|
|
|
112
120
|
pos += 2
|
|
113
121
|
firstLine = state.src.slice(pos, max)
|
|
114
122
|
|
|
115
|
-
if (silent)
|
|
123
|
+
if (silent)
|
|
124
|
+
return true
|
|
116
125
|
if (firstLine.trim().slice(-2) === '$$') {
|
|
117
126
|
// Single line expression
|
|
118
127
|
firstLine = firstLine.trim().slice(0, -2)
|
|
@@ -122,7 +131,8 @@ function math_block(state: any, start: number, end: number, silent: boolean) {
|
|
|
122
131
|
for (next = start; !found;) {
|
|
123
132
|
next++
|
|
124
133
|
|
|
125
|
-
if (next >= end)
|
|
134
|
+
if (next >= end)
|
|
135
|
+
break
|
|
126
136
|
|
|
127
137
|
pos = state.bMarks[next] + state.tShift[next]
|
|
128
138
|
max = state.eMarks[next]
|
|
@@ -48,8 +48,10 @@ export const removeNonCodeWrappedHTML = (str: string) => {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const compose = (...processors: ((str: string) => string)[]) => {
|
|
51
|
-
if (processors.length === 0)
|
|
52
|
-
|
|
51
|
+
if (processors.length === 0)
|
|
52
|
+
return (input: string) => input
|
|
53
|
+
if (processors.length === 1)
|
|
54
|
+
return processors[0]
|
|
53
55
|
return processors.reduce((prev, next) => {
|
|
54
56
|
return str => next(prev(str))
|
|
55
57
|
})
|
package/src/node/options.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface ValaxyEntryOptions {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface ResolvedValaxyOptions {
|
|
19
|
+
mode: 'dev' | 'build'
|
|
19
20
|
/**
|
|
20
21
|
* Client root path
|
|
21
22
|
* @default 'valaxy/src/client'
|
|
@@ -69,7 +70,7 @@ export function getThemeRoot(name: string, entry: string) {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
// for cli options
|
|
72
|
-
export async function resolveOptions(options: ValaxyEntryOptions) {
|
|
73
|
+
export async function resolveOptions(options: ValaxyEntryOptions, mode: ResolvedValaxyOptions['mode'] = 'dev') {
|
|
73
74
|
const clientRoot = resolve(resolveImportPath('valaxy/package.json'), 'src/client')
|
|
74
75
|
const userRoot = resolve(options.userRoot || process.cwd())
|
|
75
76
|
|
|
@@ -77,6 +78,7 @@ export async function resolveOptions(options: ValaxyEntryOptions) {
|
|
|
77
78
|
const themeRoot = getThemeRoot(theme, userRoot)
|
|
78
79
|
|
|
79
80
|
const valaxyOptions: ResolvedValaxyOptions = {
|
|
81
|
+
mode,
|
|
80
82
|
clientRoot,
|
|
81
83
|
userRoot,
|
|
82
84
|
themeRoot,
|
|
@@ -8,33 +8,58 @@ import type { ResolvedValaxyOptions, ValaxyServerOptions } from '../options'
|
|
|
8
8
|
import { toAtFS } from '../utils'
|
|
9
9
|
import { VALAXY_CONFIG_ID } from './valaxy'
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
/**
|
|
12
|
+
* for /@valaxyjs/styles
|
|
13
|
+
* @param roots
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
function generateStyles(roots: string[]) {
|
|
17
|
+
const imports: string[] = []
|
|
18
|
+
|
|
19
|
+
for (const root of roots) {
|
|
20
|
+
const styles: string[] = []
|
|
21
|
+
|
|
22
|
+
const autoloadNames = ['index', 'css-vars']
|
|
23
|
+
autoloadNames.forEach((name) => {
|
|
24
|
+
styles.push(join(root, 'styles', `${name}.css`))
|
|
25
|
+
styles.push(join(root, 'styles', `${name}.scss`))
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
for (const style of styles) {
|
|
29
|
+
if (fs.existsSync(style))
|
|
30
|
+
imports.push(`import "${toAtFS(style)}"`)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return imports.join('\n')
|
|
34
|
+
}
|
|
15
35
|
|
|
16
|
-
|
|
36
|
+
function generateLocales(roots: string[]) {
|
|
37
|
+
const imports: string[] = [
|
|
38
|
+
'const messages = { "zh-CN": {}, en: {} }',
|
|
39
|
+
]
|
|
40
|
+
const languages = ['zh-CN', 'en']
|
|
41
|
+
|
|
42
|
+
roots.forEach((root, i) => {
|
|
43
|
+
languages.forEach((lang) => {
|
|
44
|
+
const langYml = `${root}/locales/${lang}.yml`
|
|
45
|
+
if (fs.existsSync(langYml)) {
|
|
46
|
+
const varName = lang.replace('-', '') + i
|
|
47
|
+
imports.push(`import ${varName} from "${langYml}"`)
|
|
48
|
+
imports.push(`Object.assign(messages['${lang}'], ${varName})`)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
})
|
|
17
52
|
|
|
18
|
-
|
|
19
|
-
|
|
53
|
+
imports.push('export default messages')
|
|
54
|
+
return imports.join('\n')
|
|
55
|
+
}
|
|
20
56
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
join(root, 'styles', 'vars.scss'),
|
|
24
|
-
join(root, 'styles', 'index.css'),
|
|
25
|
-
join(root, 'styles', 'index.scss'),
|
|
26
|
-
]
|
|
57
|
+
export function createValaxyPlugin(options: ResolvedValaxyOptions, serverOptions: ValaxyServerOptions = {}): Plugin {
|
|
58
|
+
const valaxyPrefix = '/@valaxy'
|
|
27
59
|
|
|
28
|
-
|
|
29
|
-
if (fs.existsSync(style)) {
|
|
30
|
-
imports.push(`import "${toAtFS(style)}"`)
|
|
31
|
-
continue
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
60
|
+
let valaxyConfig = options.config
|
|
35
61
|
|
|
36
|
-
|
|
37
|
-
}
|
|
62
|
+
const roots = [options.clientRoot, options.themeRoot, options.userRoot]
|
|
38
63
|
|
|
39
64
|
return {
|
|
40
65
|
name: 'Valaxy',
|
|
@@ -60,7 +85,10 @@ export function createValaxyPlugin(options: ResolvedValaxyOptions, serverOptions
|
|
|
60
85
|
|
|
61
86
|
// generate styles
|
|
62
87
|
if (id === '/@valaxyjs/styles')
|
|
63
|
-
return
|
|
88
|
+
return generateStyles(roots)
|
|
89
|
+
|
|
90
|
+
if (id === '/@valaxyjs/locales')
|
|
91
|
+
return generateLocales(roots)
|
|
64
92
|
|
|
65
93
|
if (id.startsWith(valaxyPrefix))
|
|
66
94
|
return ''
|
|
@@ -69,7 +97,8 @@ export function createValaxyPlugin(options: ResolvedValaxyOptions, serverOptions
|
|
|
69
97
|
async handleHotUpdate(ctx) {
|
|
70
98
|
// handle valaxy.config.ts hmr
|
|
71
99
|
const { file, server } = ctx
|
|
72
|
-
if (file !== options.configFile)
|
|
100
|
+
if (file !== options.configFile)
|
|
101
|
+
return
|
|
73
102
|
|
|
74
103
|
const { config } = await resolveConfig()
|
|
75
104
|
|
|
@@ -47,7 +47,7 @@ export function createMarkdownPlugin(options: ResolvedValaxyOptions): Plugin[] {
|
|
|
47
47
|
handleHotUpdate(ctx) {
|
|
48
48
|
const { file, server } = ctx
|
|
49
49
|
// send headers
|
|
50
|
-
if (file.endsWith('.md')) {
|
|
50
|
+
if (file.endsWith('.md') && _md && _md.__data) {
|
|
51
51
|
server.ws.send({
|
|
52
52
|
type: 'custom',
|
|
53
53
|
event: 'valaxy:pageHeaders',
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import fs from 'fs'
|
|
3
2
|
import type { PluginOption } from 'vite'
|
|
4
3
|
|
|
@@ -17,7 +16,6 @@ import Inspect from 'vite-plugin-inspect'
|
|
|
17
16
|
|
|
18
17
|
import chalk from 'chalk'
|
|
19
18
|
import type { ResolvedValaxyOptions, ValaxyServerOptions } from '../options'
|
|
20
|
-
import type { Mode } from '../vite'
|
|
21
19
|
import { setupMarkdownPlugins } from '../markdown'
|
|
22
20
|
import { createMarkdownPlugin, excerpt_separator } from './markdown'
|
|
23
21
|
import { createUnocssPlugin } from './unocss'
|
|
@@ -28,12 +26,11 @@ export interface ValaxyPluginOptions {
|
|
|
28
26
|
components?: Parameters<typeof Components>[0]
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
export function ViteValaxyPlugins(
|
|
29
|
+
export async function ViteValaxyPlugins(
|
|
32
30
|
options: ResolvedValaxyOptions,
|
|
33
31
|
serverOptions: ValaxyServerOptions = {},
|
|
34
32
|
pluginOptions: ValaxyPluginOptions = {},
|
|
35
|
-
|
|
36
|
-
): (PluginOption | PluginOption[])[] | undefined {
|
|
33
|
+
): Promise<(PluginOption | PluginOption[])[] | undefined> {
|
|
37
34
|
const { clientRoot, themeRoot, userRoot } = options
|
|
38
35
|
|
|
39
36
|
const MarkdownPlugin = createMarkdownPlugin(options)
|
|
@@ -46,13 +43,49 @@ export function ViteValaxyPlugins(
|
|
|
46
43
|
|
|
47
44
|
const roots = [clientRoot, themeRoot, userRoot]
|
|
48
45
|
|
|
46
|
+
const { default: ThemePlugin } = await import(`valaxy-theme-${options.theme}`)
|
|
47
|
+
|
|
48
|
+
const customElements = new Set([
|
|
49
|
+
// katex
|
|
50
|
+
'annotation',
|
|
51
|
+
'math',
|
|
52
|
+
'menclose',
|
|
53
|
+
'mfrac',
|
|
54
|
+
'mglyph',
|
|
55
|
+
'mi',
|
|
56
|
+
'mlabeledtr',
|
|
57
|
+
'mn',
|
|
58
|
+
'mo',
|
|
59
|
+
'mover',
|
|
60
|
+
'mpadded',
|
|
61
|
+
'mphantom',
|
|
62
|
+
'mroot',
|
|
63
|
+
'mrow',
|
|
64
|
+
'mspace',
|
|
65
|
+
'msqrt',
|
|
66
|
+
'mstyle',
|
|
67
|
+
'msub',
|
|
68
|
+
'msubsup',
|
|
69
|
+
'msup',
|
|
70
|
+
'mtable',
|
|
71
|
+
'mtd',
|
|
72
|
+
'mtext',
|
|
73
|
+
'mtr',
|
|
74
|
+
'munder',
|
|
75
|
+
'munderover',
|
|
76
|
+
'semantics',
|
|
77
|
+
|
|
78
|
+
// meting
|
|
79
|
+
'meting-js',
|
|
80
|
+
])
|
|
81
|
+
|
|
49
82
|
return [
|
|
50
83
|
Vue({
|
|
51
84
|
include: [/\.vue$/, /\.md$/],
|
|
52
85
|
template: {
|
|
53
86
|
compilerOptions: {
|
|
54
87
|
isCustomElement: (tag) => {
|
|
55
|
-
return
|
|
88
|
+
return customElements.has(tag)
|
|
56
89
|
},
|
|
57
90
|
},
|
|
58
91
|
},
|
|
@@ -62,6 +95,8 @@ export function ViteValaxyPlugins(
|
|
|
62
95
|
MarkdownPlugin,
|
|
63
96
|
createConfigPlugin(options),
|
|
64
97
|
|
|
98
|
+
ThemePlugin(options.config.themeConfig),
|
|
99
|
+
|
|
65
100
|
// https://github.com/hannoeru/vite-plugin-pages
|
|
66
101
|
Pages({
|
|
67
102
|
extensions: ['vue', 'md'],
|
|
@@ -71,7 +106,8 @@ export function ViteValaxyPlugins(
|
|
|
71
106
|
*/
|
|
72
107
|
extendRoute(route) {
|
|
73
108
|
let path = route.component
|
|
74
|
-
if (!route.meta)
|
|
109
|
+
if (!route.meta)
|
|
110
|
+
route.meta = {}
|
|
75
111
|
|
|
76
112
|
if (route.path === '/')
|
|
77
113
|
route.meta.layout = 'home'
|
|
@@ -82,7 +118,7 @@ export function ViteValaxyPlugins(
|
|
|
82
118
|
path = pagePath
|
|
83
119
|
})
|
|
84
120
|
const md = fs.readFileSync(path, 'utf-8')
|
|
85
|
-
const { data, excerpt } = matter(md, { excerpt_separator })
|
|
121
|
+
const { data, excerpt, content } = matter(md, { excerpt_separator })
|
|
86
122
|
|
|
87
123
|
// warn for post frontmatter
|
|
88
124
|
if (route.path.startsWith('/posts/')) {
|
|
@@ -98,7 +134,7 @@ export function ViteValaxyPlugins(
|
|
|
98
134
|
|
|
99
135
|
// to refactor
|
|
100
136
|
// get active header by runtime query head, not render
|
|
101
|
-
mdIt.render(
|
|
137
|
+
mdIt.render(content)
|
|
102
138
|
route.meta.headers = _md.__data?.headers
|
|
103
139
|
|
|
104
140
|
// set default updated
|
|
@@ -178,6 +214,6 @@ export function ViteValaxyPlugins(
|
|
|
178
214
|
|
|
179
215
|
// https://github.com/antfu/vite-plugin-inspect
|
|
180
216
|
// Visit http://localhost:3333/__inspect/ to see the inspector
|
|
181
|
-
mode === 'dev' && Inspect(),
|
|
217
|
+
options.mode === 'dev' && Inspect(),
|
|
182
218
|
]
|
|
183
219
|
}
|
|
@@ -54,7 +54,7 @@ export const createUnocssConfig = (options: ResolvedValaxyOptions) => {
|
|
|
54
54
|
['yun-card', 'transition yun-transition shadow hover:shadow-lg'],
|
|
55
55
|
['btn', 'px-4 py-1 rounded inline-block bg-sky-600 text-white cursor-pointer hover:bg-sky-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
|
|
56
56
|
['icon-btn', 'inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-sky-600'],
|
|
57
|
-
['
|
|
57
|
+
['va-card', 'transition shadow hover:shadow-lg'],
|
|
58
58
|
],
|
|
59
59
|
presets: [
|
|
60
60
|
presetUno(),
|
|
@@ -66,7 +66,12 @@ export const createUnocssConfig = (options: ResolvedValaxyOptions) => {
|
|
|
66
66
|
presetTypography(),
|
|
67
67
|
presetWebFonts({
|
|
68
68
|
fonts: {
|
|
69
|
-
serif:
|
|
69
|
+
serif: [
|
|
70
|
+
{
|
|
71
|
+
name: 'Noto Serif SC',
|
|
72
|
+
weights: [900],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
70
75
|
// sans: 'DM Sans',
|
|
71
76
|
// mono: 'DM Mono',
|
|
72
77
|
},
|
|
@@ -75,19 +80,19 @@ export const createUnocssConfig = (options: ResolvedValaxyOptions) => {
|
|
|
75
80
|
rules: [
|
|
76
81
|
// more see '~/styles/global/helper.scss'
|
|
77
82
|
['yun-transition', {
|
|
78
|
-
'transition-duration': 'var(--
|
|
83
|
+
'transition-duration': 'var(--va-transition-duration)',
|
|
79
84
|
}],
|
|
80
85
|
['yun-text-light', {
|
|
81
|
-
color: 'var(--
|
|
86
|
+
color: 'var(--va-c-text-light)',
|
|
82
87
|
}],
|
|
83
88
|
['font-serif', {
|
|
84
|
-
'font-family': 'var(--
|
|
89
|
+
'font-family': 'var(--va-font-serif)',
|
|
85
90
|
}],
|
|
86
91
|
['font-sans', {
|
|
87
|
-
'font-family': 'var(--
|
|
92
|
+
'font-family': 'var(--va-font-sans)',
|
|
88
93
|
}],
|
|
89
94
|
['font-mono', {
|
|
90
|
-
'font-family': 'var(--
|
|
95
|
+
'font-family': 'var(--va-font-mono)',
|
|
91
96
|
}],
|
|
92
97
|
],
|
|
93
98
|
transformers: [
|
package/src/node/rss.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
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,
|
|
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
|
+
// todo i18n
|
|
72
|
+
|
|
73
|
+
// render excerpt
|
|
74
|
+
const html = markdown.render(excerpt || content)
|
|
75
|
+
.replace('src="/', `src="${DOMAIN}/`)
|
|
76
|
+
|
|
77
|
+
if (data.image?.startsWith('/'))
|
|
78
|
+
data.image = DOMAIN + data.image
|
|
79
|
+
|
|
80
|
+
posts.push({
|
|
81
|
+
title: '',
|
|
82
|
+
...data,
|
|
83
|
+
date: new Date(data.date),
|
|
84
|
+
published: new Date(data.updated || data.date),
|
|
85
|
+
content: html,
|
|
86
|
+
author: [author],
|
|
87
|
+
link: DOMAIN + i.replace(/^pages(.+)\.md$/, '$1'),
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// sort by updated
|
|
92
|
+
posts.sort((a, b) => +new Date(b.published || b.date) - +new Date(a.published || a.date))
|
|
93
|
+
// await writeFeed('feed', feedOptions, posts)
|
|
94
|
+
|
|
95
|
+
// write
|
|
96
|
+
feedOptions.author = author
|
|
97
|
+
feedOptions.image = config.author.avatar.startsWith('http') ? config.author.avatar : `${DOMAIN}${config.author.avatar}`
|
|
98
|
+
feedOptions.favicon = `${DOMAIN}/${config.feed.favicon}`
|
|
99
|
+
|
|
100
|
+
const feed = new Feed(feedOptions)
|
|
101
|
+
posts.forEach(item => feed.addItem(item))
|
|
102
|
+
// items.forEach(i=> console.log(i.title, i.date))
|
|
103
|
+
|
|
104
|
+
await fs.ensureDir(dirname(`./dist/${config.feed.name}`))
|
|
105
|
+
const path = './dist'
|
|
106
|
+
|
|
107
|
+
const types = ['xml', 'atom', 'json']
|
|
108
|
+
types.forEach((type) => {
|
|
109
|
+
let data = ''
|
|
110
|
+
let name = `${path}/${config.feed.name || 'feed'}.${type}`
|
|
111
|
+
if (type === 'xml') { data = feed.rss2() }
|
|
112
|
+
else if (type === 'atom') {
|
|
113
|
+
if (!config.feed.name)
|
|
114
|
+
name = `${path}/atom.xml`
|
|
115
|
+
data = feed.atom1()
|
|
116
|
+
}
|
|
117
|
+
else if (type === 'json') { data = feed.json1() }
|
|
118
|
+
fs.writeFileSync(name, data, 'utf-8')
|
|
119
|
+
consola.info(`${type}: ${name}`)
|
|
120
|
+
})
|
|
121
|
+
}
|