mirror of
https://github.com/vitejs/vite.git
synced 2024-11-21 14:48:41 +00:00
refactor!: remove fs.cachedChecks option (#18493)
This commit is contained in:
parent
5d6dc491b6
commit
94b0857353
@ -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`
|
||||
|
@ -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
|
||||
|
||||
|
@ -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<ResolvedConfig, FsUtils>()
|
||||
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<string, DirentCache>
|
||||
|
||||
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
|
||||
}
|
@ -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,
|
||||
}),
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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) ||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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('/')) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -44,9 +44,6 @@ export default defineConfig({
|
||||
},
|
||||
|
||||
server: {
|
||||
fs: {
|
||||
cachedChecks: false,
|
||||
},
|
||||
warmup: {
|
||||
clientFiles: ['./warmup/*'],
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user