DEV Community

Pacharapol Withayasakpunt
Pacharapol Withayasakpunt

Posted on • Originally published at gist.github.com

Multiple page Vite on Github Pages with HTML5 History Mode

Indeed, one of the way is simply pre-render (e.g. with vite-plugin-ssr).

Nonetheless, it is possible to enable fallback (without redirect settings, which doesn't exist in Github Pages), if you don't want to pre-render, and sacrifice JavaScript-ness yet. The way is, to clone dist/index.html to dist/sub/route/index.html. (SSG's also have multple **/index.html.)

import { existsSync, mkdirSync, copyFileSync } from 'fs'
import { resolve as pathResolve, join as pathJoin } from 'path'

function cloneIndexHtmlPlugin(routes: string[] = []): PluginOption {
  const name = 'CloneIndexHtmlPlugin'
  const outDir = 'dist' // config's `build.outDir`
  const src = pathJoin(outDir, 'index.html')

  return {
    name,
    closeBundle: () => {
      // routes.push(...)

      routes.map((p) => {
        const dir = pathResolve(outDir, p)
        if (!existsSync(dir)) {
          mkdirSync(dir, { recursive: true })
        }

        const dst = pathJoin(outDir, p, 'index.html')

        // It is possible to edit HTML here, too.
        copyFileSync(src, dst)
        console.log(`${name}: Copied ${src} to ${dst}`)
      })
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

And put the plugin in vite.config.ts

// https://vitejs.dev/config/
export default {
  base: `/${GITHUB_REPO_NAME}/`
  plugins: [cloneIndexHtmlPlugin()],
}
Enter fullscreen mode Exit fullscreen mode

Integrating with Vue 3 and Vue Router 4

VueRouter makes navigating easier, without full page reload; however, it does need a little extra config - baseURL.

import { createRouter, createWebHistory } from 'vue-router'

export const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    // Define your routes here
  ],
})
Enter fullscreen mode Exit fullscreen mode

Route names can be glob dynamically with import.meta.glob.

import { createRouter, createWebHistory } from 'vue-router'

export const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    // Does not allow string templating - `${PAGE_DIR}/**/*${PAGE_EXT}` does not work.
    ...Object.entries(import.meta.glob('../pages/**/*.vue')).map(
      ([p, component]) => {
        const PAGE_DIR = '../pages'
        const PAGE_EXT = '.vue'
        const PAGE_INDEX = 'index'

        let path = p.substring(PAGE_DIR.length, p.length - PAGE_EXT.length)
        if (path.endsWith(PAGE_INDEX)) {
          path = path.substring(0, path.length - PAGE_INDEX.length - 1)
        }
        if (!path) {
          path = '/'
        }

        return {
          path,
          component,
        }
      },
    ),
  ],
})
Enter fullscreen mode Exit fullscreen mode

cloneIndexHtmlPlugin should also glob - fast-glob is what import.meta.glob uses, so copy it.

import { sync as glob } from 'fast-glob'

// ...

      const PAGE_EXT = '.vue'
      const PAGE_INDEX = 'index'

      routes.push(
        ...glob(`**/*${PAGE_EXT}`, {
          cwd: 'src/pages',
        })
          .map((p) => {
            p = p.substring(0, p.length - PAGE_EXT.length)
            if (p.endsWith('/' + PAGE_INDEX)) {
              p = p.substring(0, p.length - PAGE_INDEX.length - 1)
            }

            return p
          })
          .filter((p) => p !== PAGE_INDEX),
      )
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
lexlohr profile image
Alex Lohr

Do you have a demo site on which this can be seen?

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt • Edited

patarapolw.github.io/jptools

There is no this link directly on the repo, but Environments: github-pages can be seen.

It is also deployed to Netlify (without netlify.toml), but unlike Vercel, it doesn't show up in "Environments".

Collapse
 
luckylooke profile image
Ctibor Laky

The link provided end up in 404 for me, but I have found the repo here: github.com/patarapolw/jptools 👍🙂