Skip to content
Closed
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
150 changes: 150 additions & 0 deletions packages/vite/src/node/__tests__/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,32 @@ describe('mergeConfig', () => {
expect(mergedConfig.server.hmr).toBeTruthy()
})

test('does not warn about ignored `server.hmr.server` during preview', async () => {
const warnFn = vi.fn()
const originalWarn = console.warn
console.warn = warnFn
try {
await resolveConfig(
{
configFile: false,
server: {
ws: false,
hmr: {
server: http.createServer(),
},
},
} as InlineConfig,
'serve',
'production',
'production',
true,
)
} finally {
console.warn = originalWarn
}
expect(warnFn).not.toHaveBeenCalled()
})

test('resolveConfig properly syncs hmr and ws', async () => {
const config = await resolveConfig(
{
Expand All @@ -1122,6 +1148,130 @@ describe('mergeConfig', () => {
expect(config.server.hmr.host).toBe('new-host.com')
})

test('warns when `server.hmr.server` is ignored due to `server.ws: false`', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
const trace = vi.spyOn(console, 'trace').mockImplementation(() => {})

try {
// a fresh module instance has a fresh "already warned" state
vi.resetModules()
const { resolveConfig } = await import('../config')
await resolveConfig(
{
configFile: false,
customLogger: createLogger('silent'),
server: {
ws: false,
hmr: {
server: http.createServer(),
},
},
},
'serve',
)

const reported = [...warn.mock.calls, ...trace.mock.calls]
.map((args) => String(args[0]))
.filter((message) => message.includes('`server.hmr.server` is ignored'))

expect(reported).toHaveLength(1)
} finally {
warn.mockRestore()
trace.mockRestore()
}
})

test('does not warn for `server.hmr` options that `server.ws: false` implies are ignored', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})

try {
vi.resetModules()
const { resolveConfig } = await import('../config')
await resolveConfig(
{
configFile: false,
customLogger: createLogger('silent'),
server: {
ws: false,
hmr: {
host: 'test-host.com',
port: 4000,
},
},
},
'serve',
)

const reported = warn.mock.calls
.map((args) => String(args[0]))
.filter((message) => message.includes('is ignored'))

expect(reported).toHaveLength(0)
} finally {
warn.mockRestore()
}
})

test('does not warn when `server.hmr` has no HMR server', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})

try {
vi.resetModules()
const { resolveConfig } = await import('../config')
await resolveConfig(
{
configFile: false,
customLogger: createLogger('silent'),
server: {
ws: false,
hmr: {
overlay: false,
},
},
},
'serve',
)

const reported = warn.mock.calls
.map((args) => String(args[0]))
.filter((message) => message.includes('is ignored'))

expect(reported).toHaveLength(0)
} finally {
warn.mockRestore()
}
})

test('does not warn for `server.hmr.server` on build', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})

try {
vi.resetModules()
const { resolveConfig } = await import('../config')
await resolveConfig(
{
configFile: false,
customLogger: createLogger('silent'),
server: {
ws: false,
hmr: {
server: http.createServer(),
},
},
},
'build',
)

const reported = warn.mock.calls
.map((args) => String(args[0]))
.filter((message) => message.includes('is ignored'))

expect(reported).toHaveLength(0)
} finally {
warn.mockRestore()
}
})

describe('later plugin can read `rollupOptions` set via `rolldownOptions` in earlier plugin', () => {
test('top-level config', async () => {
expect.assertions(2)
Expand Down
8 changes: 7 additions & 1 deletion packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1920,7 +1920,13 @@ export async function resolveConfig(
: ''

const input = normalizeInput(config.input)
const server = await resolveServerOptions(resolvedRoot, config.server, logger)
const server = await resolveServerOptions(
resolvedRoot,
config.server,
logger,
command,
isPreview,
)

const builder = resolveBuilderOptions(config.builder)

Expand Down
4 changes: 3 additions & 1 deletion packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,8 @@ export async function resolveServerOptions(
root: string,
raw: ServerOptions | undefined,
logger: Logger,
command: 'build' | 'serve' = 'serve',
isPreview = false,
): Promise<ResolvedServerOptions> {
const _server = mergeWithDefaults(
{
Expand All @@ -1268,7 +1270,7 @@ export async function resolveServerOptions(
raw ?? {},
)

setupHmrWsOptionCompat(_server)
setupHmrWsOptionCompat(_server, command, isPreview)

const workspaceRoot = searchForWorkspaceRoot(root)
const server: ResolvedServerOptions = {
Expand Down
38 changes: 37 additions & 1 deletion packages/vite/src/node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1424,10 +1424,46 @@ const hmrWsOptionsDeprecationCall = /* @__PURE__ */ (() => {
}
})()

const hmrServerIgnoredCall = /* @__PURE__ */ (() => {
let logged = false
return () => {
if (logged) return
logged = true
const method = process.env.VITE_DEPRECATION_TRACE ? 'trace' : 'warn'
// eslint-disable-next-line no-console
console[method](
'`server.ws` is `false`, so `server.hmr.server` is ignored. ' +
'`server.hmr.server` is the transport used for HMR, which `server.ws: false` turns off. ' +
'Remove `server.ws: false`, or use `server.ws.server` instead.',
)
}
})()

export function setupHmrWsOptionCompat(
serverConfig: Pick<ServerOptions, 'hmr' | 'ws'>,
command?: 'build' | 'serve',
isPreview = false,
): void {
if (serverConfig.hmr === false || serverConfig.ws === false) {
if (serverConfig.hmr === false) {
return
}
if (serverConfig.ws === false) {
// `server.ws: false` turns the WebSocket server off entirely, so the deprecated
// `server.hmr.*` options that are otherwise synced onto `server.ws` cannot be
// honoured here. Only `server.hmr.server` is worth reporting: it is the HMR
// transport, not a socket parameter, so losing it is not implied by `server.ws: false`
// the way losing e.g. `server.hmr.port` is.
// The warning is only meaningful when a dev server is started, so it is skipped for
// `vite build`, for `vite preview` (which serves static files and has no HMR), and
// for bare `mergeConfig` (which passes no `command`).
if (
command === 'serve' &&
!isPreview &&
isObject(serverConfig.hmr) &&
serverConfig.hmr.server !== undefined
) {
hmrServerIgnoredCall()
}
return
}
if (serverConfig.hmr === true) {
Expand Down
Loading