first commit
This commit is contained in:
10
node_modules/vitepress/dist/client/app/components/ClientOnly.js
generated
vendored
Normal file
10
node_modules/vitepress/dist/client/app/components/ClientOnly.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
export const ClientOnly = defineComponent({
|
||||
setup(_, { slots }) {
|
||||
const show = ref(false);
|
||||
onMounted(() => {
|
||||
show.value = true;
|
||||
});
|
||||
return () => (show.value && slots.default ? slots.default() : null);
|
||||
}
|
||||
});
|
||||
24
node_modules/vitepress/dist/client/app/components/Content.js
generated
vendored
Normal file
24
node_modules/vitepress/dist/client/app/components/Content.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import { useData, useRoute } from 'vitepress';
|
||||
import { defineComponent, h, watch } from 'vue';
|
||||
import { contentUpdatedCallbacks } from '../utils';
|
||||
const runCbs = () => contentUpdatedCallbacks.forEach((fn) => fn());
|
||||
export const Content = defineComponent({
|
||||
name: 'VitePressContent',
|
||||
props: {
|
||||
as: { type: [Object, String], default: 'div' }
|
||||
},
|
||||
setup(props) {
|
||||
const route = useRoute();
|
||||
const { frontmatter, site } = useData();
|
||||
watch(frontmatter, runCbs, { deep: true, flush: 'post' });
|
||||
return () => h(props.as, site.value.contentProps ?? { style: { position: 'relative' } }, [
|
||||
route.component
|
||||
? h(route.component, {
|
||||
onVnodeMounted: runCbs,
|
||||
onVnodeUpdated: runCbs,
|
||||
onVnodeUnmounted: runCbs
|
||||
})
|
||||
: '404 Page Not Found'
|
||||
]);
|
||||
}
|
||||
});
|
||||
44
node_modules/vitepress/dist/client/app/composables/codeGroups.js
generated
vendored
Normal file
44
node_modules/vitepress/dist/client/app/composables/codeGroups.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import { inBrowser, onContentUpdated } from 'vitepress';
|
||||
export function useCodeGroups() {
|
||||
if (import.meta.env.DEV) {
|
||||
onContentUpdated(() => {
|
||||
document.querySelectorAll('.vp-code-group > .blocks').forEach((el) => {
|
||||
Array.from(el.children).forEach((child) => {
|
||||
child.classList.remove('active');
|
||||
});
|
||||
activate(el.children[0]);
|
||||
});
|
||||
});
|
||||
}
|
||||
if (inBrowser) {
|
||||
window.addEventListener('click', (e) => {
|
||||
const el = e.target;
|
||||
if (el.matches('.vp-code-group input')) {
|
||||
// input <- .tabs <- .vp-code-group
|
||||
const group = el.parentElement?.parentElement;
|
||||
if (!group)
|
||||
return;
|
||||
const i = Array.from(group.querySelectorAll('input')).indexOf(el);
|
||||
if (i < 0)
|
||||
return;
|
||||
const blocks = group.querySelector('.blocks');
|
||||
if (!blocks)
|
||||
return;
|
||||
const current = Array.from(blocks.children).find((child) => child.classList.contains('active'));
|
||||
if (!current)
|
||||
return;
|
||||
const next = blocks.children[i];
|
||||
if (!next || current === next)
|
||||
return;
|
||||
current.classList.remove('active');
|
||||
activate(next);
|
||||
const label = group?.querySelector(`label[for="${el.id}"]`);
|
||||
label?.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function activate(el) {
|
||||
el.classList.add('active');
|
||||
window.dispatchEvent(new CustomEvent('vitepress:codeGroupTabActivate', { detail: el }));
|
||||
}
|
||||
77
node_modules/vitepress/dist/client/app/composables/copyCode.js
generated
vendored
Normal file
77
node_modules/vitepress/dist/client/app/composables/copyCode.js
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
import { inBrowser } from 'vitepress';
|
||||
import { isShell } from '../../shared';
|
||||
const ignoredNodes = ['.vp-copy-ignore', '.diff.remove'].join(', ');
|
||||
export function useCopyCode() {
|
||||
if (inBrowser) {
|
||||
const timeoutIdMap = new WeakMap();
|
||||
window.addEventListener('click', (e) => {
|
||||
const el = e.target;
|
||||
if (el.matches('div[class*="language-"] > button.copy')) {
|
||||
const parent = el.parentElement;
|
||||
const sibling = el.nextElementSibling?.nextElementSibling; // <pre> tag
|
||||
if (!parent || !sibling) {
|
||||
return;
|
||||
}
|
||||
// Clone the node and remove the ignored nodes
|
||||
const clone = sibling.cloneNode(true);
|
||||
clone.querySelectorAll(ignoredNodes).forEach((node) => node.remove());
|
||||
// remove extra newlines left after removing ignored nodes (affecting textContent because it is inside `<pre>`)
|
||||
// doesn't affect the newlines already in the code because they are rendered as `\n<span class="line"></span>`
|
||||
clone.innerHTML = clone.innerHTML.replace(/\n+/g, '\n');
|
||||
let text = clone.textContent || '';
|
||||
// NOTE: Any changes to this the code here may also need to update
|
||||
// `transformerDisableShellSymbolSelect` in `src/node/markdown/plugins/highlight.ts`
|
||||
const lang = /language-(\w+)/.exec(parent.className)?.[1] || '';
|
||||
if (isShell(lang)) {
|
||||
text = text.replace(/^ *(\$|>) /gm, '').trim();
|
||||
}
|
||||
copyToClipboard(text).then(() => {
|
||||
el.classList.add('copied');
|
||||
clearTimeout(timeoutIdMap.get(el));
|
||||
const timeoutId = setTimeout(() => {
|
||||
el.classList.remove('copied');
|
||||
el.blur();
|
||||
timeoutIdMap.delete(el);
|
||||
}, 2000);
|
||||
timeoutIdMap.set(el, timeoutId);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
async function copyToClipboard(text) {
|
||||
try {
|
||||
return navigator.clipboard.writeText(text);
|
||||
}
|
||||
catch {
|
||||
const element = document.createElement('textarea');
|
||||
const previouslyFocusedElement = document.activeElement;
|
||||
element.value = text;
|
||||
// Prevent keyboard from showing on mobile
|
||||
element.setAttribute('readonly', '');
|
||||
element.style.contain = 'strict';
|
||||
element.style.position = 'absolute';
|
||||
element.style.left = '-9999px';
|
||||
element.style.fontSize = '12pt'; // Prevent zooming on iOS
|
||||
const selection = document.getSelection();
|
||||
const originalRange = selection
|
||||
? selection.rangeCount > 0 && selection.getRangeAt(0)
|
||||
: null;
|
||||
document.body.appendChild(element);
|
||||
element.select();
|
||||
// Explicit selection workaround for iOS
|
||||
element.selectionStart = 0;
|
||||
element.selectionEnd = text.length;
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(element);
|
||||
if (originalRange) {
|
||||
selection.removeAllRanges(); // originalRange can't be truthy when selection is falsy
|
||||
selection.addRange(originalRange);
|
||||
}
|
||||
// Get the focus back on the previously focused element, if any
|
||||
if (previouslyFocusedElement) {
|
||||
;
|
||||
previouslyFocusedElement.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
81
node_modules/vitepress/dist/client/app/composables/head.js
generated
vendored
Normal file
81
node_modules/vitepress/dist/client/app/composables/head.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
import { watchEffect } from 'vue';
|
||||
import { createTitle, mergeHead } from '../../shared';
|
||||
export function useUpdateHead(route, siteDataByRouteRef) {
|
||||
let isFirstUpdate = true;
|
||||
let managedHeadElements = [];
|
||||
const updateHeadTags = (newTags) => {
|
||||
if (import.meta.env.PROD && isFirstUpdate) {
|
||||
// in production, the initial meta tags are already pre-rendered so we
|
||||
// skip the first update.
|
||||
isFirstUpdate = false;
|
||||
newTags.forEach((tag) => {
|
||||
const headEl = createHeadElement(tag);
|
||||
for (const el of document.head.children) {
|
||||
if (el.isEqualNode(headEl)) {
|
||||
managedHeadElements.push(el);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
const newElements = newTags.map(createHeadElement);
|
||||
managedHeadElements.forEach((oldEl, oldIndex) => {
|
||||
const matchedIndex = newElements.findIndex((newEl) => newEl?.isEqualNode(oldEl ?? null));
|
||||
if (matchedIndex !== -1) {
|
||||
delete newElements[matchedIndex];
|
||||
}
|
||||
else {
|
||||
oldEl?.remove();
|
||||
delete managedHeadElements[oldIndex];
|
||||
}
|
||||
});
|
||||
newElements.forEach((el) => el && document.head.appendChild(el));
|
||||
managedHeadElements = [...managedHeadElements, ...newElements].filter(Boolean);
|
||||
};
|
||||
watchEffect(() => {
|
||||
const pageData = route.data;
|
||||
const siteData = siteDataByRouteRef.value;
|
||||
const pageDescription = pageData && pageData.description;
|
||||
const frontmatterHead = (pageData && pageData.frontmatter.head) || [];
|
||||
// update title and description
|
||||
const title = createTitle(siteData, pageData);
|
||||
if (title !== document.title) {
|
||||
document.title = title;
|
||||
}
|
||||
const description = pageDescription || siteData.description;
|
||||
let metaDescriptionElement = document.querySelector(`meta[name=description]`);
|
||||
if (metaDescriptionElement) {
|
||||
if (metaDescriptionElement.getAttribute('content') !== description) {
|
||||
metaDescriptionElement.setAttribute('content', description);
|
||||
}
|
||||
}
|
||||
else {
|
||||
createHeadElement(['meta', { name: 'description', content: description }]);
|
||||
}
|
||||
updateHeadTags(mergeHead(siteData.head, filterOutHeadDescription(frontmatterHead)));
|
||||
});
|
||||
}
|
||||
function createHeadElement([tag, attrs, innerHTML]) {
|
||||
const el = document.createElement(tag);
|
||||
for (const key in attrs) {
|
||||
el.setAttribute(key, attrs[key]);
|
||||
}
|
||||
if (innerHTML) {
|
||||
el.innerHTML = innerHTML;
|
||||
}
|
||||
if (tag === 'script' && attrs.async == null) {
|
||||
// async is true by default for dynamically created scripts
|
||||
;
|
||||
el.async = false;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
function isMetaDescription(headConfig) {
|
||||
return (headConfig[0] === 'meta' &&
|
||||
headConfig[1] &&
|
||||
headConfig[1].name === 'description');
|
||||
}
|
||||
function filterOutHeadDescription(head) {
|
||||
return head.filter((h) => !isMetaDescription(h));
|
||||
}
|
||||
99
node_modules/vitepress/dist/client/app/composables/preFetch.js
generated
vendored
Normal file
99
node_modules/vitepress/dist/client/app/composables/preFetch.js
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// Customized pre-fetch for page chunks based on
|
||||
// https://github.com/GoogleChromeLabs/quicklink
|
||||
import { onMounted, onUnmounted, watch } from 'vue';
|
||||
import { useRoute } from '../router';
|
||||
import { inBrowser, pathToFile } from '../utils';
|
||||
const hasFetched = new Set();
|
||||
const createLink = () => document.createElement('link');
|
||||
const viaDOM = (url) => {
|
||||
const link = createLink();
|
||||
link.rel = `prefetch`;
|
||||
link.href = url;
|
||||
document.head.appendChild(link);
|
||||
};
|
||||
const viaXHR = (url) => {
|
||||
const req = new XMLHttpRequest();
|
||||
req.open('GET', url, (req.withCredentials = true));
|
||||
req.send();
|
||||
};
|
||||
let link;
|
||||
const doFetch = inBrowser &&
|
||||
(link = createLink()) &&
|
||||
link.relList &&
|
||||
link.relList.supports &&
|
||||
link.relList.supports('prefetch')
|
||||
? viaDOM
|
||||
: viaXHR;
|
||||
export function usePrefetch() {
|
||||
if (!inBrowser) {
|
||||
return;
|
||||
}
|
||||
if (!window.IntersectionObserver) {
|
||||
return;
|
||||
}
|
||||
let conn;
|
||||
if ((conn = navigator.connection) &&
|
||||
(conn.saveData || /2g/.test(conn.effectiveType))) {
|
||||
// Don't prefetch if using 2G or if Save-Data is enabled.
|
||||
return;
|
||||
}
|
||||
const rIC = window.requestIdleCallback || setTimeout;
|
||||
let observer = null;
|
||||
const observeLinks = () => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
}
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const link = entry.target;
|
||||
observer.unobserve(link);
|
||||
const { pathname } = link;
|
||||
if (!hasFetched.has(pathname)) {
|
||||
hasFetched.add(pathname);
|
||||
const pageChunkPath = pathToFile(pathname);
|
||||
if (pageChunkPath)
|
||||
doFetch(pageChunkPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
rIC(() => {
|
||||
document
|
||||
.querySelectorAll('#app a')
|
||||
.forEach((link) => {
|
||||
const { hostname, pathname } = new URL(link.href instanceof SVGAnimatedString
|
||||
? link.href.animVal
|
||||
: link.href, link.baseURI);
|
||||
const extMatch = pathname.match(/\.\w+$/);
|
||||
if (extMatch && extMatch[0] !== '.html') {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
// only prefetch same tab navigation, since a new tab will load
|
||||
// the lean js chunk instead.
|
||||
link.target !== '_blank' &&
|
||||
// only prefetch inbound links
|
||||
hostname === location.hostname) {
|
||||
if (pathname !== location.pathname) {
|
||||
observer.observe(link);
|
||||
}
|
||||
else {
|
||||
// No need to prefetch chunk for the current page, but also mark
|
||||
// it as already fetched. This is because the initial page uses its
|
||||
// lean chunk, and if we don't mark it, navigation to another page
|
||||
// with a link back to the first page will fetch its full chunk
|
||||
// which isn't needed.
|
||||
hasFetched.add(pathname);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
onMounted(observeLinks);
|
||||
const route = useRoute();
|
||||
watch(() => route.path, observeLinks);
|
||||
onUnmounted(() => {
|
||||
observer && observer.disconnect();
|
||||
});
|
||||
}
|
||||
53
node_modules/vitepress/dist/client/app/data.js
generated
vendored
Normal file
53
node_modules/vitepress/dist/client/app/data.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import siteData from '@siteData';
|
||||
import { useDark, usePreferredDark } from '@vueuse/core';
|
||||
import { computed, inject, readonly, ref, shallowRef, watch } from 'vue';
|
||||
import { APPEARANCE_KEY, createTitle, inBrowser, resolveSiteDataByRoute } from '../shared';
|
||||
export const dataSymbol = Symbol();
|
||||
// site data is a singleton
|
||||
export const siteDataRef = shallowRef(readonly(siteData));
|
||||
// per-app data
|
||||
export function initData(route) {
|
||||
const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.data.relativePath));
|
||||
const appearance = site.value.appearance; // fine with reactivity being lost here, config change triggers a restart
|
||||
const isDark = appearance === 'force-dark'
|
||||
? ref(true)
|
||||
: appearance === 'force-auto'
|
||||
? usePreferredDark()
|
||||
: appearance
|
||||
? useDark({
|
||||
storageKey: APPEARANCE_KEY,
|
||||
initialValue: () => (appearance === 'dark' ? 'dark' : 'auto'),
|
||||
...(typeof appearance === 'object' ? appearance : {})
|
||||
})
|
||||
: ref(false);
|
||||
const hashRef = ref(inBrowser ? location.hash : '');
|
||||
if (inBrowser) {
|
||||
window.addEventListener('hashchange', () => {
|
||||
hashRef.value = location.hash;
|
||||
});
|
||||
}
|
||||
watch(() => route.data, () => {
|
||||
hashRef.value = inBrowser ? location.hash : '';
|
||||
});
|
||||
return {
|
||||
site,
|
||||
theme: computed(() => site.value.themeConfig),
|
||||
page: computed(() => route.data),
|
||||
frontmatter: computed(() => route.data.frontmatter),
|
||||
params: computed(() => route.data.params),
|
||||
lang: computed(() => site.value.lang),
|
||||
dir: computed(() => route.data.frontmatter.dir || site.value.dir),
|
||||
localeIndex: computed(() => site.value.localeIndex || 'root'),
|
||||
title: computed(() => createTitle(site.value, route.data)),
|
||||
description: computed(() => route.data.description || site.value.description),
|
||||
isDark,
|
||||
hash: computed(() => hashRef.value)
|
||||
};
|
||||
}
|
||||
export function useData() {
|
||||
const data = inject(dataSymbol);
|
||||
if (!data) {
|
||||
throw new Error('vitepress data not properly injected in app');
|
||||
}
|
||||
return data;
|
||||
}
|
||||
28
node_modules/vitepress/dist/client/app/devtools.js
generated
vendored
Normal file
28
node_modules/vitepress/dist/client/app/devtools.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import { setupDevToolsPlugin } from '@vue/devtools-api';
|
||||
const COMPONENT_STATE_TYPE = 'VitePress';
|
||||
export const setupDevtools = (app, router, data) => {
|
||||
setupDevToolsPlugin({
|
||||
// fix recursive reference
|
||||
app: app,
|
||||
id: 'org.vuejs.vitepress',
|
||||
label: 'VitePress',
|
||||
packageName: 'vitepress',
|
||||
homepage: 'https://vitepress.dev',
|
||||
componentStateTypes: [COMPONENT_STATE_TYPE]
|
||||
}, (api) => {
|
||||
api.on.inspectComponent((payload) => {
|
||||
payload.instanceData.state.push({
|
||||
type: COMPONENT_STATE_TYPE,
|
||||
key: 'route',
|
||||
value: router.route,
|
||||
editable: false
|
||||
});
|
||||
payload.instanceData.state.push({
|
||||
type: COMPONENT_STATE_TYPE,
|
||||
key: 'data',
|
||||
value: data,
|
||||
editable: false
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
141
node_modules/vitepress/dist/client/app/index.js
generated
vendored
Normal file
141
node_modules/vitepress/dist/client/app/index.js
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
import RawTheme from '@theme/index';
|
||||
import { createApp as createClientApp, createSSRApp, defineComponent, h, onMounted, watchEffect } from 'vue';
|
||||
import { ClientOnly } from './components/ClientOnly';
|
||||
import { Content } from './components/Content';
|
||||
import { useCodeGroups } from './composables/codeGroups';
|
||||
import { useCopyCode } from './composables/copyCode';
|
||||
import { useUpdateHead } from './composables/head';
|
||||
import { usePrefetch } from './composables/preFetch';
|
||||
import { dataSymbol, initData, siteDataRef, useData } from './data';
|
||||
import { RouterSymbol, createRouter, scrollTo } from './router';
|
||||
import { inBrowser, pathToFile } from './utils';
|
||||
function resolveThemeExtends(theme) {
|
||||
if (theme.extends) {
|
||||
const base = resolveThemeExtends(theme.extends);
|
||||
return {
|
||||
...base,
|
||||
...theme,
|
||||
async enhanceApp(ctx) {
|
||||
if (base.enhanceApp)
|
||||
await base.enhanceApp(ctx);
|
||||
if (theme.enhanceApp)
|
||||
await theme.enhanceApp(ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
const Theme = resolveThemeExtends(RawTheme);
|
||||
const VitePressApp = defineComponent({
|
||||
name: 'VitePressApp',
|
||||
setup() {
|
||||
const { site, lang, dir } = useData();
|
||||
// change the language on the HTML element based on the current lang
|
||||
onMounted(() => {
|
||||
watchEffect(() => {
|
||||
document.documentElement.lang = lang.value;
|
||||
document.documentElement.dir = dir.value;
|
||||
});
|
||||
});
|
||||
if (import.meta.env.PROD && site.value.router.prefetchLinks) {
|
||||
// in prod mode, enable intersectionObserver based pre-fetch
|
||||
usePrefetch();
|
||||
}
|
||||
// setup global copy code handler
|
||||
useCopyCode();
|
||||
// setup global code groups handler
|
||||
useCodeGroups();
|
||||
if (Theme.setup)
|
||||
Theme.setup();
|
||||
return () => h(Theme.Layout);
|
||||
}
|
||||
});
|
||||
export async function createApp() {
|
||||
;
|
||||
globalThis.__VITEPRESS__ = true;
|
||||
const router = newRouter();
|
||||
const app = newApp();
|
||||
app.provide(RouterSymbol, router);
|
||||
const data = initData(router.route);
|
||||
app.provide(dataSymbol, data);
|
||||
// install global components
|
||||
app.component('Content', Content);
|
||||
app.component('ClientOnly', ClientOnly);
|
||||
// expose $frontmatter & $params
|
||||
Object.defineProperties(app.config.globalProperties, {
|
||||
$frontmatter: {
|
||||
get() {
|
||||
return data.frontmatter.value;
|
||||
}
|
||||
},
|
||||
$params: {
|
||||
get() {
|
||||
return data.page.value.params;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (Theme.enhanceApp) {
|
||||
await Theme.enhanceApp({
|
||||
app,
|
||||
router,
|
||||
siteData: siteDataRef
|
||||
});
|
||||
}
|
||||
// setup devtools in dev mode
|
||||
if (import.meta.env.DEV || __VUE_PROD_DEVTOOLS__) {
|
||||
import('./devtools.js').then(({ setupDevtools }) => setupDevtools(app, router, data));
|
||||
}
|
||||
return { app, router, data };
|
||||
}
|
||||
function newApp() {
|
||||
return import.meta.env.PROD
|
||||
? createSSRApp(VitePressApp)
|
||||
: createClientApp(VitePressApp);
|
||||
}
|
||||
function newRouter() {
|
||||
let isInitialPageLoad = inBrowser;
|
||||
return createRouter((path) => {
|
||||
let pageFilePath = pathToFile(path);
|
||||
let pageModule = null;
|
||||
if (pageFilePath) {
|
||||
// use lean build if this is the initial page load
|
||||
if (isInitialPageLoad) {
|
||||
pageFilePath = pageFilePath.replace(/\.js$/, '.lean.js');
|
||||
}
|
||||
if (import.meta.env.DEV) {
|
||||
pageModule = import(/*@vite-ignore*/ pageFilePath).catch(() => {
|
||||
// try with/without trailing slash
|
||||
// in prod this is handled in src/client/app/utils.ts#pathToFile
|
||||
const url = new URL(pageFilePath, 'http://a.com');
|
||||
const path = (url.pathname.endsWith('/index.md')
|
||||
? url.pathname.slice(0, -9) + '.md'
|
||||
: url.pathname.slice(0, -3) + '/index.md') +
|
||||
url.search +
|
||||
url.hash;
|
||||
return import(/*@vite-ignore*/ path);
|
||||
});
|
||||
}
|
||||
else {
|
||||
pageModule = import(/*@vite-ignore*/ pageFilePath);
|
||||
}
|
||||
}
|
||||
if (inBrowser) {
|
||||
isInitialPageLoad = false;
|
||||
}
|
||||
return pageModule;
|
||||
}, Theme.NotFound);
|
||||
}
|
||||
if (inBrowser) {
|
||||
createApp().then(({ app, router, data }) => {
|
||||
// wait until page component is fetched before mounting
|
||||
router.go(location.href, { initialLoad: true }).then(() => {
|
||||
// dynamically update head tags
|
||||
useUpdateHead(router.route, data.site);
|
||||
app.mount('#app');
|
||||
// scroll to hash on new tab during dev
|
||||
if (import.meta.env.DEV && location.hash) {
|
||||
scrollTo(location.hash);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
279
node_modules/vitepress/dist/client/app/router.js
generated
vendored
Normal file
279
node_modules/vitepress/dist/client/app/router.js
generated
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
import { inject, markRaw, nextTick, reactive, readonly } from 'vue';
|
||||
import { notFoundPageData, treatAsHtml } from '../shared';
|
||||
import { siteDataRef } from './data';
|
||||
import { getScrollOffset, inBrowser, withBase } from './utils';
|
||||
export const RouterSymbol = Symbol();
|
||||
// we are just using URL to parse the pathname and hash - the base doesn't
|
||||
// matter and is only passed to support same-host hrefs
|
||||
const fakeHost = 'http://a.com';
|
||||
const getDefaultRoute = () => ({
|
||||
path: '/',
|
||||
hash: '',
|
||||
query: '',
|
||||
component: null,
|
||||
data: notFoundPageData
|
||||
});
|
||||
export function createRouter(loadPageModule, fallbackComponent) {
|
||||
const route = reactive(getDefaultRoute());
|
||||
const router = {
|
||||
route,
|
||||
async go(href, options) {
|
||||
href = normalizeHref(href);
|
||||
if ((await router.onBeforeRouteChange?.(href)) === false)
|
||||
return;
|
||||
if (!inBrowser || (await changeRoute(href, options)))
|
||||
await loadPage(href);
|
||||
syncRouteQueryAndHash();
|
||||
await router.onAfterRouteChange?.(href);
|
||||
}
|
||||
};
|
||||
let latestPendingPath = null;
|
||||
async function loadPage(href, scrollPosition = 0, isRetry = false) {
|
||||
if ((await router.onBeforePageLoad?.(href)) === false)
|
||||
return;
|
||||
const targetLoc = new URL(href, fakeHost);
|
||||
const pendingPath = (latestPendingPath = targetLoc.pathname);
|
||||
try {
|
||||
let page = await loadPageModule(pendingPath);
|
||||
if (!page)
|
||||
throw new Error(`Page not found: ${pendingPath}`);
|
||||
if (latestPendingPath === pendingPath) {
|
||||
latestPendingPath = null;
|
||||
const { default: comp, __pageData } = page;
|
||||
if (!comp)
|
||||
throw new Error(`Invalid route component: ${comp}`);
|
||||
await router.onAfterPageLoad?.(href);
|
||||
route.path = inBrowser ? pendingPath : withBase(pendingPath);
|
||||
route.component = markRaw(comp);
|
||||
route.data = import.meta.env.PROD
|
||||
? markRaw(__pageData)
|
||||
: readonly(__pageData);
|
||||
syncRouteQueryAndHash(targetLoc);
|
||||
if (inBrowser) {
|
||||
nextTick(() => {
|
||||
let actualPathname = siteDataRef.value.base +
|
||||
__pageData.relativePath.replace(/(?:(^|\/)index)?\.md$/, '$1');
|
||||
if (!siteDataRef.value.cleanUrls && !actualPathname.endsWith('/')) {
|
||||
actualPathname += '.html';
|
||||
}
|
||||
if (actualPathname !== targetLoc.pathname) {
|
||||
targetLoc.pathname = actualPathname;
|
||||
href = actualPathname + targetLoc.search + targetLoc.hash;
|
||||
history.replaceState({}, '', href);
|
||||
}
|
||||
return scrollTo(targetLoc.hash, false, scrollPosition);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
if (!/fetch|Page not found/.test(err.message) &&
|
||||
!/^\/404(\.html|\/)?$/.test(href)) {
|
||||
console.error(err);
|
||||
}
|
||||
// retry on fetch fail: the page to hash map may have been invalidated
|
||||
// because a new deploy happened while the page is open. Try to fetch
|
||||
// the updated pageToHash map and fetch again.
|
||||
if (!isRetry) {
|
||||
try {
|
||||
const res = await fetch(siteDataRef.value.base + 'hashmap.json');
|
||||
window.__VP_HASH_MAP__ = await res.json();
|
||||
await loadPage(href, scrollPosition, true);
|
||||
return;
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
if (latestPendingPath === pendingPath) {
|
||||
latestPendingPath = null;
|
||||
route.path = inBrowser ? pendingPath : withBase(pendingPath);
|
||||
route.component = fallbackComponent ? markRaw(fallbackComponent) : null;
|
||||
const relativePath = inBrowser
|
||||
? route.path
|
||||
.replace(/(^|\/)$/, '$1index')
|
||||
.replace(/(\.html)?$/, '.md')
|
||||
.slice(siteDataRef.value.base.length)
|
||||
: '404.md';
|
||||
route.data = { ...notFoundPageData, relativePath };
|
||||
syncRouteQueryAndHash(targetLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
function syncRouteQueryAndHash(loc = inBrowser
|
||||
? location
|
||||
: { search: '', hash: '' }) {
|
||||
route.query = loc.search;
|
||||
route.hash = decodeURIComponent(loc.hash);
|
||||
}
|
||||
if (inBrowser) {
|
||||
if (history.state === null)
|
||||
history.replaceState({}, '');
|
||||
window.addEventListener('click', (e) => {
|
||||
if (e.defaultPrevented ||
|
||||
!(e.target instanceof Element) ||
|
||||
e.target.closest('button') || // temporary fix for docsearch action buttons
|
||||
e.button !== 0 ||
|
||||
e.ctrlKey ||
|
||||
e.shiftKey ||
|
||||
e.altKey ||
|
||||
e.metaKey) {
|
||||
return;
|
||||
}
|
||||
const link = e.target.closest('a');
|
||||
if (!link ||
|
||||
link.closest('.vp-raw') ||
|
||||
link.hasAttribute('download') ||
|
||||
link.hasAttribute('target')) {
|
||||
return;
|
||||
}
|
||||
const linkHref = link.getAttribute('href') ??
|
||||
(link instanceof SVGAElement ? link.getAttribute('xlink:href') : null);
|
||||
if (linkHref == null)
|
||||
return;
|
||||
const { href, origin, pathname } = new URL(linkHref, link.baseURI);
|
||||
const currentLoc = new URL(location.href); // copy to keep old data
|
||||
// only intercept inbound html links
|
||||
if (origin === currentLoc.origin && treatAsHtml(pathname)) {
|
||||
e.preventDefault();
|
||||
router.go(href, {
|
||||
// use smooth scroll when clicking on header anchor links
|
||||
smoothScroll: link.classList.contains('header-anchor')
|
||||
});
|
||||
}
|
||||
}, { capture: true });
|
||||
window.addEventListener('popstate', async (e) => {
|
||||
if (e.state === null)
|
||||
return;
|
||||
const href = normalizeHref(location.href);
|
||||
await loadPage(href, (e.state && e.state.scrollPosition) || 0);
|
||||
syncRouteQueryAndHash();
|
||||
await router.onAfterRouteChange?.(href);
|
||||
});
|
||||
window.addEventListener('hashchange', (e) => {
|
||||
e.preventDefault();
|
||||
syncRouteQueryAndHash();
|
||||
});
|
||||
}
|
||||
handleHMR(route);
|
||||
return router;
|
||||
}
|
||||
export function useRouter() {
|
||||
const router = inject(RouterSymbol);
|
||||
if (!router)
|
||||
throw new Error('useRouter() is called without provider.');
|
||||
return router;
|
||||
}
|
||||
export function useRoute() {
|
||||
return useRouter().route;
|
||||
}
|
||||
export function scrollTo(hash, smooth = false, scrollPosition = 0) {
|
||||
if (!hash || scrollPosition) {
|
||||
window.scrollTo(0, scrollPosition);
|
||||
return;
|
||||
}
|
||||
let target = null;
|
||||
try {
|
||||
target = document.getElementById(decodeURIComponent(hash).slice(1));
|
||||
}
|
||||
catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
if (!target)
|
||||
return;
|
||||
const targetTop = window.scrollY +
|
||||
target.getBoundingClientRect().top -
|
||||
getScrollOffset() +
|
||||
Number.parseInt(window.getComputedStyle(target).paddingTop, 10) || 0;
|
||||
const behavior = window.matchMedia('(prefers-reduced-motion)').matches
|
||||
? 'instant'
|
||||
: // only smooth scroll if distance is smaller than screen height
|
||||
smooth && Math.abs(targetTop - window.scrollY) <= window.innerHeight
|
||||
? 'smooth'
|
||||
: 'auto';
|
||||
const scrollToTarget = () => {
|
||||
window.scrollTo({ left: 0, top: targetTop, behavior });
|
||||
// focus the target element for better accessibility
|
||||
target.focus({ preventScroll: true });
|
||||
// return if focus worked
|
||||
if (document.activeElement === target)
|
||||
return;
|
||||
// element has tabindex already, likely not focusable
|
||||
// because of some other reason, bail out
|
||||
if (target.hasAttribute('tabindex'))
|
||||
return;
|
||||
const restoreTabindex = () => {
|
||||
target.removeAttribute('tabindex');
|
||||
target.removeEventListener('blur', restoreTabindex);
|
||||
};
|
||||
// temporarily make the target element focusable
|
||||
target.setAttribute('tabindex', '-1');
|
||||
target.addEventListener('blur', restoreTabindex);
|
||||
// try to focus again
|
||||
target.focus({ preventScroll: true });
|
||||
// remove tabindex and event listener if focus still not worked
|
||||
if (document.activeElement !== target)
|
||||
restoreTabindex();
|
||||
};
|
||||
requestAnimationFrame(scrollToTarget);
|
||||
}
|
||||
function handleHMR(route) {
|
||||
// update route.data on HMR updates of active page
|
||||
if (import.meta.hot) {
|
||||
// hot reload pageData
|
||||
import.meta.hot.on('vitepress:pageData', (payload) => {
|
||||
if (shouldHotReload(payload))
|
||||
route.data = payload.pageData;
|
||||
});
|
||||
}
|
||||
}
|
||||
function shouldHotReload(payload) {
|
||||
const payloadPath = payload.path.replace(/(?:(^|\/)index)?\.md$/, '$1');
|
||||
const locationPath = location.pathname
|
||||
.replace(/(?:(^|\/)index)?\.html$/, '')
|
||||
.slice(siteDataRef.value.base.length - 1);
|
||||
return payloadPath === locationPath;
|
||||
}
|
||||
function normalizeHref(href) {
|
||||
const url = new URL(href, fakeHost);
|
||||
url.pathname = url.pathname.replace(/(^|\/)index(\.html)?$/, '$1');
|
||||
// ensure correct deep link so page refresh lands on correct files
|
||||
if (siteDataRef.value.cleanUrls) {
|
||||
url.pathname = url.pathname.replace(/\.html$/, '');
|
||||
}
|
||||
else if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {
|
||||
url.pathname += '.html';
|
||||
}
|
||||
return url.pathname + url.search + url.hash;
|
||||
}
|
||||
async function changeRoute(href, { smoothScroll = false, initialLoad = false, replace = false } = {}) {
|
||||
const loc = normalizeHref(location.href);
|
||||
const nextUrl = new URL(href, location.origin);
|
||||
const currentUrl = new URL(loc, location.origin);
|
||||
if (href === loc) {
|
||||
if (!initialLoad) {
|
||||
scrollTo(nextUrl.hash, smoothScroll);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (replace) {
|
||||
history.replaceState({}, '', href);
|
||||
}
|
||||
else {
|
||||
// save scroll position before changing URL
|
||||
history.replaceState({ scrollPosition: window.scrollY }, '');
|
||||
history.pushState({}, '', href);
|
||||
}
|
||||
if (nextUrl.pathname === currentUrl.pathname) {
|
||||
// scroll between hash anchors on the same page, avoid duplicate entries
|
||||
if (nextUrl.hash !== currentUrl.hash) {
|
||||
window.dispatchEvent(new HashChangeEvent('hashchange', {
|
||||
oldURL: currentUrl.href,
|
||||
newURL: nextUrl.href
|
||||
}));
|
||||
scrollTo(nextUrl.hash, smoothScroll);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
10
node_modules/vitepress/dist/client/app/ssr.js
generated
vendored
Normal file
10
node_modules/vitepress/dist/client/app/ssr.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// entry for SSR
|
||||
import { renderToString } from 'vue/server-renderer';
|
||||
import { createApp } from './index';
|
||||
export async function render(path) {
|
||||
const { app, router } = await createApp();
|
||||
await router.go(path);
|
||||
const ctx = { content: '', vpSocialIcons: new Set() };
|
||||
ctx.content = await renderToString(app, ctx);
|
||||
return ctx;
|
||||
}
|
||||
1
node_modules/vitepress/dist/client/app/theme.js
generated
vendored
Normal file
1
node_modules/vitepress/dist/client/app/theme.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
120
node_modules/vitepress/dist/client/app/utils.js
generated
vendored
Normal file
120
node_modules/vitepress/dist/client/app/utils.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
import { tryOnUnmounted } from '@vueuse/core';
|
||||
import { h, onMounted, shallowRef } from 'vue';
|
||||
import { EXTERNAL_URL_RE, inBrowser, sanitizeFileName } from '../shared';
|
||||
import { siteDataRef } from './data';
|
||||
export { escapeHtml as _escapeHtml, inBrowser } from '../shared';
|
||||
/**
|
||||
* Join two paths by resolving the slash collision.
|
||||
*/
|
||||
export function joinPath(base, path) {
|
||||
return `${base}${path}`.replace(/\/+/g, '/');
|
||||
}
|
||||
/**
|
||||
* Append base to internal (non-relative) urls
|
||||
*/
|
||||
export function withBase(path) {
|
||||
return EXTERNAL_URL_RE.test(path) || !path.startsWith('/')
|
||||
? path
|
||||
: joinPath(siteDataRef.value.base, path);
|
||||
}
|
||||
/**
|
||||
* Converts a url path to the corresponding js chunk filename.
|
||||
*/
|
||||
export function pathToFile(path) {
|
||||
let pagePath = path.replace(/\.html$/, '');
|
||||
pagePath = decodeURIComponent(pagePath);
|
||||
pagePath = pagePath.replace(/\/$/, '/index'); // /foo/ -> /foo/index
|
||||
if (import.meta.env.DEV) {
|
||||
// always force re-fetch content in dev
|
||||
pagePath += `.md?t=${Date.now()}`;
|
||||
}
|
||||
else {
|
||||
// in production, each .md file is built into a .md.js file following
|
||||
// the path conversion scheme.
|
||||
// /foo/bar.html -> ./foo_bar.md
|
||||
if (inBrowser) {
|
||||
const base = import.meta.env.BASE_URL;
|
||||
pagePath =
|
||||
sanitizeFileName(pagePath.slice(base.length).replace(/\//g, '_') || 'index') + '.md';
|
||||
// client production build needs to account for page hash, which is
|
||||
// injected directly in the page's html
|
||||
let pageHash = __VP_HASH_MAP__[pagePath.toLowerCase()];
|
||||
if (!pageHash) {
|
||||
pagePath = pagePath.endsWith('_index.md')
|
||||
? pagePath.slice(0, -9) + '.md'
|
||||
: pagePath.slice(0, -3) + '_index.md';
|
||||
pageHash = __VP_HASH_MAP__[pagePath.toLowerCase()];
|
||||
}
|
||||
if (!pageHash)
|
||||
return null;
|
||||
pagePath = `${base}${__ASSETS_DIR__}/${pagePath}.${pageHash}.js`;
|
||||
}
|
||||
else {
|
||||
// ssr build uses much simpler name mapping
|
||||
pagePath = `./${sanitizeFileName(pagePath.slice(1).replace(/\//g, '_'))}.md.js`;
|
||||
}
|
||||
}
|
||||
return pagePath;
|
||||
}
|
||||
export let contentUpdatedCallbacks = [];
|
||||
/**
|
||||
* Register callback that is called every time the markdown content is updated
|
||||
* in the DOM.
|
||||
*/
|
||||
export function onContentUpdated(fn) {
|
||||
contentUpdatedCallbacks.push(fn);
|
||||
tryOnUnmounted(() => {
|
||||
contentUpdatedCallbacks = contentUpdatedCallbacks.filter((f) => f !== fn);
|
||||
});
|
||||
}
|
||||
export function defineClientComponent(loader, args, cb) {
|
||||
return {
|
||||
setup() {
|
||||
const comp = shallowRef();
|
||||
onMounted(async () => {
|
||||
let res = await loader();
|
||||
// interop module default
|
||||
if (res && (res.__esModule || res[Symbol.toStringTag] === 'Module')) {
|
||||
res = res.default;
|
||||
}
|
||||
comp.value = res;
|
||||
await cb?.();
|
||||
});
|
||||
return () => (comp.value ? h(comp.value, ...(args ?? [])) : null);
|
||||
}
|
||||
};
|
||||
}
|
||||
export function getScrollOffset() {
|
||||
let scrollOffset = siteDataRef.value.scrollOffset;
|
||||
let offset = 0;
|
||||
let padding = 24;
|
||||
if (typeof scrollOffset === 'object' && 'padding' in scrollOffset) {
|
||||
padding = scrollOffset.padding;
|
||||
scrollOffset = scrollOffset.selector;
|
||||
}
|
||||
if (typeof scrollOffset === 'number') {
|
||||
offset = scrollOffset;
|
||||
}
|
||||
else if (typeof scrollOffset === 'string') {
|
||||
offset = tryOffsetSelector(scrollOffset, padding);
|
||||
}
|
||||
else if (Array.isArray(scrollOffset)) {
|
||||
for (const selector of scrollOffset) {
|
||||
const res = tryOffsetSelector(selector, padding);
|
||||
if (res) {
|
||||
offset = res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
function tryOffsetSelector(selector, padding) {
|
||||
const el = document.querySelector(selector);
|
||||
if (!el)
|
||||
return 0;
|
||||
const bot = el.getBoundingClientRect().bottom;
|
||||
if (bot < 0)
|
||||
return 0;
|
||||
return bot + padding;
|
||||
}
|
||||
Reference in New Issue
Block a user