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

feat: add assetsBase config to support serving assets from CDN #2467

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/en/reference/site-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,25 @@ export default {
}
```

### assetsBase

- Type: `string`
- Default: `/assets/`

The base URL the site assets will be deployed at. You will need to set this if you plan to deploy your site assets to CDN. It is similar to `publicPath` in other module bundler.

The `assetsBase` is configured to `${base}assets/` by default. It should always start with a slash or a valid protocol, and always end with a slash.

::: warning
This option only takes effect in production mode.
:::

```ts
export default {
assetsBase: 'https://cdn.example.com/assets/bar/'
}
```

## Routing

### cleanUrls
Expand Down
19 changes: 19 additions & 0 deletions docs/es/reference/site-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,25 @@ export default {
}
```

### assetsBase

- Tipo: `string`
- Predeterminado: `/assets/`

La URL base donde se desplegarán los activos del sitio. Necesitarás configurar esto si planeas desplegar los activos de tu sitio en un CDN. Es similar a `publicPath` en otros empaquetadores de módulos.

El `assetsBase` se configura como `${base}assets/` por defecto. Siempre debe comenzar con una barra inclinada o un protocolo válido y siempre terminar con una barra inclinada.

::: warning
Esta opción solo tiene efecto en modo producción.
:::

```ts
export default {
assetsBase: 'https://cdn.example.com/assets/bar/'
}
```

## Roteamento {#routing}

### cleanUrls
Expand Down
19 changes: 19 additions & 0 deletions docs/ko/reference/site-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,25 @@ export default {
}
```

### assetsBase

- 타입: `string`
- 기본값: `/assets/`

사이트 에셋이 배포될 기본 URL입니다. 사이트 에셋을 CDN에 배포할 계획이라면 이 값을 설정해야 합니다. 이는 다른 모듈 번들러의 `publicPath`와 유사합니다.

`assetsBase`는 기본적으로 `${base}assets/`로 구성됩니다. 항상 슬래시로 시작하거나 유효한 프로토콜로 시작해야 하며, 항상 슬래시로 끝나야 합니다.

::: warning
이 옵션은 프로덕션 모드에서만 적용됩니다.
:::

```ts
export default {
assetsBase: 'https://cdn.example.com/assets/bar/'
}
```

## 라우팅 {#routing}

### cleanUrls
Expand Down
19 changes: 19 additions & 0 deletions docs/pt/reference/site-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,25 @@ export default {
}
```

### assetsBase

- Tipo: `string`
- Padrão: `/assets/`

A URL base onde os recursos do site serão implantados. Você precisará configurar isto se planeja implantar os recursos do seu site em uma CDN. É semelhante a `publicPath` em outros empacotadores de módulos.

O `assetsBase` é configurado como `${base}assets/` por padrão. Deve sempre começar com uma barra ou um protocolo válido, e sempre terminar com uma barra.

::: warning
Esta opção só tem efeito no modo de produção.
:::

```ts
export default {
assetsBase: 'https://cdn.example.com/assets/bar/'
}
```

## Roteamento {#routing}

### cleanUrls
Expand Down
19 changes: 19 additions & 0 deletions docs/ru/reference/site-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,25 @@ export default {
}
```

### assetsBase

- Тип: `string`
- По умолчанию: `/assets/`

Базовый URL, по которому будут размещены ресурсы сайта. Вам нужно будет установить это значение, если вы планируете размещать ресурсы вашего сайта на CDN. Это похоже на `publicPath` в других сборщиках модулей.

По умолчанию `assetsBase` настроен на `${base}assets/`. Оно всегда должно начинаться с косой черты или действительного протокола и всегда заканчиваться косой чертой.

::: warning
Этот параметр действует только в режиме производства.
:::

```ts
export default {
assetsBase: 'https://cdn.example.com/assets/bar/'
}
```

## Маршрутизация {#routing}

### cleanUrls {#cleanurls}
Expand Down
19 changes: 19 additions & 0 deletions docs/zh/reference/site-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,25 @@ export default {
}
```

### assetsBase

- 类型: `string`
- 默认值: `/assets/`

站点资源将被部署的 base URL。如果您计划将站点资源部署到 CDN,则需要设置此项。它类似于其他模块打包器中的 `publicPath`。

`assetsBase` 默认配置为 `${base}assets/`。它应该始终以斜杠或有效协议开头,并始终以斜杠结尾。

::: warning
此选项仅在生产模式下生效。
:::

```ts
export default {
assetsBase: 'https://cdn.example.com/assets/bar/'
}
```

## 路由 {#routing}

### cleanUrls
Expand Down
4 changes: 3 additions & 1 deletion src/client/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export function pathToFile(path: string) {
// /foo/bar.html -> ./foo_bar.md
if (inBrowser) {
const base = import.meta.env.BASE_URL
const assetsBase =
import.meta.env.VITE_VP_ASSETS_BASE ?? `${base}${__ASSETS_DIR__}/`
pagePath =
sanitizeFileName(
pagePath.slice(base.length).replace(/\//g, '_') || 'index'
Expand All @@ -61,7 +63,7 @@ export function pathToFile(path: string) {
pageHash = __VP_HASH_MAP__[pagePath.toLowerCase()]
}
if (!pageHash) return null
pagePath = `${base}${__ASSETS_DIR__}/${pagePath}.${pageHash}.js`
pagePath = `${assetsBase}${pagePath}.${pageHash}.js`
} else {
// ssr build uses much simpler name mapping
pagePath = `./${sanitizeFileName(
Expand Down
23 changes: 19 additions & 4 deletions src/node/build/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ import pMap from 'p-map'
import { packageDirectorySync } from 'pkg-dir'
import { rimraf } from 'rimraf'
import type { BuildOptions, Rollup } from 'vite'
import { resolveConfig, type SiteConfig } from '../config'
import { normalizeBaseUrl, resolveConfig, type SiteConfig } from '../config'
import { clearCache } from '../markdownToVue'
import { slash, type HeadConfig } from '../shared'
import { deserializeFunctions, serializeFunctions } from '../utils/fnSerialize'
import {
getDefaultAssetsBase,
isDefaultAssetsBase,
normalizeAssetUrl
} from '../utils/assetsBase'
import { task } from '../utils/task'
import { bundle } from './bundle'
import { generateSitemap } from './generateSitemap'
Expand All @@ -30,8 +35,17 @@ export async function build(
const unlinkVue = linkVue()

if (buildOptions.base) {
siteConfig.site.base = buildOptions.base
const shouldUpdateAssetsBase = isDefaultAssetsBase(
siteConfig.site.base,
siteConfig.site.assetsBase
)

siteConfig.site.base = normalizeBaseUrl(buildOptions.base)
delete buildOptions.base

if (shouldUpdateAssetsBase) {
siteConfig.site.assetsBase = getDefaultAssetsBase(siteConfig.site.base)
}
}

if (buildOptions.mpa) {
Expand All @@ -44,6 +58,7 @@ export async function build(
delete buildOptions.outDir
}

process.env.VITE_VP_ASSETS_BASE = siteConfig.site.assetsBase
try {
const { clientResult, serverResult, pageToHashMap } = await bundle(
siteConfig,
Expand Down Expand Up @@ -77,7 +92,7 @@ export async function build(
.filter(
(chunk) => chunk.type === 'asset' && !chunk.fileName.endsWith('.css')
)
.map((asset) => siteConfig.site.base + asset.fileName)
.map((asset) => normalizeAssetUrl(siteConfig.site, asset.fileName))

// default theme special handling: inject font preload
// custom themes will need to use `transformHead` to inject this
Expand Down Expand Up @@ -217,7 +232,7 @@ function generateMetadataScript(
)

const resolvedMetadataFile = path.join(config.outDir, metadataFile)
const metadataFileURL = slash(`${config.site.base}${metadataFile}`)
const metadataFileURL = slash(normalizeAssetUrl(config.site, metadataFile))

fs.ensureDirSync(path.dirname(resolvedMetadataFile))
fs.writeFileSync(resolvedMetadataFile, metadataContent)
Expand Down
19 changes: 19 additions & 0 deletions src/node/build/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { APP_PATH } from '../alias'
import type { SiteConfig } from '../config'
import { createVitePressPlugin } from '../plugin'
import { escapeRegExp, sanitizeFileName, slash } from '../shared'
import { normalizeAssetUrl } from '../utils/assetsBase'
import { task } from '../utils/task'
import { buildMPAClient } from './buildMPAClient'

Expand Down Expand Up @@ -83,6 +84,24 @@ export async function bundle(
ssr: {
noExternal: ['vitepress', '@docsearch/css']
},
experimental: {
renderBuiltUrl: (filename, type) => {
let result: string | undefined

if (type.type === 'asset') {
result = normalizeAssetUrl(config.site, filename)
}

if (config.vite?.experimental?.renderBuiltUrl) {
return (
config.vite.experimental.renderBuiltUrl(result ?? filename, type) ??
result
)
}

return result
}
},
build: {
...options,
emptyOutDir: true,
Expand Down
20 changes: 16 additions & 4 deletions src/node/build/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
type PageData,
type SSGContext
} from '../shared'
import { normalizeAssetUrl } from '../utils/assetsBase'

export async function renderPage(
render: (path: string) => Promise<SSGContext>,
Expand Down Expand Up @@ -72,7 +73,10 @@ export async function renderPage(
const title: string = createTitle(siteData, pageData)
const description: string = pageData.description || siteData.description
const stylesheetLink = cssChunk
? `<link rel="preload stylesheet" href="${siteData.base}${cssChunk.fileName}" as="style">`
? `<link rel="preload stylesheet" href="${normalizeAssetUrl(
siteData,
cssChunk.fileName
)}" as="style">`
: ''

let preloadLinks =
Expand Down Expand Up @@ -104,7 +108,9 @@ export async function renderPage(
{
rel,
// don't add base to external urls
href: (EXTERNAL_URL_RE.test(file) ? '' : siteData.base) + file
href: EXTERNAL_URL_RE.test(file)
? file
: normalizeAssetUrl(siteData, file)
}
])

Expand Down Expand Up @@ -148,7 +154,10 @@ export async function renderPage(
inlinedScript = `<script type="module">${matchingChunk.code}</script>`
fs.removeSync(path.resolve(config.outDir, matchingChunk.fileName))
} else {
inlinedScript = `<script type="module" src="${siteData.base}${matchingChunk.fileName}"></script>`
inlinedScript = `<script type="module" src="${normalizeAssetUrl(
siteData,
matchingChunk.fileName
)}"></script>`
}
}
}
Expand Down Expand Up @@ -176,7 +185,10 @@ export async function renderPage(
${metadataScript.inHead ? metadataScript.html : ''}
${
appChunk
? `<script type="module" src="${siteData.base}${appChunk.fileName}"></script>`
? `<script type="module" src="${normalizeAssetUrl(
siteData,
appChunk.fileName
)}"></script>`
: ''
}
${await renderHead(head)}
Expand Down
16 changes: 14 additions & 2 deletions src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
type SiteData
} from './shared'
import type { RawConfigExports, SiteConfig, UserConfig } from './siteConfig'
import { getDefaultAssetsBase, normalizeAssetsBase } from './utils/assetsBase'

export { resolvePages } from './plugins/dynamicRoutesPlugin'
export * from './siteConfig'
Expand Down Expand Up @@ -72,7 +73,7 @@ export async function resolveConfig(
prefix: '[vitepress]',
allowClearScreen: userConfig.vite?.clearScreen
})
const site = await resolveSiteData(root, userConfig)
const site = await resolveSiteData(root, userConfig, command, mode)
const srcDir = normalizePath(path.resolve(root, userConfig.srcDir || '.'))
const assetsDir = userConfig.assetsDir
? slash(userConfig.assetsDir).replace(/^\.?\/|\/$/g, '')
Expand Down Expand Up @@ -238,13 +239,20 @@ export async function resolveSiteData(
): Promise<SiteData> {
userConfig = userConfig || (await resolveUserConfig(root, command, mode))[0]

const base = userConfig.base ? normalizeBaseUrl(userConfig.base) : '/'
const assetsBase =
mode === 'production' && userConfig.assetsBase
? normalizeAssetsBase(userConfig.assetsBase)
: getDefaultAssetsBase(base)

return {
lang: userConfig.lang || 'en-US',
dir: userConfig.dir || 'ltr',
title: userConfig.title || 'VitePress',
titleTemplate: userConfig.titleTemplate,
description: userConfig.description || 'A VitePress site',
base: userConfig.base ? userConfig.base.replace(/([^/])$/, '$1/') : '/',
base,
assetsBase,
head: resolveSiteDataHead(userConfig),
router: {
prefetchLinks: userConfig.router?.prefetchLinks ?? true
Expand Down Expand Up @@ -300,3 +308,7 @@ function resolveSiteDataHead(userConfig?: UserConfig): HeadConfig[] {

return head
}

export function normalizeBaseUrl(baseUrl: string) {
return baseUrl.replace(/^([^/])/, '/$1').replace(/([^/])$/, '$1/')
}
Loading