vitepress 1.0.0-draft.8 → 1.0.0-rc.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 (169) hide show
  1. package/README.md +4 -3
  2. package/client.d.ts +2 -0
  3. package/dist/client/app/components/Content.js +15 -4
  4. package/dist/client/app/composables/codeGroups.js +40 -0
  5. package/dist/client/app/composables/copyCode.js +71 -0
  6. package/dist/client/app/composables/head.js +15 -44
  7. package/dist/client/app/composables/preFetch.js +9 -4
  8. package/dist/client/app/data.js +13 -16
  9. package/dist/client/app/devtools.js +28 -0
  10. package/dist/client/app/index.js +71 -46
  11. package/dist/client/app/router.js +132 -60
  12. package/dist/client/app/ssr.js +10 -0
  13. package/dist/client/app/utils.js +46 -10
  14. package/dist/client/index.d.ts +81 -87
  15. package/dist/client/index.js +3 -5
  16. package/dist/client/shared.js +83 -59
  17. package/dist/client/theme-default/Layout.vue +48 -9
  18. package/dist/client/theme-default/NotFound.vue +34 -9
  19. package/dist/client/theme-default/components/VPAlgoliaSearchBox.vue +46 -73
  20. package/dist/client/theme-default/components/VPBackdrop.vue +3 -1
  21. package/dist/client/theme-default/components/VPBadge.vue +74 -0
  22. package/dist/client/theme-default/components/VPButton.vue +6 -4
  23. package/dist/client/theme-default/components/VPCarbonAds.vue +30 -19
  24. package/dist/client/theme-default/components/VPContent.vue +30 -13
  25. package/dist/client/theme-default/components/VPDoc.vue +55 -36
  26. package/dist/client/theme-default/components/VPDocAside.vue +4 -4
  27. package/dist/client/theme-default/components/VPDocAsideCarbonAds.vue +7 -2
  28. package/dist/client/theme-default/components/VPDocAsideOutline.vue +20 -63
  29. package/dist/client/theme-default/components/VPDocAsideSponsors.vue +3 -18
  30. package/dist/client/theme-default/components/VPDocFooter.vue +55 -46
  31. package/dist/client/theme-default/components/VPDocFooterLastUpdated.vue +48 -0
  32. package/dist/client/theme-default/components/VPDocOutlineDropdown.vue +85 -0
  33. package/dist/client/theme-default/components/VPDocOutlineItem.vue +57 -0
  34. package/dist/client/theme-default/components/VPFeature.vue +113 -0
  35. package/dist/client/theme-default/components/VPFeatures.vue +119 -0
  36. package/dist/client/theme-default/components/VPFlyout.vue +20 -24
  37. package/dist/client/theme-default/components/VPFooter.vue +6 -9
  38. package/dist/client/theme-default/components/VPHero.vue +48 -29
  39. package/dist/client/theme-default/components/VPHome.vue +6 -1
  40. package/dist/client/theme-default/components/VPHomeFeatures.vue +7 -90
  41. package/dist/client/theme-default/components/VPHomeHero.vue +13 -10
  42. package/dist/client/theme-default/components/VPHomeSponsors.vue +2 -2
  43. package/dist/client/theme-default/components/VPImage.vue +46 -0
  44. package/dist/client/theme-default/components/VPLink.vue +14 -19
  45. package/dist/client/theme-default/components/VPLocalNav.vue +63 -27
  46. package/dist/client/theme-default/components/VPLocalNavOutlineDropdown.vue +162 -0
  47. package/dist/client/theme-default/components/VPLocalSearchBox.vue +924 -0
  48. package/dist/client/theme-default/components/VPMenu.vue +6 -8
  49. package/dist/client/theme-default/components/VPMenuGroup.vue +18 -6
  50. package/dist/client/theme-default/components/VPMenuLink.vue +27 -9
  51. package/dist/client/theme-default/components/VPNav.vue +15 -21
  52. package/dist/client/theme-default/components/VPNavBar.vue +150 -40
  53. package/dist/client/theme-default/components/VPNavBarAppearance.vue +1 -1
  54. package/dist/client/theme-default/components/VPNavBarExtra.vue +18 -6
  55. package/dist/client/theme-default/components/VPNavBarHamburger.vue +10 -10
  56. package/dist/client/theme-default/components/VPNavBarMenu.vue +1 -1
  57. package/dist/client/theme-default/components/VPNavBarMenuGroup.vue +17 -2
  58. package/dist/client/theme-default/components/VPNavBarMenuLink.vue +8 -12
  59. package/dist/client/theme-default/components/VPNavBarSearch.vue +141 -192
  60. package/dist/client/theme-default/components/VPNavBarSearchButton.vue +208 -0
  61. package/dist/client/theme-default/components/VPNavBarSocialLinks.vue +1 -1
  62. package/dist/client/theme-default/components/VPNavBarTitle.vue +18 -25
  63. package/dist/client/theme-default/components/VPNavBarTranslations.vue +7 -4
  64. package/dist/client/theme-default/components/VPNavScreen.vue +8 -3
  65. package/dist/client/theme-default/components/VPNavScreenAppearance.vue +5 -5
  66. package/dist/client/theme-default/components/VPNavScreenMenu.vue +2 -3
  67. package/dist/client/theme-default/components/VPNavScreenMenuGroup.vue +3 -5
  68. package/dist/client/theme-default/components/VPNavScreenMenuGroupLink.vue +12 -6
  69. package/dist/client/theme-default/components/VPNavScreenMenuGroupSection.vue +2 -3
  70. package/dist/client/theme-default/components/VPNavScreenMenuLink.vue +12 -6
  71. package/dist/client/theme-default/components/VPNavScreenSocialLinks.vue +1 -1
  72. package/dist/client/theme-default/components/VPNavScreenTranslations.vue +11 -7
  73. package/dist/client/theme-default/components/VPPage.vue +2 -0
  74. package/dist/client/theme-default/components/VPSidebar.vue +47 -28
  75. package/dist/client/theme-default/components/VPSidebarItem.vue +237 -0
  76. package/dist/client/theme-default/components/VPSkipLink.vue +3 -7
  77. package/dist/client/theme-default/components/VPSocialLink.vue +16 -28
  78. package/dist/client/theme-default/components/VPSocialLinks.vue +3 -3
  79. package/dist/client/theme-default/components/VPSponsors.vue +9 -15
  80. package/dist/client/theme-default/components/VPSponsorsGrid.vue +23 -5
  81. package/dist/client/theme-default/components/VPSwitch.vue +7 -10
  82. package/dist/client/theme-default/components/VPSwitchAppearance.vue +43 -7
  83. package/dist/client/theme-default/components/VPTeamMembers.vue +55 -0
  84. package/dist/client/theme-default/components/VPTeamMembersItem.vue +220 -0
  85. package/dist/client/theme-default/components/VPTeamPage.vue +53 -0
  86. package/dist/client/theme-default/components/VPTeamPageSection.vue +77 -0
  87. package/dist/client/theme-default/components/VPTeamPageTitle.vue +63 -0
  88. package/dist/client/theme-default/composables/aside.js +1 -1
  89. package/dist/client/theme-default/composables/data.js +2 -0
  90. package/dist/client/theme-default/composables/edit-link.js +10 -16
  91. package/dist/client/theme-default/composables/flyout.js +2 -1
  92. package/dist/client/theme-default/composables/langs.js +26 -0
  93. package/dist/client/theme-default/composables/nav.js +4 -25
  94. package/dist/client/theme-default/composables/outline.js +73 -35
  95. package/dist/client/theme-default/composables/prev-next.js +35 -5
  96. package/dist/client/theme-default/composables/sidebar.js +86 -4
  97. package/dist/client/theme-default/composables/sponsor-grid.js +2 -2
  98. package/dist/client/theme-default/fonts/inter-italic-cyrillic-ext.woff2 +0 -0
  99. package/dist/client/theme-default/fonts/inter-italic-cyrillic.woff2 +0 -0
  100. package/dist/client/theme-default/fonts/inter-italic-greek-ext.woff2 +0 -0
  101. package/dist/client/theme-default/fonts/inter-italic-greek.woff2 +0 -0
  102. package/dist/client/theme-default/fonts/inter-italic-latin-ext.woff2 +0 -0
  103. package/dist/client/theme-default/fonts/inter-italic-latin.woff2 +0 -0
  104. package/dist/client/theme-default/fonts/inter-italic-vietnamese.woff2 +0 -0
  105. package/dist/client/theme-default/fonts/inter-roman-cyrillic-ext.woff2 +0 -0
  106. package/dist/client/theme-default/fonts/inter-roman-cyrillic.woff2 +0 -0
  107. package/dist/client/theme-default/fonts/inter-roman-greek-ext.woff2 +0 -0
  108. package/dist/client/theme-default/fonts/inter-roman-greek.woff2 +0 -0
  109. package/dist/client/theme-default/fonts/inter-roman-latin-ext.woff2 +0 -0
  110. package/dist/client/theme-default/fonts/inter-roman-latin.woff2 +0 -0
  111. package/dist/client/theme-default/fonts/inter-roman-vietnamese.woff2 +0 -0
  112. package/dist/client/theme-default/index.js +2 -17
  113. package/dist/client/theme-default/styles/base.css +21 -7
  114. package/dist/client/theme-default/styles/components/custom-block.css +12 -4
  115. package/dist/client/theme-default/styles/components/vp-code-group.css +84 -0
  116. package/dist/client/theme-default/styles/components/vp-code.css +7 -0
  117. package/dist/client/theme-default/styles/components/vp-doc.css +209 -111
  118. package/dist/client/theme-default/styles/components/vp-sponsor.css +43 -18
  119. package/dist/client/theme-default/styles/fonts.css +133 -35
  120. package/dist/client/theme-default/styles/vars.css +328 -180
  121. package/dist/client/theme-default/support/sidebar.js +57 -11
  122. package/dist/client/theme-default/support/socialIcons.js +12 -0
  123. package/dist/client/theme-default/support/translation.js +48 -0
  124. package/dist/client/theme-default/support/utils.js +10 -36
  125. package/dist/client/theme-default/without-fonts.js +29 -0
  126. package/dist/node/cli.js +407 -264
  127. package/dist/node/index.d.ts +2159 -220
  128. package/dist/node/index.js +96 -13
  129. package/dist/node/{serve-9c85885c.js → serve-0989a5bc.js} +49700 -44573
  130. package/package.json +129 -87
  131. package/template/.vitepress/config.js +28 -0
  132. package/template/.vitepress/theme/Layout.vue +21 -0
  133. package/template/.vitepress/theme/index.js +25 -0
  134. package/template/.vitepress/theme/style.css +95 -0
  135. package/template/api-examples.md +49 -0
  136. package/template/index.md +28 -0
  137. package/template/markdown-examples.md +85 -0
  138. package/theme-without-fonts.d.ts +2 -0
  139. package/theme.d.ts +16 -7
  140. package/types/default-theme.d.ts +311 -49
  141. package/types/docsearch.d.ts +143 -0
  142. package/types/index.d.ts +3 -2
  143. package/types/local-search.d.ts +27 -0
  144. package/types/shared.d.ts +58 -36
  145. package/dist/client/app/components/Debug.vue +0 -86
  146. package/dist/client/theme-default/components/VPBox.vue +0 -55
  147. package/dist/client/theme-default/components/VPSidebarGroup.vue +0 -107
  148. package/dist/client/theme-default/components/VPSidebarLink.vue +0 -50
  149. package/dist/client/theme-default/components/icons/VPIconDiscord.vue +0 -5
  150. package/dist/client/theme-default/components/icons/VPIconExternalLink.vue +0 -13
  151. package/dist/client/theme-default/components/icons/VPIconFacebook.vue +0 -5
  152. package/dist/client/theme-default/components/icons/VPIconGitHub.vue +0 -5
  153. package/dist/client/theme-default/components/icons/VPIconInstagram.vue +0 -5
  154. package/dist/client/theme-default/components/icons/VPIconLinkedIn.vue +0 -5
  155. package/dist/client/theme-default/components/icons/VPIconSlack.vue +0 -5
  156. package/dist/client/theme-default/components/icons/VPIconTwitter.vue +0 -5
  157. package/dist/client/theme-default/components/icons/VPIconYouTube.vue +0 -5
  158. package/dist/client/theme-default/composables/copy-code.js +0 -33
  159. package/dist/client/theme-default/config.js +0 -1
  160. package/dist/client/theme-default/fonts/inter-cyrillic-ext.woff2 +0 -0
  161. package/dist/client/theme-default/fonts/inter-cyrillic.woff2 +0 -0
  162. package/dist/client/theme-default/fonts/inter-greek-ext.woff2 +0 -0
  163. package/dist/client/theme-default/fonts/inter-greek.woff2 +0 -0
  164. package/dist/client/theme-default/fonts/inter-latin-ext.woff2 +0 -0
  165. package/dist/client/theme-default/fonts/inter-latin.woff2 +0 -0
  166. package/dist/client/theme-default/fonts/inter-vietnamese.woff2 +0 -0
  167. package/dist/node-cjs/cli.cjs +0 -304
  168. package/dist/node-cjs/index.cjs +0 -38
  169. package/dist/node-cjs/serve-a73b4871.cjs +0 -64844
package/README.md CHANGED
@@ -1,17 +1,18 @@
1
- # VitePress (alpha) 📝💨
1
+ # VitePress (beta) 📝💨
2
2
 
3
3
  [![Test](https://github.com/vuejs/vitepress/workflows/Test/badge.svg)](https://github.com/vuejs/vitepress/actions)
4
4
  [![npm](https://img.shields.io/npm/v/vitepress)](https://www.npmjs.com/package/vitepress)
5
+ [![chat](https://img.shields.io/badge/chat-discord-blue?logo=discord)](https://chat.vuejs.org)
5
6
 
6
7
  ---
7
8
 
8
9
  VitePress is [VuePress](https://vuepress.vuejs.org)' spiritual successor, built on top of [vite](https://github.com/vitejs/vite).
9
10
 
10
- Currently, it's in the `alpha` stage. It is already suitable for out-of-the-box documentation use, but the config and theming API may still change between minor releases.
11
+ Currently, it is in the `beta` stage. It is already suitable for out-of-the-box documentation use. There still might be issues, however, we do not plan to introduce any breaking changes from here on until the stable release.
11
12
 
12
13
  ## Documentation
13
14
 
14
- To check out docs, visit [vitepress.vuejs.org](https://vitepress.vuejs.org).
15
+ To check out docs, visit [vitepress.dev](https://vitepress.dev).
15
16
 
16
17
  ## Changelog
17
18
 
package/client.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  // re-export vite client types. with strict installers like pnpm, user won't
2
2
  // be able to reference vite/client in project root.
3
3
  /// <reference types="vite/client" />
4
+
5
+ export * from './dist/client/index.js'
@@ -1,11 +1,22 @@
1
1
  import { defineComponent, h } from 'vue';
2
- import { useRoute } from '../router';
2
+ import { useData, useRoute } from 'vitepress';
3
+ import { contentUpdatedCallbacks } from '../utils';
4
+ const runCbs = () => contentUpdatedCallbacks.forEach((fn) => fn());
3
5
  export const Content = defineComponent({
4
6
  name: 'VitePressContent',
5
- setup() {
7
+ props: {
8
+ as: { type: [Object, String], default: 'div' }
9
+ },
10
+ setup(props) {
6
11
  const route = useRoute();
7
- return () => h('div', { style: { position: 'relative' } }, [
8
- route.component ? h(route.component) : null
12
+ const { site } = useData();
13
+ return () => h(props.as, site.value.contentProps ?? { style: { position: 'relative' } }, [
14
+ route.component
15
+ ? h(route.component, {
16
+ onVnodeMounted: runCbs,
17
+ onVnodeUpdated: runCbs
18
+ })
19
+ : '404 Page Not Found'
9
20
  ]);
10
21
  }
11
22
  });
@@ -0,0 +1,40 @@
1
+ import { inBrowser, onContentUpdated } from 'vitepress';
2
+ export function useCodeGroups() {
3
+ if (import.meta.env.DEV) {
4
+ onContentUpdated(() => {
5
+ document.querySelectorAll('.vp-code-group > .blocks').forEach((el) => {
6
+ Array.from(el.children).forEach((child) => {
7
+ child.classList.remove('active');
8
+ });
9
+ el.children[0].classList.add('active');
10
+ });
11
+ });
12
+ }
13
+ if (inBrowser) {
14
+ window.addEventListener('click', (e) => {
15
+ const el = e.target;
16
+ if (el.matches('.vp-code-group input')) {
17
+ // input <- .tabs <- .vp-code-group
18
+ const group = el.parentElement?.parentElement;
19
+ if (!group)
20
+ return;
21
+ const i = Array.from(group.querySelectorAll('input')).indexOf(el);
22
+ if (i < 0)
23
+ return;
24
+ const blocks = group.querySelector('.blocks');
25
+ if (!blocks)
26
+ return;
27
+ const current = Array.from(blocks.children).find((child) => child.classList.contains('active'));
28
+ if (!current)
29
+ return;
30
+ const next = blocks.children[i];
31
+ if (!next || current === next)
32
+ return;
33
+ current.classList.remove('active');
34
+ next.classList.add('active');
35
+ const label = group?.querySelector(`label[for="${el.id}"]`);
36
+ label?.scrollIntoView({ block: 'nearest' });
37
+ }
38
+ });
39
+ }
40
+ }
@@ -0,0 +1,71 @@
1
+ import { inBrowser } from 'vitepress';
2
+ export function useCopyCode() {
3
+ if (inBrowser) {
4
+ const timeoutIdMap = new WeakMap();
5
+ window.addEventListener('click', (e) => {
6
+ const el = e.target;
7
+ if (el.matches('div[class*="language-"] > button.copy')) {
8
+ const parent = el.parentElement;
9
+ const sibling = el.nextElementSibling?.nextElementSibling;
10
+ if (!parent || !sibling) {
11
+ return;
12
+ }
13
+ const isShell = /language-(shellscript|shell|bash|sh|zsh)/.test(parent.className);
14
+ let text = '';
15
+ sibling
16
+ .querySelectorAll('span.line:not(.diff.remove)')
17
+ .forEach((node) => (text += (node.textContent || '') + '\n'));
18
+ text = text.slice(0, -1);
19
+ if (isShell) {
20
+ text = text.replace(/^ *(\$|>) /gm, '').trim();
21
+ }
22
+ copyToClipboard(text).then(() => {
23
+ el.classList.add('copied');
24
+ clearTimeout(timeoutIdMap.get(el));
25
+ const timeoutId = setTimeout(() => {
26
+ el.classList.remove('copied');
27
+ el.blur();
28
+ timeoutIdMap.delete(el);
29
+ }, 2000);
30
+ timeoutIdMap.set(el, timeoutId);
31
+ });
32
+ }
33
+ });
34
+ }
35
+ }
36
+ async function copyToClipboard(text) {
37
+ try {
38
+ return navigator.clipboard.writeText(text);
39
+ }
40
+ catch {
41
+ const element = document.createElement('textarea');
42
+ const previouslyFocusedElement = document.activeElement;
43
+ element.value = text;
44
+ // Prevent keyboard from showing on mobile
45
+ element.setAttribute('readonly', '');
46
+ element.style.contain = 'strict';
47
+ element.style.position = 'absolute';
48
+ element.style.left = '-9999px';
49
+ element.style.fontSize = '12pt'; // Prevent zooming on iOS
50
+ const selection = document.getSelection();
51
+ const originalRange = selection
52
+ ? selection.rangeCount > 0 && selection.getRangeAt(0)
53
+ : null;
54
+ document.body.appendChild(element);
55
+ element.select();
56
+ // Explicit selection workaround for iOS
57
+ element.selectionStart = 0;
58
+ element.selectionEnd = text.length;
59
+ document.execCommand('copy');
60
+ document.body.removeChild(element);
61
+ if (originalRange) {
62
+ selection.removeAllRanges(); // originalRange can't be truthy when selection is falsy
63
+ selection.addRange(originalRange);
64
+ }
65
+ // Get the focus back on the previously focused element, if any
66
+ if (previouslyFocusedElement) {
67
+ ;
68
+ previouslyFocusedElement.focus();
69
+ }
70
+ }
71
+ }
@@ -1,5 +1,5 @@
1
1
  import { watchEffect } from 'vue';
2
- import { createTitle } from '../../shared';
2
+ import { createTitle, mergeHead } from '../../shared';
3
3
  export function useUpdateHead(route, siteDataByRouteRef) {
4
4
  let managedHeadTags = [];
5
5
  let isFirstUpdate = true;
@@ -10,59 +10,30 @@ export function useUpdateHead(route, siteDataByRouteRef) {
10
10
  isFirstUpdate = false;
11
11
  return;
12
12
  }
13
- const newEls = [];
14
- const commonLength = Math.min(managedHeadTags.length, newTags.length);
15
- for (let i = 0; i < commonLength; i++) {
16
- let el = managedHeadTags[i];
17
- const [tag, attrs, innerHTML = ''] = newTags[i];
18
- if (el.tagName.toLocaleLowerCase() === tag) {
19
- for (const key in attrs) {
20
- if (el.getAttribute(key) !== attrs[key]) {
21
- el.setAttribute(key, attrs[key]);
22
- }
23
- }
24
- for (let i = 0; i < el.attributes.length; i++) {
25
- const name = el.attributes[i].name;
26
- if (!(name in attrs)) {
27
- el.removeAttribute(name);
28
- }
29
- }
30
- if (el.innerHTML !== innerHTML) {
31
- el.innerHTML = innerHTML;
32
- }
33
- }
34
- else {
35
- document.head.removeChild(el);
36
- el = createHeadElement(newTags[i]);
37
- document.head.append(el);
38
- }
39
- newEls.push(el);
40
- }
41
- managedHeadTags
42
- .slice(commonLength)
43
- .forEach((el) => document.head.removeChild(el));
44
- newTags.slice(commonLength).forEach((headConfig) => {
13
+ managedHeadTags.forEach((el) => document.head.removeChild(el));
14
+ managedHeadTags = [];
15
+ newTags.forEach((headConfig) => {
45
16
  const el = createHeadElement(headConfig);
46
17
  document.head.appendChild(el);
47
- newEls.push(el);
18
+ managedHeadTags.push(el);
48
19
  });
49
- managedHeadTags = newEls;
50
20
  };
51
21
  watchEffect(() => {
52
22
  const pageData = route.data;
53
23
  const siteData = siteDataByRouteRef.value;
54
24
  const pageDescription = pageData && pageData.description;
55
- const frontmatterHead = pageData && pageData.frontmatter.head;
25
+ const frontmatterHead = (pageData && pageData.frontmatter.head) || [];
56
26
  // update title and description
57
27
  document.title = createTitle(siteData, pageData);
58
- document
59
- .querySelector(`meta[name=description]`)
60
- .setAttribute('content', pageDescription || siteData.description);
61
- updateHeadTags([
62
- // site head can only change during dev
63
- ...(import.meta.env.DEV ? siteData.head : []),
64
- ...(frontmatterHead ? filterOutHeadDescription(frontmatterHead) : [])
65
- ]);
28
+ const description = pageDescription || siteData.description;
29
+ let metaDescriptionElement = document.querySelector(`meta[name=description]`);
30
+ if (metaDescriptionElement) {
31
+ metaDescriptionElement.setAttribute('content', description);
32
+ }
33
+ else {
34
+ createHeadElement(['meta', { name: 'description', content: description }]);
35
+ }
36
+ updateHeadTags(mergeHead(siteData.head, filterOutHeadDescription(frontmatterHead)));
66
37
  });
67
38
  }
68
39
  function createHeadElement([tag, attrs, innerHTML]) {
@@ -52,14 +52,19 @@ export function usePrefetch() {
52
52
  if (!hasFetched.has(pathname)) {
53
53
  hasFetched.add(pathname);
54
54
  const pageChunkPath = pathToFile(pathname);
55
- doFetch(pageChunkPath);
55
+ if (pageChunkPath)
56
+ doFetch(pageChunkPath);
56
57
  }
57
58
  }
58
59
  });
59
60
  });
60
61
  rIC(() => {
61
- document.querySelectorAll('#app a').forEach((link) => {
62
- const { target, hostname, pathname } = link;
62
+ document
63
+ .querySelectorAll('#app a')
64
+ .forEach((link) => {
65
+ const { hostname, pathname } = new URL(link.href instanceof SVGAnimatedString
66
+ ? link.href.animVal
67
+ : link.href, link.baseURI);
63
68
  const extMatch = pathname.match(/\.\w+$/);
64
69
  if (extMatch && extMatch[0] !== '.html') {
65
70
  return;
@@ -67,7 +72,7 @@ export function usePrefetch() {
67
72
  if (
68
73
  // only prefetch same tab navigation, since a new tab will load
69
74
  // the lean js chunk instead.
70
- target !== `_blank` &&
75
+ link.target !== '_blank' &&
71
76
  // only prefetch inbound links
72
77
  hostname === location.hostname) {
73
78
  if (pathname !== location.pathname) {
@@ -1,39 +1,36 @@
1
- import { shallowRef, readonly, computed, inject } from 'vue';
2
- import serializedSiteData from '@siteData';
1
+ import { computed, inject, readonly, ref, shallowRef } from 'vue';
2
+ import siteData from '@siteData';
3
3
  import { resolveSiteDataByRoute, createTitle } from '../shared';
4
- import { withBase } from './utils';
5
4
  export const dataSymbol = Symbol();
6
- export const siteDataRef = shallowRef(parse(serializedSiteData));
7
- function parse(data) {
8
- const parsed = JSON.parse(data);
9
- return (import.meta.env.DEV ? readonly(parsed) : parsed);
10
- }
5
+ // site data is a singleton
6
+ export const siteDataRef = shallowRef((import.meta.env.PROD ? siteData : readonly(siteData)));
11
7
  // hmr
12
8
  if (import.meta.hot) {
13
9
  import.meta.hot.accept('/@siteData', (m) => {
14
- siteDataRef.value = parse(m.default);
10
+ if (m) {
11
+ siteDataRef.value = m.default;
12
+ }
15
13
  });
16
14
  }
17
15
  // per-app data
18
16
  export function initData(route) {
19
- const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.path));
17
+ const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.data.relativePath));
20
18
  return {
21
19
  site,
22
20
  theme: computed(() => site.value.themeConfig),
23
21
  page: computed(() => route.data),
24
22
  frontmatter: computed(() => route.data.frontmatter),
23
+ params: computed(() => route.data.params),
25
24
  lang: computed(() => site.value.lang),
26
- localePath: computed(() => {
27
- const { langs, lang } = site.value;
28
- const path = Object.keys(langs).find((langPath) => langs[langPath].lang === lang);
29
- return withBase(path || '/');
30
- }),
25
+ dir: computed(() => site.value.dir),
26
+ localeIndex: computed(() => site.value.localeIndex || 'root'),
31
27
  title: computed(() => {
32
28
  return createTitle(site.value, route.data);
33
29
  }),
34
30
  description: computed(() => {
35
31
  return route.data.description || site.value.description;
36
- })
32
+ }),
33
+ isDark: ref(false)
37
34
  };
38
35
  }
39
36
  export function useData() {
@@ -0,0 +1,28 @@
1
+ import { setupDevtoolsPlugin } from '@vue/devtools-api';
2
+ const COMPONENT_STATE_TYPE = 'VitePress';
3
+ export const setupDevtools = (app, router, data) => {
4
+ setupDevtoolsPlugin({
5
+ // fix recursive reference
6
+ app: app,
7
+ id: 'org.vuejs.vitepress',
8
+ label: 'VitePress',
9
+ packageName: 'vitepress',
10
+ homepage: 'https://vitepress.dev',
11
+ componentStateTypes: [COMPONENT_STATE_TYPE]
12
+ }, (api) => {
13
+ api.on.inspectComponent((payload) => {
14
+ payload.instanceData.state.push({
15
+ type: COMPONENT_STATE_TYPE,
16
+ key: 'route',
17
+ value: router.route,
18
+ editable: false
19
+ });
20
+ payload.instanceData.state.push({
21
+ type: COMPONENT_STATE_TYPE,
22
+ key: 'data',
23
+ value: data,
24
+ editable: false
25
+ });
26
+ });
27
+ });
28
+ };
@@ -1,34 +1,57 @@
1
- import { createApp as createClientApp, createSSRApp, defineAsyncComponent, h, onMounted, watch } from 'vue';
2
- import Theme from '/@theme/index';
3
- import { inBrowser, pathToFile } from './utils';
4
- import { RouterSymbol, createRouter } from './router';
5
- import { siteDataRef, useData } from './data';
1
+ import RawTheme from '@theme/index';
2
+ import { createApp as createClientApp, createSSRApp, defineComponent, h, onMounted, watchEffect } from 'vue';
3
+ import { ClientOnly } from './components/ClientOnly';
4
+ import { Content } from './components/Content';
5
+ import { useCodeGroups } from './composables/codeGroups';
6
+ import { useCopyCode } from './composables/copyCode';
6
7
  import { useUpdateHead } from './composables/head';
7
8
  import { usePrefetch } from './composables/preFetch';
8
- import { dataSymbol, initData } from './data';
9
- import { Content } from './components/Content';
10
- import { ClientOnly } from './components/ClientOnly';
11
- const NotFound = Theme.NotFound || (() => '404 Not Found');
12
- const VitePressApp = {
9
+ import { dataSymbol, initData, siteDataRef, useData } from './data';
10
+ import { RouterSymbol, createRouter, scrollTo } from './router';
11
+ import { inBrowser, pathToFile } from './utils';
12
+ function resolveThemeExtends(theme) {
13
+ if (theme.extends) {
14
+ const base = resolveThemeExtends(theme.extends);
15
+ return {
16
+ ...base,
17
+ ...theme,
18
+ async enhanceApp(ctx) {
19
+ if (base.enhanceApp)
20
+ await base.enhanceApp(ctx);
21
+ if (theme.enhanceApp)
22
+ await theme.enhanceApp(ctx);
23
+ }
24
+ };
25
+ }
26
+ return theme;
27
+ }
28
+ const Theme = resolveThemeExtends(RawTheme);
29
+ const VitePressApp = defineComponent({
13
30
  name: 'VitePressApp',
14
31
  setup() {
15
32
  const { site } = useData();
16
33
  // change the language on the HTML element based on the current lang
17
34
  onMounted(() => {
18
- watch(() => site.value.lang, (lang) => {
19
- document.documentElement.lang = lang;
20
- }, { immediate: true });
35
+ watchEffect(() => {
36
+ document.documentElement.lang = site.value.lang;
37
+ document.documentElement.dir = site.value.dir;
38
+ });
21
39
  });
22
40
  if (import.meta.env.PROD) {
23
41
  // in prod mode, enable intersectionObserver based pre-fetch
24
42
  usePrefetch();
25
43
  }
44
+ // setup global copy code handler
45
+ useCopyCode();
46
+ // setup global code groups handler
47
+ useCodeGroups();
48
+ if (Theme.setup)
49
+ Theme.setup();
26
50
  return () => h(Theme.Layout);
27
51
  }
28
- };
29
- export function createApp() {
52
+ });
53
+ export async function createApp() {
30
54
  const router = newRouter();
31
- handleHMR(router);
32
55
  const app = newApp();
33
56
  app.provide(RouterSymbol, router);
34
57
  const data = initData(router.route);
@@ -36,22 +59,30 @@ export function createApp() {
36
59
  // install global components
37
60
  app.component('Content', Content);
38
61
  app.component('ClientOnly', ClientOnly);
39
- app.component('Debug', import.meta.env.PROD
40
- ? () => null
41
- : defineAsyncComponent(() => import('./components/Debug.vue')));
42
- // expose $frontmatter
43
- Object.defineProperty(app.config.globalProperties, '$frontmatter', {
44
- get() {
45
- return data.frontmatter.value;
62
+ // expose $frontmatter & $params
63
+ Object.defineProperties(app.config.globalProperties, {
64
+ $frontmatter: {
65
+ get() {
66
+ return data.frontmatter.value;
67
+ }
68
+ },
69
+ $params: {
70
+ get() {
71
+ return data.page.value.params;
72
+ }
46
73
  }
47
74
  });
48
75
  if (Theme.enhanceApp) {
49
- Theme.enhanceApp({
76
+ await Theme.enhanceApp({
50
77
  app,
51
78
  router,
52
79
  siteData: siteDataRef
53
80
  });
54
81
  }
82
+ // setup devtools in dev mode
83
+ if (import.meta.env.DEV || __VUE_PROD_DEVTOOLS__) {
84
+ import('./devtools.js').then(({ setupDevtools }) => setupDevtools(app, router, data));
85
+ }
55
86
  return { app, router, data };
56
87
  }
57
88
  function newApp() {
@@ -64,6 +95,8 @@ function newRouter() {
64
95
  let initialPath;
65
96
  return createRouter((path) => {
66
97
  let pageFilePath = pathToFile(path);
98
+ if (!pageFilePath)
99
+ return null;
67
100
  if (isInitialPageLoad) {
68
101
  initialPath = pageFilePath;
69
102
  }
@@ -77,30 +110,22 @@ function newRouter() {
77
110
  isInitialPageLoad = false;
78
111
  }
79
112
  return import(/*@vite-ignore*/ pageFilePath);
80
- }, NotFound);
113
+ }, Theme.NotFound);
81
114
  }
82
- function handleHMR(router) {
83
- // update route.data on HMR updates of active page
84
- if (import.meta.hot) {
85
- // hot reload pageData
86
- import.meta.hot.on('vitepress:pageData', (payload) => {
87
- if (shouldHotReload(payload)) {
88
- router.route.data = payload.pageData;
115
+ if (inBrowser) {
116
+ createApp().then(({ app, router, data }) => {
117
+ // wait until page component is fetched before mounting
118
+ router.go().then(() => {
119
+ // dynamically update head tags
120
+ useUpdateHead(router.route, data.site);
121
+ app.mount('#app');
122
+ // scroll to hash on new tab during dev
123
+ if (import.meta.env.DEV && location.hash) {
124
+ const target = document.getElementById(decodeURIComponent(location.hash).slice(1));
125
+ if (target) {
126
+ scrollTo(target, location.hash);
127
+ }
89
128
  }
90
129
  });
91
- }
92
- }
93
- function shouldHotReload(payload) {
94
- const payloadPath = payload.path.replace(/(\bindex)?\.md$/, '');
95
- const locationPath = location.pathname.replace(/(\bindex)?\.html$/, '');
96
- return payloadPath === locationPath;
97
- }
98
- if (inBrowser) {
99
- const { app, router, data } = createApp();
100
- // wait until page component is fetched before mounting
101
- router.go().then(() => {
102
- // dynamically update head tags
103
- useUpdateHead(router.route, data.site);
104
- app.mount('#app');
105
130
  });
106
131
  }