Nuxt/packages/nuxt3/src/core/resolver.ts

181 lines
4.5 KiB
TypeScript
Raw Normal View History

2020-07-02 13:02:35 +00:00
import { resolve, join } from 'path'
import fs from 'fs-extra'
2020-08-04 10:10:38 +00:00
import { Nuxt } from 'src/core'
2020-07-02 13:02:35 +00:00
import {
startsWithRootAlias,
startsWithSrcAlias,
isExternalDependency,
clearRequireCache
2020-08-04 10:06:44 +00:00
} from 'src/utils'
2020-07-02 13:02:35 +00:00
2020-07-31 09:37:31 +00:00
interface ResolvePathOptions {
isAlias?: boolean
isModule?: boolean
isStyle?: boolean
}
interface RequireModuleOptions {
useESM?: boolean
isAlias?: boolean
interopDefault?: any
}
2020-07-02 13:02:35 +00:00
export default class Resolver {
2020-07-31 09:37:31 +00:00
_require: NodeJS.Require
_resolve: RequireResolve
nuxt: Nuxt
options: Nuxt['options']
constructor (nuxt: Nuxt) {
2020-07-02 13:02:35 +00:00
this.nuxt = nuxt
this.options = this.nuxt.options
// Binds
this.resolvePath = this.resolvePath.bind(this)
this.resolveAlias = this.resolveAlias.bind(this)
this.resolveModule = this.resolveModule.bind(this)
this.requireModule = this.requireModule.bind(this)
const { createRequire } = this.options
this._require = createRequire ? createRequire(module) : module.require
this._resolve = require.resolve
}
2020-07-31 09:37:31 +00:00
resolveModule (path: string) {
2020-07-02 13:02:35 +00:00
try {
return this._resolve(path, {
paths: this.options.modulesDir
})
} catch (error) {
if (error.code !== 'MODULE_NOT_FOUND') {
// TODO: remove after https://github.com/facebook/jest/pull/8487 released
if (process.env.NODE_ENV === 'test' && error.message.startsWith('Cannot resolve module')) {
return
}
throw error
}
}
}
2020-07-31 09:37:31 +00:00
resolveAlias (path: string) {
2020-07-02 13:02:35 +00:00
if (startsWithRootAlias(path)) {
return join(this.options.rootDir, path.substr(2))
}
if (startsWithSrcAlias(path)) {
return join(this.options.srcDir, path.substr(1))
}
return resolve(this.options.srcDir, path)
}
2020-07-31 09:37:31 +00:00
resolvePath (path: string, { isAlias, isModule, isStyle }: ResolvePathOptions = {}) {
2020-07-02 13:02:35 +00:00
// Fast return in case of path exists
if (fs.existsSync(path)) {
return path
}
2020-07-31 09:37:31 +00:00
let resolvedPath: string
2020-07-02 13:02:35 +00:00
// Try to resolve it as a regular module
if (isModule !== false) {
resolvedPath = this.resolveModule(path)
}
// Try to resolve alias
if (!resolvedPath && isAlias !== false) {
resolvedPath = this.resolveAlias(path)
}
// Use path for resolvedPath
if (!resolvedPath) {
resolvedPath = path
}
2020-07-31 09:37:31 +00:00
let isDirectory: boolean
2020-07-02 13:02:35 +00:00
// Check if resolvedPath exits and is not a directory
if (fs.existsSync(resolvedPath)) {
isDirectory = fs.lstatSync(resolvedPath).isDirectory()
if (!isDirectory) {
return resolvedPath
}
}
const extensions = isStyle ? this.options.styleExtensions : this.options.extensions
// Check if any resolvedPath.[ext] or resolvedPath/index.[ext] exists
for (const ext of extensions) {
if (!isDirectory && fs.existsSync(resolvedPath + '.' + ext)) {
return resolvedPath + '.' + ext
}
const resolvedPathwithIndex = join(resolvedPath, 'index.' + ext)
if (isDirectory && fs.existsSync(resolvedPathwithIndex)) {
return resolvedPathwithIndex
}
}
// If there's no index.[ext] we just return the directory path
if (isDirectory) {
return resolvedPath
}
// Give up
throw new Error(`Cannot resolve "${path}" from "${resolvedPath}"`)
}
2020-08-04 10:26:22 +00:00
requireModule <T> (path: string, { useESM, isAlias, interopDefault }: RequireModuleOptions = {}): T {
2020-07-02 13:02:35 +00:00
let resolvedPath = path
2020-07-31 09:37:31 +00:00
let requiredModule: any
2020-07-02 13:02:35 +00:00
2020-07-31 09:37:31 +00:00
let lastError: any
2020-07-02 13:02:35 +00:00
// Try to resolve path
try {
resolvedPath = this.resolvePath(path, { isAlias })
} catch (e) {
lastError = e
}
const isExternal = isExternalDependency(resolvedPath)
// in dev mode make sure to clear the require cache so after
// a dev server restart any changed file is reloaded
if (this.options.dev && !isExternal) {
clearRequireCache(resolvedPath)
}
// By default use esm only for js,mjs files outside of node_modules
if (useESM === undefined) {
useESM = !isExternal && /.(js|mjs)$/.test(resolvedPath)
}
// Try to require
try {
if (useESM) {
requiredModule = this._require(resolvedPath)
} else {
requiredModule = require(resolvedPath)
}
} catch (e) {
lastError = e
}
// Interop default
if (interopDefault !== false && requiredModule && requiredModule.default) {
requiredModule = requiredModule.default
}
// Throw error if failed to require
if (requiredModule === undefined && lastError) {
throw lastError
}
return requiredModule
}
}