UNPKG

@tanstack/vue-router

Version:

Modern and scalable routing for Vue applications

182 lines 6.96 kB
import * as Vue from 'vue'; import { isServer } from '@tanstack/router-core/isServer'; import { useRouter } from './useRouter'; const INLINE_CSS_HYDRATION_ATTR = 'data-tsr-inline-css'; const Title = Vue.defineComponent({ name: 'Title', props: { children: { type: String, default: '', }, }, setup(props) { const router = useRouter(); if (!(isServer ?? router.isServer)) { Vue.onMounted(() => { if (props.children) { document.title = props.children; } }); Vue.watch(() => props.children, (newTitle) => { if (newTitle) { document.title = newTitle; } }); } return () => Vue.h('title', {}, props.children); }, }); const Script = Vue.defineComponent({ name: 'Script', props: { attrs: { type: Object, default: () => ({}), }, children: { type: String, default: undefined, }, }, setup(props) { const router = useRouter(); const dataScript = typeof props.attrs?.type === 'string' && props.attrs.type !== '' && props.attrs.type !== 'text/javascript' && props.attrs.type !== 'module'; if (!(isServer ?? router.isServer)) { Vue.onMounted(() => { if (dataScript) return; const attrs = props.attrs; const children = props.children; if (attrs?.src) { const normSrc = (() => { try { const base = document.baseURI || window.location.href; return new URL(attrs.src, base).href; } catch { return attrs.src; } })(); const existingScript = Array.from(document.querySelectorAll('script[src]')).find((el) => el.src === normSrc); if (existingScript) { return; } const script = document.createElement('script'); for (const [key, value] of Object.entries(attrs)) { if (value !== undefined && value !== false) { script.setAttribute(key, typeof value === 'boolean' ? '' : String(value)); } } document.head.appendChild(script); } else if (typeof children === 'string') { const typeAttr = typeof attrs?.type === 'string' ? attrs.type : 'text/javascript'; const nonceAttr = typeof attrs?.nonce === 'string' ? attrs.nonce : undefined; const existingScript = Array.from(document.querySelectorAll('script:not([src])')).find((el) => { if (!(el instanceof HTMLScriptElement)) return false; const sType = el.getAttribute('type') ?? 'text/javascript'; const sNonce = el.getAttribute('nonce') ?? undefined; return (el.textContent === children && sType === typeAttr && sNonce === nonceAttr); }); if (existingScript) { return; } const script = document.createElement('script'); script.textContent = children; if (attrs) { for (const [key, value] of Object.entries(attrs)) { if (value !== undefined && value !== false) { script.setAttribute(key, typeof value === 'boolean' ? '' : String(value)); } } } document.head.appendChild(script); } }); } return () => { if (!(isServer ?? router.isServer)) { if (dataScript && typeof props.children === 'string') { return Vue.h('script', { ...props.attrs, 'data-allow-mismatch': true, innerHTML: props.children, }); } const { src: _src, ...rest } = props.attrs || {}; return Vue.h('script', { ...rest, 'data-allow-mismatch': true, innerHTML: '', }); } if (props.attrs?.src && typeof props.attrs.src === 'string') { return Vue.h('script', props.attrs); } if (typeof props.children === 'string') { return Vue.h('script', { ...props.attrs, innerHTML: props.children, }); } return null; }; }, }); const InlineCssStyle = Vue.defineComponent({ name: 'InlineCssStyle', props: { attrs: { type: Object, default: () => ({}), }, children: { type: String, default: undefined, }, }, setup(props) { const isInlineCssPlaceholder = props.children === undefined; const hydratedInlineCss = isInlineCssPlaceholder && typeof document !== 'undefined' ? (document.querySelector(`style[${INLINE_CSS_HYDRATION_ATTR}]`)?.textContent ?? '') : undefined; return () => Vue.h('style', { ...props.attrs, [INLINE_CSS_HYDRATION_ATTR]: '', 'data-allow-mismatch': true, innerHTML: isInlineCssPlaceholder ? (hydratedInlineCss ?? '') : (props.children ?? ''), }); }, }); export function Asset(asset) { const { tag, attrs, children } = asset; switch (tag) { case 'title': return Vue.h(Title, { children: children }); case 'meta': return <meta {...attrs}/>; case 'link': return <link {...attrs}/>; case 'style': if (asset.inlineCss && (process.env.TSS_INLINE_CSS_ENABLED === 'true' || (process.env.TSS_INLINE_CSS_ENABLED === undefined && isServer))) { return Vue.h(InlineCssStyle, { attrs, children }); } return (<style {...attrs} data-allow-mismatch={asset.inlineCss || undefined} innerHTML={children}/>); case 'script': return Vue.h(Script, { attrs, children: children }); default: return null; } } //# sourceMappingURL=Asset.jsx.map