Skip to content

Commit

Permalink
feat: support disabling server.watch completely
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Aug 27, 2023
1 parent 91a18c2 commit 16162d6
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 24 deletions.
4 changes: 2 additions & 2 deletions docs/config/server-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ The error that appears in the Browser when the fallback happens can be ignored.

## server.watch

- **Type:** `object`
- **Type:** `object | false`

File system watcher options to pass on to [chokidar](https://github.com/paulmillr/chokidar#api).
File system watcher options to pass on to [chokidar](https://github.com/paulmillr/chokidar#api). Pass `false` to disable watcher completely.

The Vite server watcher skips `.git/` and `node_modules/` directories by default. If you want to watch a package inside `node_modules/`, you can pass a negated glob pattern to `server.watch.ignored`. That is:

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/api-javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ interface ViteDevServer {
* Chokidar watcher instance.
* https://github.com/paulmillr/chokidar#api
*/
watcher: FSWatcher
watcher?: FSWatcher
/**
* Web socket server with `send(payload)` method.
*/
Expand Down
6 changes: 3 additions & 3 deletions packages/vite/src/node/plugins/esbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export function esbuildPlugin(config: ResolvedConfig): Plugin {
configureServer(_server) {
server = _server
server.watcher
.on('add', reloadOnTsconfigChange)
?.on('add', reloadOnTsconfigChange)
.on('change', reloadOnTsconfigChange)
.on('unlink', reloadOnTsconfigChange)
},
Expand Down Expand Up @@ -515,14 +515,14 @@ async function loadTsconfigJsonForFile(
try {
const result = await parse(filename, await tsconfckParseOptions)
// tsconfig could be out of root, make sure it is watched on dev
if (server && result.tsconfigFile !== 'no_tsconfig_file_found') {
if (server?.watcher && result.tsconfigFile !== 'no_tsconfig_file_found') {
ensureWatchedFile(server.watcher, result.tsconfigFile, server.config.root)
}
return result.tsconfig
} catch (e) {
if (e instanceof TSConfckParseError) {
// tsconfig could be out of root, make sure it is watched on dev
if (server && e.tsconfigFile) {
if (server?.watcher && e.tsconfigFile) {
ensureWatchedFile(server.watcher, e.tsconfigFile, server.config.root)
}
}
Expand Down
34 changes: 18 additions & 16 deletions packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export interface ServerOptions extends CommonServerOptions {
* chokidar watch options
* https://github.com/paulmillr/chokidar#api
*/
watch?: WatchOptions
watch?: WatchOptions | false
/**
* Create Vite dev server to be used as a middleware in an existing server
* @default false
Expand Down Expand Up @@ -194,8 +194,9 @@ export interface ViteDevServer {
/**
* chokidar watcher instance
* https://github.com/paulmillr/chokidar#api
* // TODO: Setting this as optional breaks typings without major bump. Maybe pass no-op instead?
*/
watcher: FSWatcher
watcher?: FSWatcher
/**
* web socket server with `send(payload)` method
*/
Expand Down Expand Up @@ -344,11 +345,6 @@ export async function _createServer(
const httpsOptions = await resolveHttpsConfig(config.server.https)
const { middlewareMode } = serverConfig

const resolvedWatchOptions = resolveChokidarOptions(config, {
disableGlobbing: true,
...serverConfig.watch,
})

const middlewares = connect() as Connect.Server
const httpServer = middlewareMode
? null
Expand All @@ -359,11 +355,17 @@ export async function _createServer(
setClientErrorHandler(httpServer, config.logger)
}

const watcher = chokidar.watch(
// config file dependencies and env file might be outside of root
[root, ...config.configFileDependencies, config.envDir],
resolvedWatchOptions,
) as FSWatcher
const watcher =
config.server.watch === false
? undefined
: chokidar.watch(
// config file dependencies and env file might be outside of root
[root, ...config.configFileDependencies, config.envDir],
resolveChokidarOptions(config, {
disableGlobbing: true,
...serverConfig.watch,
}),
)

const moduleGraph: ModuleGraph = new ModuleGraph((url, ssr) =>
container.resolveId(url, undefined, { ssr }),
Expand Down Expand Up @@ -456,7 +458,7 @@ export async function _createServer(
}
}
await Promise.allSettled([
watcher.close(),
watcher?.close(),
ws.close(),
container.close(),
getDepsOptimizer(server.config)?.close(),
Expand Down Expand Up @@ -548,16 +550,16 @@ export async function _createServer(
await onHMRUpdate(file, true)
}

watcher.on('change', async (file) => {
watcher?.on('change', async (file) => {
file = normalizePath(file)
// invalidate module graph cache on file change
moduleGraph.onFileChange(file)

await onHMRUpdate(file, false)
})

watcher.on('add', onFileAddUnlink)
watcher.on('unlink', onFileAddUnlink)
watcher?.on('add', onFileAddUnlink)
watcher?.on('unlink', onFileAddUnlink)

ws.on('vite:invalidate', async ({ path, message }: InvalidatePayload) => {
const mod = moduleGraph.urlToModuleMap.get(path)
Expand Down
5 changes: 4 additions & 1 deletion packages/vite/src/node/server/middlewares/indexHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,10 @@ const devHtmlHook: IndexHtmlTransformHook = async (

// ensure module in graph after successful load
const mod = await moduleGraph.ensureEntryFromUrl(url, false)
ensureWatchedFile(watcher, mod.file, config.root)

if (watcher) {
ensureWatchedFile(watcher, mod.file, config.root)
}

const result = await server!.pluginContainer.transform(code, mod.id!)
let content = ''
Expand Down
5 changes: 4 additions & 1 deletion packages/vite/src/node/server/transformRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,10 @@ async function loadAndTransform(

// ensure module in graph after successful load
mod ??= await moduleGraph._ensureEntryFromUrl(url, ssr, undefined, resolved)
ensureWatchedFile(watcher, mod.file, root)

if (watcher) {
ensureWatchedFile(watcher, mod.file, root)
}

// transform
const transformStart = debugTransform ? performance.now() : 0
Expand Down
4 changes: 4 additions & 0 deletions packages/vite/src/node/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export function resolveChokidarOptions(
config: ResolvedConfig,
options: WatchOptions | undefined,
): WatchOptions {
if (config.server.watch === false) {
return {}
}

const { ignored = [], ...otherOptions } = options ?? {}

const resolvedWatchOptions: WatchOptions = {
Expand Down
2 changes: 2 additions & 0 deletions playground/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export const ports = {
'css/postcss-plugins-different-dir': 5006,
'css/dynamic-import': 5007,
'css/lightningcss-proxy': 5008,
'watch/enabled': 5009,
'watch/disabled': 5010,
}
export const hmrPorts = {
'optimize-missing-deps': 24680,
Expand Down
90 changes: 90 additions & 0 deletions playground/watch/__tests__/watch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import path from 'node:path'
import fs from 'node:fs'
import { createServer } from 'vite'
import { afterEach, expect, test } from 'vitest'
import { isBuild, page, ports } from '~utils'

const filename = path.resolve(__dirname, '../src/header-text.mjs')
const cleanups: (() => void | Promise<void>)[] = []

afterEach(async () => {
await Promise.all(cleanups.splice(0).map((cleanup) => cleanup()))
})

test.runIf(isBuild)('watch mode is enabled by default', async () => {
const server = await createServer({
root: path.join(__dirname, '..'),
logLevel: 'silent',
server: {
port: ports['watch/enabled'],
strictPort: true,
},
})

expect(server.watcher).toBeTruthy()

cleanups.push(() => server.close())
await server.listen()

const initialHeader = 'Initial header'
const newHeader = 'New header'

await page.goto(`http://localhost:${server.config.server.port}`)
expect(await page.textContent('h1')).toBe(initialHeader)

// Edit file and wait for content to appear on page
cleanups.push(() =>
fs.writeFileSync(
filename,
`export const headerText = '${initialHeader}'\n`,
'utf8',
),
)
fs.writeFileSync(
filename,
`export const headerText = '${newHeader}'\n`,
'utf8',
)

await page.waitForSelector('text=New header')
})

test.runIf(isBuild)('watch mode can be disabled', async () => {
const server = await createServer({
root: path.join(__dirname, '..'),
logLevel: 'silent',
server: {
watch: false,
port: ports['watch/disabled'],
strictPort: true,
},
})

expect(server.watcher).toBe(undefined)

cleanups.push(() => server.close())
await server.listen()

const initialHeader = 'Initial header'
const newHeader = 'New header'

await page.goto(`http://localhost:${server.config.server.port}`)
expect(await page.textContent('h1')).toBe(initialHeader)

cleanups.push(() =>
fs.writeFileSync(
filename,
`export const headerText = '${initialHeader}'\n`,
'utf8',
),
)
fs.writeFileSync(
filename,
`export const headerText = '${newHeader}'\n`,
'utf8',
)

// Initial header should still be visible after some time
await new Promise((r) => setTimeout(r, 1000))
expect(await page.textContent('h1')).toBe(initialHeader)
})
12 changes: 12 additions & 0 deletions playground/watch/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Watch</title>
<script src="./src/index.mjs" type="module"></script>
</head>
<body>
<h1>Watch</h1>
</body>
</html>
9 changes: 9 additions & 0 deletions playground/watch/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@vitejs/test-watch",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {},
"dependencies": {},
"devDependencies": {}
}
1 change: 1 addition & 0 deletions playground/watch/src/header-text.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const headerText = 'Initial header'
3 changes: 3 additions & 0 deletions playground/watch/src/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { headerText } from './header-text.mjs'

document.querySelector('h1').textContent = headerText

0 comments on commit 16162d6

Please sign in to comment.