Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client-side redirects #4160

Open
4 tasks done
Veetaha opened this issue Aug 24, 2024 · 6 comments
Open
4 tasks done

Client-side redirects #4160

Veetaha opened this issue Aug 24, 2024 · 6 comments
Labels
enhancement New feature or request

Comments

@Veetaha
Copy link

Veetaha commented Aug 24, 2024

Is your feature request related to a problem? Please describe.

I'm using Vitepress to document my Rust library called bon. I'm hosting it on Github Pages, which is quite a limited hosting provider, but it's a simple one and it's built into Github, so it's very convenient. Github Pages doesn't support server-side redirects, however, VitePress could just do redirects client-side.

I'm preparing a 2.0 release of my library. I'm organizing different versions of the docs under v1/ and v2/ path prefixes. However, I'd like to have a special prefix (e.g. latest/) that would forward to the latest version of the docs, such that I could use this link in my blog posts, for example. The version-specific URLs are used the doc-comments of my library.

Or.. ideally, prefix-less paths should forward to the latest version of the docs automatically. A similar thing is implemented on docs.rs (the default site that generates docs for Rust libraries):

I want to have the same behaviour with Vitepress where

Describe the solution you'd like

There could be client-side redirects config in Vitepress config.mts file. For example, an object with glob patterns:

{
     "/guide/*": "/v2/guide/${1}",
     "/reference/*": "/v2/reference/${1}"
}

(inspired by Cloudflare's routing rules)

Describe alternatives you've considered

Only workarounds:

Additional context

I'm weak at frontend, and especially Vue, but I used other similar frontend frameworks ~6 years ago at university. So I'm seeking for a simple solution to this problem that doesn't involve writing a lot of Vue code or moving to another static page hosting. I adore the current VitePress design and simplicity ❤️

Validations

@brc-dd
Copy link
Member

brc-dd commented Aug 24, 2024

Easiest way would be to just use symlinks:

// .vitepress/config.ts

export default defineConfig({
  vite: {
    resolve: {
      preserveSymlinks: true
    }
  }
})
ln -s v2/guide/ ./guide
ln -s v2/reference ./reference

This won't redirect the page though. But your v2 content will be available without prefix.

@Veetaha
Copy link
Author

Veetaha commented Aug 24, 2024

Thank you for the suggestion! I've been thinking of using symlinks as well, although using them comes with some drawbacks for the doc-tests automation in my repo, and cross-platformness (if you clone the repo on Windows, symlinks will be just regular files with a path in them).

However, I suppose it'll be easier to do it this way than migrating to Cloudflare pages. This workaround will probably suffice, but I'll keep this issue open for the built-in client-side redirects feature request.

@brc-dd brc-dd added the enhancement New feature or request label Aug 24, 2024
@brc-dd
Copy link
Member

brc-dd commented Aug 24, 2024

Without symlinks, one of the way is just 404 it and redirect it from there. Because for general stuff it can't be supported properly (for example you want any request to /guide/* serves v2/guide/index.html, then for it to work without 404 status in gh pages, you'll need to generate infinite combinations of pathnames as separate html files). docs.rs is also doing server-side redirect.

Example code:

<!-- .vitepress/theme/Layout.vue -->

<script setup lang="ts">
import DefaultTheme from 'vitepress/theme'
import { inBrowser, useData, useRouter } from 'vitepress'
import { watch } from 'vue'

const { page } = useData()
const { go } = useRouter()

const redirects = Object.entries({
  '/latest/': '/v2/',
  '/guide/': '/v2/guide/',
  '/reference/': '/v2/reference/'
})

watch(
  () => page.value.isNotFound,
  (isNotFound) => {
    if (!isNotFound || !inBrowser) return
    const redirect = redirects.find(([from]) => window.location.pathname.startsWith(from))
    if (!redirect) return
    go(redirect[1] + window.location.pathname.slice(redirect[0].length))
  },
  { immediate: true }
)
</script>

<template>
  <DefaultTheme.Layout />
</template>
// .vitepress/theme/index.ts

import DefaultTheme from 'vitepress/theme'
import Layout from './Layout.vue'

export default { ...DefaultTheme, Layout }

AFAICT using 404.html is the only solution other than maybe re-writing the router to support hash-based navigation. Don't use this if your application is SEO-critical as the page will still be returned with a 404 status code.

@Veetaha
Copy link
Author

Veetaha commented Aug 24, 2024

Aha, I see so when Github Pages doesn't find the corresponding .html file for the path it returns a 404 status with 404.html. And then the client-side JS could redirect the user to the correct page. Yeah, it sounds like a hack, because there is an intermediate 404 status involved.

So now having client-side redirects sounds more like a hack than a feature 👀. I suppose such a thing is indeed the responsibility of the server side. Feel free to close this issue if such a hack shouldn't be in scope of VitePress. Although, I think this explanation may be part of the Vitepress docs

@Veetaha
Copy link
Author

Veetaha commented Aug 24, 2024

#4160 (comment)

Thank you for the code example! I was somehow thinking that Vitepress generated a single HTML for the entire build (since it's an SPA). So I was thinking client-side routing would be an obvious thing to implement in such case.

But I forgot about 404 status code in HTTP response completely, and now I see in the dist a bunch of different HTML files. I suppose Vitepress lazy-loads them under the hood without the page refresh.

@brc-dd
Copy link
Member

brc-dd commented Aug 24, 2024

Ah yeah, https://www.youtube.com/watch?v=xXrhg26VCSc (around 46 minute mark)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants