vitepress 2.0.0-alpha.17 → 2.0.0-alpha.19

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 (78) hide show
  1. package/dist/client/app/components/Content.js +1 -1
  2. package/dist/client/app/data.js +4 -14
  3. package/dist/client/app/index.js +1 -1
  4. package/dist/client/app/router.js +8 -21
  5. package/dist/client/app/utils.js +1 -35
  6. package/dist/client/index.d.ts +37 -62
  7. package/dist/client/index.js +5 -2
  8. package/dist/client/shared.js +17 -15
  9. package/dist/client/theme-default/components/VPAlgoliaSearchBox.vue +8 -9
  10. package/dist/client/theme-default/components/VPButton.vue +1 -1
  11. package/dist/client/theme-default/components/VPCarbonAds.vue +10 -5
  12. package/dist/client/theme-default/components/VPContent.vue +10 -8
  13. package/dist/client/theme-default/components/VPDoc.vue +0 -1
  14. package/dist/client/theme-default/components/VPDocAside.vue +1 -1
  15. package/dist/client/theme-default/components/VPDocAsideCarbonAds.vue +1 -1
  16. package/dist/client/theme-default/components/VPDocAsideOutline.vue +1 -1
  17. package/dist/client/theme-default/components/VPDocAsideSponsors.vue +1 -1
  18. package/dist/client/theme-default/components/VPDocFooter.vue +5 -1
  19. package/dist/client/theme-default/components/VPDocFooterLastUpdated.vue +1 -1
  20. package/dist/client/theme-default/components/VPFeatures.vue +4 -4
  21. package/dist/client/theme-default/components/VPHome.vue +3 -3
  22. package/dist/client/theme-default/components/VPHomeContent.vue +1 -0
  23. package/dist/client/theme-default/components/VPImage.vue +1 -1
  24. package/dist/client/theme-default/components/VPLink.vue +8 -8
  25. package/dist/client/theme-default/components/VPLocalNavOutlineDropdown.vue +2 -1
  26. package/dist/client/theme-default/components/VPLocalSearchBox.vue +103 -25
  27. package/dist/client/theme-default/components/VPMenu.vue +4 -4
  28. package/dist/client/theme-default/components/VPMenuLink.vue +8 -7
  29. package/dist/client/theme-default/components/VPNavBarExtra.vue +13 -9
  30. package/dist/client/theme-default/components/VPNavBarMenu.vue +17 -11
  31. package/dist/client/theme-default/components/VPNavBarMenuGroup.vue +11 -5
  32. package/dist/client/theme-default/components/VPNavBarMenuLink.vue +8 -7
  33. package/dist/client/theme-default/components/VPNavBarTranslations.vue +12 -8
  34. package/dist/client/theme-default/components/VPNavScreenMenu.vue +17 -15
  35. package/dist/client/theme-default/components/VPNavScreenMenuGroup.vue +4 -4
  36. package/dist/client/theme-default/components/VPNavScreenMenuGroupLink.vue +8 -7
  37. package/dist/client/theme-default/components/VPNavScreenMenuGroupSection.vue +5 -1
  38. package/dist/client/theme-default/components/VPNavScreenMenuLink.vue +8 -7
  39. package/dist/client/theme-default/components/VPNavScreenTranslations.vue +7 -1
  40. package/dist/client/theme-default/components/VPSidebarItem.vue +4 -4
  41. package/dist/client/theme-default/components/VPSkipLink.vue +1 -1
  42. package/dist/client/theme-default/components/VPSocialLink.vue +3 -2
  43. package/dist/client/theme-default/components/VPSocialLinks.vue +19 -10
  44. package/dist/client/theme-default/components/VPSponsors.vue +1 -1
  45. package/dist/client/theme-default/components/VPSponsorsGrid.vue +5 -5
  46. package/dist/client/theme-default/components/VPTeamMembers.vue +15 -4
  47. package/dist/client/theme-default/components/VPTeamMembersItem.vue +2 -4
  48. package/dist/client/theme-default/composables/flyout.js +1 -1
  49. package/dist/client/theme-default/composables/langs.js +39 -10
  50. package/dist/client/theme-default/composables/layout.js +1 -1
  51. package/dist/client/theme-default/composables/outline.js +21 -6
  52. package/dist/client/theme-default/composables/prev-next.js +19 -13
  53. package/dist/client/theme-default/composables/sidebar.js +27 -20
  54. package/dist/client/theme-default/fonts/inter-italic-latin.woff2 +0 -0
  55. package/dist/client/theme-default/fonts/inter-italic-symbols.woff2 +0 -0
  56. package/dist/client/theme-default/fonts/inter-roman-latin.woff2 +0 -0
  57. package/dist/client/theme-default/fonts/inter-roman-symbols.woff2 +0 -0
  58. package/dist/client/theme-default/index.js +1 -0
  59. package/dist/client/theme-default/styles/base.css +211 -141
  60. package/dist/client/theme-default/styles/components/custom-block.css +6 -1
  61. package/dist/client/theme-default/styles/components/vp-code.css +8 -0
  62. package/dist/client/theme-default/styles/components/vp-doc.css +63 -7
  63. package/dist/client/theme-default/styles/components/vp-sponsor.css +8 -0
  64. package/dist/client/theme-default/styles/fonts.css +151 -141
  65. package/dist/client/theme-default/styles/vars.css +2 -34
  66. package/dist/client/theme-default/support/docsearch.js +23 -8
  67. package/dist/client/theme-default/support/reactivity.js +2 -2
  68. package/dist/client/theme-default/support/sidebar.js +14 -10
  69. package/dist/client/theme-default/support/utils.js +13 -0
  70. package/dist/node/{chunk-CpMTk0EA.js → chunk-B0BRqWJx.js} +13032 -17533
  71. package/dist/node/cli.js +46 -62
  72. package/dist/node/index.d.ts +660 -208
  73. package/dist/node/index.js +25 -17
  74. package/dist/web-types.json +75 -0
  75. package/package.json +69 -72
  76. package/theme.d.ts +7 -1
  77. package/types/default-theme.d.ts +13 -25
  78. package/types/shared.d.ts +430 -48
@@ -8,8 +8,8 @@ export const Content = defineComponent({
8
8
  as: { type: [Object, String], default: 'div' }
9
9
  },
10
10
  setup(props) {
11
- const route = useRoute();
12
11
  const { frontmatter, site } = useData();
12
+ const route = useRoute();
13
13
  watch(frontmatter, runCbs, { deep: true, flush: 'post' });
14
14
  return () => h(props.as, site.value.contentProps ?? { style: { position: 'relative' } }, [
15
15
  route.component
@@ -1,13 +1,13 @@
1
1
  import siteData from '@siteData';
2
2
  import { useDark, usePreferredDark } from '@vueuse/core';
3
- import { computed, inject, readonly, ref, shallowRef, watch } from 'vue';
4
- import { APPEARANCE_KEY, createTitle, inBrowser, resolveSiteDataByRoute } from '../shared';
3
+ import { computed, inject, readonly, ref, shallowRef } from 'vue';
4
+ import { APPEARANCE_KEY, createTitle, resolveSiteDataByRoute } from '../shared';
5
5
  export const dataSymbol = Symbol();
6
6
  // site data is a singleton
7
7
  export const siteDataRef = shallowRef(readonly(siteData));
8
8
  // per-app data
9
9
  export function initData(route) {
10
- const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.data.relativePath));
10
+ const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.data.relativePath, route.data.filePath));
11
11
  const appearance = site.value.appearance; // fine with reactivity being lost here, config change triggers a restart
12
12
  const isDark = appearance === 'force-dark'
13
13
  ? ref(true)
@@ -20,15 +20,6 @@ export function initData(route) {
20
20
  ...(typeof appearance === 'object' ? appearance : {})
21
21
  })
22
22
  : ref(false);
23
- const hashRef = ref(inBrowser ? location.hash : '');
24
- if (inBrowser) {
25
- window.addEventListener('hashchange', () => {
26
- hashRef.value = location.hash;
27
- });
28
- }
29
- watch(() => route.data, () => {
30
- hashRef.value = inBrowser ? location.hash : '';
31
- });
32
23
  return {
33
24
  site,
34
25
  theme: computed(() => site.value.themeConfig),
@@ -40,8 +31,7 @@ export function initData(route) {
40
31
  localeIndex: computed(() => site.value.localeIndex || 'root'),
41
32
  title: computed(() => createTitle(site.value, route.data)),
42
33
  description: computed(() => route.data.description || site.value.description),
43
- isDark,
44
- hash: computed(() => hashRef.value)
34
+ isDark
45
35
  };
46
36
  }
47
37
  export function useData() {
@@ -136,7 +136,7 @@ if (inBrowser) {
136
136
  app.mount('#app');
137
137
  // scroll to hash on new tab during dev
138
138
  if (import.meta.env.DEV && location.hash) {
139
- scrollTo(location.hash);
139
+ setTimeout(() => scrollTo(location.hash), 100);
140
140
  }
141
141
  });
142
142
  });
@@ -1,7 +1,7 @@
1
1
  import { inject, markRaw, nextTick, reactive, readonly } from 'vue';
2
2
  import { notFoundPageData, treatAsHtml } from '../shared';
3
3
  import { siteDataRef } from './data';
4
- import { getScrollOffset, inBrowser, withBase } from './utils';
4
+ import { inBrowser, withBase } from './utils';
5
5
  export const RouterSymbol = Symbol();
6
6
  // we are just using URL to parse the pathname and hash - the base doesn't
7
7
  // matter and is only passed to support same-host hrefs
@@ -70,7 +70,7 @@ export function createRouter(loadPageModule, fallbackComponent) {
70
70
  history.replaceState({}, '', href);
71
71
  }
72
72
  if (!initialLoad)
73
- scrollTo(targetLoc.hash, false, scrollPosition);
73
+ scrollTo(targetLoc.hash, scrollPosition);
74
74
  });
75
75
  }
76
76
  }
@@ -143,10 +143,7 @@ export function createRouter(loadPageModule, fallbackComponent) {
143
143
  // only intercept inbound html links
144
144
  if (origin === currentLoc.origin && treatAsHtml(pathname)) {
145
145
  e.preventDefault();
146
- router.go(href, {
147
- // use smooth scroll when clicking on header anchor links
148
- smoothScroll: link.classList.contains('header-anchor')
149
- });
146
+ router.go(href);
150
147
  }
151
148
  }, { capture: true });
152
149
  window.addEventListener('popstate', async (e) => {
@@ -174,7 +171,7 @@ export function useRouter() {
174
171
  export function useRoute() {
175
172
  return useRouter().route;
176
173
  }
177
- export function scrollTo(hash, smooth = false, scrollPosition = 0) {
174
+ export function scrollTo(hash, scrollPosition = 0) {
178
175
  if (!hash || scrollPosition) {
179
176
  window.scrollTo(0, scrollPosition);
180
177
  return;
@@ -188,18 +185,8 @@ export function scrollTo(hash, smooth = false, scrollPosition = 0) {
188
185
  }
189
186
  if (!target)
190
187
  return;
191
- const targetTop = window.scrollY +
192
- target.getBoundingClientRect().top -
193
- getScrollOffset() +
194
- Number.parseInt(window.getComputedStyle(target).paddingTop, 10) || 0;
195
- const behavior = window.matchMedia('(prefers-reduced-motion)').matches
196
- ? 'instant'
197
- : // only smooth scroll if distance is smaller than screen height
198
- smooth && Math.abs(targetTop - window.scrollY) <= window.innerHeight
199
- ? 'smooth'
200
- : 'auto';
201
188
  const scrollToTarget = () => {
202
- window.scrollTo({ left: 0, top: targetTop, behavior });
189
+ target.scrollIntoView({ block: 'start' });
203
190
  // focus the target element for better accessibility
204
191
  target.focus({ preventScroll: true });
205
192
  // return if focus worked
@@ -253,14 +240,14 @@ function normalizeHref(href) {
253
240
  }
254
241
  return url.pathname + url.search + url.hash.split(':~:')[0];
255
242
  }
256
- async function changeRoute(href, { smoothScroll = false, initialLoad = false, replace = false, hasTextFragment = false } = {}) {
243
+ async function changeRoute(href, { initialLoad = false, replace = false, hasTextFragment = false } = {}) {
257
244
  const loc = normalizeHref(location.href);
258
245
  const nextUrl = new URL(href, location.origin);
259
246
  const currentUrl = new URL(loc, location.origin);
260
247
  if (href === loc) {
261
248
  if (!initialLoad) {
262
249
  if (!hasTextFragment)
263
- scrollTo(nextUrl.hash, smoothScroll);
250
+ scrollTo(nextUrl.hash);
264
251
  return false;
265
252
  }
266
253
  }
@@ -281,7 +268,7 @@ async function changeRoute(href, { smoothScroll = false, initialLoad = false, re
281
268
  newURL: nextUrl.href
282
269
  }));
283
270
  if (!hasTextFragment)
284
- scrollTo(nextUrl.hash, smoothScroll);
271
+ scrollTo(nextUrl.hash);
285
272
  }
286
273
  return false;
287
274
  }
@@ -10,7 +10,7 @@ export function joinPath(base, path) {
10
10
  return `${base}${path}`.replace(/\/+/g, '/');
11
11
  }
12
12
  /**
13
- * Append base to internal (non-relative) urls
13
+ * Prepend base to internal (non-relative) urls
14
14
  */
15
15
  export function withBase(path) {
16
16
  return EXTERNAL_URL_RE.test(path) || !path.startsWith('/')
@@ -84,37 +84,3 @@ export function defineClientComponent(loader, args, cb) {
84
84
  }
85
85
  };
86
86
  }
87
- export function getScrollOffset() {
88
- let scrollOffset = siteDataRef.value.scrollOffset;
89
- let offset = 0;
90
- let padding = 24;
91
- if (typeof scrollOffset === 'object' && 'padding' in scrollOffset) {
92
- padding = scrollOffset.padding;
93
- scrollOffset = scrollOffset.selector;
94
- }
95
- if (typeof scrollOffset === 'number') {
96
- offset = scrollOffset;
97
- }
98
- else if (typeof scrollOffset === 'string') {
99
- offset = tryOffsetSelector(scrollOffset, padding);
100
- }
101
- else if (Array.isArray(scrollOffset)) {
102
- for (const selector of scrollOffset) {
103
- const res = tryOffsetSelector(selector, padding);
104
- if (res) {
105
- offset = res;
106
- break;
107
- }
108
- }
109
- }
110
- return offset;
111
- }
112
- function tryOffsetSelector(selector, padding) {
113
- const el = document.querySelector(selector);
114
- if (!el)
115
- return 0;
116
- const bot = el.getBoundingClientRect().bottom;
117
- if (bot < 0)
118
- return 0;
119
- return bot + padding;
120
- }
@@ -1,7 +1,7 @@
1
+ import { Route, Awaitable, SiteData, VitePressData, PageData } from '../../types/shared.js';
2
+ export { HeadConfig, Header, PageData, Route, SiteData, VitePressData } from '../../types/shared.js';
1
3
  import * as vue from 'vue';
2
- import { Component, Ref, InjectionKey, App, AsyncComponentLoader } from 'vue';
3
- import { PageData, Awaitable, SiteData } from '../../types/shared.js';
4
- export { HeadConfig, Header, PageData, SiteData } from '../../types/shared.js';
4
+ import { App, Ref, Component, InjectionKey, AsyncComponentLoader } from 'vue';
5
5
 
6
6
  declare const inBrowser: boolean;
7
7
  /**
@@ -9,13 +9,6 @@ declare const inBrowser: boolean;
9
9
  */
10
10
  declare function escapeHtml(str: string): string;
11
11
 
12
- interface Route {
13
- path: string;
14
- hash: string;
15
- query: string;
16
- data: PageData;
17
- component: Component | null;
18
- }
19
12
  interface Router {
20
13
  /**
21
14
  * Current route.
@@ -26,7 +19,6 @@ interface Router {
26
19
  */
27
20
  go: (to: string, options?: {
28
21
  initialLoad?: boolean;
29
- smoothScroll?: boolean;
30
22
  replace?: boolean;
31
23
  }) => Promise<void>;
32
24
  /**
@@ -50,41 +42,6 @@ interface Router {
50
42
  declare function useRouter(): Router;
51
43
  declare function useRoute(): Route;
52
44
 
53
- declare const dataSymbol: InjectionKey<VitePressData>;
54
- interface VitePressData<T = any> {
55
- /**
56
- * Site-level metadata
57
- */
58
- site: Ref<SiteData<T>>;
59
- /**
60
- * themeConfig from .vitepress/config.js
61
- */
62
- theme: Ref<T>;
63
- /**
64
- * Page-level metadata
65
- */
66
- page: Ref<PageData>;
67
- /**
68
- * page frontmatter data
69
- */
70
- frontmatter: Ref<PageData['frontmatter']>;
71
- /**
72
- * dynamic route params
73
- */
74
- params: Ref<PageData['params']>;
75
- title: Ref<string>;
76
- description: Ref<string>;
77
- lang: Ref<string>;
78
- dir: Ref<string>;
79
- localeIndex: Ref<string>;
80
- isDark: Ref<boolean>;
81
- /**
82
- * Current location hash
83
- */
84
- hash: Ref<string>;
85
- }
86
- declare function useData<T = any>(): VitePressData<T>;
87
-
88
45
  interface EnhanceAppContext {
89
46
  app: App;
90
47
  router: Router;
@@ -104,21 +61,9 @@ interface Theme {
104
61
  NotFound?: Component;
105
62
  }
106
63
 
107
- /**
108
- * Append base to internal (non-relative) urls
109
- */
110
- declare function withBase(path: string): string;
111
- /**
112
- * Register callback that is called every time the markdown content is updated
113
- * in the DOM.
114
- */
115
- declare function onContentUpdated(fn: () => any): void;
116
- declare function defineClientComponent(loader: AsyncComponentLoader, args?: any[], cb?: () => Awaitable<void>): {
117
- setup(): () => vue.VNode<vue.RendererNode, vue.RendererElement, {
118
- [key: string]: any;
119
- }> | null;
120
- };
121
- declare function getScrollOffset(): number;
64
+ declare const ClientOnly: vue.DefineComponent<{}, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
65
+ [key: string]: any;
66
+ }>[] | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
122
67
 
123
68
  declare const Content: vue.DefineComponent<vue.ExtractPropTypes<{
124
69
  as: {
@@ -136,4 +81,34 @@ declare const Content: vue.DefineComponent<vue.ExtractPropTypes<{
136
81
  as: string | Record<string, any>;
137
82
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
138
83
 
139
- export { Content, type EnhanceAppContext, type Route, type Router, type Theme, type VitePressData, escapeHtml as _escapeHtml, dataSymbol, defineClientComponent, getScrollOffset, inBrowser, onContentUpdated, useData, useRoute, useRouter, withBase };
84
+ declare const dataSymbol: InjectionKey<VitePressData>;
85
+
86
+ declare function useData<T = any>(): VitePressData<T>;
87
+
88
+ /**
89
+ * Prepend base to internal (non-relative) urls
90
+ */
91
+ declare function withBase(path: string): string;
92
+ /**
93
+ * Register callback that is called every time the markdown content is updated
94
+ * in the DOM.
95
+ */
96
+ declare function onContentUpdated(fn: () => any): void;
97
+ declare function defineClientComponent(loader: AsyncComponentLoader, args?: any[], cb?: () => Awaitable<void>): {
98
+ setup(): () => vue.VNode<vue.RendererNode, vue.RendererElement, {
99
+ [key: string]: any;
100
+ }> | null;
101
+ };
102
+
103
+ declare module 'vue' {
104
+ interface GlobalComponents {
105
+ ClientOnly: typeof ClientOnly;
106
+ Content: typeof Content;
107
+ }
108
+ interface ComponentCustomProperties {
109
+ $frontmatter: PageData['frontmatter'];
110
+ $params: PageData['params'];
111
+ }
112
+ }
113
+
114
+ export { ClientOnly, Content, type EnhanceAppContext, type Router, type Theme, escapeHtml as _escapeHtml, dataSymbol, defineClientComponent, inBrowser, onContentUpdated, useData, useRoute, useRouter, withBase };
@@ -1,9 +1,12 @@
1
1
  // exports in this file are exposed to themes and md files via 'vitepress'
2
2
  // so the user can do `import { useRoute, useData } from 'vitepress'`
3
+ // components
4
+ import { ClientOnly } from './app/components/ClientOnly';
5
+ import { Content } from './app/components/Content';
3
6
  // composables
4
7
  export { dataSymbol, useData } from './app/data';
5
8
  export { useRoute, useRouter } from './app/router';
6
9
  // utilities
7
- export { _escapeHtml, defineClientComponent, getScrollOffset, inBrowser, onContentUpdated, withBase } from './app/utils';
10
+ export { _escapeHtml, defineClientComponent, inBrowser, onContentUpdated, withBase } from './app/utils';
8
11
  // components
9
- export { Content } from './app/components/Content';
12
+ export { ClientOnly, Content };
@@ -4,7 +4,12 @@ export const VP_SOURCE_KEY = '[VP_SOURCE]';
4
4
  const UnpackStackView = Symbol('stack-view:unpack');
5
5
  const HASH_WITHOUT_FRAGMENT_RE = /#.*?(?=:~:|$)/;
6
6
  const HASH_OR_QUERY_RE = /[?#].*$/;
7
- const INDEX_OR_EXT_RE = /(?:(^|\/)index)?\.(?:md|html)$/;
7
+ const INDEX_OR_EXT_RE = /(?:(^|\/)index)?(?:\.(?:md|html))?$/;
8
+ // https://github.com/rollup/rollup/blob/fec513270c6ac350072425cc045db367656c623b/src/utils/sanitizeFileName.ts
9
+ const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g;
10
+ const DRIVE_LETTER_REGEX = /^[a-z]:/i;
11
+ const KNOWN_EXTENSIONS = new Set();
12
+ const shellLangs = ['shellscript', 'shell', 'bash', 'sh', 'zsh'];
8
13
  export const inBrowser = typeof document !== 'undefined';
9
14
  export const notFoundPageData = {
10
15
  relativePath: '404.md',
@@ -16,10 +21,7 @@ export const notFoundPageData = {
16
21
  lastUpdated: 0,
17
22
  isNotFound: true
18
23
  };
19
- export function isActive(currentPath, matchPath, asRegex = false) {
20
- if (matchPath === undefined) {
21
- return false;
22
- }
24
+ export function isActive(currentPath, currentHash, matchPath, asRegex = false, skipHashCheck = false) {
23
25
  currentPath = normalize(`/${currentPath}`);
24
26
  if (asRegex) {
25
27
  return new RegExp(matchPath).test(currentPath);
@@ -27,9 +29,12 @@ export function isActive(currentPath, matchPath, asRegex = false) {
27
29
  if (normalize(matchPath) !== currentPath) {
28
30
  return false;
29
31
  }
32
+ if (skipHashCheck) {
33
+ return true;
34
+ }
30
35
  const hashMatch = matchPath.match(HASH_WITHOUT_FRAGMENT_RE);
31
36
  if (hashMatch) {
32
- return (inBrowser ? location.hash : '') === hashMatch[0];
37
+ return currentHash === hashMatch[0];
33
38
  }
34
39
  return true;
35
40
  }
@@ -44,16 +49,18 @@ export function isExternal(path) {
44
49
  export function getLocaleForPath(siteData, relativePath) {
45
50
  return (Object.keys(siteData?.locales || {}).find((key) => key !== 'root' &&
46
51
  !isExternal(key) &&
47
- isActive(relativePath, `^/${key}/`, true)) || 'root');
52
+ isActive(relativePath, '', `^/${key}/`, true)) || 'root');
48
53
  }
49
54
  /**
50
55
  * this merges the locales data to the main data by the route
51
56
  */
52
- export function resolveSiteDataByRoute(siteData, relativePath) {
57
+ export function resolveSiteDataByRoute(siteData, relativePath, filePath) {
53
58
  const localeIndex = getLocaleForPath(siteData, relativePath);
54
- const { label, link, ...localeConfig } = siteData.locales[localeIndex] ?? {};
59
+ const { label, link, markdown, ...localeConfig } = siteData.locales[localeIndex] ?? {};
55
60
  Object.assign(localeConfig, { localeIndex });
56
- const additionalConfigs = resolveAdditionalConfig(siteData, relativePath);
61
+ // additional configs are colocated with sources, so resolve them by the
62
+ // source path (filePath) rather than the rewritten one
63
+ const additionalConfigs = resolveAdditionalConfig(siteData, filePath || relativePath);
57
64
  if (inBrowser && import.meta.env?.DEV) {
58
65
  ;
59
66
  localeConfig[VP_SOURCE_KEY] = `locale config (${localeIndex})`;
@@ -119,9 +126,6 @@ export function mergeHead(...headArrays) {
119
126
  }
120
127
  return merged;
121
128
  }
122
- // https://github.com/rollup/rollup/blob/fec513270c6ac350072425cc045db367656c623b/src/utils/sanitizeFileName.ts
123
- const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g;
124
- const DRIVE_LETTER_REGEX = /^[a-z]:/i;
125
129
  export function sanitizeFileName(name) {
126
130
  const match = DRIVE_LETTER_REGEX.exec(name);
127
131
  const driveLetter = match ? match[0] : '';
@@ -134,7 +138,6 @@ export function sanitizeFileName(name) {
134
138
  export function slash(p) {
135
139
  return p.replace(/\\/g, '/');
136
140
  }
137
- const KNOWN_EXTENSIONS = new Set();
138
141
  export function treatAsHtml(filename) {
139
142
  if (KNOWN_EXTENSIONS.size === 0) {
140
143
  const extraExts = (typeof process === 'object' && process.env?.VITE_EXTRA_EXTENSIONS) ||
@@ -237,7 +240,6 @@ stackView.unpack = function (obj) {
237
240
  export function isObject(value) {
238
241
  return Object.prototype.toString.call(value) === '[object Object]';
239
242
  }
240
- const shellLangs = ['shellscript', 'shell', 'bash', 'sh', 'zsh'];
241
243
  export function isShell(lang) {
242
244
  return shellLangs.includes(lang);
243
245
  }
@@ -1,12 +1,16 @@
1
1
  <script setup lang="ts">
2
2
  import type { DocSearchInstance, DocSearchProps } from '@docsearch/js'
3
- import type { SidepanelInstance, SidepanelProps } from '@docsearch/sidepanel-js'
3
+ import type { SidepanelInstance } from '@docsearch/sidepanel-js'
4
4
  import { inBrowser, useRouter } from 'vitepress'
5
5
  import type { DefaultTheme } from 'vitepress/theme'
6
6
  import { nextTick, onUnmounted, watch } from 'vue'
7
7
  import type { DocSearchAskAi } from '../../../../types/docsearch'
8
8
  import { useData } from '../composables/data'
9
- import { resolveMode, validateCredentials } from '../support/docsearch'
9
+ import {
10
+ buildSidePanelProps,
11
+ resolveMode,
12
+ validateCredentials
13
+ } from '../support/docsearch'
10
14
 
11
15
  import '../styles/docsearch.css'
12
16
 
@@ -105,12 +109,7 @@ async function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
105
109
  if (currentInitialize !== initializeCount) return
106
110
 
107
111
  sidepanelInstance = sidepanel({
108
- ...(askAi.sidePanel === true ? {} : askAi.sidePanel),
109
- container: '#vp-docsearch-sidepanel',
110
- indexName: askAi.indexName ?? userOptions.indexName,
111
- appId: askAi.appId ?? userOptions.appId,
112
- apiKey: askAi.apiKey ?? userOptions.apiKey,
113
- assistantId: askAi.assistantId,
112
+ ...buildSidePanelProps(askAi, userOptions),
114
113
  onOpen: focusInput,
115
114
  onClose: onClose.bind(null, 'sidepanel'),
116
115
  onReady: () => {
@@ -122,7 +121,7 @@ async function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
122
121
  keyboardShortcuts: {
123
122
  'Ctrl/Cmd+I': false
124
123
  }
125
- } as SidepanelProps)
124
+ })
126
125
  }
127
126
 
128
127
  const options = {
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
- import { normalizeLink } from '../support/utils'
4
3
  import { EXTERNAL_URL_RE } from '../../shared'
4
+ import { normalizeLink } from '../support/utils'
5
5
 
6
6
  interface Props {
7
7
  tag?: string
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
+ import { useRoute } from 'vitepress'
2
3
  import type { DefaultTheme } from 'vitepress/theme'
3
- import { ref, watch, onMounted } from 'vue'
4
+ import { onMounted, ref, watch } from 'vue'
4
5
  import { useAside } from '../composables/aside'
5
- import { useData } from '../composables/data'
6
6
 
7
- const { page } = useData()
7
+ const route = useRoute()
8
8
  const props = defineProps<{
9
9
  carbonAds: DefaultTheme.CarbonAdsOptions
10
10
  }>()
@@ -19,15 +19,20 @@ let isInitialized = false
19
19
  function init() {
20
20
  if (!isInitialized) {
21
21
  isInitialized = true
22
+ const params = new URLSearchParams({
23
+ serve: carbonOptions.code,
24
+ placement: carbonOptions.placement,
25
+ format: carbonOptions?.format || 'classic',
26
+ })
22
27
  const s = document.createElement('script')
23
28
  s.id = '_carbonads_js'
24
- s.src = `//cdn.carbonads.com/carbon.js?serve=${carbonOptions.code}&placement=${carbonOptions.placement}`
29
+ s.src = `//cdn.carbonads.com/carbon.js?${params.toString()}`
25
30
  s.async = true
26
31
  container.value.appendChild(s)
27
32
  }
28
33
  }
29
34
 
30
- watch(() => page.value.relativePath, () => {
35
+ watch(() => route.data.relativePath, () => {
31
36
  if (isInitialized && isAsideEnabled.value) {
32
37
  ;(window as any)._carbonads?.refresh()
33
38
  }
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import { resolveDynamicComponent } from 'vue'
2
3
  import NotFound from '../NotFound.vue'
3
4
  import { useData } from '../composables/data'
4
5
  import { useLayout } from '../composables/layout'
@@ -8,6 +9,10 @@ import VPPage from './VPPage.vue'
8
9
 
9
10
  const { page, frontmatter } = useData()
10
11
  const { isHome, hasSidebar } = useLayout()
12
+
13
+ function isRegistered(component: string): boolean {
14
+ return typeof resolveDynamicComponent(component) !== 'string'
15
+ }
11
16
  </script>
12
17
 
13
18
  <template>
@@ -18,12 +23,12 @@ const { isHome, hasSidebar } = useLayout()
18
23
  >
19
24
  <slot name="not-found" v-if="page.isNotFound"><NotFound /></slot>
20
25
 
21
- <VPPage v-else-if="frontmatter.layout === 'page'">
26
+ <VPPage v-else-if="frontmatter.layout === 'page' && !isRegistered('page')">
22
27
  <template #page-top><slot name="page-top" /></template>
23
28
  <template #page-bottom><slot name="page-bottom" /></template>
24
29
  </VPPage>
25
30
 
26
- <VPHome v-else-if="frontmatter.layout === 'home'">
31
+ <VPHome v-else-if="frontmatter.layout === 'home' && !isRegistered('home')">
27
32
  <template #home-hero-before><slot name="home-hero-before" /></template>
28
33
  <template #home-hero-info-before><slot name="home-hero-info-before" /></template>
29
34
  <template #home-hero-info><slot name="home-hero-info" /></template>
@@ -36,12 +41,7 @@ const { isHome, hasSidebar } = useLayout()
36
41
  <template #home-features-after><slot name="home-features-after" /></template>
37
42
  </VPHome>
38
43
 
39
- <component
40
- v-else-if="frontmatter.layout && frontmatter.layout !== 'doc'"
41
- :is="frontmatter.layout"
42
- />
43
-
44
- <VPDoc v-else>
44
+ <VPDoc v-else-if="(!frontmatter.layout || frontmatter.layout === 'doc') && !isRegistered('doc')">
45
45
  <template #doc-top><slot name="doc-top" /></template>
46
46
  <template #doc-bottom><slot name="doc-bottom" /></template>
47
47
 
@@ -56,6 +56,8 @@ const { isHome, hasSidebar } = useLayout()
56
56
  <template #aside-ads-after><slot name="aside-ads-after" /></template>
57
57
  <template #aside-bottom><slot name="aside-bottom" /></template>
58
58
  </VPDoc>
59
+
60
+ <component v-else :is="frontmatter.layout || 'doc'" />
59
61
  </div>
60
62
  </template>
61
63
 
@@ -7,7 +7,6 @@ import VPDocAside from './VPDocAside.vue'
7
7
  import VPDocFooter from './VPDocFooter.vue'
8
8
 
9
9
  const { theme } = useData()
10
-
11
10
  const route = useRoute()
12
11
  const { hasSidebar, hasAside, leftAside } = useLayout()
13
12
 
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { useData } from '../composables/data'
3
- import VPDocAsideOutline from './VPDocAsideOutline.vue'
4
3
  import VPDocAsideCarbonAds from './VPDocAsideCarbonAds.vue'
4
+ import VPDocAsideOutline from './VPDocAsideOutline.vue'
5
5
 
6
6
  const { theme } = useData()
7
7
  </script>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { defineAsyncComponent } from 'vue'
3
2
  import type { DefaultTheme } from 'vitepress/theme'
3
+ import { defineAsyncComponent } from 'vue'
4
4
 
5
5
  defineProps<{
6
6
  carbonAds: DefaultTheme.CarbonAdsOptions
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import { ref } from 'vue'
3
3
  import { useData } from '../composables/data'
4
+ import { useLayout } from '../composables/layout'
4
5
  import { resolveTitle, useActiveAnchor } from '../composables/outline'
5
6
  import VPDocOutlineItem from './VPDocOutlineItem.vue'
6
- import { useLayout } from '../composables/layout'
7
7
 
8
8
  const { theme } = useData()
9
9
 
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { Sponsors } from './VPSponsors.vue'
3
- import type { Sponsor } from './VPSponsorsGrid.vue'
4
3
  import VPSponsors from './VPSponsors.vue'
4
+ import type { Sponsor } from './VPSponsorsGrid.vue'
5
5
 
6
6
  defineProps<{
7
7
  tier?: string
@@ -3,8 +3,8 @@ import { computed } from 'vue'
3
3
  import { useData } from '../composables/data'
4
4
  import { useEditLink } from '../composables/edit-link'
5
5
  import { usePrevNext } from '../composables/prev-next'
6
- import VPLink from './VPLink.vue'
7
6
  import VPDocFooterLastUpdated from './VPDocFooterLastUpdated.vue'
7
+ import VPLink from './VPLink.vue'
8
8
 
9
9
  const { theme, page, frontmatter } = useData()
10
10
 
@@ -53,6 +53,8 @@ const showFooter = computed(
53
53
  v-if="control.prev?.link"
54
54
  class="pager-link prev"
55
55
  :href="control.prev.link"
56
+ :target="control.prev.target"
57
+ :rel="control.prev.rel"
56
58
  >
57
59
  <span
58
60
  class="desc"
@@ -66,6 +68,8 @@ const showFooter = computed(
66
68
  v-if="control.next?.link"
67
69
  class="pager-link next"
68
70
  :href="control.next.link"
71
+ :target="control.next.target"
72
+ :rel="control.next.rel"
69
73
  >
70
74
  <span
71
75
  class="desc"
@@ -39,7 +39,7 @@ onMounted(() => {
39
39
 
40
40
  <template>
41
41
  <p class="VPLastUpdated">
42
- {{ theme.lastUpdated?.text || theme.lastUpdatedText || 'Last updated' }}:
42
+ {{ theme.lastUpdated?.text || 'Last updated' }}:
43
43
  <time ref="timeRef" :datetime="isoDatetime">{{ datetime }}</time>
44
44
  </p>
45
45
  </template>