UNPKG

@eggjs/vuepress-theme-egg

Version:
294 lines (260 loc) 7.19 kB
export const hashRE = /#.*$/; export const extRE = /\.(md|html)$/; export const endingSlashRE = /\/$/; export const outboundRE = /^(https?:|mailto:|tel:)/; /* * find parent vm by ref * @param {String} ref * @param {Vue} vm * @param {any} def default value * @returns {Element} */ export function findContainerInVm(ref, vm, def) { if (!ref) return def; let container; let parent = vm; while ((parent = parent.$parent) && !container) { container = parent.$refs[ref]; } // Ensure it's html element (ref could be component) if (container && container.$el) { container = container.$el; } return container || def; } export function normalize(path) { return decodeURI(path) .replace(hashRE, '') .replace(extRE, ''); } export function getHash(path) { const match = path.match(hashRE); if (match) { return match[0]; } } export function isExternal(path) { return outboundRE.test(path); } export function isMailto(path) { return /^mailto:/.test(path); } export function isTel(path) { return /^tel:/.test(path); } export function getAbsoluteTop(dom) { return dom && dom.getBoundingClientRect ? dom.getBoundingClientRect().top + document.body.scrollTop + document.documentElement.scrollTop : 0; } export function ensureExt(path) { if (isExternal(path)) { return path; } const hashMatch = path.match(hashRE); const hash = hashMatch ? hashMatch[0] : ''; const normalized = normalize(path); if (endingSlashRE.test(normalized)) { return path; } return normalized + '.html' + hash; } export function isActive(route, path) { const routeHash = route.hash; const linkHash = getHash(path); if (linkHash && routeHash !== linkHash) { return false; } const routePath = normalize(route.path); const pagePath = normalize(path); return routePath === pagePath; } export function resolvePage(pages, rawPath, base) { if (base) { rawPath = resolvePath(rawPath, base); } const path = normalize(rawPath); for (let i = 0; i < pages.length; i++) { if (normalize(pages[i].regularPath) === path) { return Object.assign({}, pages[i], { type: 'page', path: ensureExt(pages[i].path), }); } } console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`); return {}; } function resolvePath(relative, base, append) { const firstChar = relative.charAt(0); if (firstChar === '/') { return relative; } if (firstChar === '?' || firstChar === '#') { return base + relative; } const stack = base.split('/'); // remove trailing segment if: // - not appending // - appending to trailing slash (last segment is empty) if (!append || !stack[stack.length - 1]) { stack.pop(); } // resolve relative path const segments = relative.replace(/^\//, '').split('/'); for (let i = 0; i < segments.length; i++) { const segment = segments[i]; if (segment === '..') { stack.pop(); } else if (segment !== '.') { stack.push(segment); } } // ensure leading slash if (stack[0] !== '') { stack.unshift(''); } return stack.join('/'); } /** * @param { Page } page * @param { string } regularPath * @param { SiteData } site * @param { string } localePath * @return { SidebarGroup } */ export function resolveSidebarItems(page, regularPath, site, localePath) { const { pages, themeConfig } = site; const localeConfig = localePath && themeConfig.locales ? themeConfig.locales[localePath] || themeConfig : themeConfig; const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar; if (pageSidebarConfig === 'auto') { return resolveHeaders(page); } const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar; if (!sidebarConfig) { return []; } const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig); return config ? config.map(item => resolveItem(item, pages, base)) : []; } export function resolveNavItems(navItems, site, base = '/', depth = 1) { const { pages } = site; return navItems.map(item => { if (typeof item === 'string') { const result = resolvePage( pages, item, item.startsWith('/') ? '/' : base ); return { text: result.title, link: result.regularPath, items: [], type: 'link', }; } let newBase = base; if (item.link) { item.link = ensureExt(resolvePath(item.link, base)); const link = item.link.split(/#|\?/)[0]; // base transform : aaa => aaa/, bbb.html => /, aaa/bbb.html => aaa/ newBase = link.endsWith('/') ? link : (link.endsWith('.html') ? (link.substring(0, link.lastIndexOf('/') + 1) || '/') : `${link}/`); newBase = newBase.startsWith('/') ? newBase : `${base}${newBase}`; } if (item.items && depth < 3) { item.items = resolveNavItems(item.items, site, newBase, depth + 1); } item.text = item.text || (getHash(item.link) || '').substring(1); item.type = (item.items && item.items.length) ? 'links' : 'link'; return item; }); } /** * @param { Page } page * @return { SidebarGroup } */ function resolveHeaders(page) { const headers = groupHeaders(page.headers || []); return [{ type: 'group', collapsable: false, title: page.title, children: headers.map(h => ({ type: 'auto', title: h.title, basePath: page.path, path: page.path + '#' + h.slug, children: h.children || [], })), }]; } export function groupHeaders(headers) { // group h3s under h2 headers = headers.map(h => Object.assign({}, h)); let lastH2; headers.forEach(h => { if (h.level === 2) { lastH2 = h; } else if (lastH2) { (lastH2.children || (lastH2.children = [])).push(h); } }); return headers.filter(h => h.level === 2); } /** * @param { Route } route * @param { Array<string|string[]> | Array<SidebarGroup> | [link: string]: SidebarConfig } config * @return { base: string, config: SidebarConfig } */ export function resolveMatchingConfig(regularPath, config) { if (Array.isArray(config)) { return { base: '/', config, }; } for (const base in config) { if (ensureEndingSlash(regularPath).indexOf(base) === 0) { return { base, config: config[base], }; } } return {}; } function ensureEndingSlash(path) { return /(\.html|\/)$/.test(path) ? path : path + '/'; } function resolveItem(item, pages, base, isNested) { if (typeof item === 'string') { return resolvePage(pages, item, base); } else if (Array.isArray(item)) { return Object.assign(resolvePage(pages, item[0], base), { title: item[1], }); } if (isNested) { console.error( '[vuepress] Nested sidebar groups are not supported. ' + 'Consider using navbar + categories instead.' ); } const children = item.children || []; return { type: 'group', title: item.title, children: children.map(child => resolveItem(child, pages, base, true)), collapsable: item.collapsable !== false, }; }