var fs = require('fs'); var path = require('path'); var requireRelative = require('require-relative'); var normalizePreset = require('./normalize-preset'); var join = path.join; var dirname = path.dirname; var relative = path.relative; function getModulePath(filepath) { return filepath.replace(/(.*([\\/]node_modules|\.\.)[\\/](@[^\\/]+[\\/])?[^\\/]+)([\\/].*)?$/g, '$1') } // Attempt to require a module, returning false on error function req(name) { try { return require(name); } catch (err) { return false; } } // Attempt to resolve a module, returning `undefined` on error function resolve(name, relativeTo) { var path = false; try { path = requireRelative.resolve(name, relativeTo); } catch (err) { console.log('resolve failed for "'+name+'": '+err); } return path; } // fast npm always use symlink // and it resulted in module duplicated // or alias function fillAliasMap(path, list, map) { return list.reduce(function (symlinkedList, mod) { var realPath = relative(path, fs.realpathSync(join(path, mod))); if (realPath !== mod) { symlinkedList[realPath] = (mod); } return symlinkedList; }, map || {}); } // Get a list of child module names for the given module path function getChildren(path, type, alias) { var modules; try { modules = fs.readdirSync(join(path, 'node_modules')); } catch (err) { path = path.replace(/([\\/]node_modules)([\\/].*)?$/g, '$1'); modules = fs.readdirSync(path); } var children = (modules || []) .filter( realFile ) .sort( reverseSorter(type==='plugin' ? isPlugin : isPreset) ); fillAliasMap(path, children, alias); return children; } // is a filename an actual file function realFile(f) { return f && f.charAt(0)!=='.'; } // ascending sort based on the truthiness of a function function reverseSorter(comparison) { return function(a, b) { var ca = comparison(a), cb = comparison(b); return ca===cb ? 0 : (ca ? 1 : cb ? -1 : 0); } } function isPreset(name) { return name && name.match(/^babel\-preset\-/g); } function isPlugin(name) { return name && name.match(/^babel\-plugin\-/g); } // find the matching module *instance* in a list of module paths, remove and return it function findAndRemove(preset, path, list) { for (var i=list.length; i--; ) { var p = resolve(list[i], path), v = p && req(p); if (v && v===preset) { list.splice(i, 1); return p; } } } /** Resolve & serialize a Babel preset to a filename-based representation. * Nested filepaths are relative to `relativeTo` if specified. * Presets referenced as Strings (uncommon) are treated as dependencies of the preset that returned them. */ function loadPreset(name, opts, relativeTo) { var path = resolve(name, relativeTo), mod = normalizePreset(path && req(path), null, opts.options); if (!mod) throw new Error('Preset "'+name+'" not found.'); path = dirname(path); var out = { alias: {} }; if (mod.presets) { var availablePresets = getChildren(path, 'preset', out.alias); out.presets = mod.presets.map(function(preset) { if (typeof preset!=='string') { preset = findAndRemove(preset, path, availablePresets); } return loadPreset(preset, opts, path); }); } if (mod.plugins) { var availablePlugins = getChildren(path, 'plugin', out.alias); out.plugins = mod.plugins.map(function(plugin) { var name = Array.isArray(plugin) ? plugin[0] : plugin; if (typeof name!=='string') { if (name._original_name) { // console.log('using _original_name: ', name._original_name); name = name._original_name; } else { name = findAndRemove(name, path, availablePlugins); } } if (!name) return plugin; name = resolve(name, path); name = getModulePath(name); if (opts) { if (opts.cwd) name = relative(opts.cwd, name); if (opts.transform) name = opts.transform(name); } return Array.isArray(plugin) ? [name].concat(plugin.slice(1)) : name; }); } return out; } module.exports = loadPreset;