diff --git a/docs/config/server-options.md b/docs/config/server-options.md index 32fbdb2a6..780e45bf8 100644 --- a/docs/config/server-options.md +++ b/docs/config/server-options.md @@ -329,14 +329,6 @@ export default defineConfig({ Blocklist for sensitive files being restricted to be served by Vite dev server. This will have higher priority than [`server.fs.allow`](#server-fs-allow). [picomatch patterns](https://github.com/micromatch/picomatch#globbing-features) are supported. -## server.fs.cachedChecks - -- **Type:** `boolean` -- **Default:** `false` -- **Experimental** - -Caches filenames of accessed directories to avoid repeated filesystem operations. Particularly in Windows, this could result in a performance boost. It is disabled by default due to edge cases when writing a file in a cached folder and immediately importing it. - ## server.origin - **Type:** `string` diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 1c08978b7..3886d6875 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -66,6 +66,8 @@ There are other breaking changes which only affect few users. - [`commonjsOptions.strictRequires`](https://github.com/rollup/plugins/blob/master/packages/commonjs/README.md#strictrequires) is now `true` by default (was `'auto'` before). - [[#18243] chore(deps)!: migrate `fast-glob` to `tinyglobby`](https://github.com/vitejs/vite/pull/18243) - Range braces (`{01..03}` ⇒ `['01', '02', '03']`) and incremental braces (`{2..8..2}` ⇒ `['2', '4', '6', '8']`) are no longer supported in globs. +- [[#18493] refactor!: remove fs.cachedChecks option](https://github.com/vitejs/vite/pull/18493) + - This opt-in optimization was removed due to edge cases when writing a file in a cached folder and immediately importing it. ## Migration from v4 diff --git a/packages/vite/src/node/fsUtils.ts b/packages/vite/src/node/fsUtils.ts deleted file mode 100644 index a295d4fc4..000000000 --- a/packages/vite/src/node/fsUtils.ts +++ /dev/null @@ -1,433 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' -import type { FSWatcher } from 'dep-types/chokidar' -import type { ResolvedConfig } from './config' -import { - isInNodeModules, - normalizePath, - safeRealpathSync, - tryStatSync, -} from './utils' - -export interface FsUtils { - existsSync: (path: string) => boolean - isDirectory: (path: string) => boolean - - tryResolveRealFile: ( - path: string, - preserveSymlinks?: boolean, - ) => string | undefined - tryResolveRealFileWithExtensions: ( - path: string, - extensions: string[], - preserveSymlinks?: boolean, - ) => string | undefined - tryResolveRealFileOrType: ( - path: string, - preserveSymlinks?: boolean, - ) => { path?: string; type: 'directory' | 'file' } | undefined - - initWatcher?: (watcher: FSWatcher) => void -} - -// An implementation of fsUtils without caching -export const commonFsUtils: FsUtils = { - existsSync: fs.existsSync, - isDirectory, - - tryResolveRealFile, - tryResolveRealFileWithExtensions, - tryResolveRealFileOrType, -} - -const cachedFsUtilsMap = new WeakMap() -export function getFsUtils(config: ResolvedConfig): FsUtils { - let fsUtils = cachedFsUtilsMap.get(config) - if (!fsUtils) { - if ( - config.command !== 'serve' || - config.server.fs.cachedChecks !== true || - config.server.watch?.ignored || - process.versions.pnp - ) { - // cached fsUtils is only used in the dev server for now - // it is disabled by default due to potential edge cases when writing a file - // and reading it immediately - // It is also disabled when there aren't custom watcher ignored patterns configured - // and if yarn pnp isn't used - fsUtils = commonFsUtils - } else if ( - !config.resolve.preserveSymlinks && - config.root !== getRealPath(config.root) - ) { - fsUtils = commonFsUtils - } else { - fsUtils = createCachedFsUtils(config) - } - cachedFsUtilsMap.set(config, fsUtils) - } - return fsUtils -} - -type DirentsMap = Map - -type DirentCacheType = - | 'directory' - | 'file' - | 'symlink' - | 'error' - | 'directory_maybe_symlink' - | 'file_maybe_symlink' - -interface DirentCache { - dirents?: DirentsMap - type: DirentCacheType -} - -function readDirCacheSync(file: string): undefined | DirentsMap { - let dirents: fs.Dirent[] - try { - dirents = fs.readdirSync(file, { withFileTypes: true }) - } catch { - return - } - return direntsToDirentMap(dirents) -} - -function direntsToDirentMap(fsDirents: fs.Dirent[]): DirentsMap { - const dirents: DirentsMap = new Map() - for (const dirent of fsDirents) { - // We ignore non directory, file, and symlink entries - const type = dirent.isDirectory() - ? 'directory' - : dirent.isSymbolicLink() - ? 'symlink' - : dirent.isFile() - ? 'file' - : undefined - if (type) { - dirents.set(dirent.name, { type }) - } - } - return dirents -} - -function ensureFileMaybeSymlinkIsResolved( - direntCache: DirentCache, - filePath: string, -) { - if (direntCache.type !== 'file_maybe_symlink') return - - const isSymlink = fs - .lstatSync(filePath, { throwIfNoEntry: false }) - ?.isSymbolicLink() - direntCache.type = - isSymlink === undefined ? 'error' : isSymlink ? 'symlink' : 'file' -} - -function pathUntilPart(root: string, parts: string[], i: number): string { - let p = root - for (let k = 0; k < i; k++) p += '/' + parts[k] - return p -} - -export function createCachedFsUtils(config: ResolvedConfig): FsUtils { - const root = config.root // root is resolved and normalized, so it doesn't have a trailing slash - const rootDirPath = `${root}/` - const rootCache: DirentCache = { type: 'directory' } // dirents will be computed lazily - - const getDirentCacheSync = (parts: string[]): DirentCache | undefined => { - let direntCache: DirentCache = rootCache - for (let i = 0; i < parts.length; i++) { - if (direntCache.type === 'directory') { - let dirPath - if (!direntCache.dirents) { - dirPath = pathUntilPart(root, parts, i) - const dirents = readDirCacheSync(dirPath) - if (!dirents) { - direntCache.type = 'error' - return - } - direntCache.dirents = dirents - } - const nextDirentCache = direntCache.dirents!.get(parts[i]) - if (!nextDirentCache) { - return - } - if (nextDirentCache.type === 'directory_maybe_symlink') { - dirPath ??= pathUntilPart(root, parts, i + 1) - const isSymlink = fs - .lstatSync(dirPath, { throwIfNoEntry: false }) - ?.isSymbolicLink() - nextDirentCache.type = isSymlink ? 'symlink' : 'directory' - } - direntCache = nextDirentCache - } else if (direntCache.type === 'symlink') { - // early return if we encounter a symlink - return direntCache - } else if (direntCache.type === 'error') { - return direntCache - } else { - if (i !== parts.length - 1) { - return - } - if (direntCache.type === 'file_maybe_symlink') { - ensureFileMaybeSymlinkIsResolved( - direntCache, - pathUntilPart(root, parts, i), - ) - return direntCache - } else if (direntCache.type === 'file') { - return direntCache - } else { - return - } - } - } - return direntCache - } - - function getDirentCacheFromPath( - normalizedFile: string, - ): DirentCache | false | undefined { - // path.posix.normalize may return a path either with / or without / - if (normalizedFile[normalizedFile.length - 1] === '/') { - normalizedFile = normalizedFile.slice(0, -1) - } - if (normalizedFile === root) { - return rootCache - } - if (!normalizedFile.startsWith(rootDirPath)) { - return undefined - } - const pathFromRoot = normalizedFile.slice(rootDirPath.length) - const parts = pathFromRoot.split('/') - const direntCache = getDirentCacheSync(parts) - if (!direntCache || direntCache.type === 'error') { - return false - } - return direntCache - } - - function onPathAdd( - file: string, - type: 'directory_maybe_symlink' | 'file_maybe_symlink', - ) { - const direntCache = getDirentCacheFromPath( - normalizePath(path.dirname(file)), - ) - if ( - direntCache && - direntCache.type === 'directory' && - direntCache.dirents - ) { - direntCache.dirents.set(path.basename(file), { type }) - } - } - - function onPathUnlink(file: string) { - const direntCache = getDirentCacheFromPath( - normalizePath(path.dirname(file)), - ) - if ( - direntCache && - direntCache.type === 'directory' && - direntCache.dirents - ) { - direntCache.dirents.delete(path.basename(file)) - } - } - - return { - existsSync(file: string) { - if (isInNodeModules(file)) { - return fs.existsSync(file) - } - const normalizedFile = normalizePath(file) - const direntCache = getDirentCacheFromPath(normalizedFile) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return fs.existsSync(file) - } - return !!direntCache - }, - tryResolveRealFile( - file: string, - preserveSymlinks?: boolean, - ): string | undefined { - if (isInNodeModules(file)) { - return tryResolveRealFile(file, preserveSymlinks) - } - const normalizedFile = normalizePath(file) - const direntCache = getDirentCacheFromPath(normalizedFile) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return tryResolveRealFile(file, preserveSymlinks) - } - if (!direntCache || direntCache.type === 'directory') { - return - } - // We can avoid getRealPath even if preserveSymlinks is false because we know it's - // a file without symlinks in its path - return normalizedFile - }, - tryResolveRealFileWithExtensions( - file: string, - extensions: string[], - preserveSymlinks?: boolean, - ): string | undefined { - if (isInNodeModules(file)) { - return tryResolveRealFileWithExtensions( - file, - extensions, - preserveSymlinks, - ) - } - const normalizedFile = normalizePath(file) - const dirPath = path.posix.dirname(normalizedFile) - const direntCache = getDirentCacheFromPath(dirPath) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return tryResolveRealFileWithExtensions( - file, - extensions, - preserveSymlinks, - ) - } - if (!direntCache || direntCache.type !== 'directory') { - return - } - - if (!direntCache.dirents) { - const dirents = readDirCacheSync(dirPath) - if (!dirents) { - direntCache.type = 'error' - return - } - direntCache.dirents = dirents - } - - const base = path.posix.basename(normalizedFile) - for (const ext of extensions) { - const fileName = base + ext - const fileDirentCache = direntCache.dirents.get(fileName) - if (fileDirentCache) { - const filePath = dirPath + '/' + fileName - ensureFileMaybeSymlinkIsResolved(fileDirentCache, filePath) - if (fileDirentCache.type === 'symlink') { - // fallback to built-in fs for symlinked files - return tryResolveRealFile(filePath, preserveSymlinks) - } - if (fileDirentCache.type === 'file') { - return filePath - } - } - } - }, - tryResolveRealFileOrType( - file: string, - preserveSymlinks?: boolean, - ): { path?: string; type: 'directory' | 'file' } | undefined { - if (isInNodeModules(file)) { - return tryResolveRealFileOrType(file, preserveSymlinks) - } - const normalizedFile = normalizePath(file) - const direntCache = getDirentCacheFromPath(normalizedFile) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return tryResolveRealFileOrType(file, preserveSymlinks) - } - if (!direntCache) { - return - } - if (direntCache.type === 'directory') { - return { type: 'directory' } - } - // We can avoid getRealPath even if preserveSymlinks is false because we know it's - // a file without symlinks in its path - return { path: normalizedFile, type: 'file' } - }, - isDirectory(dirPath: string) { - if (isInNodeModules(dirPath)) { - return isDirectory(dirPath) - } - const direntCache = getDirentCacheFromPath(normalizePath(dirPath)) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return isDirectory(dirPath) - } - return direntCache && direntCache.type === 'directory' - }, - - initWatcher(watcher: FSWatcher) { - watcher.on('add', (file) => { - onPathAdd(file, 'file_maybe_symlink') - }) - watcher.on('addDir', (dir) => { - onPathAdd(dir, 'directory_maybe_symlink') - }) - watcher.on('unlink', onPathUnlink) - watcher.on('unlinkDir', onPathUnlink) - }, - } -} - -function tryResolveRealFile( - file: string, - preserveSymlinks?: boolean, -): string | undefined { - const stat = tryStatSync(file) - if (stat?.isFile()) return getRealPath(file, preserveSymlinks) -} - -function tryResolveRealFileWithExtensions( - filePath: string, - extensions: string[], - preserveSymlinks?: boolean, -): string | undefined { - for (const ext of extensions) { - const res = tryResolveRealFile(filePath + ext, preserveSymlinks) - if (res) return res - } -} - -function tryResolveRealFileOrType( - file: string, - preserveSymlinks?: boolean, -): { path?: string; type: 'directory' | 'file' } | undefined { - const fileStat = tryStatSync(file) - if (fileStat?.isFile()) { - return { path: getRealPath(file, preserveSymlinks), type: 'file' } - } - if (fileStat?.isDirectory()) { - return { type: 'directory' } - } - return -} - -function getRealPath(resolved: string, preserveSymlinks?: boolean): string { - if (!preserveSymlinks) { - resolved = safeRealpathSync(resolved) - } - return normalizePath(resolved) -} - -function isDirectory(path: string): boolean { - const stat = tryStatSync(path) - return stat?.isDirectory() ?? false -} diff --git a/packages/vite/src/node/idResolver.ts b/packages/vite/src/node/idResolver.ts index 5923e985a..24b93bca9 100644 --- a/packages/vite/src/node/idResolver.ts +++ b/packages/vite/src/node/idResolver.ts @@ -5,7 +5,6 @@ import type { EnvironmentPluginContainer } from './server/pluginContainer' import { createEnvironmentPluginContainer } from './server/pluginContainer' import { resolvePlugin } from './plugins/resolve' import type { InternalResolveOptions } from './plugins/resolve' -import { getFsUtils } from './fsUtils' import type { Environment } from './environment' import type { PartialEnvironment } from './baseEnvironment' @@ -69,7 +68,6 @@ export function createIdResolver( preferRelative: false, tryIndex: true, ...options, - fsUtils: getFsUtils(config), // Ignore sideEffects and other computations as we only need the id idOnly: true, }), diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 8281c1bcb..f13d920fb 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import fs from 'node:fs' import { performance } from 'node:perf_hooks' import colors from 'picocolors' import MagicString from 'magic-string' @@ -52,7 +53,6 @@ import { transformStableResult, urlRE, } from '../utils' -import { getFsUtils } from '../fsUtils' import { checkPublicFile } from '../publicDir' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' @@ -107,7 +107,6 @@ export function normalizeResolvedIdToUrl( ): string { const root = environment.config.root const depsOptimizer = environment.depsOptimizer - const fsUtils = getFsUtils(environment.getTopLevelConfig()) // normalize all imports into resolved URLs // e.g. `import 'foo'` -> `import '/@fs/.../node_modules/foo/index.js'` @@ -121,7 +120,7 @@ export function normalizeResolvedIdToUrl( // We'll remove this as soon we're able to fix the react plugins. (resolved.id !== '/@react-refresh' && path.isAbsolute(resolved.id) && - fsUtils.existsSync(cleanUrl(resolved.id))) + fs.existsSync(cleanUrl(resolved.id))) ) { // an optimized deps may not yet exists in the filesystem, or // a regular file exists but is out of root: rewrite to absolute /@fs/ paths diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 1ce247db4..3d0933d8e 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -4,7 +4,6 @@ import type { PluginHookUtils, ResolvedConfig } from '../config' import { isDepOptimizationDisabled } from '../optimizer' import type { HookHandler, Plugin, PluginWithRequiredHook } from '../plugin' import { watchPackageDataPlugin } from '../packages' -import { getFsUtils } from '../fsUtils' import { jsonPlugin } from './json' import { resolvePlugin } from './resolve' import { optimizedDepsPlugin } from './optimizedDeps' @@ -65,7 +64,6 @@ export async function resolvePlugins( isBuild, packageCache: config.packageCache, asSrc: true, - fsUtils: getFsUtils(config), optimizeDeps: true, externalize: true, }, diff --git a/packages/vite/src/node/plugins/preAlias.ts b/packages/vite/src/node/plugins/preAlias.ts index aa83bfe49..7422544bb 100644 --- a/packages/vite/src/node/plugins/preAlias.ts +++ b/packages/vite/src/node/plugins/preAlias.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import fs from 'node:fs' import type { Alias, AliasOptions, @@ -13,7 +14,6 @@ import { isOptimizable, moduleListContains, } from '../utils' -import { getFsUtils } from '../fsUtils' import { cleanUrl, withTrailingSlash } from '../../shared/utils' import { tryOptimizedResolve } from './resolve' @@ -23,7 +23,6 @@ import { tryOptimizedResolve } from './resolve' export function preAliasPlugin(config: ResolvedConfig): Plugin { const findPatterns = getAliasPatterns(config.resolve.alias) const isBuild = config.command === 'build' - const fsUtils = getFsUtils(config) return { name: 'vite:pre-alias', async resolveId(id, importer, options) { @@ -60,7 +59,7 @@ export function preAliasPlugin(config: ResolvedConfig): Plugin { const isVirtual = resolvedId === id || resolvedId.includes('\0') if ( !isVirtual && - fsUtils.existsSync(resolvedId) && + fs.existsSync(resolvedId) && !moduleListContains(optimizeDeps.exclude, id) && path.isAbsolute(resolvedId) && (isInNodeModules(resolvedId) || diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 5935a3c59..e998fc0a1 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -38,8 +38,6 @@ import { optimizedDepInfoFromFile, optimizedDepInfoFromId } from '../optimizer' import type { DepsOptimizer } from '../optimizer' import type { DepOptimizationOptions, SSROptions } from '..' import type { PackageCache, PackageData } from '../packages' -import type { FsUtils } from '../fsUtils' -import { commonFsUtils } from '../fsUtils' import { shouldExternalize } from '../external' import { findNearestMainPackageData, @@ -113,7 +111,6 @@ interface ResolvePluginOptions { isBuild: boolean isProduction: boolean packageCache?: PackageCache - fsUtils?: FsUtils /** * src code mode also attempts the following: * - resolving /xxx as URLs @@ -629,13 +626,8 @@ function tryCleanFsResolve( ): string | undefined { const { tryPrefix, extensions, preserveSymlinks } = options - const fsUtils = options.fsUtils ?? commonFsUtils - // Optimization to get the real type or file type (directory, file, other) - const fileResult = fsUtils.tryResolveRealFileOrType( - file, - options.preserveSymlinks, - ) + const fileResult = tryResolveRealFileOrType(file, options.preserveSymlinks) if (fileResult?.path) return fileResult.path @@ -645,13 +637,13 @@ function tryCleanFsResolve( const possibleJsToTs = options.isFromTsImporter && isPossibleTsOutput(file) if (possibleJsToTs || options.extensions.length || tryPrefix) { const dirPath = path.dirname(file) - if (fsUtils.isDirectory(dirPath)) { + if (isDirectory(dirPath)) { if (possibleJsToTs) { // try resolve .js, .mjs, .cjs or .jsx import to typescript file const fileExt = path.extname(file) const fileName = file.slice(0, -fileExt.length) if ( - (res = fsUtils.tryResolveRealFile( + (res = tryResolveRealFile( fileName + fileExt.replace('js', 'ts'), preserveSymlinks, )) @@ -660,16 +652,13 @@ function tryCleanFsResolve( // for .js, also try .tsx if ( fileExt === '.js' && - (res = fsUtils.tryResolveRealFile( - fileName + '.tsx', - preserveSymlinks, - )) + (res = tryResolveRealFile(fileName + '.tsx', preserveSymlinks)) ) return res } if ( - (res = fsUtils.tryResolveRealFileWithExtensions( + (res = tryResolveRealFileWithExtensions( file, extensions, preserveSymlinks, @@ -680,11 +669,10 @@ function tryCleanFsResolve( if (tryPrefix) { const prefixed = `${dirPath}/${options.tryPrefix}${path.basename(file)}` - if ((res = fsUtils.tryResolveRealFile(prefixed, preserveSymlinks))) - return res + if ((res = tryResolveRealFile(prefixed, preserveSymlinks))) return res if ( - (res = fsUtils.tryResolveRealFileWithExtensions( + (res = tryResolveRealFileWithExtensions( prefixed, extensions, preserveSymlinks, @@ -702,7 +690,7 @@ function tryCleanFsResolve( if (!skipPackageJson) { let pkgPath = `${dirPath}/package.json` try { - if (fsUtils.existsSync(pkgPath)) { + if (fs.existsSync(pkgPath)) { if (!options.preserveSymlinks) { pkgPath = safeRealpathSync(pkgPath) } @@ -718,7 +706,7 @@ function tryCleanFsResolve( } if ( - (res = fsUtils.tryResolveRealFileWithExtensions( + (res = tryResolveRealFileWithExtensions( `${dirPath}/index`, extensions, preserveSymlinks, @@ -728,7 +716,7 @@ function tryCleanFsResolve( if (tryPrefix) { if ( - (res = fsUtils.tryResolveRealFileWithExtensions( + (res = tryResolveRealFileWithExtensions( `${dirPath}/${options.tryPrefix}index`, extensions, preserveSymlinks, @@ -1322,3 +1310,48 @@ function mapWithBrowserField( function equalWithoutSuffix(path: string, key: string, suffix: string) { return key.endsWith(suffix) && key.slice(0, -suffix.length) === path } + +function tryResolveRealFile( + file: string, + preserveSymlinks?: boolean, +): string | undefined { + const stat = tryStatSync(file) + if (stat?.isFile()) return getRealPath(file, preserveSymlinks) +} + +function tryResolveRealFileWithExtensions( + filePath: string, + extensions: string[], + preserveSymlinks?: boolean, +): string | undefined { + for (const ext of extensions) { + const res = tryResolveRealFile(filePath + ext, preserveSymlinks) + if (res) return res + } +} + +function tryResolveRealFileOrType( + file: string, + preserveSymlinks?: boolean, +): { path?: string; type: 'directory' | 'file' } | undefined { + const fileStat = tryStatSync(file) + if (fileStat?.isFile()) { + return { path: getRealPath(file, preserveSymlinks), type: 'file' } + } + if (fileStat?.isDirectory()) { + return { type: 'directory' } + } + return +} + +function getRealPath(resolved: string, preserveSymlinks?: boolean): string { + if (!preserveSymlinks) { + resolved = safeRealpathSync(resolved) + } + return normalizePath(resolved) +} + +function isDirectory(path: string): boolean { + const stat = tryStatSync(path) + return stat?.isDirectory() ?? false +} diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 05edbf761..ffc857712 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -36,7 +36,6 @@ import { setupSIGTERMListener, teardownSIGTERMListener, } from '../utils' -import { getFsUtils } from '../fsUtils' import { ssrLoadModule } from '../ssr/ssrModuleLoader' import { ssrFixStacktrace, ssrRewriteStacktrace } from '../ssr/ssrStacktrace' import { ssrTransform } from '../ssr/ssrTransform' @@ -218,14 +217,6 @@ export interface FileSystemServeOptions { * @default ['.env', '.env.*', '*.{crt,pem}', '**\/.git/**'] */ deny?: string[] - - /** - * Enable caching of fs calls. It is enabled by default if no custom watch ignored patterns are provided. - * - * @experimental - * @default undefined - */ - cachedChecks?: boolean } export type ServerHook = ( @@ -810,8 +801,6 @@ export async function _createServer( await onHMRUpdate('update', file) }) - getFsUtils(config).initWatcher?.(watcher) - watcher.on('add', (file) => { onFileAddUnlink(file, false) }) @@ -889,13 +878,7 @@ export async function _createServer( // html fallback if (config.appType === 'spa' || config.appType === 'mpa') { - middlewares.use( - htmlFallbackMiddleware( - root, - config.appType === 'spa', - getFsUtils(config), - ), - ) + middlewares.use(htmlFallbackMiddleware(root, config.appType === 'spa')) } // run post config hooks @@ -1095,7 +1078,6 @@ export function resolveServerOptions( strict: server.fs?.strict ?? true, allow: allowDirs, deny, - cachedChecks: server.fs?.cachedChecks, } if (server.origin?.endsWith('/')) { diff --git a/packages/vite/src/node/server/middlewares/htmlFallback.ts b/packages/vite/src/node/server/middlewares/htmlFallback.ts index a7f9d826a..4dbfea353 100644 --- a/packages/vite/src/node/server/middlewares/htmlFallback.ts +++ b/packages/vite/src/node/server/middlewares/htmlFallback.ts @@ -1,8 +1,7 @@ import path from 'node:path' +import fs from 'node:fs' import type { Connect } from 'dep-types/connect' import { createDebugger } from '../../utils' -import type { FsUtils } from '../../fsUtils' -import { commonFsUtils } from '../../fsUtils' import { cleanUrl } from '../../../shared/utils' const debug = createDebugger('vite:html-fallback') @@ -10,7 +9,6 @@ const debug = createDebugger('vite:html-fallback') export function htmlFallbackMiddleware( root: string, spaFallback: boolean, - fsUtils: FsUtils = commonFsUtils, ): Connect.NextHandleFunction { // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return function viteHtmlFallbackMiddleware(req, _res, next) { @@ -37,7 +35,7 @@ export function htmlFallbackMiddleware( // so we need to check if the file exists if (pathname.endsWith('.html')) { const filePath = path.join(root, pathname) - if (fsUtils.existsSync(filePath)) { + if (fs.existsSync(filePath)) { debug?.(`Rewriting ${req.method} ${req.url} to ${url}`) req.url = url return next() @@ -46,7 +44,7 @@ export function htmlFallbackMiddleware( // trailing slash should check for fallback index.html else if (pathname[pathname.length - 1] === '/') { const filePath = path.join(root, pathname, 'index.html') - if (fsUtils.existsSync(filePath)) { + if (fs.existsSync(filePath)) { const newUrl = url + 'index.html' debug?.(`Rewriting ${req.method} ${req.url} to ${newUrl}`) req.url = newUrl @@ -56,7 +54,7 @@ export function htmlFallbackMiddleware( // non-trailing slash should check for fallback .html else { const filePath = path.join(root, pathname + '.html') - if (fsUtils.existsSync(filePath)) { + if (fs.existsSync(filePath)) { const newUrl = url + '.html' debug?.(`Rewriting ${req.method} ${req.url} to ${newUrl}`) req.url = newUrl diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 29dbfb415..27610bb50 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs' import fsp from 'node:fs/promises' import path from 'node:path' import MagicString from 'magic-string' @@ -40,7 +41,6 @@ import { processSrcSetSync, stripBase, } from '../../utils' -import { getFsUtils } from '../../fsUtils' import { checkPublicFile } from '../../publicDir' import { isCSSRequest } from '../../plugins/css' import { getCodeWithSourcemap, injectSourcesContent } from '../sourcemap' @@ -195,7 +195,7 @@ const devHtmlHook: IndexHtmlTransformHook = async ( let proxyModuleUrl: string const trailingSlash = htmlPath.endsWith('/') - if (!trailingSlash && getFsUtils(config).existsSync(filename)) { + if (!trailingSlash && fs.existsSync(filename)) { proxyModulePath = htmlPath proxyModuleUrl = proxyModulePath } else { @@ -441,7 +441,6 @@ export function indexHtmlMiddleware( server: ViteDevServer | PreviewServer, ): Connect.NextHandleFunction { const isDev = isDevServer(server) - const fsUtils = getFsUtils(server.config) // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return async function viteIndexHtmlMiddleware(req, res, next) { @@ -459,7 +458,7 @@ export function indexHtmlMiddleware( filePath = path.join(root, decodeURIComponent(url)) } - if (fsUtils.existsSync(filePath)) { + if (fs.existsSync(filePath)) { const headers = isDev ? server.config.server.headers : server.config.preview.headers diff --git a/playground/html/vite.config.js b/playground/html/vite.config.js index d71da5209..484bfa9ce 100644 --- a/playground/html/vite.config.js +++ b/playground/html/vite.config.js @@ -44,9 +44,6 @@ export default defineConfig({ }, server: { - fs: { - cachedChecks: false, - }, warmup: { clientFiles: ['./warmup/*'], },