1 | var _ = require("lodash");
|
2 | var path = require("path");
|
3 | var babel = require("babel-standalone");
|
4 |
|
5 | /**
|
6 | * A Babel plugin as defined in `babelOptions.plugins`
|
7 | * @typedef {string|Function|<string, Object>[]|<Function, Object>[]} BabelPlugin
|
8 | */
|
9 |
|
10 | /**
|
11 | * The options needed to load the babel plugins not bundled in babel-standalone
|
12 | * @typedef {Object} LoadCustomPluginsOptions
|
13 | * @property {string} baseURL The loader baseURL value
|
14 | * @property {string} loaderEnv The loader env value
|
15 | * @property {Object} babelOptions The babel configuration options
|
16 | */
|
17 |
|
18 | /**
|
19 | * Returns a list of babel plugins to be used by transpile
|
20 | * @param {LoadCustomPluginsOptions} opts The options object
|
21 | * @return {BabelPlugin[]} The list of plugins to be used by transpile
|
22 | */
|
23 | module.exports = function(opts) {
|
24 | var processed = [];
|
25 | var babelOptions = opts.babelOptions || {};
|
26 | var babelEnvConfig = babelOptions.env || {};
|
27 |
|
28 | var babelEnv = process.env.BABEL_ENV ||
|
29 | process.env.NODE_ENV ||
|
30 | opts.loaderEnv;
|
31 |
|
32 | // process plugins in babelOptions.plugins
|
33 | processed = processed.concat(processPlugins(opts.baseURL, babelOptions.plugins));
|
34 |
|
35 | // process environment dependant plugins
|
36 | _.keys(babelEnvConfig).forEach(function(envName) {
|
37 | if (envName === babelEnv) {
|
38 | var plugins = babelEnvConfig[envName].plugins || [];
|
39 | processed = processed.concat(processPlugins(opts.baseURL, plugins));
|
40 | }
|
41 | });
|
42 |
|
43 | return processed;
|
44 | };
|
45 |
|
46 | /**
|
47 | * Collect builtin plugin names and non builtin plugin functions
|
48 | * @param {string} baseURL The loader baseURL value
|
49 | * @param {BabelPlugin[]} plugins An array of babel plugins
|
50 | * @return {BabelPlugin[]} An array of babel plugins filtered by the babel
|
51 | * environment name and with non-builtins replaced
|
52 | * by their respective functions
|
53 | */
|
54 | function processPlugins(baseURL, plugins) {
|
55 | var normalized = [];
|
56 |
|
57 | // path.resolve does not work correctly if baseURL starts with "file:"
|
58 | baseURL = baseURL.replace("file:", "");
|
59 | plugins = plugins || [];
|
60 |
|
61 | plugins.forEach(function(plugin) {
|
62 | var name = getPluginName(plugin);
|
63 |
|
64 | if (isPluginFunction(plugin) || isBuiltinPlugin(name)) {
|
65 | normalized.push(plugin);
|
66 | }
|
67 | else if (!isBuiltinPlugin(name)) {
|
68 | var npmPluginNameOrPath = getNpmPluginNameOrPath(baseURL, name);
|
69 |
|
70 | // load the plugin!
|
71 | var pluginFn = require(npmPluginNameOrPath);
|
72 |
|
73 | if (_.isString(plugin)) {
|
74 | normalized.push(pluginFn);
|
75 | }
|
76 | else if (_.isArray(plugin)) {
|
77 | // [ pluginName, pluginOptions ]
|
78 | normalized.push([pluginFn, plugin[1]]);
|
79 | }
|
80 | }
|
81 | });
|
82 |
|
83 | return normalized;
|
84 | }
|
85 |
|
86 | /**
|
87 | * Whether the plugin function was provided instead of the plugin name
|
88 | * @param {BabelPlugin} plugin
|
89 | * @return {boolean} `true` if a plugin function was provided, `false` otherwise
|
90 | */
|
91 | function isPluginFunction(plugin) {
|
92 | return _.isFunction(plugin) || _.isFunction(_.head(plugin));
|
93 | }
|
94 |
|
95 | function getNpmPluginNameOrPath(baseURL, name) {
|
96 | var isPath = /\//;
|
97 | var isNpmPluginName = /^(?:babel-plugin-)/;
|
98 |
|
99 | if (isPath.test(name)) {
|
100 | return path.resolve(baseURL, name);
|
101 | }
|
102 | else if (!isNpmPluginName.test(name)) {
|
103 | return "babel-plugin-" + name;
|
104 | }
|
105 |
|
106 | return name;
|
107 | }
|
108 |
|
109 | /**
|
110 | * Gets the plugin name
|
111 | * @param {BabelPlugin} plugin An item inside of `babelOptions.plugins`
|
112 | * @return {?string} The plugin name
|
113 | */
|
114 | function getPluginName(plugin) {
|
115 | if (isPluginFunction(plugin)) return null;
|
116 |
|
117 | return _.isString(plugin) ? plugin : _.head(plugin);
|
118 | }
|
119 |
|
120 | /**
|
121 | * Whether the plugin is built in babel-standalone
|
122 | *
|
123 | * @param {string} name A plugin path or shorthand name.
|
124 | * @return {boolean} `true` if plugin is bundled, `false` otherwise
|
125 | *
|
126 | * Babel plugins can be set using the following variations:
|
127 | *
|
128 | * 1) the npm plugin name, which by convention starts with `babel-plugin-`
|
129 | * (e.g babel-plugin-transform-decorators).
|
130 | * 2) A shorthand name, which is the npm name without the `babel-plugin-` prefix
|
131 | * 3) A path to where the plugin function is defined
|
132 | *
|
133 | * babel-standalone registers the plugins bundled with it using the shorthand
|
134 | * version.
|
135 | */
|
136 | function isBuiltinPlugin(name) {
|
137 | var isNpmPluginName = /^(?:babel-plugin-)/;
|
138 | var availablePlugins = babel.availablePlugins;
|
139 |
|
140 | // if the full npm plugin name was set in `babelOptions.plugins`, use the
|
141 | // shorthand to check whether the plugin is included in babel-standalone;
|
142 | // shorthand plugin names are used internally by babel-standalone
|
143 | var shorthand = isNpmPluginName.test(name) ?
|
144 | name.replace("babel-plugin-", "") :
|
145 | name;
|
146 |
|
147 | return !!availablePlugins[shorthand];
|
148 | }
|