resolve.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. "use strict"
  2. exports.__esModule = true
  3. const pkgDir = require('pkg-dir')
  4. const fs = require('fs')
  5. const path = require('path')
  6. const hashObject = require('./hash').hashObject
  7. , ModuleCache = require('./ModuleCache').default
  8. const CASE_SENSITIVE_FS = !fs.existsSync(path.join(__dirname, 'reSOLVE.js'))
  9. exports.CASE_SENSITIVE_FS = CASE_SENSITIVE_FS
  10. const fileExistsCache = new ModuleCache()
  11. function tryRequire(target) {
  12. let resolved;
  13. try {
  14. // Check if the target exists
  15. resolved = require.resolve(target);
  16. } catch(e) {
  17. // If the target does not exist then just return undefined
  18. return undefined;
  19. }
  20. // If the target exists then return the loaded module
  21. return require(resolved);
  22. }
  23. // http://stackoverflow.com/a/27382838
  24. exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cacheSettings) {
  25. // don't care if the FS is case-sensitive
  26. if (CASE_SENSITIVE_FS) return true
  27. // null means it resolved to a builtin
  28. if (filepath === null) return true
  29. if (filepath.toLowerCase() === process.cwd().toLowerCase()) return true
  30. const parsedPath = path.parse(filepath)
  31. , dir = parsedPath.dir
  32. let result = fileExistsCache.get(filepath, cacheSettings)
  33. if (result != null) return result
  34. // base case
  35. if (dir === '' || parsedPath.root === filepath) {
  36. result = true
  37. } else {
  38. const filenames = fs.readdirSync(dir)
  39. if (filenames.indexOf(parsedPath.base) === -1) {
  40. result = false
  41. } else {
  42. result = fileExistsWithCaseSync(dir, cacheSettings)
  43. }
  44. }
  45. fileExistsCache.set(filepath, result)
  46. return result
  47. }
  48. function relative(modulePath, sourceFile, settings) {
  49. return fullResolve(modulePath, sourceFile, settings).path
  50. }
  51. function fullResolve(modulePath, sourceFile, settings) {
  52. // check if this is a bonus core module
  53. const coreSet = new Set(settings['import/core-modules'])
  54. if (coreSet != null && coreSet.has(modulePath)) return { found: true, path: null }
  55. const sourceDir = path.dirname(sourceFile)
  56. , cacheKey = sourceDir + hashObject(settings).digest('hex') + modulePath
  57. const cacheSettings = ModuleCache.getSettings(settings)
  58. const cachedPath = fileExistsCache.get(cacheKey, cacheSettings)
  59. if (cachedPath !== undefined) return { found: true, path: cachedPath }
  60. function cache(resolvedPath) {
  61. fileExistsCache.set(cacheKey, resolvedPath)
  62. }
  63. function withResolver(resolver, config) {
  64. function v1() {
  65. try {
  66. const resolved = resolver.resolveImport(modulePath, sourceFile, config)
  67. if (resolved === undefined) return { found: false }
  68. return { found: true, path: resolved }
  69. } catch (err) {
  70. return { found: false }
  71. }
  72. }
  73. function v2() {
  74. return resolver.resolve(modulePath, sourceFile, config)
  75. }
  76. switch (resolver.interfaceVersion) {
  77. case 2:
  78. return v2()
  79. default:
  80. case 1:
  81. return v1()
  82. }
  83. }
  84. const configResolvers = (settings['import/resolver']
  85. || { 'node': settings['import/resolve'] }) // backward compatibility
  86. const resolvers = resolverReducer(configResolvers, new Map())
  87. for (let pair of resolvers) {
  88. let name = pair[0]
  89. , config = pair[1]
  90. const resolver = requireResolver(name, sourceFile)
  91. , resolved = withResolver(resolver, config)
  92. if (!resolved.found) continue
  93. // else, counts
  94. cache(resolved.path)
  95. return resolved
  96. }
  97. // failed
  98. // cache(undefined)
  99. return { found: false }
  100. }
  101. exports.relative = relative
  102. function resolverReducer(resolvers, map) {
  103. if (resolvers instanceof Array) {
  104. resolvers.forEach(r => resolverReducer(r, map))
  105. return map
  106. }
  107. if (typeof resolvers === 'string') {
  108. map.set(resolvers, null)
  109. return map
  110. }
  111. if (typeof resolvers === 'object') {
  112. for (let key in resolvers) {
  113. map.set(key, resolvers[key])
  114. }
  115. return map
  116. }
  117. throw new Error('invalid resolver config')
  118. }
  119. function getBaseDir(sourceFile) {
  120. return pkgDir.sync(sourceFile) || process.cwd()
  121. }
  122. function requireResolver(name, sourceFile) {
  123. // Try to resolve package with conventional name
  124. let resolver = tryRequire(`eslint-import-resolver-${name}`) ||
  125. tryRequire(name) ||
  126. tryRequire(path.resolve(getBaseDir(sourceFile), name))
  127. if (!resolver) {
  128. throw new Error(`unable to load resolver "${name}".`)
  129. } else {
  130. return resolver;
  131. }
  132. }
  133. const erroredContexts = new Set()
  134. /**
  135. * Given
  136. * @param {string} p - module path
  137. * @param {object} context - ESLint context
  138. * @return {string} - the full module filesystem path;
  139. * null if package is core;
  140. * undefined if not found
  141. */
  142. function resolve(p, context) {
  143. try {
  144. return relative( p
  145. , context.getFilename()
  146. , context.settings
  147. )
  148. } catch (err) {
  149. if (!erroredContexts.has(context)) {
  150. context.report({
  151. message: `Resolve error: ${err.message}`,
  152. loc: { line: 1, column: 0 },
  153. })
  154. erroredContexts.add(context)
  155. }
  156. }
  157. }
  158. resolve.relative = relative
  159. exports.default = resolve