Skip to content

Commit 3192aeb

Browse files
authored
feat: support base option in dev mode (#2028)
1 parent e871f43 commit 3192aeb

File tree

4 files changed

+32
-16
lines changed

4 files changed

+32
-16
lines changed

docs/builtin/cli.md

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Start a local server for Slidev.
3939
Options:
4040

4141
- `--port`, `-p` (`number`, default: `3030`): port number.
42+
- `--base` (`string`, default: `/`): base URL (see https://vitejs.dev/config/shared-options.html#base).
4243
- `--open`, `-o` (`boolean`, default: `false`): open in the browser.
4344
- `--remote [password]` (`string`): listen to the public host and enable remote control, if a value is passed then the presenter mode is private and only accessible by passing the given password in the URL query `password` parameter.
4445
- `--bind` (`string`, default: `0.0.0.0`): specify which IP addresses the server should listen on in the remote mode.

packages/slidev/node/cli.ts

+23-14
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,21 @@ cli.command(
9999
alias: 'f',
100100
default: false,
101101
type: 'boolean',
102-
describe: 'force the optimizer to ignore the cache and re-bundle ',
102+
describe: 'force the optimizer to ignore the cache and re-bundle',
103103
})
104104
.option('bind', {
105105
type: 'string',
106106
default: '0.0.0.0',
107107
describe: 'specify which IP addresses the server should listen on in remote mode',
108108
})
109+
.option('base', {
110+
type: 'string',
111+
describe: 'base URL. Example: /demo/',
112+
default: '/',
113+
})
109114
.strict()
110115
.help(),
111-
async ({ entry, theme, port: userPort, open, log, remote, tunnel, force, inspect, bind }) => {
116+
async ({ entry, theme, port: userPort, open, log, remote, tunnel, force, inspect, bind, base }) => {
112117
let server: ViteDevServer | undefined
113118
let port = 3030
114119

@@ -126,7 +131,7 @@ cli.command(
126131
async function initServer() {
127132
if (server)
128133
await server.close()
129-
const options = await resolveOptions({ entry, remote, theme, inspect }, 'dev')
134+
const options = await resolveOptions({ entry, remote, theme, inspect, base }, 'dev')
130135
const host = remote !== undefined ? bind : 'localhost'
131136
port = userPort || await getPort({
132137
port: 3030,
@@ -150,6 +155,7 @@ cli.command(
150155
force,
151156
},
152157
logLevel: log as LogLevel,
158+
base,
153159
},
154160
{
155161
async loadData(loadedSource) {
@@ -201,7 +207,7 @@ cli.command(
201207
if (remote)
202208
publicIp = await import('public-ip').then(r => r.publicIpv4())
203209

204-
lastRemoteUrl = printInfo(options, port, remote, tunnelUrl, publicIp)
210+
lastRemoteUrl = printInfo(options, port, base, remote, tunnelUrl, publicIp)
205211
}
206212

207213
async function openTunnel(port: number) {
@@ -225,7 +231,7 @@ cli.command(
225231
name: 'o',
226232
fullname: 'open',
227233
action() {
228-
openBrowser(`http://localhost:${port}`)
234+
openBrowser(`http://localhost:${port}${base}`)
229235
},
230236
},
231237
{
@@ -334,7 +340,7 @@ cli.command(
334340
})
335341
.option('base', {
336342
type: 'string',
337-
describe: 'output base',
343+
describe: 'output base. Example: /demo/',
338344
})
339345
.option('download', {
340346
alias: 'd',
@@ -353,7 +359,7 @@ cli.command(
353359
const { build } = await import('./commands/build')
354360

355361
for (const entryFile of entry as unknown as string[]) {
356-
const options = await resolveOptions({ entry: entryFile, theme, inspect, download }, 'build')
362+
const options = await resolveOptions({ entry: entryFile, theme, inspect, download, base }, 'build')
357363

358364
printInfo(options)
359365
await build(
@@ -621,10 +627,13 @@ function exportOptions<T>(args: Argv<T>) {
621627
function printInfo(
622628
options: ResolvedSlidevOptions,
623629
port?: number,
630+
base?: string,
624631
remote?: string,
625632
tunnelUrl?: string,
626633
publicIp?: string,
627634
) {
635+
const baseUrl = port && `http://localhost:${bold(port + (base?.slice(0, -1) || ''))}`
636+
628637
console.log()
629638
console.log()
630639
console.log(` ${cyan('●') + blue('■') + yellow('▲')}`)
@@ -637,22 +646,22 @@ function printInfo(
637646
console.log(dim(' css engine ') + blue('unocss'))
638647
console.log(dim(' entry ') + dim(path.normalize(path.dirname(options.entry)) + path.sep) + path.basename(options.entry))
639648

640-
if (port) {
649+
if (baseUrl) {
641650
const query = remote ? `?password=${remote}` : ''
642651
const presenterPath = `${options.data.config.routerMode === 'hash' ? '/#/' : '/'}presenter/${query}`
643652
const entryPath = `${options.data.config.routerMode === 'hash' ? '/#/' : '/'}entry${query}/`
644653
const overviewPath = `${options.data.config.routerMode === 'hash' ? '/#/' : '/'}overview${query}/`
645654
console.log()
646-
console.log(`${dim(' public slide show ')} > ${cyan(`http://localhost:${bold(port)}/`)}`)
655+
console.log(`${dim(' public slide show ')} > ${cyan(`${baseUrl}/`)}`)
647656
if (query)
648-
console.log(`${dim(' private slide show ')} > ${cyan(`http://localhost:${bold(port)}/${query}`)}`)
657+
console.log(`${dim(' private slide show ')} > ${cyan(`${baseUrl}/${query}`)}`)
649658
if (options.utils.define.__SLIDEV_FEATURE_PRESENTER__)
650-
console.log(`${dim(' presenter mode ')} > ${blue(`http://localhost:${bold(port)}${presenterPath}`)}`)
651-
console.log(`${dim(' slides overview ')} > ${blue(`http://localhost:${bold(port)}${overviewPath}`)}`)
659+
console.log(`${dim(' presenter mode ')} > ${blue(`${baseUrl}${presenterPath}`)}`)
660+
console.log(`${dim(' slides overview ')} > ${blue(`${baseUrl}${overviewPath}`)}`)
652661
if (options.utils.define.__SLIDEV_FEATURE_BROWSER_EXPORTER__)
653-
console.log(`${dim(' export slides')} > ${blue(`http://localhost:${bold(port)}/export/`)}`)
662+
console.log(`${dim(' export slides')} > ${blue(`${baseUrl}/export/`)}`)
654663
if (options.inspect)
655-
console.log(`${dim(' vite inspector')} > ${yellow(`http://localhost:${bold(port)}/__inspect/`)}`)
664+
console.log(`${dim(' vite inspector')} > ${yellow(`${baseUrl}/__inspect/`)}`)
656665

657666
let lastRemoteUrl = ''
658667

packages/slidev/node/setups/indexHtml.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function toAttrValue(unsafe: unknown) {
1313
return JSON.stringify(escapeHtml(String(unsafe)))
1414
}
1515

16-
export default function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data }: Omit<ResolvedSlidevOptions, 'utils'>): string {
16+
export default function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data, base }: Omit<ResolvedSlidevOptions, 'utils'>): string {
1717
let main = readFileSync(join(clientRoot, 'index.html'), 'utf-8')
1818
let head = ''
1919
let body = ''
@@ -55,8 +55,9 @@ export default function setupIndexHtml({ mode, entry, clientRoot, userRoot, root
5555
if (data.headmatter.lang)
5656
main = main.replace('<html lang="en">', `<html lang="${data.headmatter.lang}">`)
5757

58+
const baseInDev = mode === 'dev' && base ? base.slice(0, -1) : ''
5859
main = main
59-
.replace('__ENTRY__', toAtFS(join(clientRoot, 'main.ts')))
60+
.replace('__ENTRY__', baseInDev + toAtFS(join(clientRoot, 'main.ts')))
6061
.replace('<!-- head -->', head)
6162
.replace('<!-- body -->', body)
6263

packages/types/src/options.ts

+5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ export interface SlidevEntryOptions {
3535
* Build with --download option
3636
*/
3737
download?: boolean
38+
39+
/**
40+
* Base URL in dev or build mode
41+
*/
42+
base?: string
3843
}
3944

4045
export interface ResolvedSlidevOptions extends RootsInfo, SlidevEntryOptions {

0 commit comments

Comments
 (0)