Skip to content

Commit 6a127d6

Browse files
authored
fix: don't add outDirs to watch.ignored if emptyOutDir is false (#16453)
1 parent 11444dc commit 6a127d6

File tree

3 files changed

+100
-39
lines changed

3 files changed

+100
-39
lines changed

packages/vite/src/node/build.ts

+25-30
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ import { ssrManifestPlugin } from './ssr/ssrManifestPlugin'
5151
import { loadFallbackPlugin } from './plugins/loadFallback'
5252
import { findNearestPackageData } from './packages'
5353
import type { PackageCache } from './packages'
54-
import { resolveChokidarOptions } from './watch'
54+
import {
55+
getResolvedOutDirs,
56+
resolveChokidarOptions,
57+
resolveEmptyOutDir,
58+
} from './watch'
5559
import { completeSystemWrapPlugin } from './plugins/completeSystemWrap'
5660
import { mergeConfig } from './publicUtils'
5761
import { webWorkerPostPlugin } from './plugins/worker'
@@ -655,7 +659,17 @@ export async function build(
655659
normalizedOutputs.push(buildOutputOptions(outputs))
656660
}
657661

658-
const outDirs = normalizedOutputs.map(({ dir }) => resolve(dir!))
662+
const resolvedOutDirs = getResolvedOutDirs(
663+
config.root,
664+
options.outDir,
665+
options.rollupOptions?.output,
666+
)
667+
const emptyOutDir = resolveEmptyOutDir(
668+
options.emptyOutDir,
669+
config.root,
670+
resolvedOutDirs,
671+
config.logger,
672+
)
659673

660674
// watch file changes with rollup
661675
if (config.build.watch) {
@@ -664,6 +678,8 @@ export async function build(
664678
const resolvedChokidarOptions = resolveChokidarOptions(
665679
config,
666680
config.build.watch.chokidar,
681+
resolvedOutDirs,
682+
emptyOutDir,
667683
)
668684

669685
const { watch } = await import('rollup')
@@ -680,7 +696,7 @@ export async function build(
680696
if (event.code === 'BUNDLE_START') {
681697
config.logger.info(colors.cyan(`\nbuild started...`))
682698
if (options.write) {
683-
prepareOutDir(outDirs, options.emptyOutDir, config)
699+
prepareOutDir(resolvedOutDirs, emptyOutDir, config)
684700
}
685701
} else if (event.code === 'BUNDLE_END') {
686702
event.result.close()
@@ -699,7 +715,7 @@ export async function build(
699715
bundle = await rollup(rollupOptions)
700716

701717
if (options.write) {
702-
prepareOutDir(outDirs, options.emptyOutDir, config)
718+
prepareOutDir(resolvedOutDirs, emptyOutDir, config)
703719
}
704720

705721
const res: RollupOutput[] = []
@@ -726,36 +742,15 @@ export async function build(
726742
}
727743

728744
function prepareOutDir(
729-
outDirs: string[],
745+
outDirs: Set<string>,
730746
emptyOutDir: boolean | null,
731747
config: ResolvedConfig,
732748
) {
733-
const nonDuplicateDirs = new Set(outDirs)
734-
let outside = false
735-
if (emptyOutDir == null) {
736-
for (const outDir of nonDuplicateDirs) {
737-
if (
738-
fs.existsSync(outDir) &&
739-
!normalizePath(outDir).startsWith(withTrailingSlash(config.root))
740-
) {
741-
// warn if outDir is outside of root
742-
config.logger.warn(
743-
colors.yellow(
744-
`\n${colors.bold(`(!)`)} outDir ${colors.white(
745-
colors.dim(outDir),
746-
)} is not inside project root and will not be emptied.\n` +
747-
`Use --emptyOutDir to override.\n`,
748-
),
749-
)
750-
outside = true
751-
break
752-
}
753-
}
754-
}
755-
for (const outDir of nonDuplicateDirs) {
756-
if (!outside && emptyOutDir !== false && fs.existsSync(outDir)) {
749+
const outDirsArray = [...outDirs]
750+
for (const outDir of outDirs) {
751+
if (emptyOutDir !== false && fs.existsSync(outDir)) {
757752
// skip those other outDirs which are nested in current outDir
758-
const skipDirs = outDirs
753+
const skipDirs = outDirsArray
759754
.map((dir) => {
760755
const relative = path.relative(outDir, dir)
761756
if (

packages/vite/src/node/server/index.ts

+25-5
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ import type { BindCLIShortcutsOptions } from '../shortcuts'
4747
import { CLIENT_DIR, DEFAULT_DEV_PORT } from '../constants'
4848
import type { Logger } from '../logger'
4949
import { printServerUrls } from '../logger'
50-
import { createNoopWatcher, resolveChokidarOptions } from '../watch'
50+
import {
51+
createNoopWatcher,
52+
getResolvedOutDirs,
53+
resolveChokidarOptions,
54+
resolveEmptyOutDir,
55+
} from '../watch'
5156
import { initPublicFiles } from '../publicDir'
5257
import { getEnvFilesForMode } from '../env'
5358
import type { FetchResult } from '../../runtime/types'
@@ -428,10 +433,25 @@ export async function _createServer(
428433
const httpsOptions = await resolveHttpsConfig(config.server.https)
429434
const { middlewareMode } = serverConfig
430435

431-
const resolvedWatchOptions = resolveChokidarOptions(config, {
432-
disableGlobbing: true,
433-
...serverConfig.watch,
434-
})
436+
const resolvedOutDirs = getResolvedOutDirs(
437+
config.root,
438+
config.build.outDir,
439+
config.build.rollupOptions?.output,
440+
)
441+
const emptyOutDir = resolveEmptyOutDir(
442+
config.build.emptyOutDir,
443+
config.root,
444+
resolvedOutDirs,
445+
)
446+
const resolvedWatchOptions = resolveChokidarOptions(
447+
config,
448+
{
449+
disableGlobbing: true,
450+
...serverConfig.watch,
451+
},
452+
resolvedOutDirs,
453+
emptyOutDir,
454+
)
435455

436456
const middlewares = connect() as Connect.Server
437457
const httpServer = middlewareMode

packages/vite/src/node/watch.ts

+50-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,58 @@ import { EventEmitter } from 'node:events'
22
import path from 'node:path'
33
import glob from 'fast-glob'
44
import type { FSWatcher, WatchOptions } from 'dep-types/chokidar'
5-
import { arraify } from './utils'
6-
import type { ResolvedConfig } from '.'
5+
import type { OutputOptions } from 'rollup'
6+
import * as colors from 'picocolors'
7+
import { withTrailingSlash } from '../shared/utils'
8+
import { arraify, normalizePath } from './utils'
9+
import type { ResolvedConfig } from './config'
10+
import type { Logger } from './logger'
11+
12+
export function getResolvedOutDirs(
13+
root: string,
14+
outDir: string,
15+
outputOptions: OutputOptions[] | OutputOptions | undefined,
16+
): Set<string> {
17+
const resolvedOutDir = path.resolve(root, outDir)
18+
if (!outputOptions) return new Set([resolvedOutDir])
19+
20+
return new Set(
21+
arraify(outputOptions).map(({ dir }) =>
22+
dir ? path.resolve(root, dir) : resolvedOutDir,
23+
),
24+
)
25+
}
26+
27+
export function resolveEmptyOutDir(
28+
emptyOutDir: boolean | null,
29+
root: string,
30+
outDirs: Set<string>,
31+
logger?: Logger,
32+
): boolean {
33+
if (emptyOutDir != null) return emptyOutDir
34+
35+
for (const outDir of outDirs) {
36+
if (!normalizePath(outDir).startsWith(withTrailingSlash(root))) {
37+
// warn if outDir is outside of root
38+
logger?.warn(
39+
colors.yellow(
40+
`\n${colors.bold(`(!)`)} outDir ${colors.white(
41+
colors.dim(outDir),
42+
)} is not inside project root and will not be emptied.\n` +
43+
`Use --emptyOutDir to override.\n`,
44+
),
45+
)
46+
return false
47+
}
48+
}
49+
return true
50+
}
751

852
export function resolveChokidarOptions(
953
config: ResolvedConfig,
1054
options: WatchOptions | undefined,
55+
resolvedOutDirs: Set<string>,
56+
emptyOutDir: boolean,
1157
): WatchOptions {
1258
const { ignored: ignoredList, ...otherOptions } = options ?? {}
1359
const ignored: WatchOptions['ignored'] = [
@@ -17,9 +63,9 @@ export function resolveChokidarOptions(
1763
glob.escapePath(config.cacheDir) + '/**',
1864
...arraify(ignoredList || []),
1965
]
20-
if (config.build.outDir) {
66+
if (emptyOutDir) {
2167
ignored.push(
22-
glob.escapePath(path.resolve(config.root, config.build.outDir)) + '/**',
68+
...[...resolvedOutDirs].map((outDir) => glob.escapePath(outDir) + '/**'),
2369
)
2470
}
2571

0 commit comments

Comments
 (0)