2022-02-07 21:00:20 +00:00
|
|
|
import { promises as fsp, existsSync } from 'fs'
|
2022-02-07 21:39:31 +00:00
|
|
|
import { fileURLToPath } from 'url'
|
2022-02-07 21:00:20 +00:00
|
|
|
import { basename, dirname, resolve, join, normalize, isAbsolute } from 'pathe'
|
2022-01-13 18:21:49 +00:00
|
|
|
import { globby } from 'globby'
|
2022-02-07 21:00:20 +00:00
|
|
|
import { useNuxt } from './context'
|
2022-02-18 18:14:57 +00:00
|
|
|
import { tryResolveModule } from './internal/cjs'
|
2022-02-28 16:11:46 +00:00
|
|
|
import { isIgnored } from './ignore'
|
2020-07-02 13:02:35 +00:00
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
export interface ResolvePathOptions {
|
|
|
|
/** Base for resolving paths from. Default is Nuxt rootDir. */
|
|
|
|
cwd?: string
|
|
|
|
|
|
|
|
/** An object of aliases. Default is Nuxt configured aliases. */
|
2021-04-02 11:47:01 +00:00
|
|
|
alias?: Record<string, string>
|
2022-02-07 21:00:20 +00:00
|
|
|
|
|
|
|
/** The file extensions to try. Default is Nuxt configured extensions. */
|
2021-04-02 11:47:01 +00:00
|
|
|
extensions?: string[]
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
/**
|
|
|
|
* Resolve full path to a file or directory respecting Nuxt alias and extensions options
|
|
|
|
*
|
|
|
|
* If path could not be resolved, normalized input path will be returned
|
|
|
|
*/
|
|
|
|
export async function resolvePath (path: string, opts: ResolvePathOptions = {}): Promise<string> {
|
|
|
|
// Always normalize input
|
2022-02-07 21:39:31 +00:00
|
|
|
const _path = path
|
2022-02-07 21:00:20 +00:00
|
|
|
path = normalize(path)
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
// Fast return if the path exists
|
2022-02-07 21:39:31 +00:00
|
|
|
if (isAbsolute(path) && existsSync(path)) {
|
2021-04-02 11:47:01 +00:00
|
|
|
return path
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
// Use current nuxt options
|
|
|
|
const nuxt = useNuxt()
|
2022-02-07 21:39:31 +00:00
|
|
|
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 : []
|
2020-07-02 13:02:35 +00:00
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
// Resolve aliases
|
|
|
|
path = resolveAlias(path)
|
2020-07-02 13:02:35 +00:00
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
// Resolve relative to cwd
|
|
|
|
if (!isAbsolute(path)) {
|
|
|
|
path = resolve(cwd, path)
|
|
|
|
}
|
2021-06-30 13:55:11 +00:00
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// Check if resolvedPath is a file
|
|
|
|
let isDirectory = false
|
2022-02-07 21:00:20 +00:00
|
|
|
if (existsSync(path)) {
|
|
|
|
isDirectory = (await fsp.lstat(path)).isDirectory()
|
2021-04-02 11:47:01 +00:00
|
|
|
if (!isDirectory) {
|
2022-02-07 21:00:20 +00:00
|
|
|
return path
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// Check possible extensions
|
2022-02-07 21:00:20 +00:00
|
|
|
for (const ext of extensions) {
|
|
|
|
// path.[ext]
|
|
|
|
const pathWithExt = path + ext
|
2022-03-15 16:57:41 +00:00
|
|
|
if (existsSync(pathWithExt)) {
|
2022-02-07 21:00:20 +00:00
|
|
|
return pathWithExt
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
2022-02-07 21:00:20 +00:00
|
|
|
// path/index.[ext]
|
|
|
|
const pathWithIndex = join(path, 'index' + ext)
|
|
|
|
if (isDirectory && existsSync(pathWithIndex)) {
|
|
|
|
return pathWithIndex
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
// Try to resolve as module id
|
2022-02-07 21:39:31 +00:00
|
|
|
const resolveModulePath = tryResolveModule(_path, { paths: [cwd, ...modulesDir] })
|
2022-02-07 21:00:20 +00:00
|
|
|
if (resolveModulePath) {
|
|
|
|
return resolveModulePath
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
// Return normalized input
|
|
|
|
return path
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
/**
|
|
|
|
* Try to resolve first existing file in paths
|
|
|
|
*/
|
2022-03-15 16:57:41 +00:00
|
|
|
export async function findPath (paths: string|string[], opts?: ResolvePathOptions, pathType: 'file' | 'dir' = 'file'): Promise<string|null> {
|
|
|
|
if (!Array.isArray(paths)) {
|
|
|
|
paths = [paths]
|
|
|
|
}
|
2022-02-07 21:00:20 +00:00
|
|
|
for (const path of paths) {
|
|
|
|
const rPath = await resolvePath(path, opts)
|
|
|
|
if (await existsSensitive(rPath)) {
|
2022-03-15 16:57:41 +00:00
|
|
|
const isDirectory = (await fsp.lstat(rPath)).isDirectory()
|
|
|
|
if (!pathType || (pathType === 'file' && !isDirectory) || (pathType === 'dir' && isDirectory)) {
|
|
|
|
return rPath
|
|
|
|
}
|
2022-02-07 21:00:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null
|
2021-06-30 13:55:11 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/**
|
2022-02-07 21:00:20 +00:00
|
|
|
* Resolve path aliases respecting Nuxt alias options
|
2021-04-15 18:49:29 +00:00
|
|
|
*/
|
2022-02-07 21:00:20 +00:00
|
|
|
export function resolveAlias (path: string, alias?: Record<string, string>): string {
|
|
|
|
if (!alias) {
|
2022-02-07 21:39:31 +00:00
|
|
|
alias = useNuxt()?.options.alias || {}
|
2022-02-07 21:00:20 +00:00
|
|
|
}
|
2021-04-02 11:47:01 +00:00
|
|
|
for (const key in alias) {
|
2022-01-17 12:51:08 +00:00
|
|
|
if (key === '@' && !path.startsWith('@/')) { continue } // Don't resolve @foo/bar
|
2021-04-02 11:47:01 +00:00
|
|
|
if (path.startsWith(key)) {
|
2022-01-17 12:51:08 +00:00
|
|
|
path = alias[key] + path.slice(key.length)
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
2021-04-02 11:47:01 +00:00
|
|
|
return path
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 21:39:31 +00:00
|
|
|
export interface Resolver {
|
|
|
|
resolve(...path): string
|
|
|
|
resolvePath(path: string, opts?: ResolvePathOptions): Promise<string>
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a relative resolver
|
|
|
|
*/
|
|
|
|
export function createResolver (base: string | URL): Resolver {
|
|
|
|
if (!base) {
|
|
|
|
throw new Error('`base` argument is missing for createResolver(base)!')
|
|
|
|
}
|
|
|
|
|
|
|
|
base = base.toString()
|
|
|
|
if (base.startsWith('file://')) {
|
|
|
|
base = dirname(fileURLToPath(base))
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
resolve: (...path) => resolve(base as string, ...path),
|
|
|
|
resolvePath: (path, opts) => resolvePath(path, { cwd: base as string, ...opts })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 21:00:20 +00:00
|
|
|
// --- Internal ---
|
|
|
|
|
|
|
|
async function existsSensitive (path: string) {
|
|
|
|
if (!existsSync(path)) { return false }
|
|
|
|
const dirFiles = await fsp.readdir(dirname(path))
|
|
|
|
return dirFiles.includes(basename(path))
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
2021-05-20 11:42:41 +00:00
|
|
|
|
2022-01-18 16:43:41 +00:00
|
|
|
export async function resolveFiles (path: string, pattern: string | string[]) {
|
2022-02-07 21:00:20 +00:00
|
|
|
const files = await globby(pattern, { cwd: path, followSymbolicLinks: true })
|
2022-02-28 16:11:46 +00:00
|
|
|
return files.filter(p => !isIgnored(p)).map(p => resolve(path, p))
|
2021-05-20 11:42:41 +00:00
|
|
|
}
|