Skip to content

feat: support base option in dev mode #2028

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

Merged
merged 3 commits into from
Jan 30, 2025
Merged
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
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