Skip to content

Commit

Permalink
feat: vite preview port is used automatically +1 (#4219)
Browse files Browse the repository at this point in the history
  • Loading branch information
ygj6 committed Jul 19, 2021
1 parent 02e45a0 commit 179a057
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 77 deletions.
5 changes: 4 additions & 1 deletion packages/vite/src/node/cli.ts
Expand Up @@ -190,6 +190,7 @@ cli
.option('--port <port>', `[number] specify port`)
.option('--https', `[boolean] use TLS + HTTP/2`)
.option('--open [path]', `[boolean | string] open browser on startup`)
.option('--strictPort', `[boolean] exit if specified port is already in use`)
.action(
async (
root: string,
Expand All @@ -198,6 +199,7 @@ cli
port?: number
https?: boolean
open?: boolean | string
strictPort?: boolean
} & GlobalCLIOptions
) => {
try {
Expand All @@ -208,7 +210,8 @@ cli
configFile: options.config,
logLevel: options.logLevel,
server: {
open: options.open
open: options.open,
strictPort: options.strictPort
}
},
'serve',
Expand Down
37 changes: 25 additions & 12 deletions packages/vite/src/node/preview.ts
Expand Up @@ -5,7 +5,11 @@ import connect from 'connect'
import compression from 'compression'
import { ResolvedConfig } from '.'
import { Connect } from 'types/connect'
import { resolveHttpsConfig, resolveHttpServer } from './server/http'
import {
resolveHttpsConfig,
resolveHttpServer,
httpServerStart
} from './server/http'
import { openBrowser } from './server/openBrowser'
import corsMiddleware from 'cors'
import { proxyMiddleware } from './server/middlewares/proxy'
Expand Down Expand Up @@ -53,17 +57,26 @@ export async function preview(
const logger = config.logger
const base = config.base

httpServer.listen(port, hostname.host, () => {
logger.info(
chalk.cyan(`\n vite v${require('vite/package.json').version}`) +
chalk.green(` build preview server running at:\n`)
)
const serverPort = await httpServerStart(httpServer, {
port,
strictPort: options.strictPort,
host: hostname.host,
logger
})

printServerUrls(hostname, protocol, port, base, logger.info)
logger.info(
chalk.cyan(`\n vite v${require('vite/package.json').version}`) +
chalk.green(` build preview server running at:\n`)
)

if (options.open) {
const path = typeof options.open === 'string' ? options.open : base
openBrowser(`${protocol}://${hostname.name}:${port}${path}`, true, logger)
}
})
printServerUrls(hostname, protocol, serverPort, base, logger.info)

if (options.open) {
const path = typeof options.open === 'string' ? options.open : base
openBrowser(
`${protocol}://${hostname.name}:${serverPort}${path}`,
true,
logger
)
}
}
37 changes: 37 additions & 0 deletions packages/vite/src/node/server/http.ts
Expand Up @@ -4,6 +4,7 @@ import { Server as HttpServer } from 'http'
import { ServerOptions as HttpsServerOptions } from 'https'
import { ResolvedConfig, ServerOptions } from '..'
import { Connect } from 'types/connect'
import { Logger } from '../logger'

export async function resolveHttpServer(
{ proxy }: ServerOptions,
Expand Down Expand Up @@ -159,3 +160,39 @@ async function getCertificate(config: ResolvedConfig) {
return content
}
}

export async function httpServerStart(
httpServer: HttpServer,
serverOptions: {
port: number
strictPort: boolean | undefined
host: string | undefined
logger: Logger
}
): Promise<number> {
return new Promise((resolve, reject) => {
let { port, strictPort, host, logger } = serverOptions

const onError = (e: Error & { code?: string }) => {
if (e.code === 'EADDRINUSE') {
if (strictPort) {
httpServer.removeListener('error', onError)
reject(new Error(`Port ${port} is already in use`))
} else {
logger.info(`Port ${port} is in use, trying another one...`)
httpServer.listen(++port, host)
}
} else {
httpServer.removeListener('error', onError)
reject(e)
}
}

httpServer.on('error', onError)

httpServer.listen(port, host, () => {
httpServer.removeListener('error', onError)
resolve(port)
})
})
}
110 changes: 46 additions & 64 deletions packages/vite/src/node/server/index.ts
Expand Up @@ -8,7 +8,7 @@ import corsMiddleware from 'cors'
import chalk from 'chalk'
import { AddressInfo } from 'net'
import chokidar from 'chokidar'
import { resolveHttpsConfig, resolveHttpServer } from './http'
import { resolveHttpsConfig, resolveHttpServer, httpServerStart } from './http'
import { resolveConfig, InlineConfig, ResolvedConfig } from '../config'
import {
createPluginContainer,
Expand Down Expand Up @@ -587,85 +587,67 @@ async function startServer(
}

const options = server.config.server
let port = inlinePort || options.port || 3000
const port = inlinePort || options.port || 3000
const hostname = resolveHostname(options.host)

const protocol = options.https ? 'https' : 'http'
const info = server.config.logger.info
const base = server.config.base

return new Promise((resolve, reject) => {
const onError = (e: Error & { code?: string }) => {
if (e.code === 'EADDRINUSE') {
if (options.strictPort) {
httpServer.removeListener('error', onError)
reject(new Error(`Port ${port} is already in use`))
} else {
info(`Port ${port} is in use, trying another one...`)
httpServer.listen(++port, hostname.host)
}
} else {
httpServer.removeListener('error', onError)
reject(e)
}
}
const serverPort = await httpServerStart(httpServer, {
port,
strictPort: options.strictPort,
host: hostname.host,
logger: server.config.logger
})

httpServer.on('error', onError)
info(
chalk.cyan(`\n vite v${require('vite/package.json').version}`) +
chalk.green(` dev server running at:\n`),
{
clear: !server.config.logger.hasWarned
}
)

httpServer.listen(port, hostname.host, () => {
httpServer.removeListener('error', onError)
printServerUrls(hostname, protocol, serverPort, base, info)

info(
chalk.cyan(`\n vite v${require('vite/package.json').version}`) +
chalk.green(` dev server running at:\n`),
{
clear: !server.config.logger.hasWarned
}
// @ts-ignore
if (global.__vite_start_time) {
info(
chalk.cyan(
// @ts-ignore
`\n ready in ${Date.now() - global.__vite_start_time}ms.\n`
)
)
}

printServerUrls(hostname, protocol, port, base, info)

// @ts-ignore
if (global.__vite_start_time) {
// @ts-ignore
const profileSession = global.__vite_profile_session
if (profileSession) {
profileSession.post('Profiler.stop', (err: any, { profile }: any) => {
// Write profile to disk, upload, etc.
if (!err) {
const outPath = path.resolve('./vite-profile.cpuprofile')
fs.writeFileSync(outPath, JSON.stringify(profile))
info(
chalk.cyan(
// @ts-ignore
`\n ready in ${Date.now() - global.__vite_start_time}ms.\n`
)
chalk.yellow(` CPU profile written to ${chalk.white.dim(outPath)}\n`)
)
} else {
throw err
}
})
}

// @ts-ignore
const profileSession = global.__vite_profile_session
if (profileSession) {
profileSession.post('Profiler.stop', (err: any, { profile }: any) => {
// Write profile to disk, upload, etc.
if (!err) {
const outPath = path.resolve('./vite-profile.cpuprofile')
fs.writeFileSync(outPath, JSON.stringify(profile))
info(
chalk.yellow(
` CPU profile written to ${chalk.white.dim(outPath)}\n`
)
)
} else {
throw err
}
})
}

if (options.open && !isRestart) {
const path = typeof options.open === 'string' ? options.open : base
openBrowser(
`${protocol}://${hostname.name}:${port}${path}`,
true,
server.config.logger
)
}
if (options.open && !isRestart) {
const path = typeof options.open === 'string' ? options.open : base
openBrowser(
`${protocol}://${hostname.name}:${serverPort}${path}`,
true,
server.config.logger
)
}

resolve(server)
})
})
return server
}

function createServerCloseFn(server: http.Server | null) {
Expand Down

0 comments on commit 179a057

Please sign in to comment.