config-chain.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.buildPresetChain = buildPresetChain;
  6. exports.buildRootChain = buildRootChain;
  7. exports.buildPresetChainWalker = void 0;
  8. function _path() {
  9. const data = _interopRequireDefault(require("path"));
  10. _path = function () {
  11. return data;
  12. };
  13. return data;
  14. }
  15. function _debug() {
  16. const data = _interopRequireDefault(require("debug"));
  17. _debug = function () {
  18. return data;
  19. };
  20. return data;
  21. }
  22. var _options = require("./validation/options");
  23. var _patternToRegex = _interopRequireDefault(require("./pattern-to-regex"));
  24. var _files = require("./files");
  25. var _caching = require("./caching");
  26. var _configDescriptors = require("./config-descriptors");
  27. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  28. const debug = (0, _debug().default)("babel:config:config-chain");
  29. function buildPresetChain(arg, context) {
  30. const chain = buildPresetChainWalker(arg, context);
  31. if (!chain) return null;
  32. return {
  33. plugins: dedupDescriptors(chain.plugins),
  34. presets: dedupDescriptors(chain.presets),
  35. options: chain.options.map(o => normalizeOptions(o))
  36. };
  37. }
  38. const buildPresetChainWalker = makeChainWalker({
  39. init: arg => arg,
  40. root: preset => loadPresetDescriptors(preset),
  41. env: (preset, envName) => loadPresetEnvDescriptors(preset)(envName),
  42. overrides: (preset, index) => loadPresetOverridesDescriptors(preset)(index),
  43. overridesEnv: (preset, index, envName) => loadPresetOverridesEnvDescriptors(preset)(index)(envName)
  44. });
  45. exports.buildPresetChainWalker = buildPresetChainWalker;
  46. const loadPresetDescriptors = (0, _caching.makeWeakCache)(preset => buildRootDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors));
  47. const loadPresetEnvDescriptors = (0, _caching.makeWeakCache)(preset => (0, _caching.makeStrongCache)(envName => buildEnvDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, envName)));
  48. const loadPresetOverridesDescriptors = (0, _caching.makeWeakCache)(preset => (0, _caching.makeStrongCache)(index => buildOverrideDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, index)));
  49. const loadPresetOverridesEnvDescriptors = (0, _caching.makeWeakCache)(preset => (0, _caching.makeStrongCache)(index => (0, _caching.makeStrongCache)(envName => buildOverrideEnvDescriptors(preset, preset.alias, _configDescriptors.createUncachedDescriptors, index, envName))));
  50. function buildRootChain(opts, context) {
  51. const programmaticChain = loadProgrammaticChain({
  52. options: opts,
  53. dirname: context.cwd
  54. }, context);
  55. if (!programmaticChain) return null;
  56. let configFile;
  57. if (typeof opts.configFile === "string") {
  58. configFile = (0, _files.loadConfig)(opts.configFile, context.cwd, context.envName, context.caller);
  59. } else if (opts.configFile !== false) {
  60. configFile = (0, _files.findRootConfig)(context.root, context.envName, context.caller);
  61. }
  62. let {
  63. babelrc,
  64. babelrcRoots
  65. } = opts;
  66. let babelrcRootsDirectory = context.cwd;
  67. const configFileChain = emptyChain();
  68. if (configFile) {
  69. const validatedFile = validateConfigFile(configFile);
  70. const result = loadFileChain(validatedFile, context);
  71. if (!result) return null;
  72. if (babelrc === undefined) {
  73. babelrc = validatedFile.options.babelrc;
  74. }
  75. if (babelrcRoots === undefined) {
  76. babelrcRootsDirectory = validatedFile.dirname;
  77. babelrcRoots = validatedFile.options.babelrcRoots;
  78. }
  79. mergeChain(configFileChain, result);
  80. }
  81. const pkgData = typeof context.filename === "string" ? (0, _files.findPackageData)(context.filename) : null;
  82. let ignoreFile, babelrcFile;
  83. const fileChain = emptyChain();
  84. if ((babelrc === true || babelrc === undefined) && pkgData && babelrcLoadEnabled(context, pkgData, babelrcRoots, babelrcRootsDirectory)) {
  85. ({
  86. ignore: ignoreFile,
  87. config: babelrcFile
  88. } = (0, _files.findRelativeConfig)(pkgData, context.envName, context.caller));
  89. if (ignoreFile && shouldIgnore(context, ignoreFile.ignore, null, ignoreFile.dirname)) {
  90. return null;
  91. }
  92. if (babelrcFile) {
  93. const result = loadFileChain(validateBabelrcFile(babelrcFile), context);
  94. if (!result) return null;
  95. mergeChain(fileChain, result);
  96. }
  97. }
  98. const chain = mergeChain(mergeChain(mergeChain(emptyChain(), configFileChain), fileChain), programmaticChain);
  99. return {
  100. plugins: dedupDescriptors(chain.plugins),
  101. presets: dedupDescriptors(chain.presets),
  102. options: chain.options.map(o => normalizeOptions(o)),
  103. ignore: ignoreFile || undefined,
  104. babelrc: babelrcFile || undefined,
  105. config: configFile || undefined
  106. };
  107. }
  108. function babelrcLoadEnabled(context, pkgData, babelrcRoots, babelrcRootsDirectory) {
  109. if (typeof babelrcRoots === "boolean") return babelrcRoots;
  110. const absoluteRoot = context.root;
  111. if (babelrcRoots === undefined) {
  112. return pkgData.directories.indexOf(absoluteRoot) !== -1;
  113. }
  114. let babelrcPatterns = babelrcRoots;
  115. if (!Array.isArray(babelrcPatterns)) babelrcPatterns = [babelrcPatterns];
  116. babelrcPatterns = babelrcPatterns.map(pat => {
  117. return typeof pat === "string" ? _path().default.resolve(babelrcRootsDirectory, pat) : pat;
  118. });
  119. if (babelrcPatterns.length === 1 && babelrcPatterns[0] === absoluteRoot) {
  120. return pkgData.directories.indexOf(absoluteRoot) !== -1;
  121. }
  122. return babelrcPatterns.some(pat => {
  123. if (typeof pat === "string") {
  124. pat = (0, _patternToRegex.default)(pat, babelrcRootsDirectory);
  125. }
  126. return pkgData.directories.some(directory => {
  127. return matchPattern(pat, babelrcRootsDirectory, directory, context);
  128. });
  129. });
  130. }
  131. const validateConfigFile = (0, _caching.makeWeakCache)(file => ({
  132. filepath: file.filepath,
  133. dirname: file.dirname,
  134. options: (0, _options.validate)("configfile", file.options)
  135. }));
  136. const validateBabelrcFile = (0, _caching.makeWeakCache)(file => ({
  137. filepath: file.filepath,
  138. dirname: file.dirname,
  139. options: (0, _options.validate)("babelrcfile", file.options)
  140. }));
  141. const validateExtendFile = (0, _caching.makeWeakCache)(file => ({
  142. filepath: file.filepath,
  143. dirname: file.dirname,
  144. options: (0, _options.validate)("extendsfile", file.options)
  145. }));
  146. const loadProgrammaticChain = makeChainWalker({
  147. root: input => buildRootDescriptors(input, "base", _configDescriptors.createCachedDescriptors),
  148. env: (input, envName) => buildEnvDescriptors(input, "base", _configDescriptors.createCachedDescriptors, envName),
  149. overrides: (input, index) => buildOverrideDescriptors(input, "base", _configDescriptors.createCachedDescriptors, index),
  150. overridesEnv: (input, index, envName) => buildOverrideEnvDescriptors(input, "base", _configDescriptors.createCachedDescriptors, index, envName)
  151. });
  152. const loadFileChain = makeChainWalker({
  153. root: file => loadFileDescriptors(file),
  154. env: (file, envName) => loadFileEnvDescriptors(file)(envName),
  155. overrides: (file, index) => loadFileOverridesDescriptors(file)(index),
  156. overridesEnv: (file, index, envName) => loadFileOverridesEnvDescriptors(file)(index)(envName)
  157. });
  158. const loadFileDescriptors = (0, _caching.makeWeakCache)(file => buildRootDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors));
  159. const loadFileEnvDescriptors = (0, _caching.makeWeakCache)(file => (0, _caching.makeStrongCache)(envName => buildEnvDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, envName)));
  160. const loadFileOverridesDescriptors = (0, _caching.makeWeakCache)(file => (0, _caching.makeStrongCache)(index => buildOverrideDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, index)));
  161. const loadFileOverridesEnvDescriptors = (0, _caching.makeWeakCache)(file => (0, _caching.makeStrongCache)(index => (0, _caching.makeStrongCache)(envName => buildOverrideEnvDescriptors(file, file.filepath, _configDescriptors.createUncachedDescriptors, index, envName))));
  162. function buildRootDescriptors({
  163. dirname,
  164. options
  165. }, alias, descriptors) {
  166. return descriptors(dirname, options, alias);
  167. }
  168. function buildEnvDescriptors({
  169. dirname,
  170. options
  171. }, alias, descriptors, envName) {
  172. const opts = options.env && options.env[envName];
  173. return opts ? descriptors(dirname, opts, `${alias}.env["${envName}"]`) : null;
  174. }
  175. function buildOverrideDescriptors({
  176. dirname,
  177. options
  178. }, alias, descriptors, index) {
  179. const opts = options.overrides && options.overrides[index];
  180. if (!opts) throw new Error("Assertion failure - missing override");
  181. return descriptors(dirname, opts, `${alias}.overrides[${index}]`);
  182. }
  183. function buildOverrideEnvDescriptors({
  184. dirname,
  185. options
  186. }, alias, descriptors, index, envName) {
  187. const override = options.overrides && options.overrides[index];
  188. if (!override) throw new Error("Assertion failure - missing override");
  189. const opts = override.env && override.env[envName];
  190. return opts ? descriptors(dirname, opts, `${alias}.overrides[${index}].env["${envName}"]`) : null;
  191. }
  192. function makeChainWalker({
  193. root,
  194. env,
  195. overrides,
  196. overridesEnv
  197. }) {
  198. return (input, context, files = new Set()) => {
  199. const {
  200. dirname
  201. } = input;
  202. const flattenedConfigs = [];
  203. const rootOpts = root(input);
  204. if (configIsApplicable(rootOpts, dirname, context)) {
  205. flattenedConfigs.push(rootOpts);
  206. const envOpts = env(input, context.envName);
  207. if (envOpts && configIsApplicable(envOpts, dirname, context)) {
  208. flattenedConfigs.push(envOpts);
  209. }
  210. (rootOpts.options.overrides || []).forEach((_, index) => {
  211. const overrideOps = overrides(input, index);
  212. if (configIsApplicable(overrideOps, dirname, context)) {
  213. flattenedConfigs.push(overrideOps);
  214. const overrideEnvOpts = overridesEnv(input, index, context.envName);
  215. if (overrideEnvOpts && configIsApplicable(overrideEnvOpts, dirname, context)) {
  216. flattenedConfigs.push(overrideEnvOpts);
  217. }
  218. }
  219. });
  220. }
  221. if (flattenedConfigs.some(({
  222. options: {
  223. ignore,
  224. only
  225. }
  226. }) => shouldIgnore(context, ignore, only, dirname))) {
  227. return null;
  228. }
  229. const chain = emptyChain();
  230. for (const op of flattenedConfigs) {
  231. if (!mergeExtendsChain(chain, op.options, dirname, context, files)) {
  232. return null;
  233. }
  234. mergeChainOpts(chain, op);
  235. }
  236. return chain;
  237. };
  238. }
  239. function mergeExtendsChain(chain, opts, dirname, context, files) {
  240. if (opts.extends === undefined) return true;
  241. const file = (0, _files.loadConfig)(opts.extends, dirname, context.envName, context.caller);
  242. if (files.has(file)) {
  243. throw new Error(`Configuration cycle detected loading ${file.filepath}.\n` + `File already loaded following the config chain:\n` + Array.from(files, file => ` - ${file.filepath}`).join("\n"));
  244. }
  245. files.add(file);
  246. const fileChain = loadFileChain(validateExtendFile(file), context, files);
  247. files.delete(file);
  248. if (!fileChain) return false;
  249. mergeChain(chain, fileChain);
  250. return true;
  251. }
  252. function mergeChain(target, source) {
  253. target.options.push(...source.options);
  254. target.plugins.push(...source.plugins);
  255. target.presets.push(...source.presets);
  256. return target;
  257. }
  258. function mergeChainOpts(target, {
  259. options,
  260. plugins,
  261. presets
  262. }) {
  263. target.options.push(options);
  264. target.plugins.push(...plugins());
  265. target.presets.push(...presets());
  266. return target;
  267. }
  268. function emptyChain() {
  269. return {
  270. options: [],
  271. presets: [],
  272. plugins: []
  273. };
  274. }
  275. function normalizeOptions(opts) {
  276. const options = Object.assign({}, opts);
  277. delete options.extends;
  278. delete options.env;
  279. delete options.overrides;
  280. delete options.plugins;
  281. delete options.presets;
  282. delete options.passPerPreset;
  283. delete options.ignore;
  284. delete options.only;
  285. delete options.test;
  286. delete options.include;
  287. delete options.exclude;
  288. if (options.hasOwnProperty("sourceMap")) {
  289. options.sourceMaps = options.sourceMap;
  290. delete options.sourceMap;
  291. }
  292. return options;
  293. }
  294. function dedupDescriptors(items) {
  295. const map = new Map();
  296. const descriptors = [];
  297. for (const item of items) {
  298. if (typeof item.value === "function") {
  299. const fnKey = item.value;
  300. let nameMap = map.get(fnKey);
  301. if (!nameMap) {
  302. nameMap = new Map();
  303. map.set(fnKey, nameMap);
  304. }
  305. let desc = nameMap.get(item.name);
  306. if (!desc) {
  307. desc = {
  308. value: item
  309. };
  310. descriptors.push(desc);
  311. if (!item.ownPass) nameMap.set(item.name, desc);
  312. } else {
  313. desc.value = item;
  314. }
  315. } else {
  316. descriptors.push({
  317. value: item
  318. });
  319. }
  320. }
  321. return descriptors.reduce((acc, desc) => {
  322. acc.push(desc.value);
  323. return acc;
  324. }, []);
  325. }
  326. function configIsApplicable({
  327. options
  328. }, dirname, context) {
  329. return (options.test === undefined || configFieldIsApplicable(context, options.test, dirname)) && (options.include === undefined || configFieldIsApplicable(context, options.include, dirname)) && (options.exclude === undefined || !configFieldIsApplicable(context, options.exclude, dirname));
  330. }
  331. function configFieldIsApplicable(context, test, dirname) {
  332. const patterns = Array.isArray(test) ? test : [test];
  333. return matchesPatterns(context, patterns, dirname);
  334. }
  335. function shouldIgnore(context, ignore, only, dirname) {
  336. if (ignore && matchesPatterns(context, ignore, dirname)) {
  337. debug("Ignored %o because it matched one of %O from %o", context.filename, ignore, dirname);
  338. return true;
  339. }
  340. if (only && !matchesPatterns(context, only, dirname)) {
  341. debug("Ignored %o because it failed to match one of %O from %o", context.filename, only, dirname);
  342. return true;
  343. }
  344. return false;
  345. }
  346. function matchesPatterns(context, patterns, dirname) {
  347. return patterns.some(pattern => matchPattern(pattern, dirname, context.filename, context));
  348. }
  349. function matchPattern(pattern, dirname, pathToTest, context) {
  350. if (typeof pattern === "function") {
  351. return !!pattern(pathToTest, {
  352. dirname,
  353. envName: context.envName,
  354. caller: context.caller
  355. });
  356. }
  357. if (typeof pathToTest !== "string") {
  358. throw new Error(`Configuration contains string/RegExp pattern, but no filename was passed to Babel`);
  359. }
  360. if (typeof pattern === "string") {
  361. pattern = (0, _patternToRegex.default)(pattern, dirname);
  362. }
  363. return pattern.test(pathToTest);
  364. }