mirror of https://github.com/YunYouJun/valaxy
feat: support client redirect (#314)
* feat: valaxy-addon-client-redirects * feat: move client redirect feature into valaxy
This commit is contained in:
parent
5d9b43a788
commit
615f0c5a89
|
@ -144,4 +144,11 @@ export default defineSiteConfig({
|
||||||
encrypt: {
|
encrypt: {
|
||||||
enable: true,
|
enable: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
redirects: [
|
||||||
|
{
|
||||||
|
from: '/foo',
|
||||||
|
to: '/about',
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
|
@ -539,7 +539,15 @@ password: your_password
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
|
|
||||||
|
<div lang="zh-CN">
|
||||||
|
如果在文章的 Front Matter 中设置了 `password`,文章中的部分加密将被忽略。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div lang="en">
|
||||||
If you set `password` in Front Matter, partial encryption will be ignored.
|
If you set `password` in Front Matter, partial encryption will be ignored.
|
||||||
|
</div>
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: zh-CN
|
::: zh-CN
|
||||||
|
@ -558,6 +566,62 @@ Wrap content to be encrypted in `<!-- valaxy-encrypt-start:your_password --><!--
|
||||||
Examples can be found in [Partial Content Encryption](/examples/partial-content-encryption)。
|
Examples can be found in [Partial Content Encryption](/examples/partial-content-encryption)。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### 客户端重定向 {lang="zh-CN"}
|
||||||
|
|
||||||
|
### Client Redirects {lang="en"}
|
||||||
|
|
||||||
|
::: zh-CN
|
||||||
|
这会生成额外的 HTML 页面,用与跳转到 valaxy 中已有的页面。
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: en
|
||||||
|
This will generate additional HTML pages, used to jump to the valaxy's existing pages.
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
|
||||||
|
<div lang="zh-CN">
|
||||||
|
客户端重定向只在 SSG build 时启用
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div lang="en">
|
||||||
|
Client redirects will only be enabled in SSG build
|
||||||
|
</div>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: zh-CN
|
||||||
|
例如:
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: en
|
||||||
|
For example:
|
||||||
|
:::
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// site.config.ts
|
||||||
|
export default defineSiteConfig({
|
||||||
|
redirects: [
|
||||||
|
{
|
||||||
|
from: ['/foo', '/bar'],
|
||||||
|
to: '/about',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: '/v1/about',
|
||||||
|
to: '/about',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
::: zh-CN
|
||||||
|
`/foo`, `/bar`, `/v1/about` 这些路由会被重定向到 `/about`。
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: en
|
||||||
|
`/foo`, `/bar`, `/v1/about` these routes will be redirected to `/about`。
|
||||||
|
:::
|
||||||
|
|
||||||
## 主题配置 {lang="zh-CN"}
|
## 主题配置 {lang="zh-CN"}
|
||||||
|
|
||||||
## Theme Config {lang="en"}
|
## Theme Config {lang="en"}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { resolve } from 'node:path'
|
import { join, resolve } from 'node:path'
|
||||||
import type { InlineConfig } from 'vite'
|
import type { InlineConfig } from 'vite'
|
||||||
import { mergeConfig as mergeViteConfig, build as viteBuild } from 'vite'
|
import { mergeConfig as mergeViteConfig, build as viteBuild } from 'vite'
|
||||||
import { build as viteSsgBuild } from 'vite-ssg/node'
|
import { build as viteSsgBuild } from 'vite-ssg/node'
|
||||||
|
@ -8,6 +8,7 @@ import fs from 'fs-extra'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import type { ResolvedValaxyOptions } from './options'
|
import type { ResolvedValaxyOptions } from './options'
|
||||||
import { ViteValaxyPlugins } from './plugins/preset'
|
import { ViteValaxyPlugins } from './plugins/preset'
|
||||||
|
import { collectRedirects, writeRedirectFiles } from './utils/clientRedirects'
|
||||||
|
|
||||||
export async function build(
|
export async function build(
|
||||||
options: ResolvedValaxyOptions,
|
options: ResolvedValaxyOptions,
|
||||||
|
@ -55,6 +56,7 @@ export async function ssgBuild(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* post process for ssg fix extra string like `/html>` `ml>` `l>`
|
* post process for ssg fix extra string like `/html>` `ml>` `l>`
|
||||||
|
* handle tasks after ssg build
|
||||||
* todo find why
|
* todo find why
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
|
@ -72,4 +74,22 @@ export async function postProcessForSSG(options: ResolvedValaxyOptions) {
|
||||||
await fs.writeFile(indexPath, indexFile.slice(0, htmlTagStart + htmlTag.length), 'utf-8')
|
await fs.writeFile(indexPath, indexFile.slice(0, htmlTagStart + htmlTag.length), 'utf-8')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await generateClientRedirects(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateClientRedirects(options: ResolvedValaxyOptions) {
|
||||||
|
const outputPath = resolve(options.userRoot, 'dist')
|
||||||
|
const redirectRules = collectRedirects(options.config.siteConfig?.redirects ?? [])
|
||||||
|
|
||||||
|
const task = redirectRules.map(async (rule) => {
|
||||||
|
const fromPath = join(outputPath, `${rule.from}.html`)
|
||||||
|
const toPath = join(outputPath, `${rule.to}.html`)
|
||||||
|
const routeExist = await fs.pathExists(toPath)
|
||||||
|
if (!routeExist)
|
||||||
|
throw new Error(`the route of '${rule.to}' not exists`)
|
||||||
|
await writeRedirectFiles(rule.to, fromPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.all(task)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { writeFile } from 'node:fs/promises'
|
||||||
|
import { ensureFile } from 'fs-extra'
|
||||||
|
import type { RedirectRule } from '../../types'
|
||||||
|
|
||||||
|
function handleRoute(route: string) {
|
||||||
|
if (route === '/')
|
||||||
|
return '/index'
|
||||||
|
if (route.endsWith('/'))
|
||||||
|
return route.slice(0, -1)
|
||||||
|
return route
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RedirectItem {
|
||||||
|
from: string
|
||||||
|
to: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collectRedirects(redirectRule: RedirectRule[]) {
|
||||||
|
const redirects: RedirectItem[] = []
|
||||||
|
for (const rule of redirectRule) {
|
||||||
|
if (Array.isArray(rule.from)) {
|
||||||
|
for (const from of rule.from) {
|
||||||
|
redirects.push({
|
||||||
|
from: handleRoute(from),
|
||||||
|
to: handleRoute(rule.to),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
redirects.push({
|
||||||
|
from: handleRoute(rule.from),
|
||||||
|
to: handleRoute(rule.to),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirects
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function writeRedirectFiles(route: string, filePath: string) {
|
||||||
|
await ensureFile(filePath)
|
||||||
|
await writeFile(filePath, `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="refresh" content="0; url=${route}">
|
||||||
|
<link rel="canonical" href="${route}">
|
||||||
|
</head>
|
||||||
|
<script>
|
||||||
|
window.location.href = '${route}' + window.location.search + window.location.hash
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
}
|
|
@ -22,6 +22,11 @@ export interface SocialLink {
|
||||||
color: string
|
color: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RedirectRule {
|
||||||
|
to: string
|
||||||
|
from: string | string[]
|
||||||
|
}
|
||||||
|
|
||||||
// shared with valaxy node and client
|
// shared with valaxy node and client
|
||||||
export interface SiteConfig {
|
export interface SiteConfig {
|
||||||
/**
|
/**
|
||||||
|
@ -313,6 +318,12 @@ export interface SiteConfig {
|
||||||
* @description:zh-CN 限制代码块的高度,单位是 px
|
* @description:zh-CN 限制代码块的高度,单位是 px
|
||||||
*/
|
*/
|
||||||
codeHeightLimit?: number
|
codeHeightLimit?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description:en-US client redirect rules
|
||||||
|
* @description:zh-CN 客户端重定向规则
|
||||||
|
*/
|
||||||
|
redirects?: RedirectRule[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PartialDeep<T> = {
|
export type PartialDeep<T> = {
|
||||||
|
|
Loading…
Reference in New Issue