From e8789b900c1cca3e34f18d61d558e7914df8d963 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 21 Jan 2025 11:33:12 +0000 Subject: [PATCH] perf(kit): reduce duplication between `findPath` and `resolvePath` (#30682) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- packages/kit/src/resolve.ts | 192 ++++++++++++++++++++---------------- 1 file changed, 108 insertions(+), 84 deletions(-) diff --git a/packages/kit/src/resolve.ts b/packages/kit/src/resolve.ts index 31ae08d2ed..aed41f2cbb 100644 --- a/packages/kit/src/resolve.ts +++ b/packages/kit/src/resolve.ts @@ -1,4 +1,4 @@ -import { existsSync, promises as fsp } from 'node:fs' +import { promises as fsp } from 'node:fs' import { fileURLToPath } from 'node:url' import { basename, dirname, isAbsolute, join, normalize, resolve } from 'pathe' import { globby } from 'globby' @@ -38,97 +38,30 @@ export interface ResolvePathOptions { * If path could not be resolved, normalized input path will be returned */ export async function resolvePath (path: string, opts: ResolvePathOptions = {}): Promise { - // Always normalize input - const _path = path - path = normalize(path) + const res = await _resolvePathGranularly(path, opts) - // Fast return if the path exists - if (isAbsolute(path)) { - if (opts?.virtual && existsInVFS(path)) { - return path - } - if (existsSync(path) && !(await isDirectory(path))) { - return path - } - } - - // Use current nuxt options - const nuxt = tryUseNuxt() - const cwd = opts.cwd || (nuxt ? nuxt.options.rootDir : process.cwd()) - const extensions = opts.extensions || (nuxt ? nuxt.options.extensions : ['.ts', '.mjs', '.cjs', '.json']) - const modulesDir = nuxt ? nuxt.options.modulesDir : [] - - // Resolve aliases - path = resolveAlias(path) - - // Resolve relative to cwd - if (!isAbsolute(path)) { - path = resolve(cwd, path) - } - - // Check if resolvedPath is a file - if (opts?.virtual && existsInVFS(path, nuxt)) { - return path - } - - let _isDir = false - if (existsSync(path)) { - _isDir = await isDirectory(path) - if (!_isDir) { - return path - } - } - - // Check possible extensions - for (const ext of extensions) { - // path.[ext] - const pathWithExt = path + ext - if (opts?.virtual && existsInVFS(pathWithExt, nuxt)) { - return pathWithExt - } - if (existsSync(pathWithExt)) { - return pathWithExt - } - // path/index.[ext] - const pathWithIndex = join(path, 'index' + ext) - if (opts?.virtual && existsInVFS(pathWithIndex, nuxt)) { - return pathWithIndex - } - if (_isDir && existsSync(pathWithIndex)) { - return pathWithIndex - } - } - - // Try to resolve as module id - const resolveModulePath = await _resolvePath(_path, { url: [cwd, ...modulesDir] }).catch(() => null) - if (resolveModulePath) { - return resolveModulePath + if (res.type === 'file') { + return res.path } // Return normalized input - return opts.fallbackToOriginal ? _path : path + return opts.fallbackToOriginal ? path : res.path } /** * Try to resolve first existing file in paths */ export async function findPath (paths: string | string[], opts?: ResolvePathOptions, pathType: 'file' | 'dir' = 'file'): Promise { - const nuxt = opts?.virtual ? tryUseNuxt() : undefined - for (const path of toArray(paths)) { - const rPath = await resolvePath(path, opts) + const res = await _resolvePathGranularly(path, opts) - // Check VFS - if (opts?.virtual && existsInVFS(rPath, nuxt)) { - return rPath + if (!res.type || (pathType && res.type !== pathType)) { + continue } // Check file system - if (await existsSensitive(rPath)) { - const _isDir = await isDirectory(rPath) - if (!pathType || (pathType === 'file' && !_isDir) || (pathType === 'dir' && _isDir)) { - return rPath - } + if (res.virtual || await existsSensitive(res.path)) { + return res.path } } return null @@ -186,15 +119,106 @@ export async function resolveNuxtModule (base: string, paths: string[]): Promise // --- Internal --- -async function existsSensitive (path: string) { - if (!existsSync(path)) { return false } - const dirFiles = await fsp.readdir(dirname(path)) - return dirFiles.includes(basename(path)) +interface PathResolution { + path: string + type?: 'file' | 'dir' + virtual?: boolean } -// Usage note: We assume path existence is already ensured -async function isDirectory (path: string) { - return (await fsp.lstat(path)).isDirectory() +async function _resolvePathType (path: string, opts: ResolvePathOptions = {}, skipFs = false): Promise { + if (opts?.virtual && existsInVFS(path)) { + return { + path, + type: 'file', + virtual: true, + } + } + + if (skipFs) { + return + } + + const fd = await fsp.open(path, 'r').catch(() => null) + try { + const stats = await fd?.stat() + if (stats) { + return { + path, + type: stats.isFile() ? 'file' : 'dir', + virtual: false, + } + } + } finally { + fd?.close() + } +} + +async function _resolvePathGranularly (path: string, opts: ResolvePathOptions = {}): Promise { + // Always normalize input + const _path = path + path = normalize(path) + + // Fast return if the path exists + if (isAbsolute(path)) { + const res = await _resolvePathType(path, opts) + if (res && res.type === 'file') { + return res + } + } + + // Use current nuxt options + const nuxt = tryUseNuxt() + const cwd = opts.cwd || (nuxt ? nuxt.options.rootDir : process.cwd()) + const extensions = opts.extensions || (nuxt ? nuxt.options.extensions : ['.ts', '.mjs', '.cjs', '.json']) + const modulesDir = nuxt ? nuxt.options.modulesDir : [] + + // Resolve aliases + path = resolveAlias(path) + + // Resolve relative to cwd + if (!isAbsolute(path)) { + path = resolve(cwd, path) + } + + const res = await _resolvePathType(path, opts) + if (res && res.type === 'file') { + return res + } + + // Check possible extensions + for (const ext of extensions) { + // path.[ext] + const extPath = await _resolvePathType(path + ext, opts) + if (extPath && extPath.type === 'file') { + return extPath + } + + // path/index.[ext] + const indexPath = await _resolvePathType(join(path, 'index' + ext), opts, res?.type !== 'dir' /* skip checking if parent is not a directory */) + if (indexPath && indexPath.type === 'file') { + return indexPath + } + } + + // Try to resolve as module id + const resolvedModulePath = await _resolvePath(_path, { url: [cwd, ...modulesDir] }).catch(() => null) + if (resolvedModulePath) { + return { + path: resolvedModulePath, + type: 'file', + virtual: false, + } + } + + // Return normalized input + return { + path, + } +} + +async function existsSensitive (path: string) { + const dirFiles = await fsp.readdir(dirname(path)).catch(() => null) + return dirFiles && dirFiles.includes(basename(path)) } function existsInVFS (path: string, nuxt = tryUseNuxt()) {