Skip to content

Commit

Permalink
feat: add basePath
Browse files Browse the repository at this point in the history
Co-Authored-By: Fuxiao Peng <[email protected]>
  • Loading branch information
jxom and xx1124961758 committed Feb 18, 2024
1 parent 458d43f commit aa4dcde
Show file tree
Hide file tree
Showing 17 changed files with 162 additions and 41 deletions.
3 changes: 2 additions & 1 deletion src/app/components/ExternalLink.css.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createVar, style } from '@vanilla-extract/css'

export const arrowColor = createVar('arrowColor')
export const iconUrl = createVar('iconUrl')

export const root = style({
selectors: {
Expand All @@ -13,7 +14,7 @@ export const root = style({
marginLeft: '0.325em',
marginRight: '0.25em',
width: '0.5em',
mask: 'url(/.vocs/icons/arrow-diagonal.svg) no-repeat center / contain',
mask: `${iconUrl} no-repeat center / contain`,
},
},
})
7 changes: 7 additions & 0 deletions src/app/components/ExternalLink.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { clsx } from 'clsx'

import { assignInlineVars } from '@vanilla-extract/dynamic'
import { forwardRef } from 'react'
import { useConfig } from '../hooks/useConfig.js'
import * as styles from './ExternalLink.css.js'

export type ExternalLinkProps = React.DetailedHTMLProps<
Expand All @@ -10,6 +12,8 @@ export type ExternalLinkProps = React.DetailedHTMLProps<

export const ExternalLink = forwardRef(
({ className, children, hideExternalIcon, href, ...props }: ExternalLinkProps, ref) => {
const { basePath } = useConfig()
const assetBasePath = import.meta.env.PROD ? basePath : ''
return (
<a
ref={ref as any}
Expand All @@ -20,6 +24,9 @@ export const ExternalLink = forwardRef(
href={href}
target="_blank"
rel="noopener noreferrer"
style={assignInlineVars({
[styles.iconUrl]: `url(${assetBasePath}/.vocs/icons/arrow-diagonal.svg)`,
})}
{...props}
>
{children}
Expand Down
9 changes: 6 additions & 3 deletions src/app/components/MobileTopNav.css.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { keyframes, style } from '@vanilla-extract/css'
import { createVar, keyframes, style } from '@vanilla-extract/css'
import {
borderRadiusVars,
contentVars,
Expand Down Expand Up @@ -205,6 +205,9 @@ export const navigationItem = style(
'navigationItem',
)

export const chevronDownIcon = createVar('chevronDownIcon')
export const chevronUpIcon = createVar('chevronUpIcon')

export const navigationTrigger = style(
{
selectors: {
Expand All @@ -215,10 +218,10 @@ export const navigationTrigger = style(
height: '0.625em',
marginLeft: '0.325em',
width: '0.625em',
mask: 'url(/.vocs/icons/chevron-down.svg) no-repeat center / contain',
mask: `${chevronDownIcon} no-repeat center / contain`,
},
'&[data-state="open"]::after': {
mask: 'url(/.vocs/icons/chevron-up.svg) no-repeat center / contain',
mask: `${chevronUpIcon} no-repeat center / contain`,
},
},
},
Expand Down
8 changes: 8 additions & 0 deletions src/app/components/MobileTopNav.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Accordion from '@radix-ui/react-accordion'
import { assignInlineVars } from '@vanilla-extract/dynamic'
import clsx from 'clsx'
import { type ComponentType, useMemo, useState } from 'react'
import { useLocation } from 'react-router-dom'
Expand Down Expand Up @@ -123,6 +124,9 @@ function CompactNavigation({ items }: { items: Config.ParsedTopNavItem[] }) {
const activeIds = useActiveNavIds({ pathname, items })
const activeItem = items.filter((item) => item.id === activeIds[0])[0]

const { basePath } = useConfig()
const assetBasePath = import.meta.env.PROD ? basePath : ''

return (
<div className={clsx(styles.navigation, styles.navigation_compact)}>
{activeItem ? (
Expand Down Expand Up @@ -154,6 +158,10 @@ function CompactNavigation({ items }: { items: Config.ParsedTopNavItem[] }) {
<Accordion.Trigger
className={clsx(styles.navigationItem, styles.navigationTrigger)}
data-active={activeIds.includes(item.id)}
style={assignInlineVars({
[styles.chevronDownIcon]: `url(${assetBasePath}/.vocs/icons/chevron-down.svg)`,
[styles.chevronUpIcon]: `url(${assetBasePath}/.vocs/icons/chevron-up.svg)`,
})}
>
{item.text}
</Accordion.Trigger>
Expand Down
6 changes: 4 additions & 2 deletions src/app/components/NavigationMenu.css.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { keyframes, style } from '@vanilla-extract/css'
import { createVar, keyframes, style } from '@vanilla-extract/css'
import {
borderRadiusVars,
fontSizeVars,
Expand Down Expand Up @@ -49,6 +49,8 @@ export const link = style(

export const item = style({}, 'item')

export const chevronDownIcon = createVar('chevronDownIcon')

export const trigger = style(
[
link,
Expand All @@ -62,7 +64,7 @@ export const trigger = style(
height: '0.625em',
marginLeft: '0.325em',
width: '0.625em',
mask: 'url(/.vocs/icons/chevron-down.svg) no-repeat center / contain',
mask: `${chevronDownIcon} no-repeat center / contain`,
},
},
},
Expand Down
23 changes: 16 additions & 7 deletions src/app/components/NavigationMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as NavigationMenu from '@radix-ui/react-navigation-menu'
import { assignInlineVars } from '@vanilla-extract/dynamic'
import clsx from 'clsx'
import type { ReactNode } from 'react'

import { useConfig } from '../hooks/useConfig.js'
import { Link as Link_ } from './Link.js'
import * as styles from './NavigationMenu.css.js'

Expand Down Expand Up @@ -46,13 +48,20 @@ export const Trigger = ({
...props
}: NavigationMenu.NavigationMenuTriggerProps & {
active?: boolean
}) => (
<NavigationMenu.Trigger
{...props}
data-active={active}
className={clsx(className, styles.trigger)}
/>
)
}) => {
const { basePath } = useConfig()
const assetBasePath = import.meta.env.PROD ? basePath : ''
return (
<NavigationMenu.Trigger
{...props}
data-active={active}
className={clsx(className, styles.trigger)}
style={assignInlineVars({
[styles.chevronDownIcon]: `url(${assetBasePath}/.vocs/icons/chevron-down.svg)`,
})}
/>
)
}

export const Content = (props: NavigationMenu.NavigationMenuContentProps) => (
<NavigationMenu.Content {...props} className={clsx(props.className, styles.content)} />
Expand Down
6 changes: 4 additions & 2 deletions src/app/components/mdx/AutolinkIcon.css.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { style } from '@vanilla-extract/css'
import { createVar, style } from '@vanilla-extract/css'

import { primitiveColorVars } from '../../styles/vars.css.js'
import { root as Autolink } from './Autolink.css.js'

export const iconUrl = createVar('iconUrl')

export const root = style({
backgroundColor: primitiveColorVars.textAccent,
display: 'inline-block',
marginLeft: '0.25em',
height: '0.8em',
width: '0.8em',
mask: 'url(/.vocs/icons/link.svg) no-repeat center / contain',
mask: `${iconUrl} no-repeat center / contain`,
transition: 'background-color 0.1s',
selectors: {
[`${Autolink}:hover &`]: {
Expand Down
14 changes: 13 additions & 1 deletion src/app/components/mdx/AutolinkIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { assignInlineVars } from '@vanilla-extract/dynamic'
import { clsx } from 'clsx'
import { type DetailedHTMLProps, type ImgHTMLAttributes } from 'react'

import { useConfig } from '../../hooks/useConfig.js'
import * as styles from './AutolinkIcon.css.js'

export function AutolinkIcon(
props: DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>,
) {
return <div {...props} className={clsx(props.className, styles.root)} />
const { basePath } = useConfig()
const assetBasePath = import.meta.env.PROD ? basePath : ''
return (
<div
{...props}
className={clsx(props.className, styles.root)}
style={assignInlineVars({
[styles.iconUrl]: `url(${assetBasePath}/.vocs/icons/link.svg)`,
})}
/>
)
}
16 changes: 10 additions & 6 deletions src/app/hooks/useConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@ import { config as virtualConfig } from 'virtual:config'

const ConfigContext = createContext(virtualConfig)

const configHash = import.meta.env.DEV
export const configHash = import.meta.env.DEV
? bytesToHex(sha256(JSON.stringify(virtualConfig))).slice(0, 8)
: ''

export function getConfig(): ParsedConfig {
if (typeof window !== 'undefined' && import.meta.env.DEV) {
const storedConfig = window.localStorage.getItem(`vocs.config.${configHash}`)
if (storedConfig) return JSON.parse(storedConfig)
}
return virtualConfig
}

export function ConfigProvider({
children,
config: initialConfig,
}: { children: ReactNode; config?: ParsedConfig }) {
const [config, setConfig] = useState(() => {
if (initialConfig) return initialConfig
if (typeof window !== 'undefined' && import.meta.env.DEV) {
const storedConfig = window.localStorage.getItem(`vocs.config.${configHash}`)
if (storedConfig) return JSON.parse(storedConfig)
}
return virtualConfig
return getConfig()
})

useEffect(() => {
Expand Down
8 changes: 5 additions & 3 deletions src/app/index.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ import './styles/index.css.js'

import { hydrateRoot } from 'react-dom/client'
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
import { ConfigProvider } from './hooks/useConfig.js'
import { ConfigProvider, getConfig } from './hooks/useConfig.js'
import { routes } from './routes.js'
import { hydrateLazyRoutes } from './utils/hydrateLazyRoutes.js'
import { removeTempStyles } from './utils/removeTempStyles.js'

hydrate()

async function hydrate() {
await hydrateLazyRoutes(routes)
const basePath = getConfig().basePath

await hydrateLazyRoutes(routes, basePath)
removeTempStyles()

const router = createBrowserRouter(routes)
const router = createBrowserRouter(routes, { basename: basePath })
hydrateRoot(
document.getElementById('app')!,
<ConfigProvider>
Expand Down
10 changes: 6 additions & 4 deletions src/app/index.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ export async function prerender(location: string) {
).filter(Boolean) as RouteObject[]

const { config } = await resolveVocsConfig()
const { basePath } = config

const body = renderToString(
<ConfigProvider config={config}>
<StaticRouter location={location}>
<StaticRouter location={location} basename={basePath}>
<Routes>
{unwrappedRoutes.map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
Expand All @@ -51,16 +52,17 @@ export async function prerender(location: string) {
}

export async function render(req: Request) {
const { query, dataRoutes } = createStaticHandler(routes)
const { config } = await resolveVocsConfig()
const { basePath } = config

const { query, dataRoutes } = createStaticHandler(routes, { basename: basePath })
const fetchRequest = createFetchRequest(req)
const context = (await query(fetchRequest)) as StaticHandlerContext

if (context instanceof Response) throw context

const router = createStaticRouter(dataRoutes, context)

const { config } = await resolveVocsConfig()

const body = renderToString(
<ConfigProvider config={config}>
<StaticRouterProvider router={router} context={context} />
Expand Down
4 changes: 3 additions & 1 deletion src/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ function Head({ frontmatter }: { frontmatter: Module['frontmatter'] }) {

const enableTitleTemplate = config.title && !title.includes(config.title)

const isLocalhost = typeof window !== 'undefined' && window.location.hostname === 'localhost'

return (
<Helmet
defaultTitle={config.title}
Expand All @@ -64,7 +66,7 @@ function Head({ frontmatter }: { frontmatter: Module['frontmatter'] }) {
{title && <title>{title}</title>}

{/* Base URL */}
{baseUrl && import.meta.env.PROD && <base href={baseUrl} />}
{baseUrl && import.meta.env.PROD && !isLocalhost && <base href={baseUrl} />}

{/* Description */}
{description !== 'undefined' && <meta name="description" content={description} />}
Expand Down
4 changes: 2 additions & 2 deletions src/app/utils/hydrateLazyRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { type RouteObject, matchRoutes } from 'react-router-dom'

export async function hydrateLazyRoutes(routes: RouteObject[]) {
export async function hydrateLazyRoutes(routes: RouteObject[], basePath: string | undefined) {
// Determine if any of the initial routes are lazy
const lazyMatches = matchRoutes(routes, window.location)?.filter((m) => m.route.lazy)
const lazyMatches = matchRoutes(routes, window.location, basePath)?.filter((m) => m.route.lazy)

// Load the lazy matches and update the routes before creating your router
// so we can hydrate the SSR-rendered content synchronously
Expand Down
Loading

0 comments on commit aa4dcde

Please sign in to comment.