Skip to content

Commit

Permalink
feat: support base option in dev mode (#2028)
Browse files Browse the repository at this point in the history
  • Loading branch information
KermanX authored Jan 30, 2025
1 parent e871f43 commit 3192aeb
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 16 deletions.
1 change: 1 addition & 0 deletions docs/builtin/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Start a local server for Slidev.
Options:

- `--port`, `-p` (`number`, default: `3030`): port number.
- `--base` (`string`, default: `/`): base URL (see https://vitejs.dev/config/shared-options.html#base).
- `--open`, `-o` (`boolean`, default: `false`): open in the browser.
- `--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.
- `--bind` (`string`, default: `0.0.0.0`): specify which IP addresses the server should listen on in the remote mode.
Expand Down
37 changes: 23 additions & 14 deletions packages/slidev/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,21 @@ cli.command(
alias: 'f',
default: false,
type: 'boolean',
describe: 'force the optimizer to ignore the cache and re-bundle ',
describe: 'force the optimizer to ignore the cache and re-bundle',
})
.option('bind', {
type: 'string',
default: '0.0.0.0',
describe: 'specify which IP addresses the server should listen on in remote mode',
})
.option('base', {
type: 'string',
describe: 'base URL. Example: /demo/',
default: '/',
})
.strict()
.help(),
async ({ entry, theme, port: userPort, open, log, remote, tunnel, force, inspect, bind }) => {
async ({ entry, theme, port: userPort, open, log, remote, tunnel, force, inspect, bind, base }) => {
let server: ViteDevServer | undefined
let port = 3030

Expand All @@ -126,7 +131,7 @@ cli.command(
async function initServer() {
if (server)
await server.close()
const options = await resolveOptions({ entry, remote, theme, inspect }, 'dev')
const options = await resolveOptions({ entry, remote, theme, inspect, base }, 'dev')
const host = remote !== undefined ? bind : 'localhost'
port = userPort || await getPort({
port: 3030,
Expand All @@ -150,6 +155,7 @@ cli.command(
force,
},
logLevel: log as LogLevel,
base,
},
{
async loadData(loadedSource) {
Expand Down Expand Up @@ -201,7 +207,7 @@ cli.command(
if (remote)
publicIp = await import('public-ip').then(r => r.publicIpv4())

lastRemoteUrl = printInfo(options, port, remote, tunnelUrl, publicIp)
lastRemoteUrl = printInfo(options, port, base, remote, tunnelUrl, publicIp)
}

async function openTunnel(port: number) {
Expand All @@ -225,7 +231,7 @@ cli.command(
name: 'o',
fullname: 'open',
action() {
openBrowser(`http://localhost:${port}`)
openBrowser(`http://localhost:${port}${base}`)
},
},
{
Expand Down Expand Up @@ -334,7 +340,7 @@ cli.command(
})
.option('base', {
type: 'string',
describe: 'output base',
describe: 'output base. Example: /demo/',
})
.option('download', {
alias: 'd',
Expand All @@ -353,7 +359,7 @@ cli.command(
const { build } = await import('./commands/build')

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

printInfo(options)
await build(
Expand Down Expand Up @@ -621,10 +627,13 @@ function exportOptions<T>(args: Argv<T>) {
function printInfo(
options: ResolvedSlidevOptions,
port?: number,
base?: string,
remote?: string,
tunnelUrl?: string,
publicIp?: string,
) {
const baseUrl = port && `http://localhost:${bold(port + (base?.slice(0, -1) || ''))}`

console.log()
console.log()
console.log(` ${cyan('●') + blue('■') + yellow('▲')}`)
Expand All @@ -637,22 +646,22 @@ function printInfo(
console.log(dim(' css engine ') + blue('unocss'))
console.log(dim(' entry ') + dim(path.normalize(path.dirname(options.entry)) + path.sep) + path.basename(options.entry))

if (port) {
if (baseUrl) {
const query = remote ? `?password=${remote}` : ''
const presenterPath = `${options.data.config.routerMode === 'hash' ? '/#/' : '/'}presenter/${query}`
const entryPath = `${options.data.config.routerMode === 'hash' ? '/#/' : '/'}entry${query}/`
const overviewPath = `${options.data.config.routerMode === 'hash' ? '/#/' : '/'}overview${query}/`
console.log()
console.log(`${dim(' public slide show ')} > ${cyan(`http://localhost:${bold(port)}/`)}`)
console.log(`${dim(' public slide show ')} > ${cyan(`${baseUrl}/`)}`)
if (query)
console.log(`${dim(' private slide show ')} > ${cyan(`http://localhost:${bold(port)}/${query}`)}`)
console.log(`${dim(' private slide show ')} > ${cyan(`${baseUrl}/${query}`)}`)
if (options.utils.define.__SLIDEV_FEATURE_PRESENTER__)
console.log(`${dim(' presenter mode ')} > ${blue(`http://localhost:${bold(port)}${presenterPath}`)}`)
console.log(`${dim(' slides overview ')} > ${blue(`http://localhost:${bold(port)}${overviewPath}`)}`)
console.log(`${dim(' presenter mode ')} > ${blue(`${baseUrl}${presenterPath}`)}`)
console.log(`${dim(' slides overview ')} > ${blue(`${baseUrl}${overviewPath}`)}`)
if (options.utils.define.__SLIDEV_FEATURE_BROWSER_EXPORTER__)
console.log(`${dim(' export slides')} > ${blue(`http://localhost:${bold(port)}/export/`)}`)
console.log(`${dim(' export slides')} > ${blue(`${baseUrl}/export/`)}`)
if (options.inspect)
console.log(`${dim(' vite inspector')} > ${yellow(`http://localhost:${bold(port)}/__inspect/`)}`)
console.log(`${dim(' vite inspector')} > ${yellow(`${baseUrl}/__inspect/`)}`)

let lastRemoteUrl = ''

Expand Down
5 changes: 3 additions & 2 deletions packages/slidev/node/setups/indexHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function toAttrValue(unsafe: unknown) {
return JSON.stringify(escapeHtml(String(unsafe)))
}

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

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

Expand Down
5 changes: 5 additions & 0 deletions packages/types/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export interface SlidevEntryOptions {
* Build with --download option
*/
download?: boolean

/**
* Base URL in dev or build mode
*/
base?: string
}

export interface ResolvedSlidevOptions extends RootsInfo, SlidevEntryOptions {
Expand Down

0 comments on commit 3192aeb

Please sign in to comment.