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.silent(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
|