valaxy 0.5.0 → 0.6.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 (64) hide show
  1. package/README.md +0 -4
  2. package/{dist/types/index.mjs → client/app/data.ts} +0 -0
  3. package/client/components/PostCard.vue +4 -2
  4. package/client/components/ValaxyBg.vue +1 -1
  5. package/client/components/ValaxyFooter.vue +1 -1
  6. package/client/components/ValaxyMd.vue +34 -27
  7. package/client/components/ValaxyToc.vue +3 -3
  8. package/client/composables/copy-code.ts +92 -0
  9. package/client/composables/outline.ts +168 -0
  10. package/client/composables/sidebar.ts +46 -1
  11. package/client/config.ts +30 -7
  12. package/client/main.ts +4 -1
  13. package/client/modules/valaxy.ts +19 -7
  14. package/client/shims.d.ts +6 -1
  15. package/client/styles/common/code.scss +191 -167
  16. package/client/styles/common/markdown.scss +0 -2
  17. package/client/styles/css-vars.scss +30 -7
  18. package/client/styles/palette.scss +21 -2
  19. package/client/styles/vars.scss +32 -18
  20. package/client/utils/helper.ts +22 -0
  21. package/client/utils/sidebar.ts +26 -0
  22. package/config/index.ts +18 -0
  23. package/dist/chunk-23IW567G.mjs +40 -0
  24. package/dist/chunk-YUC5WP5Y.js +40 -0
  25. package/dist/{config-7bd43d41.d.ts → config-112ac884.d.ts} +15 -4
  26. package/dist/index.d.ts +361 -0
  27. package/dist/index.js +1 -0
  28. package/dist/index.mjs +1 -0
  29. package/dist/node/cli.js +7 -11
  30. package/dist/node/cli.mjs +7 -11
  31. package/dist/node/index.d.ts +3 -2
  32. package/dist/node/index.js +1 -1
  33. package/dist/node/index.mjs +1 -1
  34. package/index.ts +3 -0
  35. package/node/config.ts +9 -23
  36. package/node/markdown/highlight.ts +27 -39
  37. package/node/markdown/index.ts +30 -16
  38. package/node/markdown/markdown-it/highlightLines.ts +1 -1
  39. package/node/markdown/markdownToVue.ts +275 -0
  40. package/node/options.ts +16 -0
  41. package/node/plugins/extendConfig.ts +4 -3
  42. package/node/plugins/index.ts +104 -7
  43. package/node/plugins/preset.ts +8 -8
  44. package/node/rss.ts +1 -1
  45. package/node/shims.d.ts +0 -5
  46. package/node/utils/getGitTimestamp.ts +13 -0
  47. package/node/utils/index.ts +11 -0
  48. package/package.json +20 -9
  49. package/shared/index.ts +1 -0
  50. package/tsup.config.ts +5 -3
  51. package/types/config.ts +7 -4
  52. package/types/data.ts +31 -0
  53. package/types/index.ts +1 -0
  54. package/types/posts.ts +1 -1
  55. package/dist/chunk-RSQONJW3.mjs +0 -86
  56. package/dist/chunk-XQIGHIAX.js +0 -86
  57. package/dist/client/index.d.ts +0 -188
  58. package/dist/client/index.js +0 -1
  59. package/dist/client/index.mjs +0 -1
  60. package/dist/posts-32f55e33.d.ts +0 -117
  61. package/dist/types/index.d.ts +0 -8
  62. package/dist/types/index.js +0 -1
  63. package/index.d.ts +0 -3
  64. package/node/plugins/markdown.ts +0 -54
@@ -1,10 +1,10 @@
1
1
  import * as vite from 'vite';
2
2
  import { InlineConfig } from 'vite';
3
- import { V as ValaxyConfig } from '../config-7bd43d41.js';
3
+ import { V as ValaxyConfig } from '../config-112ac884.js';
4
4
  import 'type-fest';
5
5
  import 'unocss/vite';
6
- import 'vite-plugin-md';
7
6
  import 'markdown-it';
7
+ import 'shiki';
8
8
  import 'markdown-it-anchor';
9
9
  import 'katex';
10
10
 
@@ -36,6 +36,7 @@ interface ResolvedValaxyOptions {
36
36
  * config file path
37
37
  */
38
38
  configFile: string;
39
+ pages: string[];
39
40
  }
40
41
  interface ValaxyServerOptions {
41
42
  onConfigReload?: (newConfig: ValaxyConfig, config: ValaxyConfig) => void;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkXQIGHIAXjs = require('../chunk-XQIGHIAX.js');require('../chunk-U5OMNIOK.js');exports.createServer = _chunkXQIGHIAXjs.g;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkYUC5WP5Yjs = require('../chunk-YUC5WP5Y.js');require('../chunk-U5OMNIOK.js');exports.createServer = _chunkYUC5WP5Yjs.f;
@@ -1 +1 @@
1
- import{g as o}from"../chunk-RSQONJW3.mjs";import"../chunk-EAN2KU6W.mjs";export{o as createServer};
1
+ import{f as o}from"../chunk-23IW567G.mjs";import"../chunk-EAN2KU6W.mjs";export{o as createServer};
package/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './client'
2
+ export * from './types'
3
+ export * from './config'
package/node/config.ts CHANGED
@@ -2,12 +2,11 @@
2
2
  import fs from 'fs'
3
3
  import { loadConfig } from 'unconfig'
4
4
  import defu from 'defu'
5
- import type { YunTheme } from 'valaxy-theme-yun'
6
- import type { UserConfig, ValaxyConfig } from '../types'
5
+ import type { ValaxyConfig } from '../types'
7
6
  import type { ValaxyEntryOptions } from './options'
8
7
 
9
8
  const defaultValaxyConfig: ValaxyConfig = {
10
- url: '',
9
+ url: '/',
11
10
  lang: 'en',
12
11
  title: 'Valaxy Blog',
13
12
  description: 'A blog generated by Valaxy.',
@@ -22,13 +21,15 @@ const defaultValaxyConfig: ValaxyConfig = {
22
21
  message: 'All at sea.',
23
22
  },
24
23
  },
25
- favicon: 'favicon.svg',
24
+ favicon: '/favicon.svg',
26
25
  feed: {
27
26
  name: '',
28
27
  favicon: 'favicon.svg',
29
28
  },
30
29
  social: [],
31
30
 
31
+ lastUpdated: true,
32
+
32
33
  license: {
33
34
  enabled: true,
34
35
  language: '',
@@ -96,9 +97,10 @@ const defaultValaxyConfig: ValaxyConfig = {
96
97
 
97
98
  unocss: {},
98
99
 
99
- markdown: {
100
- excerpt: '<!-- more -->',
101
- },
100
+ // markdown: {
101
+ // excerpt: '<!-- more -->',
102
+ // },
103
+
102
104
  markdownIt: {
103
105
  toc: {
104
106
  includeLevel: [1, 2, 3, 4],
@@ -152,19 +154,3 @@ export async function resolveConfig(options: ValaxyEntryOptions = {}) {
152
154
  theme,
153
155
  }
154
156
  }
155
-
156
- /**
157
- * Type config helper
158
- */
159
- export function defineConfig(config: UserConfig<YunTheme.Config>) {
160
- return config
161
- }
162
-
163
- /**
164
- * Type config helper for custom theme config
165
- */
166
- export function defineConfigWithTheme<ThemeConfig>(
167
- config: UserConfig<ThemeConfig>,
168
- ) {
169
- return config
170
- }
@@ -1,50 +1,38 @@
1
+ import { getHighlighter } from 'shiki'
1
2
  import consola from 'consola'
2
- import escapeHtml from 'escape-html'
3
- import prism from 'prismjs'
3
+ import type { ThemeOptions } from '../markdown'
4
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')
5
+ export async function highlight(theme: ThemeOptions = 'material-palenight') {
6
+ const themes = typeof theme === 'string' ? [theme] : [theme.dark, theme.light]
7
+ const highlighter = await getHighlighter({ themes })
8
+ const preRE = /^<pre.*?>/
8
9
 
9
- // required to make embedded highlighting work...
10
- loadLanguages(['markup', 'css', 'javascript'])
10
+ return (str: string, lang: string) => {
11
+ lang = lang || 'text'
11
12
 
12
- function wrap(code: string, lang: string): string {
13
- if (lang === 'text')
14
- code = escapeHtml(code)
13
+ // https://stackoverflow.com/questions/22268952/what-is-the-difference-between-yaml-and-yml-extension
14
+ // use yaml better
15
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'
16
+ // adaptive
17
+ if (lang === 'yml') {
18
+ lang = 'yaml'
19
+ consola.warn('[shiki] It is recommended to use `.yaml` instead of `.yml`.')
20
+ }
27
21
 
28
- if (lang === 'md')
29
- lang = 'markdown'
22
+ if (typeof theme === 'string') {
23
+ return highlighter
24
+ .codeToHtml(str, { lang, theme })
25
+ .replace(preRE, '<pre v-pre>')
26
+ }
30
27
 
31
- if (lang === 'ts')
32
- lang = 'typescript'
28
+ const dark = highlighter
29
+ .codeToHtml(str, { lang, theme: theme.dark })
30
+ .replace(preRE, '<pre v-pre class="vp-code-dark">')
33
31
 
34
- if (lang === 'py')
35
- lang = 'python'
32
+ const light = highlighter
33
+ .codeToHtml(str, { lang, theme: theme.light })
34
+ .replace(preRE, '<pre v-pre class="vp-code-light">')
36
35
 
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)
36
+ return dark + light
48
37
  }
49
- return wrap(str, 'text')
50
38
  }
@@ -1,4 +1,6 @@
1
- import type MarkdownIt from 'markdown-it'
1
+ import MarkdownIt from 'markdown-it'
2
+
3
+ import type { Theme } from 'shiki'
2
4
 
3
5
  import Anchor from 'markdown-it-anchor'
4
6
  import Emoji from 'markdown-it-emoji'
@@ -8,23 +10,16 @@ import TaskLists from 'markdown-it-task-lists'
8
10
  import attrs from 'markdown-it-attrs'
9
11
 
10
12
  import type { KatexOptions } from 'katex'
13
+ import type { Header } from '../../types'
11
14
  import Katex from './markdown-it/katex'
12
15
  import { containerPlugin } from './markdown-it/container'
13
- // import { headingPlugin } from './markdown-it/headings'
16
+ import { headingPlugin } from './markdown-it/headings'
14
17
  import { slugify } from './slugify'
15
18
  import { parseHeader } from './markdown-it/parseHeader'
16
19
  import { highlight } from './highlight'
17
20
  import { highlightLinePlugin, preWrapperPlugin } from './markdown-it/highlightLines'
18
21
 
19
- export interface Header {
20
- level: number
21
- title: string
22
- slug: string
23
- /**
24
- * i18n
25
- */
26
- lang?: string
27
- }
22
+ export type ThemeOptions = Theme | { light: Theme; dark: Theme }
28
23
 
29
24
  export interface MarkdownParsedData {
30
25
  hoistedTags?: string[]
@@ -32,6 +27,8 @@ export interface MarkdownParsedData {
32
27
  headers?: Header[]
33
28
  }
34
29
  export interface MarkdownRenderer extends MarkdownIt {
30
+ __path: string
31
+ __relativePath: string
35
32
  __data: MarkdownParsedData
36
33
  }
37
34
 
@@ -46,12 +43,13 @@ export interface MarkdownOptions extends MarkdownIt.Options {
46
43
  [key: string]: any
47
44
  }
48
45
  katex?: KatexOptions
46
+ /**
47
+ * shiki
48
+ */
49
+ theme?: ThemeOptions
49
50
  }
50
51
 
51
- export function setupMarkdownPlugins(md: MarkdownIt, mdOptions: MarkdownOptions = {}) {
52
- md.set({
53
- highlight,
54
- })
52
+ export async function setupMarkdownPlugins(md: MarkdownIt, mdOptions: MarkdownOptions = {}) {
55
53
  md
56
54
  .use(highlightLinePlugin)
57
55
  .use(preWrapperPlugin)
@@ -59,7 +57,7 @@ export function setupMarkdownPlugins(md: MarkdownIt, mdOptions: MarkdownOptions
59
57
  // conflict with {% %}
60
58
  .use(attrs)
61
59
  // generate toc in client
62
- // .use(headingPlugin, mdOptions?.toc?.includeLevel)
60
+ .use(headingPlugin, mdOptions?.toc?.includeLevel)
63
61
  // .use(lineNumberPlugin)
64
62
  // https://github.com/arve0/markdown-it-attrs
65
63
  // add classes
@@ -93,3 +91,19 @@ export function setupMarkdownPlugins(md: MarkdownIt, mdOptions: MarkdownOptions
93
91
 
94
92
  return md as MarkdownRenderer
95
93
  }
94
+
95
+ export const createMarkdownRenderer = async (
96
+ srcDir: string,
97
+ options: MarkdownOptions = {},
98
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
99
+ base = '/',
100
+ ): Promise<MarkdownRenderer> => {
101
+ const md = MarkdownIt({
102
+ html: true,
103
+ linkify: true,
104
+ highlight: await highlight(options.theme),
105
+ ...options,
106
+ }) as MarkdownRenderer
107
+ await setupMarkdownPlugins(md)
108
+ return md
109
+ }
@@ -65,7 +65,7 @@ export const preWrapperPlugin = (md: MarkdownIt) => {
65
65
  const [tokens, idx] = args
66
66
  const token = tokens[idx]
67
67
  const rawCode = fence(...args)
68
- return `<div class="language-${token.info.trim()}">${rawCode}</div>`
68
+ return `<div class="language-${token.info.trim()}"><span class="copy"></span>${rawCode}</div>`
69
69
  }
70
70
  }
71
71
 
@@ -0,0 +1,275 @@
1
+ // copy from vitepress
2
+ import fs from 'fs'
3
+ import path from 'path'
4
+ import c from 'picocolors'
5
+ import matter from 'gray-matter'
6
+ import LRUCache from 'lru-cache'
7
+ import _debug from 'debug'
8
+ import { getGitTimestamp, slash, transformObject } from '../utils'
9
+ import { EXTERNAL_URL_RE } from '../../shared'
10
+ import type { HeadConfig, PageData } from '../../types'
11
+ import { deeplyParseHeader } from './markdown-it/parseHeader'
12
+ import { createMarkdownRenderer } from '.'
13
+ import type { MarkdownOptions } from '.'
14
+
15
+ const debug = _debug('vitepress:md')
16
+ const cache = new LRUCache<string, MarkdownCompileResult>({ max: 1024 })
17
+ const includesRE = /<!--\s*@include:\s*(.*?)\s*-->/g
18
+
19
+ export interface MarkdownCompileResult {
20
+ vueSrc: string
21
+ pageData: PageData
22
+ deadLinks: string[]
23
+ includes: string[]
24
+ }
25
+
26
+ const inferTitle = (frontmatter: Record<string, any>, content: string) => {
27
+ if (frontmatter.title)
28
+ return deeplyParseHeader(frontmatter.title)
29
+
30
+ const match = content.match(/^\s*#+\s+(.*)/m)
31
+
32
+ if (match)
33
+ return deeplyParseHeader(match[1].trim())
34
+
35
+ return ''
36
+ }
37
+
38
+ const getHeadMetaContent = (
39
+ head: HeadConfig[],
40
+ name: string,
41
+ ): string | undefined => {
42
+ if (!head || !head.length)
43
+ return undefined
44
+
45
+ const meta = head.find(([tag, attrs = {}]) => {
46
+ return tag === 'meta' && attrs.name === name && attrs.content
47
+ })
48
+
49
+ return meta && meta[1].content
50
+ }
51
+
52
+ const inferDescription = (frontmatter: Record<string, any>) => {
53
+ const { description, head } = frontmatter
54
+
55
+ if (description !== undefined)
56
+ return description
57
+
58
+ return (head && getHeadMetaContent(head, 'description')) || ''
59
+ }
60
+
61
+ export async function createMarkdownToVueRenderFn(
62
+ srcDir: string,
63
+ options: MarkdownOptions = {},
64
+ pages: string[],
65
+ userDefines: Record<string, any> | undefined,
66
+ isBuild = false,
67
+ base = '/',
68
+ includeLastUpdatedData = false,
69
+ ) {
70
+ const md = await createMarkdownRenderer(srcDir, options, base)
71
+
72
+ pages = pages.map(p => slash(p.replace(/\.md$/, '')))
73
+
74
+ const userDefineRegex = userDefines
75
+ ? new RegExp(
76
+ `\\b(${Object.keys(userDefines)
77
+ .map(key => key.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'))
78
+ .join('|')})`,
79
+ 'g',
80
+ )
81
+ : null
82
+
83
+ return async (
84
+ src: string,
85
+ file: string,
86
+ publicDir: string,
87
+ ): Promise<MarkdownCompileResult> => {
88
+ const relativePath = slash(path.relative(srcDir, file))
89
+ const dir = path.dirname(file)
90
+
91
+ const cached = cache.get(src)
92
+ if (cached) {
93
+ debug(`[cache hit] ${relativePath}`)
94
+ return cached
95
+ }
96
+
97
+ const start = Date.now()
98
+
99
+ // resolve includes
100
+ const includes: string[] = []
101
+ src = src.replace(includesRE, (_, m1) => {
102
+ const includePath = path.join(dir, m1)
103
+ const content = fs.readFileSync(includePath, 'utf-8')
104
+ includes.push(slash(includePath))
105
+ return content
106
+ })
107
+
108
+ const { content, data: frontmatter } = matter(src)
109
+
110
+ // reset state before render
111
+ md.__path = file
112
+ md.__relativePath = relativePath
113
+
114
+ let html = md.render(content)
115
+ const data = md.__data
116
+
117
+ if (isBuild) {
118
+ // avoid env variables being replaced by vite
119
+ html = html
120
+ .replace(/\bimport\.meta/g, 'import.<wbr/>meta')
121
+ .replace(/\bprocess\.env/g, 'process.<wbr/>env')
122
+
123
+ // also avoid replacing vite user defines
124
+ if (userDefineRegex) {
125
+ html = html.replace(
126
+ userDefineRegex,
127
+ _ => `${_[0]}<wbr/>${_.slice(1)}`,
128
+ )
129
+ }
130
+ }
131
+
132
+ // validate data.links
133
+ const deadLinks: string[] = []
134
+ const recordDeadLink = (url: string) => {
135
+ console.warn(
136
+ c.yellow(
137
+ `\n(!) Found dead link ${c.cyan(url)} in file ${c.white(c.dim(file))}`,
138
+ ),
139
+ )
140
+ deadLinks.push(url)
141
+ }
142
+
143
+ if (data.links) {
144
+ const dir = path.dirname(file)
145
+ for (let url of data.links) {
146
+ if (/\.(?!html|md)\w+($|\?)/i.test(url))
147
+ continue
148
+
149
+ if (url.replace(EXTERNAL_URL_RE, '').startsWith('//localhost:')) {
150
+ recordDeadLink(url)
151
+ continue
152
+ }
153
+
154
+ url = url.replace(/[?#].*$/, '').replace(/\.(html|md)$/, '')
155
+ if (url.endsWith('/'))
156
+ url += 'index'
157
+ const resolved = decodeURIComponent(
158
+ slash(
159
+ url.startsWith('/')
160
+ ? url.slice(1)
161
+ : path.relative(srcDir, path.resolve(dir, url)),
162
+ ),
163
+ )
164
+ if (
165
+ !pages.includes(resolved)
166
+ && !fs.existsSync(path.resolve(dir, publicDir, `${resolved}.html`))
167
+ )
168
+ recordDeadLink(url)
169
+ }
170
+ }
171
+
172
+ const pageData: PageData = {
173
+ title: inferTitle(frontmatter, content),
174
+ titleTemplate: frontmatter.titleTemplate,
175
+ description: inferDescription(frontmatter),
176
+ frontmatter,
177
+ headers: data.headers || [],
178
+ relativePath,
179
+ path: path.join(srcDir, relativePath),
180
+ }
181
+
182
+ if (includeLastUpdatedData)
183
+ pageData.lastUpdated = await getGitTimestamp(file)
184
+
185
+ const pageComponent = 'ValaxyMain'
186
+
187
+ function generateSlots() {
188
+ const slots = [
189
+ 'main-header',
190
+ 'main-header-after',
191
+ 'main-nav',
192
+ 'main-content',
193
+ 'main-content-after',
194
+ 'footer',
195
+ 'aside',
196
+ 'aside-custom',
197
+ ]
198
+ const slotsText = slots.map(s => `<template #${s}><slot name="${s}" /></template>`).join('')
199
+ return slotsText
200
+ }
201
+ const vueSrc
202
+ = `${genPageDataCode(data.hoistedTags || [], pageData).join('\n')
203
+ }\n<template><${pageComponent} :frontmatter="frontmatter" :data="data">
204
+ <template #main-content-md>${html}</template>
205
+ ${generateSlots()}
206
+ <slot />
207
+ </${pageComponent}></template>`
208
+
209
+ debug(`[render] ${file} in ${Date.now() - start}ms.`)
210
+
211
+ const result = {
212
+ vueSrc,
213
+ pageData,
214
+ deadLinks,
215
+ includes,
216
+ }
217
+ cache.set(src, result)
218
+ return result
219
+ }
220
+ }
221
+
222
+ const scriptRE = /<\/script>/
223
+ const scriptLangTsRE = /<\s*script[^>]*\blang=['"]ts['"][^>]*/
224
+ const scriptSetupRE = /<\s*script[^>]*\bsetup\b[^>]*/
225
+ const scriptClientRE = /<\s*script[^>]*\bclient\b[^>]*/
226
+ const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/
227
+ const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)as(\s*)default/
228
+
229
+ function genPageDataCode(tags: string[], data: PageData) {
230
+ const code = ''
231
+
232
+ const existingScriptIndex = tags.findIndex((tag) => {
233
+ return (
234
+ scriptRE.test(tag)
235
+ && !scriptSetupRE.test(tag)
236
+ && !scriptClientRE.test(tag)
237
+ )
238
+ })
239
+
240
+ const isUsingTS = tags.findIndex(tag => scriptLangTsRE.test(tag)) > -1
241
+
242
+ const exportScript = `
243
+ export default {
244
+ name:'${data.relativePath}',
245
+ data() {
246
+ return {
247
+ frontmatter:${transformObject(data.frontmatter)},
248
+ data: ${transformObject(data)}
249
+ }
250
+ }
251
+ }`
252
+
253
+ if (existingScriptIndex > -1) {
254
+ const tagSrc = tags[existingScriptIndex]
255
+ // user has <script> tag inside markdown
256
+ // if it doesn't have export default it will error out on build
257
+ const hasDefaultExport
258
+ = defaultExportRE.test(tagSrc) || namedDefaultExportRE.test(tagSrc)
259
+ tags[existingScriptIndex] = tagSrc.replace(
260
+ scriptRE,
261
+ `${code
262
+ + (hasDefaultExport
263
+ ? ''
264
+ : `\n${exportScript}`)
265
+ }</script>`,
266
+ )
267
+ }
268
+ else {
269
+ tags.unshift(
270
+ `<script ${isUsingTS ? 'lang="ts"' : ''}>${code}\n${exportScript}</script>`,
271
+ )
272
+ }
273
+
274
+ return tags
275
+ }
package/node/options.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { dirname, resolve } from 'path'
2
2
  import _debug from 'debug'
3
+ import fg from 'fast-glob'
3
4
  import type { ValaxyConfig } from '../types'
4
5
  import { resolveConfig } from './config'
5
6
  import { resolveImportPath } from './utils'
@@ -43,6 +44,7 @@ export interface ResolvedValaxyOptions {
43
44
  * config file path
44
45
  */
45
46
  configFile: string
47
+ pages: string[]
46
48
  }
47
49
 
48
50
  export interface ValaxyServerOptions {
@@ -77,6 +79,19 @@ export async function resolveOptions(options: ValaxyEntryOptions, mode: Resolved
77
79
  const { config: valaxyConfig, configFile, theme } = await resolveConfig(options)
78
80
  const themeRoot = getThemeRoot(theme, userRoot)
79
81
 
82
+ // Important: fast-glob doesn't guarantee order of the returned files.
83
+ // We must sort the pages so the input list to rollup is stable across
84
+ // builds - otherwise different input order could result in different exports
85
+ // order in shared chunks which in turns invalidates the hash of every chunk!
86
+ // JavaScript built-in sort() is mandated to be stable as of ES2019 and
87
+ // supported in Node 12+, which is required by Vite.
88
+ const pages = (
89
+ await fg(['**.md'], {
90
+ cwd: userRoot,
91
+ ignore: ['**/node_modules'],
92
+ })
93
+ ).sort()
94
+
80
95
  const valaxyOptions: ResolvedValaxyOptions = {
81
96
  mode,
82
97
  clientRoot,
@@ -85,6 +100,7 @@ export async function resolveOptions(options: ValaxyEntryOptions, mode: Resolved
85
100
  theme,
86
101
  config: valaxyConfig,
87
102
  configFile: configFile || '',
103
+ pages,
88
104
  }
89
105
  debug(valaxyOptions)
90
106
 
@@ -14,13 +14,13 @@ export function createConfigPlugin(options: ResolvedValaxyOptions): Plugin {
14
14
  '@/': `${toAtFS(options.userRoot)}/`,
15
15
  '~/': `${toAtFS(options.clientRoot)}/`,
16
16
  'valaxy/client': `${toAtFS(options.clientRoot)}/`,
17
+ 'valaxy': toAtFS(resolve(options.clientRoot, 'index.ts')),
17
18
  '@valaxyjs/client': `${toAtFS(options.clientRoot)}/`,
18
19
  '@valaxyjs/config': '/@valaxyjs/config',
20
+ '@valaxyjs/context': '/@valaxyjs/context',
19
21
  'valaxy/package.json': toAtFS(resolve(options.clientRoot, '../../package.json')),
20
- 'valaxy/': `${toAtFS(resolve(options.clientRoot, '..'))}/`,
21
- 'valaxy': toAtFS(resolve(options.clientRoot, '../index.ts')),
22
22
  [`valaxy-theme-${options.theme}/`]: `${toAtFS(resolve(options.themeRoot))}/`,
23
- [`valaxy-theme-${options.theme}`]: `${toAtFS(resolve(options.themeRoot))}`,
23
+ [`valaxy-theme-${options.theme}`]: `${toAtFS(resolve(options.themeRoot))}/index.ts`,
24
24
  },
25
25
  },
26
26
 
@@ -37,6 +37,7 @@ export function createConfigPlugin(options: ResolvedValaxyOptions): Plugin {
37
37
  'nprogress',
38
38
  ],
39
39
 
40
+ exclude: ['@docsearch/js'],
40
41
  },
41
42
  }
42
43
  return mergeConfig(config, injection)