aboutsummaryrefslogtreecommitdiff
path: root/node_modules/hullabaloo-config-manager/lib/resolvePluginsAndPresets.js
blob: b72ab40908a6ddb84af38fa0d3e238e2445fabaf (plain)
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
'use strict'

const path = require('path')

const ExtendableError = require('es6-error')
const pkgDir = require('pkg-dir')
const resolveFrom = require('resolve-from')

class ResolveError extends ExtendableError {
  constructor (source, kind, ref) {
    super(`${source}: Couldn't find ${kind} ${JSON.stringify(ref)} relative to directory`)
    this.source = source
    this.ref = ref
    this.isPlugin = kind === 'plugin'
    this.isPreset = kind === 'preset'
  }
}

function normalize (arr) {
  if (!Array.isArray(arr)) return []

  return arr.map(item => Array.isArray(item) ? item[0] : item)
}

function isFilePath (ref) {
  return path.isAbsolute(ref) || ref.startsWith('./') || ref.startsWith('../')
}

function resolveName (name, fromDir, cache) {
  if (cache.has(name)) return cache.get(name)

  const filename = resolveFrom(fromDir, name)
  cache.set(name, filename)
  return filename
}

function resolvePackage (filename, fromFile) {
  if (fromFile) return null

  return pkgDir.sync(filename)
}

function resolvePluginsAndPresets (chains, sharedCache) {
  const dirCaches = (sharedCache && sharedCache.pluginsAndPresets) || new Map()
  const getCache = dir => {
    if (dirCaches.has(dir)) return dirCaches.get(dir)

    const cache = new Map()
    dirCaches.set(dir, cache)
    return cache
  }

  const byConfig = new Map()
  for (const chain of chains) {
    for (const config of chain) {
      if (byConfig.has(config)) continue

      const plugins = new Map()
      const presets = new Map()
      byConfig.set(config, { plugins, presets })

      const fromDir = config.dir
      const cache = getCache(fromDir)
      const resolve = (kind, ref) => {
        const possibleNames = []
        if (isFilePath(ref)) {
          possibleNames.push({ fromFile: true, name: ref })
        } else {
          if (kind === 'plugin') {
            // Expand possible plugin names, see
            // https://github.com/babel/babel/blob/510e93b2bd434f05c816fe6639137b35bac267ed/packages/babel-core/src/helpers/get-possible-plugin-names.js

            // Babel doesn't expand scoped plugin references. @ is only valid at
            // the start of a package name, so disregard refs that would result
            // in `babel-plugin-@scope/name`.
            if (!ref.startsWith('@')) {
              const name = `babel-plugin-${ref}`
              possibleNames.push({ fromFile: false, name })
            }
          } else {
            // Expand possible preset names, see
            // https://github.com/babel/babel/blob/510e93b2bd434f05c816fe6639137b35bac267ed/packages/babel-core/src/helpers/get-possible-preset-names.js

            if (ref.startsWith('@')) {
              const matches = /^(@.+?)\/([^/]+)(.*)/.exec(ref)
              const scope = matches[1]
              const partialName = matches[2]
              const remainder = matches[3]

              const name = `${scope}/babel-preset-${partialName}${remainder}`
              possibleNames.push({ fromFile: false, name })
            } else {
              const name = `babel-preset-${ref}`
              possibleNames.push({ fromFile: false, name })
            }
          }

          possibleNames.push({ fromFile: false, name: ref })
        }

        let entry = null
        for (const possibility of possibleNames) {
          const filename = resolveName(possibility.name, fromDir, cache)
          if (filename) {
            const fromPackage = resolvePackage(filename, possibility.fromFile)
            entry = { filename, fromPackage }
            break
          }
        }
        if (!entry) {
          throw new ResolveError(config.source, kind, ref)
        }

        if (kind === 'plugin') {
          plugins.set(ref, entry)
        } else {
          presets.set(ref, entry)
        }
      }

      for (const ref of normalize(config.options.plugins)) {
        resolve('plugin', ref)
      }
      for (const ref of normalize(config.options.presets)) {
        resolve('preset', ref)
      }
    }
  }

  return byConfig
}

module.exports = resolvePluginsAndPresets