All files resolver.ts

100% Statements 51/51
100% Branches 24/24
100% Functions 11/11
100% Lines 49/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 1131x   1x 3x     3x           1x 51x 51x                       1x 89x 89x 78x 78x 72x     11x 11x 10x       7x     1x 89x     1x 94x 94x           1x 130x 21x     109x 109x 109x 61x       48x               1x 36x 36x 36x             1x 16x 16x 16x 10x     6x 6x           1x 36x 36x 20x 20x 20x         16x   1x  
import * as path from "./path"
 
export abstract class Resolver {
  private _extensions: string[] = []
 
  public constructor() {
    this.extensions = [".js"]
  }
 
  /**
   * The list of acceptable extensions for resolving module files.
   */
  public set extensions(extensions: string[]) {
    this._extensions = extensions.map((extension) => {
      return /^\./.test(extension) ? extension : `.${extension}`
    })
  }
 
  protected abstract isFile(filePath: string): boolean
 
  protected abstract readFile(filePath: string): Uint8Array
  protected abstract readFile(filePath: string, encoding: "utf8"): string
 
  /**
   * Normalize and resolve importPath from directoryPath.
   */
  protected resolvePath(importPath: string, directoryPath: string): string {
    const basePath = path.normalize(directoryPath)
    if (this.isRelativePath(importPath)) {
      const candidate = this.maybeResolvePath(importPath, basePath)
      if (candidate) {
        return candidate
      }
    } else {
      const candidate = this.maybeResolveModule(importPath, basePath)
      if (candidate) {
        return candidate
      }
    }
 
    throw new Error(`Unable to resolve ${importPath} from ${directoryPath}`)
  }
 
  private isRelativePath(importPath: string): boolean {
    return /^\.\.?(\/|$)/.test(importPath)
  }
 
  private maybeResolvePath(importPath: string, basePath: string): string | undefined {
    const filePath = path.join(basePath, importPath)
    return this.maybeResolveFile(filePath) || this.maybeResolveDirectory(filePath)
  }
 
  /**
   * Try the raw path and all the valid extensions.
   */
  private maybeResolveFile(filePath: string): string | undefined {
    if (this.isFile(filePath)) {
      return filePath
    }
 
    for (let i = 0; i < this._extensions.length; ++i) {
      const withExt = `${filePath}${this._extensions[i]}`
      if (this.isFile(withExt)) {
        return withExt
      }
    }
 
    return undefined
  }
 
  /**
   * Try resolving using `package.json` inside a directory. If there is no
   * `package.json` or no `module` nor `main` specified within it, load the
   * index file if there is one instead.
   */
  private maybeResolveDirectory(directoryPath: string): string | undefined {
    const json = this.maybeGetPackageJson(directoryPath)
    const entry = (json && (json.module || json.main)) || "index"
    return this.maybeResolveFile(path.join(directoryPath, entry))
  }
 
  /**
   * Try resolving a module by traversing upwards and looking into the
   * `node_modules` it encounters along the way.
   */
  private maybeResolveModule(importPath: string, basePath: string): string | undefined {
    const nodeModulePath = path.join(basePath, "node_modules")
    const candidate = this.maybeResolvePath(importPath, nodeModulePath)
    if (candidate) {
      return candidate
    }
 
    const dirname = path.dirname(basePath)
    return dirname !== basePath ? this.maybeResolveModule(importPath, dirname) : undefined
  }
 
  /**
   * Try getting a `package.json` from a directory.
   */
  private maybeGetPackageJson(directoryPath: string): { main?: string; module?: string } | undefined {
    const jsonPath = path.join(directoryPath, "package.json")
    if (this.isFile(jsonPath)) {
      const body = this.readFile(jsonPath, "utf8")
      try {
        return JSON.parse(body)
      } catch (e) {
        // Ignore JSON errors.
      }
    }
    return undefined
  }
}