vitepress 1.0.0-alpha.9 → 1.0.0-beta.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 (137) hide show
  1. package/README.md +4 -3
  2. package/dist/client/app/components/ClientOnly.js +10 -10
  3. package/dist/client/app/components/Content.js +21 -11
  4. package/dist/client/app/composables/codeGroups.js +19 -0
  5. package/dist/client/{theme-default/composables/copy-code.js → app/composables/copyCode.js} +72 -71
  6. package/dist/client/app/composables/head.js +51 -51
  7. package/dist/client/app/composables/preFetch.js +99 -94
  8. package/dist/client/app/data.js +42 -44
  9. package/dist/client/app/devtools.js +28 -28
  10. package/dist/client/app/index.js +130 -92
  11. package/dist/client/app/router.js +248 -200
  12. package/dist/client/app/ssr.js +10 -9
  13. package/dist/client/app/theme.js +1 -1
  14. package/dist/client/app/utils.js +80 -47
  15. package/dist/client/index.d.ts +87 -116
  16. package/dist/client/index.js +9 -9
  17. package/dist/client/shared.js +111 -109
  18. package/dist/client/theme-default/Layout.vue +26 -4
  19. package/dist/client/theme-default/NotFound.vue +20 -4
  20. package/dist/client/theme-default/components/VPAlgoliaSearchBox.vue +32 -25
  21. package/dist/client/theme-default/components/VPBackdrop.vue +3 -1
  22. package/dist/client/theme-default/components/VPBadge.vue +64 -0
  23. package/dist/client/theme-default/components/VPButton.vue +3 -3
  24. package/dist/client/theme-default/components/VPCarbonAds.vue +31 -20
  25. package/dist/client/theme-default/components/VPContent.vue +20 -14
  26. package/dist/client/theme-default/components/VPDoc.vue +31 -10
  27. package/dist/client/theme-default/components/VPDocAside.vue +4 -4
  28. package/dist/client/theme-default/components/VPDocAsideCarbonAds.vue +6 -1
  29. package/dist/client/theme-default/components/VPDocAsideOutline.vue +21 -68
  30. package/dist/client/theme-default/components/VPDocFooter.vue +17 -19
  31. package/dist/client/theme-default/components/VPDocFooterLastUpdated.vue +7 -8
  32. package/dist/client/theme-default/components/VPDocOutlineDropdown.vue +84 -0
  33. package/dist/client/theme-default/components/VPDocOutlineItem.vue +59 -0
  34. package/dist/client/theme-default/components/VPFeature.vue +72 -13
  35. package/dist/client/theme-default/components/VPFeatures.vue +13 -3
  36. package/dist/client/theme-default/components/VPFlyout.vue +4 -22
  37. package/dist/client/theme-default/components/VPFooter.vue +3 -6
  38. package/dist/client/theme-default/components/VPHero.vue +28 -13
  39. package/dist/client/theme-default/components/VPHome.vue +4 -1
  40. package/dist/client/theme-default/components/VPHomeFeatures.vue +1 -1
  41. package/dist/client/theme-default/components/VPHomeHero.vue +5 -2
  42. package/dist/client/theme-default/components/VPHomeSponsors.vue +1 -1
  43. package/dist/client/theme-default/components/VPImage.vue +14 -2
  44. package/dist/client/theme-default/components/VPLink.vue +10 -5
  45. package/dist/client/theme-default/components/VPLocalNav.vue +50 -25
  46. package/dist/client/theme-default/components/VPLocalNavOutlineDropdown.vue +147 -0
  47. package/dist/client/theme-default/components/VPLocalSearchBox.vue +904 -0
  48. package/dist/client/theme-default/components/VPMenu.vue +5 -15
  49. package/dist/client/theme-default/components/VPMenuGroup.vue +3 -2
  50. package/dist/client/theme-default/components/VPMenuLink.vue +10 -11
  51. package/dist/client/theme-default/components/VPNav.vue +5 -25
  52. package/dist/client/theme-default/components/VPNavBar.vue +136 -48
  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/VPNavBarMenu.vue +1 -1
  56. package/dist/client/theme-default/components/VPNavBarMenuGroup.vue +2 -2
  57. package/dist/client/theme-default/components/VPNavBarMenuLink.vue +6 -10
  58. package/dist/client/theme-default/components/VPNavBarSearch.vue +139 -210
  59. package/dist/client/theme-default/components/VPNavBarSearchButton.vue +207 -0
  60. package/dist/client/theme-default/components/VPNavBarSocialLinks.vue +1 -1
  61. package/dist/client/theme-default/components/VPNavBarTitle.vue +12 -22
  62. package/dist/client/theme-default/components/VPNavBarTranslations.vue +7 -4
  63. package/dist/client/theme-default/components/VPNavScreen.vue +5 -3
  64. package/dist/client/theme-default/components/VPNavScreenAppearance.vue +5 -5
  65. package/dist/client/theme-default/components/VPNavScreenMenu.vue +2 -3
  66. package/dist/client/theme-default/components/VPNavScreenMenuGroup.vue +3 -5
  67. package/dist/client/theme-default/components/VPNavScreenMenuGroupLink.vue +12 -6
  68. package/dist/client/theme-default/components/VPNavScreenMenuGroupSection.vue +1 -2
  69. package/dist/client/theme-default/components/VPNavScreenMenuLink.vue +12 -6
  70. package/dist/client/theme-default/components/VPNavScreenSocialLinks.vue +1 -1
  71. package/dist/client/theme-default/components/VPNavScreenTranslations.vue +11 -7
  72. package/dist/client/theme-default/components/VPPage.vue +2 -0
  73. package/dist/client/theme-default/components/VPSidebar.vue +45 -23
  74. package/dist/client/theme-default/components/VPSidebarItem.vue +230 -0
  75. package/dist/client/theme-default/components/VPSkipLink.vue +1 -5
  76. package/dist/client/theme-default/components/VPSocialLink.vue +3 -1
  77. package/dist/client/theme-default/components/VPSocialLinks.vue +2 -2
  78. package/dist/client/theme-default/components/VPSponsors.vue +1 -1
  79. package/dist/client/theme-default/components/VPSponsorsGrid.vue +2 -2
  80. package/dist/client/theme-default/components/VPSwitch.vue +7 -10
  81. package/dist/client/theme-default/components/VPSwitchAppearance.vue +43 -7
  82. package/dist/client/theme-default/components/VPTeamMembersItem.vue +11 -6
  83. package/dist/client/theme-default/components/VPTeamPageSection.vue +1 -1
  84. package/dist/client/theme-default/composables/aside.js +17 -17
  85. package/dist/client/theme-default/composables/data.js +2 -0
  86. package/dist/client/theme-default/composables/edit-link.js +16 -11
  87. package/dist/client/theme-default/composables/flyout.js +41 -40
  88. package/dist/client/theme-default/composables/langs.js +26 -0
  89. package/dist/client/theme-default/composables/nav.js +30 -53
  90. package/dist/client/theme-default/composables/outline.js +155 -121
  91. package/dist/client/theme-default/composables/prev-next.js +44 -22
  92. package/dist/client/theme-default/composables/sidebar.js +130 -63
  93. package/dist/client/theme-default/composables/sponsor-grid.js +94 -94
  94. package/dist/client/theme-default/index.js +3 -23
  95. package/dist/client/theme-default/styles/base.css +18 -4
  96. package/dist/client/theme-default/styles/components/custom-block.css +10 -5
  97. package/dist/client/theme-default/styles/components/vp-code-group.css +87 -0
  98. package/dist/client/theme-default/styles/components/vp-doc.css +107 -28
  99. package/dist/client/theme-default/styles/components/vp-sponsor.css +3 -3
  100. package/dist/client/theme-default/styles/fonts.css +13 -65
  101. package/dist/client/theme-default/styles/vars.css +261 -173
  102. package/dist/client/theme-default/support/sidebar.js +73 -37
  103. package/dist/client/theme-default/support/socialIcons.js +12 -11
  104. package/dist/client/theme-default/support/translation.js +48 -0
  105. package/dist/client/theme-default/support/utils.js +36 -63
  106. package/dist/client/theme-default/without-fonts.js +28 -0
  107. package/dist/node/cli.js +394 -267
  108. package/dist/node/index.d.ts +2004 -372
  109. package/dist/node/index.js +91 -12
  110. package/dist/node/{serve-dfb48091.js → serve-9050e258.js} +51782 -52505
  111. package/dist/node-cjs/cli.cjs +395 -268
  112. package/dist/node-cjs/index.cjs +105 -24
  113. package/dist/node-cjs/{serve-865d0baf.cjs → serve-1a76c42b.cjs} +51615 -52347
  114. package/package.json +89 -69
  115. package/template/.vitepress/config.js +28 -0
  116. package/template/.vitepress/theme/Layout.vue +21 -0
  117. package/template/.vitepress/theme/index.js +25 -0
  118. package/template/.vitepress/theme/style.css +95 -0
  119. package/template/api-examples.md +49 -0
  120. package/template/index.md +28 -0
  121. package/template/markdown-examples.md +85 -0
  122. package/theme-without-fonts.d.ts +1 -0
  123. package/theme.d.ts +14 -11
  124. package/types/default-theme.d.ts +120 -46
  125. package/types/docsearch.d.ts +143 -0
  126. package/types/index.d.ts +2 -34
  127. package/types/local-search.d.ts +27 -0
  128. package/types/shared.d.ts +52 -46
  129. package/dist/client/theme-default/components/VPSidebarGroup.vue +0 -124
  130. package/dist/client/theme-default/components/VPSidebarLink.vue +0 -69
  131. package/dist/client/theme-default/fonts/inter-cyrillic-ext.woff2 +0 -0
  132. package/dist/client/theme-default/fonts/inter-cyrillic.woff2 +0 -0
  133. package/dist/client/theme-default/fonts/inter-greek-ext.woff2 +0 -0
  134. package/dist/client/theme-default/fonts/inter-greek.woff2 +0 -0
  135. package/dist/client/theme-default/fonts/inter-latin-ext.woff2 +0 -0
  136. package/dist/client/theme-default/fonts/inter-latin.woff2 +0 -0
  137. package/dist/client/theme-default/fonts/inter-vietnamese.woff2 +0 -0
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
 
@@ -1,10 +1,10 @@
1
- import { defineComponent, ref, onMounted } from 'vue';
2
- export const ClientOnly = defineComponent({
3
- setup(_, { slots }) {
4
- const show = ref(false);
5
- onMounted(() => {
6
- show.value = true;
7
- });
8
- return () => (show.value && slots.default ? slots.default() : null);
9
- }
10
- });
1
+ import { defineComponent, ref, onMounted } from 'vue';
2
+ export const ClientOnly = defineComponent({
3
+ setup(_, { slots }) {
4
+ const show = ref(false);
5
+ onMounted(() => {
6
+ show.value = true;
7
+ });
8
+ return () => (show.value && slots.default ? slots.default() : null);
9
+ }
10
+ });
@@ -1,11 +1,21 @@
1
- import { defineComponent, h } from 'vue';
2
- import { useRoute } from '../router.js';
3
- export const Content = defineComponent({
4
- name: 'VitePressContent',
5
- setup() {
6
- const route = useRoute();
7
- return () => h('div', { style: { position: 'relative' } }, [
8
- route.component ? h(route.component) : null
9
- ]);
10
- }
11
- });
1
+ import { defineComponent, h } from 'vue';
2
+ import { useRoute } from '../router';
3
+ import { contentUpdatedCallbacks } from '../utils';
4
+ const runCbs = () => contentUpdatedCallbacks.forEach((fn) => fn());
5
+ export const Content = defineComponent({
6
+ name: 'VitePressContent',
7
+ props: {
8
+ as: { type: [Object, String], default: 'div' }
9
+ },
10
+ setup(props) {
11
+ const route = useRoute();
12
+ return () => h(props.as, { style: { position: 'relative' } }, [
13
+ route.component
14
+ ? h(route.component, {
15
+ onVnodeMounted: runCbs,
16
+ onVnodeUpdated: runCbs
17
+ })
18
+ : '404 Page Not Found'
19
+ ]);
20
+ }
21
+ });
@@ -0,0 +1,19 @@
1
+ import { inBrowser } from 'vitepress';
2
+ export function useCodeGroups() {
3
+ if (inBrowser) {
4
+ window.addEventListener('click', (e) => {
5
+ const el = e.target;
6
+ if (el.matches('.vp-code-group input')) {
7
+ // input <- .tabs <- .vp-code-group
8
+ const group = el.parentElement?.parentElement;
9
+ const i = Array.from(group?.querySelectorAll('input') || []).indexOf(el);
10
+ const current = group?.querySelector('div[class*="language-"].active');
11
+ const next = group?.querySelectorAll('div[class*="language-"]:not(.language-id)')?.[i];
12
+ if (current && next && current !== next) {
13
+ current.classList.remove('active');
14
+ next.classList.add('active');
15
+ }
16
+ }
17
+ });
18
+ }
19
+ }
@@ -1,71 +1,72 @@
1
- import { nextTick, watch } from 'vue';
2
- import { inBrowser, useData } from 'vitepress';
3
- export function useCopyCode() {
4
- const { page } = useData();
5
- if (inBrowser)
6
- watch(() => page.value.relativePath, () => {
7
- nextTick(() => {
8
- document
9
- .querySelectorAll('.vp-doc div[class*="language-"] > button.copy')
10
- .forEach(handleElement);
11
- });
12
- }, { immediate: true, flush: 'post' });
13
- }
14
- async function copyToClipboard(text) {
15
- try {
16
- return navigator.clipboard.writeText(text);
17
- }
18
- catch {
19
- const element = document.createElement('textarea');
20
- const previouslyFocusedElement = document.activeElement;
21
- element.value = text;
22
- // Prevent keyboard from showing on mobile
23
- element.setAttribute('readonly', '');
24
- element.style.contain = 'strict';
25
- element.style.position = 'absolute';
26
- element.style.left = '-9999px';
27
- element.style.fontSize = '12pt'; // Prevent zooming on iOS
28
- const selection = document.getSelection();
29
- const originalRange = selection
30
- ? selection.rangeCount > 0 && selection.getRangeAt(0)
31
- : null;
32
- document.body.appendChild(element);
33
- element.select();
34
- // Explicit selection workaround for iOS
35
- element.selectionStart = 0;
36
- element.selectionEnd = text.length;
37
- document.execCommand('copy');
38
- document.body.removeChild(element);
39
- if (originalRange) {
40
- selection.removeAllRanges(); // originalRange can't be truthy when selection is falsy
41
- selection.addRange(originalRange);
42
- }
43
- // Get the focus back on the previously focused element, if any
44
- if (previouslyFocusedElement) {
45
- ;
46
- previouslyFocusedElement.focus();
47
- }
48
- }
49
- }
50
- function handleElement(el) {
51
- el.onclick = () => {
52
- const parent = el.parentElement;
53
- const sibling = el.nextElementSibling
54
- ?.nextElementSibling;
55
- if (!parent || !sibling) {
56
- return;
57
- }
58
- const isShell = /language-(shellscript|shell|bash|sh|zsh)/.test(parent.classList.toString());
59
- let { innerText: text = '' } = sibling;
60
- if (isShell) {
61
- text = text.replace(/^ *(\$|>) /gm, '');
62
- }
63
- copyToClipboard(text).then(() => {
64
- el.classList.add('copied');
65
- setTimeout(() => {
66
- el.classList.remove('copied');
67
- el.blur();
68
- }, 2000);
69
- });
70
- };
71
- }
1
+ import { inBrowser } from 'vitepress';
2
+ export function useCopyCode() {
3
+ if (inBrowser) {
4
+ const timeoutIdMap = new Map();
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
10
+ ?.nextElementSibling;
11
+ if (!parent || !sibling) {
12
+ return;
13
+ }
14
+ const isShell = /language-(shellscript|shell|bash|sh|zsh)/.test(parent.className);
15
+ let text = '';
16
+ sibling
17
+ .querySelectorAll('span.line:not(.diff.remove)')
18
+ .forEach((node) => (text += (node.textContent || '') + '\n'));
19
+ text = text.slice(0, -1);
20
+ if (isShell) {
21
+ text = text.replace(/^ *(\$|>) /gm, '').trim();
22
+ }
23
+ copyToClipboard(text).then(() => {
24
+ el.classList.add('copied');
25
+ clearTimeout(timeoutIdMap.get(el));
26
+ const timeoutId = setTimeout(() => {
27
+ el.classList.remove('copied');
28
+ el.blur();
29
+ timeoutIdMap.delete(el);
30
+ }, 2000);
31
+ timeoutIdMap.set(el, timeoutId);
32
+ });
33
+ }
34
+ });
35
+ }
36
+ }
37
+ async function copyToClipboard(text) {
38
+ try {
39
+ return navigator.clipboard.writeText(text);
40
+ }
41
+ catch {
42
+ const element = document.createElement('textarea');
43
+ const previouslyFocusedElement = document.activeElement;
44
+ element.value = text;
45
+ // Prevent keyboard from showing on mobile
46
+ element.setAttribute('readonly', '');
47
+ element.style.contain = 'strict';
48
+ element.style.position = 'absolute';
49
+ element.style.left = '-9999px';
50
+ element.style.fontSize = '12pt'; // Prevent zooming on iOS
51
+ const selection = document.getSelection();
52
+ const originalRange = selection
53
+ ? selection.rangeCount > 0 && selection.getRangeAt(0)
54
+ : null;
55
+ document.body.appendChild(element);
56
+ element.select();
57
+ // Explicit selection workaround for iOS
58
+ element.selectionStart = 0;
59
+ element.selectionEnd = text.length;
60
+ document.execCommand('copy');
61
+ document.body.removeChild(element);
62
+ if (originalRange) {
63
+ selection.removeAllRanges(); // originalRange can't be truthy when selection is falsy
64
+ selection.addRange(originalRange);
65
+ }
66
+ // Get the focus back on the previously focused element, if any
67
+ if (previouslyFocusedElement) {
68
+ ;
69
+ previouslyFocusedElement.focus();
70
+ }
71
+ }
72
+ }
@@ -1,51 +1,51 @@
1
- import { watchEffect } from 'vue';
2
- import { createTitle, mergeHead } from '../../shared.js';
3
- export function useUpdateHead(route, siteDataByRouteRef) {
4
- let managedHeadTags = [];
5
- let isFirstUpdate = true;
6
- const updateHeadTags = (newTags) => {
7
- if (import.meta.env.PROD && isFirstUpdate) {
8
- // in production, the initial meta tags are already pre-rendered so we
9
- // skip the first update.
10
- isFirstUpdate = false;
11
- return;
12
- }
13
- managedHeadTags.forEach((el) => document.head.removeChild(el));
14
- managedHeadTags = [];
15
- newTags.forEach((headConfig) => {
16
- const el = createHeadElement(headConfig);
17
- document.head.appendChild(el);
18
- managedHeadTags.push(el);
19
- });
20
- };
21
- watchEffect(() => {
22
- const pageData = route.data;
23
- const siteData = siteDataByRouteRef.value;
24
- const pageDescription = pageData && pageData.description;
25
- const frontmatterHead = (pageData && pageData.frontmatter.head) || [];
26
- // update title and description
27
- document.title = createTitle(siteData, pageData);
28
- document
29
- .querySelector(`meta[name=description]`)
30
- .setAttribute('content', pageDescription || siteData.description);
31
- updateHeadTags(mergeHead(siteData.head, filterOutHeadDescription(frontmatterHead)));
32
- });
33
- }
34
- function createHeadElement([tag, attrs, innerHTML]) {
35
- const el = document.createElement(tag);
36
- for (const key in attrs) {
37
- el.setAttribute(key, attrs[key]);
38
- }
39
- if (innerHTML) {
40
- el.innerHTML = innerHTML;
41
- }
42
- return el;
43
- }
44
- function isMetaDescription(headConfig) {
45
- return (headConfig[0] === 'meta' &&
46
- headConfig[1] &&
47
- headConfig[1].name === 'description');
48
- }
49
- function filterOutHeadDescription(head) {
50
- return head.filter((h) => !isMetaDescription(h));
51
- }
1
+ import { watchEffect } from 'vue';
2
+ import { createTitle, mergeHead } from '../../shared';
3
+ export function useUpdateHead(route, siteDataByRouteRef) {
4
+ let managedHeadTags = [];
5
+ let isFirstUpdate = true;
6
+ const updateHeadTags = (newTags) => {
7
+ if (import.meta.env.PROD && isFirstUpdate) {
8
+ // in production, the initial meta tags are already pre-rendered so we
9
+ // skip the first update.
10
+ isFirstUpdate = false;
11
+ return;
12
+ }
13
+ managedHeadTags.forEach((el) => document.head.removeChild(el));
14
+ managedHeadTags = [];
15
+ newTags.forEach((headConfig) => {
16
+ const el = createHeadElement(headConfig);
17
+ document.head.appendChild(el);
18
+ managedHeadTags.push(el);
19
+ });
20
+ };
21
+ watchEffect(() => {
22
+ const pageData = route.data;
23
+ const siteData = siteDataByRouteRef.value;
24
+ const pageDescription = pageData && pageData.description;
25
+ const frontmatterHead = (pageData && pageData.frontmatter.head) || [];
26
+ // update title and description
27
+ document.title = createTitle(siteData, pageData);
28
+ document
29
+ .querySelector(`meta[name=description]`)
30
+ .setAttribute('content', pageDescription || siteData.description);
31
+ updateHeadTags(mergeHead(siteData.head, filterOutHeadDescription(frontmatterHead)));
32
+ });
33
+ }
34
+ function createHeadElement([tag, attrs, innerHTML]) {
35
+ const el = document.createElement(tag);
36
+ for (const key in attrs) {
37
+ el.setAttribute(key, attrs[key]);
38
+ }
39
+ if (innerHTML) {
40
+ el.innerHTML = innerHTML;
41
+ }
42
+ return el;
43
+ }
44
+ function isMetaDescription(headConfig) {
45
+ return (headConfig[0] === 'meta' &&
46
+ headConfig[1] &&
47
+ headConfig[1].name === 'description');
48
+ }
49
+ function filterOutHeadDescription(head) {
50
+ return head.filter((h) => !isMetaDescription(h));
51
+ }
@@ -1,94 +1,99 @@
1
- // Customized pre-fetch for page chunks based on
2
- // https://github.com/GoogleChromeLabs/quicklink
3
- import { useRoute } from '../router.js';
4
- import { onMounted, onUnmounted, watch } from 'vue';
5
- import { inBrowser, pathToFile } from '../utils.js';
6
- const hasFetched = new Set();
7
- const createLink = () => document.createElement('link');
8
- const viaDOM = (url) => {
9
- const link = createLink();
10
- link.rel = `prefetch`;
11
- link.href = url;
12
- document.head.appendChild(link);
13
- };
14
- const viaXHR = (url) => {
15
- const req = new XMLHttpRequest();
16
- req.open('GET', url, (req.withCredentials = true));
17
- req.send();
18
- };
19
- let link;
20
- const doFetch = inBrowser &&
21
- (link = createLink()) &&
22
- link.relList &&
23
- link.relList.supports &&
24
- link.relList.supports('prefetch')
25
- ? viaDOM
26
- : viaXHR;
27
- export function usePrefetch() {
28
- if (!inBrowser) {
29
- return;
30
- }
31
- if (!window.IntersectionObserver) {
32
- return;
33
- }
34
- let conn;
35
- if ((conn = navigator.connection) &&
36
- (conn.saveData || /2g/.test(conn.effectiveType))) {
37
- // Don't prefetch if using 2G or if Save-Data is enabled.
38
- return;
39
- }
40
- const rIC = window.requestIdleCallback || setTimeout;
41
- let observer = null;
42
- const observeLinks = () => {
43
- if (observer) {
44
- observer.disconnect();
45
- }
46
- observer = new IntersectionObserver((entries) => {
47
- entries.forEach((entry) => {
48
- if (entry.isIntersecting) {
49
- const link = entry.target;
50
- observer.unobserve(link);
51
- const { pathname } = link;
52
- if (!hasFetched.has(pathname)) {
53
- hasFetched.add(pathname);
54
- const pageChunkPath = pathToFile(pathname);
55
- doFetch(pageChunkPath);
56
- }
57
- }
58
- });
59
- });
60
- rIC(() => {
61
- document.querySelectorAll('#app a').forEach((link) => {
62
- const { target, hostname, pathname } = link;
63
- const extMatch = pathname.match(/\.\w+$/);
64
- if (extMatch && extMatch[0] !== '.html') {
65
- return;
66
- }
67
- if (
68
- // only prefetch same tab navigation, since a new tab will load
69
- // the lean js chunk instead.
70
- target !== `_blank` &&
71
- // only prefetch inbound links
72
- hostname === location.hostname) {
73
- if (pathname !== location.pathname) {
74
- observer.observe(link);
75
- }
76
- else {
77
- // No need to prefetch chunk for the current page, but also mark
78
- // it as already fetched. This is because the initial page uses its
79
- // lean chunk, and if we don't mark it, navigation to another page
80
- // with a link back to the first page will fetch its full chunk
81
- // which isn't needed.
82
- hasFetched.add(pathname);
83
- }
84
- }
85
- });
86
- });
87
- };
88
- onMounted(observeLinks);
89
- const route = useRoute();
90
- watch(() => route.path, observeLinks);
91
- onUnmounted(() => {
92
- observer && observer.disconnect();
93
- });
94
- }
1
+ // Customized pre-fetch for page chunks based on
2
+ // https://github.com/GoogleChromeLabs/quicklink
3
+ import { useRoute } from '../router';
4
+ import { onMounted, onUnmounted, watch } from 'vue';
5
+ import { inBrowser, pathToFile } from '../utils';
6
+ const hasFetched = new Set();
7
+ const createLink = () => document.createElement('link');
8
+ const viaDOM = (url) => {
9
+ const link = createLink();
10
+ link.rel = `prefetch`;
11
+ link.href = url;
12
+ document.head.appendChild(link);
13
+ };
14
+ const viaXHR = (url) => {
15
+ const req = new XMLHttpRequest();
16
+ req.open('GET', url, (req.withCredentials = true));
17
+ req.send();
18
+ };
19
+ let link;
20
+ const doFetch = inBrowser &&
21
+ (link = createLink()) &&
22
+ link.relList &&
23
+ link.relList.supports &&
24
+ link.relList.supports('prefetch')
25
+ ? viaDOM
26
+ : viaXHR;
27
+ export function usePrefetch() {
28
+ if (!inBrowser) {
29
+ return;
30
+ }
31
+ if (!window.IntersectionObserver) {
32
+ return;
33
+ }
34
+ let conn;
35
+ if ((conn = navigator.connection) &&
36
+ (conn.saveData || /2g/.test(conn.effectiveType))) {
37
+ // Don't prefetch if using 2G or if Save-Data is enabled.
38
+ return;
39
+ }
40
+ const rIC = window.requestIdleCallback || setTimeout;
41
+ let observer = null;
42
+ const observeLinks = () => {
43
+ if (observer) {
44
+ observer.disconnect();
45
+ }
46
+ observer = new IntersectionObserver((entries) => {
47
+ entries.forEach((entry) => {
48
+ if (entry.isIntersecting) {
49
+ const link = entry.target;
50
+ observer.unobserve(link);
51
+ const { pathname } = link;
52
+ if (!hasFetched.has(pathname)) {
53
+ hasFetched.add(pathname);
54
+ const pageChunkPath = pathToFile(pathname);
55
+ doFetch(pageChunkPath);
56
+ }
57
+ }
58
+ });
59
+ });
60
+ rIC(() => {
61
+ document
62
+ .querySelectorAll('#app a')
63
+ .forEach((link) => {
64
+ const { target } = link;
65
+ const { hostname, pathname } = new URL(link.href instanceof SVGAnimatedString
66
+ ? link.href.animVal
67
+ : link.href, link.baseURI);
68
+ const extMatch = pathname.match(/\.\w+$/);
69
+ if (extMatch && extMatch[0] !== '.html') {
70
+ return;
71
+ }
72
+ if (
73
+ // only prefetch same tab navigation, since a new tab will load
74
+ // the lean js chunk instead.
75
+ target !== `_blank` &&
76
+ // only prefetch inbound links
77
+ hostname === location.hostname) {
78
+ if (pathname !== location.pathname) {
79
+ observer.observe(link);
80
+ }
81
+ else {
82
+ // No need to prefetch chunk for the current page, but also mark
83
+ // it as already fetched. This is because the initial page uses its
84
+ // lean chunk, and if we don't mark it, navigation to another page
85
+ // with a link back to the first page will fetch its full chunk
86
+ // which isn't needed.
87
+ hasFetched.add(pathname);
88
+ }
89
+ }
90
+ });
91
+ });
92
+ };
93
+ onMounted(observeLinks);
94
+ const route = useRoute();
95
+ watch(() => route.path, observeLinks);
96
+ onUnmounted(() => {
97
+ observer && observer.disconnect();
98
+ });
99
+ }
@@ -1,44 +1,42 @@
1
- import { shallowRef, readonly, computed, inject } from 'vue';
2
- import siteData from '@siteData';
3
- import { resolveSiteDataByRoute, createTitle } from '../shared.js';
4
- import { withBase } from './utils.js';
5
- export const dataSymbol = Symbol();
6
- // site data is a singleton
7
- export const siteDataRef = shallowRef((import.meta.env.PROD ? siteData : readonly(siteData)));
8
- // hmr
9
- if (import.meta.hot) {
10
- import.meta.hot.accept('/@siteData', (m) => {
11
- if (m) {
12
- siteDataRef.value = m.default;
13
- }
14
- });
15
- }
16
- // per-app data
17
- export function initData(route) {
18
- const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.path));
19
- return {
20
- site,
21
- theme: computed(() => site.value.themeConfig),
22
- page: computed(() => route.data),
23
- frontmatter: computed(() => route.data.frontmatter),
24
- lang: computed(() => site.value.lang),
25
- localePath: computed(() => {
26
- const { langs, lang } = site.value;
27
- const path = Object.keys(langs).find((langPath) => langs[langPath].lang === lang);
28
- return withBase(path || '/');
29
- }),
30
- title: computed(() => {
31
- return createTitle(site.value, route.data);
32
- }),
33
- description: computed(() => {
34
- return route.data.description || site.value.description;
35
- })
36
- };
37
- }
38
- export function useData() {
39
- const data = inject(dataSymbol);
40
- if (!data) {
41
- throw new Error('vitepress data not properly injected in app');
42
- }
43
- return data;
44
- }
1
+ import { computed, inject, readonly, ref, shallowRef } from 'vue';
2
+ import siteData from '@siteData';
3
+ import { resolveSiteDataByRoute, createTitle } from '../shared';
4
+ export const dataSymbol = Symbol();
5
+ // site data is a singleton
6
+ export const siteDataRef = shallowRef((import.meta.env.PROD ? siteData : readonly(siteData)));
7
+ // hmr
8
+ if (import.meta.hot) {
9
+ import.meta.hot.accept('/@siteData', (m) => {
10
+ if (m) {
11
+ siteDataRef.value = m.default;
12
+ }
13
+ });
14
+ }
15
+ // per-app data
16
+ export function initData(route) {
17
+ const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.data.relativePath));
18
+ return {
19
+ site,
20
+ theme: computed(() => site.value.themeConfig),
21
+ page: computed(() => route.data),
22
+ frontmatter: computed(() => route.data.frontmatter),
23
+ params: computed(() => route.data.params),
24
+ lang: computed(() => site.value.lang),
25
+ dir: computed(() => site.value.dir),
26
+ localeIndex: computed(() => site.value.localeIndex || 'root'),
27
+ title: computed(() => {
28
+ return createTitle(site.value, route.data);
29
+ }),
30
+ description: computed(() => {
31
+ return route.data.description || site.value.description;
32
+ }),
33
+ isDark: ref(false)
34
+ };
35
+ }
36
+ export function useData() {
37
+ const data = inject(dataSymbol);
38
+ if (!data) {
39
+ throw new Error('vitepress data not properly injected in app');
40
+ }
41
+ return data;
42
+ }