2021-06-30 13:55:11 +00:00
|
|
|
import { existsSync, lstatSync, readdirSync } from 'fs'
|
2021-09-27 12:49:36 +00:00
|
|
|
import { basename, dirname, resolve, join } from 'pathe'
|
2022-01-13 18:21:49 +00:00
|
|
|
import { globby } from 'globby'
|
2020-07-02 13:02:35 +00:00
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
export interface ResolveOptions {
|
2021-04-15 18:49:29 +00:00
|
|
|
/**
|
|
|
|
* The base path against which to resolve the path
|
|
|
|
*
|
|
|
|
* @default .
|
|
|
|
*/
|
2021-04-02 11:47:01 +00:00
|
|
|
base?: string
|
2021-04-15 18:49:29 +00:00
|
|
|
/**
|
|
|
|
* An object of aliases (alias, path) to take into account, for example
|
|
|
|
* `{ 'example/alias': '/full/path/to/alias' }`
|
|
|
|
*/
|
2021-04-02 11:47:01 +00:00
|
|
|
alias?: Record<string, string>
|
2021-04-15 18:49:29 +00:00
|
|
|
/** The file extensions to try (for example, ['js', 'ts']) */
|
2021-04-02 11:47:01 +00:00
|
|
|
extensions?: string[]
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
function resolvePath (path: string, opts: ResolveOptions = {}) {
|
2021-04-15 18:49:29 +00:00
|
|
|
// Fast return if the path exists
|
2021-06-30 13:55:11 +00:00
|
|
|
if (existsSyncSensitive(path)) {
|
2021-04-02 11:47:01 +00:00
|
|
|
return path
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
let resolvedPath: string
|
2020-07-02 13:02:35 +00:00
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// Resolve alias
|
|
|
|
if (opts.alias) {
|
|
|
|
resolvedPath = resolveAlias(path, opts.alias)
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// Resolve relative to base or cwd
|
|
|
|
resolvedPath = resolve(opts.base || '.', resolvedPath)
|
2020-07-02 13:02:35 +00:00
|
|
|
|
2021-06-30 13:55:11 +00:00
|
|
|
const resolvedPathFiles = readdirSync(dirname(resolvedPath))
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// Check if resolvedPath is a file
|
|
|
|
let isDirectory = false
|
2021-06-30 13:55:11 +00:00
|
|
|
if (existsSyncSensitive(resolvedPath, resolvedPathFiles)) {
|
2021-04-02 11:47:01 +00:00
|
|
|
isDirectory = lstatSync(resolvedPath).isDirectory()
|
|
|
|
if (!isDirectory) {
|
|
|
|
return resolvedPath
|
|
|
|
}
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// Check possible extensions
|
|
|
|
for (const ext of opts.extensions) {
|
|
|
|
// resolvedPath.[ext]
|
|
|
|
const resolvedPathwithExt = resolvedPath + ext
|
2021-06-30 13:55:11 +00:00
|
|
|
if (!isDirectory && existsSyncSensitive(resolvedPathwithExt, resolvedPathFiles)) {
|
2021-04-02 11:47:01 +00:00
|
|
|
return resolvedPathwithExt
|
|
|
|
}
|
|
|
|
// resolvedPath/index.[ext]
|
|
|
|
const resolvedPathwithIndex = join(resolvedPath, 'index' + ext)
|
2021-06-30 13:55:11 +00:00
|
|
|
if (isDirectory && existsSyncSensitive(resolvedPathwithIndex)) {
|
2021-04-02 11:47:01 +00:00
|
|
|
return resolvedPathwithIndex
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// If extension check fails and resolvedPath is a valid directory, return it
|
|
|
|
if (isDirectory) {
|
|
|
|
return resolvedPath
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// Give up if it is neither a directory
|
|
|
|
throw new Error(`Cannot resolve "${path}" from "${resolvedPath}"`)
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2021-06-30 13:55:11 +00:00
|
|
|
function existsSyncSensitive (path: string, files?: string[]) {
|
|
|
|
if (!existsSync(path)) { return false }
|
|
|
|
const _files = files || readdirSync(dirname(path))
|
|
|
|
return _files.includes(basename(path))
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/**
|
|
|
|
* Return a path with any relevant aliases resolved.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* ```js
|
|
|
|
* const aliases = { 'test': '/here/there' }
|
|
|
|
* resolveAlias('test/everywhere', aliases)
|
|
|
|
* // '/here/there/everywhere'
|
|
|
|
*/
|
2021-04-02 11:47:01 +00:00
|
|
|
export function resolveAlias (path: string, alias: ResolveOptions['alias']) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/**
|
|
|
|
* Resolve the path of a file but don't emit an error,
|
|
|
|
* even if the module can't be resolved.
|
|
|
|
*/
|
2021-04-02 11:47:01 +00:00
|
|
|
export function tryResolvePath (path: string, opts: ResolveOptions = {}) {
|
|
|
|
try {
|
|
|
|
return resolvePath(path, opts)
|
|
|
|
} catch (e) {
|
|
|
|
}
|
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[]) {
|
2021-05-20 11:42:41 +00:00
|
|
|
const files = await globby(pattern, {
|
|
|
|
cwd: path,
|
|
|
|
followSymbolicLinks: true
|
|
|
|
})
|
|
|
|
return files.map(p => resolve(path, p))
|
|
|
|
}
|