Nuxt/packages/kit/src/resolve.ts

114 lines
3.0 KiB
TypeScript
Raw Normal View History

import { existsSync, lstatSync, readdirSync } from 'fs'
import { basename, dirname, resolve, join } from 'pathe'
import { globby } from 'globby'
2020-07-02 13:02:35 +00:00
export interface ResolveOptions {
2021-04-15 18:49:29 +00:00
/**
* The base path against which to resolve the path
*
* @default .
*/
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' }`
*/
alias?: Record<string, string>
2021-04-15 18:49:29 +00:00
/** The file extensions to try (for example, ['js', 'ts']) */
extensions?: string[]
2020-07-02 13:02:35 +00:00
}
function resolvePath (path: string, opts: ResolveOptions = {}) {
2021-04-15 18:49:29 +00:00
// Fast return if the path exists
if (existsSyncSensitive(path)) {
return path
2020-07-02 13:02:35 +00:00
}
let resolvedPath: string
2020-07-02 13:02:35 +00:00
// Resolve alias
if (opts.alias) {
resolvedPath = resolveAlias(path, opts.alias)
2020-07-02 13:02:35 +00:00
}
// Resolve relative to base or cwd
resolvedPath = resolve(opts.base || '.', resolvedPath)
2020-07-02 13:02:35 +00:00
const resolvedPathFiles = readdirSync(dirname(resolvedPath))
// Check if resolvedPath is a file
let isDirectory = false
if (existsSyncSensitive(resolvedPath, resolvedPathFiles)) {
isDirectory = lstatSync(resolvedPath).isDirectory()
if (!isDirectory) {
return resolvedPath
}
2020-07-02 13:02:35 +00:00
}
// Check possible extensions
for (const ext of opts.extensions) {
// resolvedPath.[ext]
const resolvedPathwithExt = resolvedPath + ext
if (!isDirectory && existsSyncSensitive(resolvedPathwithExt, resolvedPathFiles)) {
return resolvedPathwithExt
}
// resolvedPath/index.[ext]
const resolvedPathwithIndex = join(resolvedPath, 'index' + ext)
if (isDirectory && existsSyncSensitive(resolvedPathwithIndex)) {
return resolvedPathwithIndex
2020-07-02 13:02:35 +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
}
// Give up if it is neither a directory
throw new Error(`Cannot resolve "${path}" from "${resolvedPath}"`)
2020-07-02 13:02:35 +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'
*/
export function resolveAlias (path: string, alias: ResolveOptions['alias']) {
for (const key in alias) {
if (key === '@' && !path.startsWith('@/')) { continue } // Don't resolve @foo/bar
if (path.startsWith(key)) {
path = alias[key] + path.slice(key.length)
}
2020-07-02 13:02:35 +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.
*/
export function tryResolvePath (path: string, opts: ResolveOptions = {}) {
try {
return resolvePath(path, opts)
} catch (e) {
}
2020-07-02 13:02:35 +00:00
}
export async function resolveFiles (path: string, pattern: string | string[]) {
const files = await globby(pattern, {
cwd: path,
followSymbolicLinks: true
})
return files.map(p => resolve(path, p))
}