Skip to content

Commit 809d4bd

Browse files
authored
feat: support base option during dev, deprecate build.base (#1556)
1 parent 6e7f652 commit 809d4bd

25 files changed

+384
-153
lines changed

docs/config/index.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ export default ({ command, mode }) => {
9898

9999
See [Project Root](/guide/#project-root) for more details.
100100

101+
### base
102+
103+
- **Type:** `string`
104+
- **Default:** `/`
105+
106+
Base public path when served in development or production. Note the path should start and end with `/`. See [Public Base Path](/guide/build#public-base-path) for more details.
107+
101108
### mode
102109

103110
- **Type:** `string`
@@ -322,13 +329,6 @@ export default ({ command, mode }) => {
322329

323330
## Build Options
324331

325-
### build.base
326-
327-
- **Type:** `string`
328-
- **Default:** `/`
329-
330-
Base public path when served in production. Note the path should start and end with `/`. See [Public Base Path](/guide/build#public-base-path) for more details.
331-
332332
### build.target
333333

334334
- **Type:** `string`

docs/guide/build.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Legacy browsers can be supported via [@vitejs/plugin-legacy](https://github.com/
2323

2424
- Related: [Asset Handling](./features#asset-handling)
2525

26-
If you are deploying your project under a nested public path, simply specify the [`build.base` config option](/config/#build-base) and all asset paths will be rewritten accordingly. This option can also be specified as a command line flag, e.g. `vite build --base=/my/public/path/`.
26+
If you are deploying your project under a nested public path, simply specify the [`base` config option](/config/#base) and all asset paths will be rewritten accordingly. This option can also be specified as a command line flag, e.g. `vite build --base=/my/public/path/`.
2727

2828
JS-imported asset URLs, CSS `url()` references, and asset references in your `.html` files are all automatically adjusted to respect this option during build.
2929

docs/guide/env-and-mode.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Vite exposes env variables on the special **`import.meta.env`** object. Some bui
66

77
- **`import.meta.env.MODE`**: {string} the [mode](#modes) the app is running in.
88

9-
- **`import.meta.env.BASE_URL`**: {string} the base url the app is being served from. In development, this is always `/`. In production, this is determined by the [`build.base` config option](/config/#build-base).
9+
- **`import.meta.env.BASE_URL`**: {string} the base url the app is being served from. This is determined by the [`base` config option](/config/#base).
1010

1111
- **`import.meta.env.PROD`**: {boolean} whether the app is running in production.
1212

packages/playground/assets/__tests__/assets.spec.ts

+27-3
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,40 @@ import {
1010

1111
const assetMatch = isBuild
1212
? /\/foo\/assets\/asset\.\w{8}\.png/
13-
: '/nested/asset.png'
13+
: '/foo/nested/asset.png'
1414

15-
const iconMatch = isBuild ? `/foo/icon.png` : `icon.png`
15+
const iconMatch = `/foo/icon.png`
1616

1717
test('should have no 404s', () => {
1818
browserLogs.forEach((msg) => {
1919
expect(msg).not.toMatch('404')
2020
})
2121
})
2222

23+
describe('injected scripts', () => {
24+
test('@vite/client', async () => {
25+
const hasClient = await page.$(
26+
'script[type="module"][src="/foo/@vite/client"]'
27+
)
28+
if (isBuild) {
29+
expect(hasClient).toBeFalsy()
30+
} else {
31+
expect(hasClient).toBeTruthy()
32+
}
33+
})
34+
35+
test('html-proxy', async () => {
36+
const hasHtmlProxy = await page.$(
37+
'script[type="module"][src="/foo/index.html?html-proxy&index=0.js"]'
38+
)
39+
if (isBuild) {
40+
expect(hasHtmlProxy).toBeFalsy()
41+
} else {
42+
expect(hasHtmlProxy).toBeTruthy()
43+
}
44+
})
45+
})
46+
2347
describe('raw references from /public', () => {
2448
test('load raw js from /public', async () => {
2549
expect(await page.textContent('.raw-js')).toMatch('[success]')
@@ -70,7 +94,7 @@ describe('css url() references', () => {
7094
})
7195

7296
test('base64 inline', async () => {
73-
const match = isBuild ? `data:image/png;base64` : `/icon.png`
97+
const match = isBuild ? `data:image/png;base64` : `/foo/nested/icon.png`
7498
expect(await getBg('.css-url-base64-inline')).toMatch(match)
7599
expect(await getBg('.css-url-quotes-base64-inline')).toMatch(match)
76100
})

packages/playground/assets/vite.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
* @type {import('vite').UserConfig}
33
*/
44
module.exports = {
5+
base: '/foo/',
56
build: {
6-
base: '/foo/',
77
outDir: 'dist/foo'
88
}
99
}

packages/plugin-legacy/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ function viteLegacyPlugin(options = {}) {
265265
tag: 'script',
266266
attrs: {
267267
type: 'module',
268-
src: `${config.build.base}${modernPolyfillFilename}`
268+
src: `${config.base}${modernPolyfillFilename}`
269269
}
270270
})
271271
} else if (modernPolyfills.size) {
@@ -295,7 +295,7 @@ function viteLegacyPlugin(options = {}) {
295295
tag: 'script',
296296
attrs: {
297297
nomodule: true,
298-
src: `${config.build.base}${legacyPolyfillFilename}`
298+
src: `${config.base}${legacyPolyfillFilename}`
299299
},
300300
injectTo: 'body'
301301
})
@@ -318,7 +318,7 @@ function viteLegacyPlugin(options = {}) {
318318
// script content will stay consistent - which allows using a constant
319319
// hash value for CSP.
320320
id: legacyEntryId,
321-
'data-src': config.build.base + legacyEntryFilename
321+
'data-src': config.base + legacyEntryFilename
322322
},
323323
children: systemJSInlineCode,
324324
injectTo: 'body'

packages/plugin-vue/src/template.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ export function resolveTemplateCompilerOptions(
108108
// request
109109
if (filename.startsWith(options.root)) {
110110
assetUrlOptions = {
111-
base: '/' + slash(path.relative(options.root, path.dirname(filename)))
111+
base:
112+
options.devServer.config.base +
113+
slash(path.relative(options.root, path.dirname(filename)))
112114
}
113115
}
114116
} else {

packages/vite/src/client/client.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ErrorOverlay, overlayId } from './overlay'
33

44
// injected by the hmr plugin when served
55
declare const __ROOT__: string
6+
declare const __BASE__: string
67
declare const __MODE__: string
78
declare const __DEFINES__: Record<string, any>
89
declare const __HMR_PROTOCOL__: string
@@ -38,6 +39,8 @@ const socketProtocol =
3839
__HMR_PROTOCOL__ || (location.protocol === 'https:' ? 'wss' : 'ws')
3940
const socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`
4041
const socket = new WebSocket(`${socketProtocol}://${socketHost}`, 'vite-hmr')
42+
const base = __BASE__ || '/'
43+
const baseNoSlash = base.replace(/\/$/, '')
4144

4245
function warnFailedFetch(err: Error, path: string | string[]) {
4346
if (!err.message.match('fetch')) {
@@ -107,9 +110,10 @@ async function handleMessage(payload: HMRPayload) {
107110
// if html file is edited, only reload the page if the browser is
108111
// currently on that page.
109112
const pagePath = location.pathname
113+
const payloadPath = baseNoSlash + payload.path
110114
if (
111-
pagePath === payload.path ||
112-
(pagePath.endsWith('/') && pagePath + 'index.html' === payload.path)
115+
pagePath === payloadPath ||
116+
(pagePath.endsWith('/') && pagePath + 'index.html' === payloadPath)
113117
) {
114118
location.reload()
115119
}
@@ -259,6 +263,9 @@ export function removeStyle(id: string) {
259263
}
260264

261265
async function fetchUpdate({ path, acceptedPath, timestamp }: Update) {
266+
path = baseNoSlash + path
267+
acceptedPath = baseNoSlash + acceptedPath
268+
262269
const mod = hotModulesMap.get(path)
263270
if (!mod) {
264271
// In a code-splitting project,

packages/vite/src/node/build.ts

+5-9
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { ssrManifestPlugin } from './ssr/ssrManifestPlugin'
3434
export interface BuildOptions {
3535
/**
3636
* Base public path when served in production.
37-
* @default '/'
37+
* @deprecated `base` is now a root-level config option.
3838
*/
3939
base?: string
4040
/**
@@ -168,11 +168,10 @@ export interface LibraryOptions {
168168

169169
export type LibraryFormats = 'es' | 'cjs' | 'umd' | 'iife'
170170

171-
export function resolveBuildOptions(
172-
raw?: BuildOptions
173-
): Required<BuildOptions> {
174-
const resolved: Required<BuildOptions> = {
175-
base: '/',
171+
export type ResolvedBuildOptions = Required<Omit<BuildOptions, 'base'>>
172+
173+
export function resolveBuildOptions(raw?: BuildOptions): ResolvedBuildOptions {
174+
const resolved: ResolvedBuildOptions = {
176175
target: 'modules',
177176
polyfillDynamicImport: raw?.target !== 'esnext' && !raw?.lib,
178177
outDir: 'dist',
@@ -207,9 +206,6 @@ export function resolveBuildOptions(
207206
resolved.target = 'es2019'
208207
}
209208

210-
// ensure base ending slash
211-
resolved.base = resolved.base.replace(/([^/])$/, '$1/')
212-
213209
// normalize false string into actual false
214210
if ((resolved.minify as any) === 'false') {
215211
resolved.minify = false

packages/vite/src/node/cli.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface GlobalCLIOptions {
1717
config?: string
1818
c?: boolean | string
1919
root?: string
20+
base?: string
2021
r?: string
2122
mode?: string
2223
m?: string
@@ -38,6 +39,7 @@ function cleanOptions(options: GlobalCLIOptions) {
3839
delete ret.config
3940
delete ret.c
4041
delete ret.root
42+
delete ret.base
4143
delete ret.r
4244
delete ret.mode
4345
delete ret.m
@@ -50,6 +52,7 @@ function cleanOptions(options: GlobalCLIOptions) {
5052
cli
5153
.option('-c, --config <file>', `[string] use specified config file`)
5254
.option('-r, --root <path>', `[string] use specified root directory`)
55+
.option('--base <path>', `[string] public base path (default: /)`)
5356
.option('-l, --logLevel <level>', `[string] silent | error | warn | all`)
5457
.option('--clearScreen', `[boolean] allow/disable clear screen when logging`)
5558
.option('-d, --debug [feat]', `[string | boolean] show debug logs`)
@@ -77,6 +80,7 @@ cli
7780
try {
7881
const server = await createServer({
7982
root,
83+
base: options.base,
8084
mode: options.mode,
8185
configFile: options.config,
8286
logLevel: options.logLevel,
@@ -95,7 +99,6 @@ cli
9599
// build
96100
cli
97101
.command('build [root]')
98-
.option('--base <path>', `[string] public base path (default: /)`)
99102
.option('--target <target>', `[string] transpile target (default: 'modules')`)
100103
.option('--outDir <dir>', `[string] output directory (default: dist)`)
101104
.option(
@@ -141,6 +144,7 @@ cli
141144
try {
142145
await build({
143146
root,
147+
base: options.base,
144148
mode: options.mode,
145149
configFile: options.config,
146150
logLevel: options.logLevel,
@@ -169,6 +173,7 @@ cli
169173
const config = await resolveConfig(
170174
{
171175
root,
176+
base: options.base,
172177
configFile: options.config,
173178
logLevel: options.logLevel
174179
},

0 commit comments

Comments
 (0)