feat/clean-urls (#929)

* clean urls

* Better comment for option

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>

* Remove typing because implicit

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>

* suggestion to simplify check

* clean urls in next/previous and sidebar

* accept suggestion

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>

* accept suggestion

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>

* documentation of option

* nicer without trailing slash

* trailing slash option

* Fixing trailing slash setup

* new options + improved documentation

* feat: add without subfolders option for clean urls

* fix: remove .html from algolia results

* chore: update deps

Co-authored-by: Georges Gomes <georges.gomes@gmail.com>
pull/1136/head
Divyansh Singh 3 years ago committed by GitHub
parent 2a0ccc2e1d
commit 56229e4efe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,6 +8,7 @@ export default defineConfig({
description: 'Vite & Vue powered static site generator.',
lastUpdated: true,
cleanUrls: 'without-subfolders',
themeConfig: {
nav: nav(),

@ -115,7 +115,7 @@ Below shows the the full option you may define within this object.
interface MarkdownOptions extends MarkdownIt.Options {
// Syntax highlight theme for Shiki.
// See: https://github.com/shikijs/shiki/blob/main/docs/themes.md#all-themes
theme?: Shiki.Theme | { light: Shiki.Theme, dark: Shiki.Theme }
theme?: Shiki.Theme | { light: Shiki.Theme; dark: Shiki.Theme }
// Enable line numbers in code block.
lineNumbers?: boolean
@ -173,3 +173,27 @@ export default {
}
```
## cleanUrls (Experimental)
- Type: `'disabled' | 'without-subfolders' | 'with-subfolders'`
- Default: `'disabled'`
Allows removing trailing `.html` from URLs and, optionally, generating clean directory structure. Available modes:
| Mode | Page | Generated Page | URL |
| :--------------------: | :-------: | :---------------: | :---------: |
| `'disabled'` | `/foo.md` | `/foo.html` | `/foo.html` |
| `'without-subfolders'` | `/foo.md` | `/foo.html` | `/foo` |
| `'with-subfolders'` | `/foo.md` | `/foo/index.html` | `/foo` |
::: warning
Enabling this may require additional configuration on your hosting platform. For it to work, your server must serve the generated page on requesting the URL (see above table) **without a redirect**.
:::
```ts
export default {
cleanUrls: 'with-subfolders'
}
```

@ -12,7 +12,7 @@ If you're coming from VitePress 0.x version, there're several breaking changes d
- `children` key is now named `items`.
- Top level item may not contain `link` at the moment. We're planning to bring it back.
- `repo`, `repoLabel`, `docsDir`, `docsBranch`, `editLinks`, `editLinkText` are removed in favor of more flexible api.
- For adding GitHub link with icon to the nav, use [Social Links](./theme-nav.html#navigation-links) feature.
- For adding GitHub link with icon to the nav, use [Social Links](./theme-nav#navigation-links) feature.
- For adding "Edit this page" feature, use [Edit Link](./theme-edit-link) feature.
- `lastUpdated` option is now split into `config.lastUpdated` and `themeConfig.lastUpdatedText`.
- `carbonAds.carbon` is changed to `carbonAds.code`.

@ -4,7 +4,7 @@
### Images
Unlike VuePress, VitePress handles [`base`](/guide/asset-handling.html#base-url) of your config automatically when you use static image.
Unlike VuePress, VitePress handles [`base`](./asset-handling#base-url) of your config automatically when you use static image.
Hence, now you can render images without `img` tag.
@ -14,7 +14,7 @@ Hence, now you can render images without `img` tag.
```
::: warning
For dynamic images you still need `withBase` as shown in [Base URL guide](/guide/asset-handling.html#base-url).
For dynamic images you still need `withBase` as shown in [Base URL guide](./asset-handling#base-url).
:::
Use `<img.*withBase\('(.*)'\).*alt="([^"]*)".*>` regex to find and replace it with `![$2]($1)` to replace all the images with `![](...)` syntax.

@ -4,7 +4,7 @@ The Nav is the navigation bar displayed on top of the page. It contains the site
## Site Title and Logo
By default, nav shows the title of the site refferencing [`config.title`](../config/app-configs.html#title) value. If you would like to change what's displayed on nav, you may define custom text in `themeConfig.siteTitle` option.
By default, nav shows the title of the site refferencing [`config.title`](../config/app-configs#title) value. If you would like to change what's displayed on nav, you may define custom text in `themeConfig.siteTitle` option.
```js
export default {

@ -75,7 +75,7 @@
"@docsearch/js": "^3.1.1",
"@vitejs/plugin-vue": "^3.0.0-beta.1",
"@vue/devtools-api": "^6.2.0",
"@vueuse/core": "^8.7.5",
"@vueuse/core": "^8.9.0",
"body-scroll-lock": "^4.0.0-beta.0",
"shiki": "^0.10.1",
"vite": "^3.0.0-beta.7",
@ -97,7 +97,7 @@
"@types/markdown-it": "^12.2.3",
"@types/micromatch": "^4.0.2",
"@types/minimist": "^1.2.2",
"@types/node": "^18.0.1",
"@types/node": "^18.0.3",
"@types/polka": "^0.5.4",
"@types/prompts": "^2.0.14",
"chokidar": "^3.5.3",
@ -138,8 +138,8 @@
"sirv": "^2.0.2",
"supports-color": "^9.2.2",
"typescript": "^4.7.4",
"vitest": "^0.17.0",
"vue-tsc": "^0.38.2"
"vitest": "^0.17.1",
"vue-tsc": "^0.38.3"
},
"pnpm": {
"peerDependencyRules": {

@ -21,12 +21,12 @@ importers:
'@types/markdown-it': ^12.2.3
'@types/micromatch': ^4.0.2
'@types/minimist': ^1.2.2
'@types/node': ^18.0.1
'@types/node': ^18.0.3
'@types/polka': ^0.5.4
'@types/prompts': ^2.0.14
'@vitejs/plugin-vue': ^3.0.0-beta.1
'@vue/devtools-api': ^6.2.0
'@vueuse/core': ^8.7.5
'@vueuse/core': ^8.9.0
body-scroll-lock: ^4.0.0-beta.0
chokidar: ^3.5.3
compression: ^1.7.4
@ -68,9 +68,9 @@ importers:
supports-color: ^9.2.2
typescript: ^4.7.4
vite: ^3.0.0-beta.7
vitest: ^0.17.0
vitest: ^0.17.1
vue: ^3.2.37
vue-tsc: ^0.38.2
vue-tsc: ^0.38.3
dependencies:
'@docsearch/css': 3.1.1
'@docsearch/js': 3.1.1
@ -138,8 +138,8 @@ importers:
sirv: 2.0.2
supports-color: 9.2.2
typescript: 4.7.4
vitest: 0.17.0_supports-color@9.2.2
vue-tsc: 0.38.2_typescript@4.7.4
vitest: 0.17.1_supports-color@9.2.2
vue-tsc: 0.38.3_typescript@4.7.4
docs:
specifiers: {}
@ -312,7 +312,7 @@ packages:
resolution: {integrity: sha512-bt7l2aKRoSnLUuX+s4LVQ1a7AF2c9myiZNv5uvQCePG5tpvVGpwrnMwqVXOUJn9q6FwVVhOrQMO/t+QmnnAEUw==}
dependencies:
'@docsearch/react': 3.1.1
preact: 10.8.2
preact: 10.9.0
transitivePeerDependencies:
- '@algolia/client-search'
- '@types/react'
@ -695,32 +695,32 @@ packages:
vue: 3.2.37
dev: false
/@volar/code-gen/0.38.2:
resolution: {integrity: sha512-H81I6d7rZB7teqL+zhK/Xz1v0/kKkUwkB0Aq6b4+BTCqcJeiZkoWxd0gFhrhWTnUoqiM83lhoTGo2vkvx5YagQ==}
/@volar/code-gen/0.38.3:
resolution: {integrity: sha512-0yCkDtaxffyfC9e2dSLGXJmG3b0rCfTa6vqxjr70ZFTtcf/VytmMBwboFicnm+Zoen9EI8wUNfw4upw9Slz5RQ==}
dependencies:
'@volar/source-map': 0.38.2
'@volar/source-map': 0.38.3
dev: true
/@volar/source-map/0.38.2:
resolution: {integrity: sha512-DWcYbYt9SPwk0r4VmXk1F0v4X5+hCqH1JRkAWSeJymQyXCQ2OQDEbY2PF12a7y2qn4FUBD2gOba2TynAqI8ZFQ==}
/@volar/source-map/0.38.3:
resolution: {integrity: sha512-8aVM+r4lsHnLjhvnjQ6kn4J++3I6VXtJblcGzWuIOn9M8pJmRGW6Si/eOVjayLWfvPCxXUM7e3sg4Nm2tufTmg==}
dev: true
/@volar/vue-code-gen/0.38.2:
resolution: {integrity: sha512-whLunD6phSGWBUHZKdTxeglrpzQu26ii8CRVapFdjfyMaVhQ7ESNeIAhkTVyg2ovOPc0PiDYPQEPzfWAADIWog==}
/@volar/vue-code-gen/0.38.3:
resolution: {integrity: sha512-euVuKtwV/KurRSVwNz5bZbCBJLwVOE56+Uh2PhsHcAM5Wzlt82cwLj07FbFagCftoC3IC/bsn43yuLc2I+ZjAQ==}
dependencies:
'@volar/code-gen': 0.38.2
'@volar/source-map': 0.38.2
'@volar/code-gen': 0.38.3
'@volar/source-map': 0.38.3
'@vue/compiler-core': 3.2.37
'@vue/compiler-dom': 3.2.37
'@vue/shared': 3.2.37
dev: true
/@volar/vue-typescript/0.38.2:
resolution: {integrity: sha512-5IKvSK2m5yUmH6iu/tNScVlvJGuiHawTfSmjxaMs+/tod25WeK37LEdf+pdKtlJ30bYTQmmkAuEfG01QvvBRGQ==}
/@volar/vue-typescript/0.38.3:
resolution: {integrity: sha512-rXh4RQBZrNfkiSnpBYbHrsxg7vBbZeYsGFgE/n8FVLcZfGlelsdXFIINsr/aZGUCJre9I15wQ44eEmXnc4+qww==}
dependencies:
'@volar/code-gen': 0.38.2
'@volar/source-map': 0.38.2
'@volar/vue-code-gen': 0.38.2
'@volar/code-gen': 0.38.3
'@volar/source-map': 0.38.3
'@volar/vue-code-gen': 0.38.3
'@vue/compiler-sfc': 3.2.37
'@vue/reactivity': 3.2.37
dev: true
@ -805,8 +805,8 @@ packages:
/@vue/shared/3.2.37:
resolution: {integrity: sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==}
/@vueuse/core/8.8.0_vue@3.2.37:
resolution: {integrity: sha512-TyvcNuA6O9WGkT8oQB4ERt8aBxe/e0fUs3SnibaxtLOr4eVXq42m3sLZgwgWOrJi4s9/8pTsMaJNn/6BUefwpQ==}
/@vueuse/core/8.9.0_vue@3.2.37:
resolution: {integrity: sha512-eKWehF6gsiLYxnYM/1xgDu16bKED7AWvkk56JIFNQes8OKgktr3Jc1wUy8UWIulrnwCXICUu9YUo+Wkq4r2JNw==}
peerDependencies:
'@vue/composition-api': ^1.1.0
vue: ^2.6.0 || ^3.2.0
@ -817,18 +817,18 @@ packages:
optional: true
dependencies:
'@types/web-bluetooth': 0.0.14
'@vueuse/metadata': 8.8.0
'@vueuse/shared': 8.8.0_vue@3.2.37
'@vueuse/metadata': 8.9.0
'@vueuse/shared': 8.9.0_vue@3.2.37
vue: 3.2.37
vue-demi: 0.13.2_vue@3.2.37
dev: false
/@vueuse/metadata/8.8.0:
resolution: {integrity: sha512-bRF+QPrw/RtP0al3nT/DtJ7CN0a6y6tEEO6hQ4CuJcGuUqd15eCOF6WKqQnC5DRaGFhsq/YwnQYsLTdJsW8f1A==}
/@vueuse/metadata/8.9.0:
resolution: {integrity: sha512-pjkIbQgJPRUrxK5/iXVKQFGC+OhJ+Vd6fhBsdwgj+NNJEHUotRliYymwdvhnEke/o+kkulT0xMvoK19nyPoiMw==}
dev: false
/@vueuse/shared/8.8.0_vue@3.2.37:
resolution: {integrity: sha512-DNZEs5Wy8hxxjAyWni6UK4BX/OGa8R7g0GX1tid5+AvmRbUwvUXL+0lVmGEuWPSQY4OZdYef1lvuFCi4Bfd59A==}
/@vueuse/shared/8.9.0_vue@3.2.37:
resolution: {integrity: sha512-Pmu3Fopk/JJjN8b90uQuFrVCc/RPcSA/0zDFRTyn3YIhoB5ESna/1Sac5WZxK+n82g/ERXHHQTetGI9yxEdPfA==}
peerDependencies:
'@vue/composition-api': ^1.1.0
vue: ^2.6.0 || ^3.2.0
@ -2890,8 +2890,8 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/preact/10.8.2:
resolution: {integrity: sha512-AKGt0BsDSiAYzVS78jZ9qRwuorY2CoSZtf1iOC6gLb/3QyZt+fLT09aYJBjRc/BEcRc4j+j3ggERMdNE43i1LQ==}
/preact/10.9.0:
resolution: {integrity: sha512-jO6/OvCRL+OT8gst/+Q2ir7dMybZAX8ioP02Zmzh3BkQMHLyqZSujvxbUriXvHi8qmhcHKC2Gwbog6Kt+YTh+Q==}
dev: false
/prettier/2.7.1:
@ -3577,8 +3577,8 @@ packages:
optionalDependencies:
fsevents: 2.3.2
/vitest/0.17.0_supports-color@9.2.2:
resolution: {integrity: sha512-5YO9ubHo0Zg35mea3+zZAr4sCku32C3usvIH5COeJB48TZV/R0J9aGNtGOOqEWZYfOKP0pGZUvTokne3x/QEFg==}
/vitest/0.17.1_supports-color@9.2.2:
resolution: {integrity: sha512-d6NsFC6FPmZ5XdiSYfW5rwJ/b8060wqe2steNNlVbhO69HWma6CucIm5g7PXlCSkmKvrdEbUsZHPAarlH83VGw==}
engines: {node: '>=v14.16.0'}
hasBin: true
peerDependencies:
@ -3639,13 +3639,13 @@ packages:
vue: 3.2.37
dev: false
/vue-tsc/0.38.2_typescript@4.7.4:
resolution: {integrity: sha512-+OMmpw9BZC9khul3I1HGtWchv7BCiaM7NvfdilVAiOFkjnivIoaW6jJm6YPQJaEPouePtpkDUWovyzgNxWdDsw==}
/vue-tsc/0.38.3_typescript@4.7.4:
resolution: {integrity: sha512-mWlneSF+PG2kXYGJI12N4XEAG4ljAkae7IcB93fspqSkEt/oKwDbWy3DzcPSgUm0LsXqOUprTMaZkwDVSRBIvw==}
hasBin: true
peerDependencies:
typescript: '*'
dependencies:
'@volar/vue-typescript': 0.38.2
'@volar/vue-typescript': 0.38.3
typescript: 4.7.4
dev: true

@ -40,11 +40,14 @@ export function createRouter(
const route = reactive(getDefaultRoute())
function go(href: string = inBrowser ? location.href : '/') {
// ensure correct deep link so page refresh lands on correct files.
const url = new URL(href, fakeHost)
if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {
url.pathname += '.html'
href = url.pathname + url.search + url.hash
if (siteDataRef.value.cleanUrls === 'disabled') {
// ensure correct deep link so page refresh lands on correct files.
// if cleanUrls is enabled, the server should handle this
if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {
url.pathname += '.html'
href = url.pathname + url.search + url.hash
}
}
if (inBrowser) {
// save scroll position before changing url
@ -96,7 +99,7 @@ export function createRouter(
}
}
} catch (err: any) {
if (!err.message.match(/fetch/) && !href.match(/^[\\/]404\.html$/)) {
if (!/fetch/.test(err.message) && !/^\/404(\.html|\/)?$/.test(href)) {
console.error(err)
}

@ -6,7 +6,7 @@ import { useRouter, useRoute, useData } from 'vitepress'
const router = useRouter()
const route = useRoute()
const { theme } = useData()
const { theme, site } = useData()
onMounted(() => {
initialize(theme.value.algolia)
@ -120,7 +120,12 @@ function isSpecialClick(event: MouseEvent) {
function getRelativePath(absoluteUrl: string) {
const { pathname, hash } = new URL(absoluteUrl)
return pathname + hash
return (
pathname.replace(
/\.html$/,
site.value.cleanUrls !== 'disabled' ? '' : '.html'
) + hash
)
}
</script>

@ -1,5 +1,5 @@
import { ref } from 'vue'
import { withBase } from 'vitepress'
import { withBase, useData } from 'vitepress'
export const HASH_RE = /#.*$/
export const EXT_RE = /(index)?\.(md|html)$/
@ -74,12 +74,16 @@ export function normalizeLink(url: string): string {
return url
}
const { site } = useData()
const { pathname, search, hash } = new URL(url, 'http://example.com')
const normalizedPath =
pathname.endsWith('/') || pathname.endsWith('.html')
? url
: `${pathname.replace(/(\.md)?$/, '.html')}${search}${hash}`
: `${pathname.replace(
/(\.md)?$/,
site.value.cleanUrls === 'disabled' ? '.html' : ''
)}${search}${hash}`
return withBase(normalizedPath)
}

@ -150,7 +150,15 @@ export async function renderPage(
${inlinedScript}
</body>
</html>`.trim()
const htmlFileName = path.join(config.outDir, page.replace(/\.md$/, '.html'))
const createSubDirectory =
config.cleanUrls === 'with-subfolders' &&
!/(^|\/)(index|404).md$/.test(page)
const htmlFileName = path.join(
config.outDir,
page.replace(/\.md$/, createSubDirectory ? '/index.html' : '.html')
)
await fs.ensureDir(path.dirname(htmlFileName))
await fs.writeFile(htmlFileName, html)
}

@ -16,7 +16,8 @@ import {
LocaleConfig,
DefaultTheme,
APPEARANCE_KEY,
createLangDictionary
createLangDictionary,
CleanUrlsMode
} from './shared'
import { resolveAliases, DEFAULT_THEME_PATH } from './alias'
import { MarkdownOptions } from './markdown/markdown'
@ -60,7 +61,7 @@ export interface UserConfig<ThemeConfig = any> {
scrollOffset?: number | string
/**
* Enable MPA / zero-JS mode
* Enable MPA / zero-JS mode.
* @experimental
*/
mpa?: boolean
@ -71,6 +72,19 @@ export interface UserConfig<ThemeConfig = any> {
* @default false
*/
ignoreDeadLinks?: boolean
/**
* @experimental
* Remove '.html' from URLs and generate clean directory structure.
*
* Available Modes:
* - `disabled`: generates `/foo.html` for every `/foo.md` and shows `/foo.html` in browser
* - `without-subfolders`: generates `/foo.html` for every `/foo.md` but shows `/foo` in browser
* - `with-subfolders`: generates `/foo/index.html` for every `/foo.md` and shows `/foo` in browser
*
* @default 'disabled'
*/
cleanUrls?: CleanUrlsMode
}
export type RawConfigExports<ThemeConfig = any> =
@ -88,6 +102,7 @@ export interface SiteConfig<ThemeConfig = any>
| 'mpa'
| 'lastUpdated'
| 'ignoreDeadLinks'
| 'cleanUrls'
> {
root: string
srcDir: string
@ -166,7 +181,8 @@ export async function resolveConfig(
vite: userConfig.vite,
shouldPreload: userConfig.shouldPreload,
mpa: !!userConfig.mpa,
ignoreDeadLinks: userConfig.ignoreDeadLinks
ignoreDeadLinks: userConfig.ignoreDeadLinks,
cleanUrls: userConfig.cleanUrls || 'disabled'
}
return config
@ -270,7 +286,8 @@ export async function resolveSiteData(
themeConfig: userConfig.themeConfig || {},
locales: userConfig.locales || {},
langs: createLangDictionary(userConfig),
scrollOffset: userConfig.scrollOffset || 90
scrollOffset: userConfig.scrollOffset || 90,
cleanUrls: userConfig.cleanUrls || 'disabled'
}
}

@ -5,7 +5,7 @@
import MarkdownIt from 'markdown-it'
import { MarkdownRenderer } from '../markdown'
import { URL } from 'url'
import { EXTERNAL_URL_RE } from '../../shared'
import { EXTERNAL_URL_RE, CleanUrlsMode } from '../../shared'
const indexRE = /(^|.*\/)index.md(#?.*)$/i
@ -37,7 +37,7 @@ export const linkPlugin = (
// links to files (other than html/md)
!/\.(?!html|md)\w+($|\?)/i.test(url)
) {
normalizeHref(hrefAttr)
normalizeHref(hrefAttr, env.cleanUrl)
}
// encode vite-specific replace strings in case they appear in URLs
@ -50,7 +50,10 @@ export const linkPlugin = (
return self.renderToken(tokens, idx, options)
}
function normalizeHref(hrefAttr: [string, string]) {
function normalizeHref(
hrefAttr: [string, string],
shouldCleanUrls: CleanUrlsMode
) {
let url = hrefAttr[1]
const indexMatch = url.match(indexRE)
@ -59,12 +62,19 @@ export const linkPlugin = (
url = path + hash
} else {
let cleanUrl = url.replace(/[?#].*$/, '')
// .md -> .html
// transform foo.md -> foo[.html]
if (cleanUrl.endsWith('.md')) {
cleanUrl = cleanUrl.replace(/\.md$/, '.html')
cleanUrl = cleanUrl.replace(
/\.md$/,
shouldCleanUrls === 'disabled' ? '.html' : ''
)
}
// ./foo -> ./foo.html
if (!cleanUrl.endsWith('.html') && !cleanUrl.endsWith('/')) {
// transform ./foo -> ./foo[.html]
if (
shouldCleanUrls === 'disabled' &&
!cleanUrl.endsWith('.html') &&
!cleanUrl.endsWith('/')
) {
cleanUrl += '.html'
}
const parsed = new URL(url, 'http://a.com')

@ -3,7 +3,7 @@ import path from 'path'
import c from 'picocolors'
import matter from 'gray-matter'
import LRUCache from 'lru-cache'
import { PageData, HeadConfig, EXTERNAL_URL_RE } from './shared'
import { PageData, HeadConfig, EXTERNAL_URL_RE, CleanUrlsMode } from './shared'
import { slash } from './utils/slash'
import { deeplyParseHeader } from './utils/parseHeader'
import { getGitTimestamp } from './utils/getGitTimestamp'
@ -28,7 +28,8 @@ export async function createMarkdownToVueRenderFn(
userDefines: Record<string, any> | undefined,
isBuild = false,
base = '/',
includeLastUpdatedData = false
includeLastUpdatedData = false,
cleanUrls: CleanUrlsMode = 'disabled'
) {
const md = await createMarkdownRenderer(srcDir, options, base)
@ -67,7 +68,7 @@ export async function createMarkdownToVueRenderFn(
md.__path = file
md.__relativePath = relativePath
const html = md.render(content)
const html = md.render(content, { path: file, relativePath, cleanUrls })
const data = md.__data
// validate data.links

@ -45,7 +45,9 @@ export async function createVitePressPlugin(
vue: userVuePluginOptions,
vite: userViteConfig,
pages,
ignoreDeadLinks
ignoreDeadLinks,
lastUpdated,
cleanUrls
} = siteConfig
let markdownToVue: Awaited<ReturnType<typeof createMarkdownToVueRenderFn>>
@ -83,7 +85,8 @@ export async function createVitePressPlugin(
config.define,
config.command === 'build',
config.base,
siteConfig.lastUpdated
lastUpdated,
cleanUrls
)
},

@ -7,7 +7,8 @@ export type {
LocaleConfig,
Header,
DefaultTheme,
PageDataPayload
PageDataPayload,
CleanUrlsMode
} from '../../types/shared'
export const EXTERNAL_URL_RE = /^https?:/i

6
types/shared.d.ts vendored

@ -18,8 +18,14 @@ export interface Header {
slug: string
}
export type CleanUrlsMode =
| 'disabled'
| 'without-subfolders'
| 'with-subfolders'
export interface SiteData<ThemeConfig = any> {
base: string
cleanUrls?: CleanUrlsMode
/**
* Language of the site as it should be set on the `html` element.

Loading…
Cancel
Save