UNPKG

15.6 kBJavaScriptView Raw
1import * as babel from '@babel/core';
2import { createFilter } from '@rollup/pluginutils';
3import { addNamed } from '@babel/helper-module-imports';
4
5function _defineProperty(obj, key, value) {
6 if (key in obj) {
7 Object.defineProperty(obj, key, {
8 value: value,
9 enumerable: true,
10 configurable: true,
11 writable: true
12 });
13 } else {
14 obj[key] = value;
15 }
16
17 return obj;
18}
19
20function ownKeys(object, enumerableOnly) {
21 var keys = Object.keys(object);
22
23 if (Object.getOwnPropertySymbols) {
24 var symbols = Object.getOwnPropertySymbols(object);
25 if (enumerableOnly) symbols = symbols.filter(function (sym) {
26 return Object.getOwnPropertyDescriptor(object, sym).enumerable;
27 });
28 keys.push.apply(keys, symbols);
29 }
30
31 return keys;
32}
33
34function _objectSpread2(target) {
35 for (var i = 1; i < arguments.length; i++) {
36 var source = arguments[i] != null ? arguments[i] : {};
37
38 if (i % 2) {
39 ownKeys(Object(source), true).forEach(function (key) {
40 _defineProperty(target, key, source[key]);
41 });
42 } else if (Object.getOwnPropertyDescriptors) {
43 Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
44 } else {
45 ownKeys(Object(source)).forEach(function (key) {
46 Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
47 });
48 }
49 }
50
51 return target;
52}
53
54function _objectWithoutPropertiesLoose(source, excluded) {
55 if (source == null) return {};
56 var target = {};
57 var sourceKeys = Object.keys(source);
58 var key, i;
59
60 for (i = 0; i < sourceKeys.length; i++) {
61 key = sourceKeys[i];
62 if (excluded.indexOf(key) >= 0) continue;
63 target[key] = source[key];
64 }
65
66 return target;
67}
68
69function _objectWithoutProperties(source, excluded) {
70 if (source == null) return {};
71
72 var target = _objectWithoutPropertiesLoose(source, excluded);
73
74 var key, i;
75
76 if (Object.getOwnPropertySymbols) {
77 var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
78
79 for (i = 0; i < sourceSymbolKeys.length; i++) {
80 key = sourceSymbolKeys[i];
81 if (excluded.indexOf(key) >= 0) continue;
82 if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
83 target[key] = source[key];
84 }
85 }
86
87 return target;
88}
89
90const BUNDLED = 'bundled';
91const INLINE = 'inline';
92const RUNTIME = 'runtime';
93const EXTERNAL = 'external'; // NOTE: DO NOT REMOVE the null character `\0` as it may be used by other plugins
94// e.g. https://github.com/rollup/rollup-plugin-node-resolve/blob/313a3e32f432f9eb18cc4c231cc7aac6df317a51/src/index.js#L74
95
96const HELPERS = '\0rollupPluginBabelHelpers.js';
97
98function importHelperPlugin({
99 types: t
100}) {
101 return {
102 pre(file) {
103 const cachedHelpers = {};
104 file.set('helperGenerator', name => {
105 if (!file.availableHelper(name)) {
106 return null;
107 }
108
109 if (cachedHelpers[name]) {
110 return t.cloneNode(cachedHelpers[name]);
111 }
112
113 return cachedHelpers[name] = addNamed(file.path, name, HELPERS);
114 });
115 }
116
117 };
118}
119
120const addBabelPlugin = (options, plugin) => {
121 return _objectSpread2(_objectSpread2({}, options), {}, {
122 plugins: options.plugins.concat(plugin)
123 });
124};
125const warned = {};
126function warnOnce(ctx, msg) {
127 if (warned[msg]) return;
128 warned[msg] = true;
129 ctx.warn(msg);
130}
131const regExpCharactersRegExp = /[\\^$.*+?()[\]{}|]/g;
132const escapeRegExpCharacters = str => str.replace(regExpCharactersRegExp, '\\$&');
133function stripQuery(id) {
134 // strip query params from import
135 const [bareId, query] = id.split('?');
136 const suffix = `${query ? `?${query}` : ''}`;
137 return {
138 bareId,
139 query,
140 suffix
141 };
142}
143
144const MODULE_ERROR = 'Rollup requires that your Babel configuration keeps ES6 module syntax intact. ' + 'Unfortunately it looks like your configuration specifies a module transformer ' + 'to replace ES6 modules with another module format. To continue you have to disable it.' + '\n\n' + "Most commonly it's a CommonJS transform added by @babel/preset-env - " + 'in such case you should disable it by adding `modules: false` option to that preset ' + '(described in more detail here - https://github.com/rollup/plugins/tree/master/packages/babel#modules ).';
145const UNEXPECTED_ERROR = 'An unexpected situation arose. Please raise an issue at ' + 'https://github.com/rollup/plugins/issues. Thanks!';
146const PREFLIGHT_TEST_STRING = '__ROLLUP__PREFLIGHT_CHECK_DO_NOT_TOUCH__';
147const PREFLIGHT_INPUT = `export default "${PREFLIGHT_TEST_STRING}";`;
148
149function helpersTestTransform() {
150 return {
151 visitor: {
152 StringLiteral(path, state) {
153 if (path.node.value === PREFLIGHT_TEST_STRING) {
154 path.replaceWith(state.file.addHelper('inherits'));
155 }
156 }
157
158 }
159 };
160}
161
162const mismatchError = (actual, expected, filename) => `You have declared using "${expected}" babelHelpers, but transforming ${filename} resulted in "${actual}". Please check your configuration.`; // Revert to /\/helpers\/(esm\/)?inherits/ when Babel 8 gets released, this was fixed in https://github.com/babel/babel/issues/14185
163
164
165const inheritsHelperRe = /[\\/]+helpers[\\/]+(esm[\\/]+)?inherits/;
166async function preflightCheck(ctx, babelHelpers, transformOptions) {
167 const finalOptions = addBabelPlugin(transformOptions, helpersTestTransform);
168 const check = (await babel.transformAsync(PREFLIGHT_INPUT, finalOptions)).code; // Babel sometimes splits ExportDefaultDeclaration into 2 statements, so we also check for ExportNamedDeclaration
169
170 if (!/export (d|{)/.test(check)) {
171 ctx.error(MODULE_ERROR);
172 }
173
174 if (inheritsHelperRe.test(check)) {
175 if (babelHelpers === RUNTIME) {
176 return;
177 }
178
179 ctx.error(mismatchError(RUNTIME, babelHelpers, transformOptions.filename));
180 }
181
182 if (check.includes('babelHelpers.inherits')) {
183 if (babelHelpers === EXTERNAL) {
184 return;
185 }
186
187 ctx.error(mismatchError(EXTERNAL, babelHelpers, transformOptions.filename));
188 } // test unminifiable string content
189
190
191 if (check.includes('Super expression must either be null or a function')) {
192 if (babelHelpers === INLINE || babelHelpers === BUNDLED) {
193 return;
194 }
195
196 if (babelHelpers === RUNTIME && !transformOptions.plugins.length) {
197 ctx.error(`You must use the \`@babel/plugin-transform-runtime\` plugin when \`babelHelpers\` is "${RUNTIME}".\n`);
198 }
199
200 ctx.error(mismatchError(INLINE, babelHelpers, transformOptions.filename));
201 }
202
203 ctx.error(UNEXPECTED_ERROR);
204}
205
206async function transformCode(inputCode, babelOptions, overrides, customOptions, ctx, finalizeOptions) {
207 // loadPartialConfigAsync has become available in @babel/core@7.8.0
208 const config = await (babel.loadPartialConfigAsync || babel.loadPartialConfig)(babelOptions); // file is ignored by babel
209
210 if (!config) {
211 return null;
212 }
213
214 let transformOptions = !overrides.config ? config.options : await overrides.config.call(ctx, config, {
215 code: inputCode,
216 customOptions
217 });
218
219 if (finalizeOptions) {
220 transformOptions = await finalizeOptions(transformOptions);
221 }
222
223 if (!overrides.result) {
224 const {
225 code,
226 map
227 } = await babel.transformAsync(inputCode, transformOptions);
228 return {
229 code,
230 map
231 };
232 }
233
234 const result = await babel.transformAsync(inputCode, transformOptions);
235 const {
236 code,
237 map
238 } = await overrides.result.call(ctx, result, {
239 code: inputCode,
240 customOptions,
241 config,
242 transformOptions
243 });
244 return {
245 code,
246 map
247 };
248}
249
250const unpackOptions = (_ref = {}) => {
251 let {
252 extensions = babel.DEFAULT_EXTENSIONS,
253 // rollup uses sourcemap, babel uses sourceMaps
254 // just normalize them here so people don't have to worry about it
255 sourcemap = true,
256 sourcemaps = true,
257 sourceMap = true,
258 sourceMaps = true
259 } = _ref,
260 rest = _objectWithoutProperties(_ref, ["extensions", "sourcemap", "sourcemaps", "sourceMap", "sourceMaps"]);
261
262 return _objectSpread2(_objectSpread2({
263 extensions,
264 plugins: [],
265 sourceMaps: sourcemap && sourcemaps && sourceMap && sourceMaps
266 }, rest), {}, {
267 caller: _objectSpread2({
268 name: '@rollup/plugin-babel'
269 }, rest.caller)
270 });
271};
272
273const warnAboutDeprecatedHelpersOption = ({
274 deprecatedOption,
275 suggestion
276}) => {
277 // eslint-disable-next-line no-console
278 console.warn(`\`${deprecatedOption}\` has been removed in favor a \`babelHelpers\` option. Try changing your configuration to \`${suggestion}\`. ` + `Refer to the documentation to learn more: https://github.com/rollup/plugins/tree/master/packages/babel#babelhelpers`);
279};
280
281const unpackInputPluginOptions = (_ref2, rollupVersion) => {
282 let {
283 skipPreflightCheck = false
284 } = _ref2,
285 rest = _objectWithoutProperties(_ref2, ["skipPreflightCheck"]);
286
287 if ('runtimeHelpers' in rest) {
288 warnAboutDeprecatedHelpersOption({
289 deprecatedOption: 'runtimeHelpers',
290 suggestion: `babelHelpers: 'runtime'`
291 });
292 } else if ('externalHelpers' in rest) {
293 warnAboutDeprecatedHelpersOption({
294 deprecatedOption: 'externalHelpers',
295 suggestion: `babelHelpers: 'external'`
296 });
297 } else if (!rest.babelHelpers) {
298 // eslint-disable-next-line no-console
299 console.warn("babelHelpers: 'bundled' option was used by default. It is recommended to configure this option explicitly, read more here: " + 'https://github.com/rollup/plugins/tree/master/packages/babel#babelhelpers');
300 }
301
302 return unpackOptions(_objectSpread2(_objectSpread2({}, rest), {}, {
303 skipPreflightCheck,
304 babelHelpers: rest.babelHelpers || BUNDLED,
305 caller: _objectSpread2({
306 supportsStaticESM: true,
307 supportsDynamicImport: true,
308 supportsTopLevelAwait: true,
309 // todo: remove version checks for 1.20 - 1.25 when we bump peer deps
310 supportsExportNamespaceFrom: !rollupVersion.match(/^1\.2[0-5]\./)
311 }, rest.caller)
312 }));
313};
314
315const unpackOutputPluginOptions = (options, {
316 format
317}) => unpackOptions(_objectSpread2(_objectSpread2({
318 configFile: false,
319 sourceType: format === 'es' ? 'module' : 'script'
320}, options), {}, {
321 caller: _objectSpread2({
322 supportsStaticESM: format === 'es'
323 }, options.caller)
324}));
325
326function getOptionsWithOverrides(pluginOptions = {}, overrides = {}) {
327 if (!overrides.options) return {
328 customOptions: null,
329 pluginOptionsWithOverrides: pluginOptions
330 };
331 const overridden = overrides.options(pluginOptions);
332
333 if (typeof overridden.then === 'function') {
334 throw new Error(".options hook can't be asynchronous. It should return `{ customOptions, pluginsOptions }` synchronously.");
335 }
336
337 return {
338 customOptions: overridden.customOptions || null,
339 pluginOptionsWithOverrides: overridden.pluginOptions || pluginOptions
340 };
341}
342
343const returnObject = () => {
344 return {};
345};
346
347function createBabelInputPluginFactory(customCallback = returnObject) {
348 const overrides = customCallback(babel);
349 return pluginOptions => {
350 const {
351 customOptions,
352 pluginOptionsWithOverrides
353 } = getOptionsWithOverrides(pluginOptions, overrides);
354 let babelHelpers;
355 let babelOptions;
356 let filter;
357 let skipPreflightCheck;
358 return {
359 name: 'babel',
360
361 options() {
362 // todo: remove options hook and hoist declarations when version checks are removed
363 let exclude;
364 let include;
365 let extensions;
366 let customFilter;
367
368 var _unpackInputPluginOpt = unpackInputPluginOptions(pluginOptionsWithOverrides, this.meta.rollupVersion);
369
370 ({
371 exclude,
372 extensions,
373 babelHelpers,
374 include,
375 filter: customFilter,
376 skipPreflightCheck
377 } = _unpackInputPluginOpt);
378 babelOptions = _objectWithoutProperties(_unpackInputPluginOpt, ["exclude", "extensions", "babelHelpers", "include", "filter", "skipPreflightCheck"]);
379 const extensionRegExp = new RegExp(`(${extensions.map(escapeRegExpCharacters).join('|')})$`);
380
381 if (customFilter && (include || exclude)) {
382 throw new Error('Could not handle include or exclude with custom filter together');
383 }
384
385 const userDefinedFilter = typeof customFilter === 'function' ? customFilter : createFilter(include, exclude);
386
387 filter = id => extensionRegExp.test(stripQuery(id).bareId) && userDefinedFilter(id);
388
389 return null;
390 },
391
392 resolveId(id) {
393 if (id !== HELPERS) {
394 return null;
395 }
396
397 return id;
398 },
399
400 load(id) {
401 if (id !== HELPERS) {
402 return null;
403 }
404
405 return babel.buildExternalHelpers(null, 'module');
406 },
407
408 transform(code, filename) {
409 if (!filter(filename)) return null;
410 if (filename === HELPERS) return null;
411 return transformCode(code, _objectSpread2(_objectSpread2({}, babelOptions), {}, {
412 filename
413 }), overrides, customOptions, this, async transformOptions => {
414 if (!skipPreflightCheck) {
415 await preflightCheck(this, babelHelpers, transformOptions);
416 }
417
418 return babelHelpers === BUNDLED ? addBabelPlugin(transformOptions, importHelperPlugin) : transformOptions;
419 });
420 }
421
422 };
423 };
424}
425
426function getRecommendedFormat(rollupFormat) {
427 switch (rollupFormat) {
428 case 'amd':
429 return 'amd';
430
431 case 'iife':
432 case 'umd':
433 return 'umd';
434
435 case 'system':
436 return 'systemjs';
437
438 default:
439 return '<module format>';
440 }
441}
442
443function createBabelOutputPluginFactory(customCallback = returnObject) {
444 const overrides = customCallback(babel);
445 return pluginOptions => {
446 const {
447 customOptions,
448 pluginOptionsWithOverrides
449 } = getOptionsWithOverrides(pluginOptions, overrides);
450 return {
451 name: 'babel',
452
453 renderStart(outputOptions) {
454 const {
455 extensions,
456 include,
457 exclude,
458 allowAllFormats
459 } = pluginOptionsWithOverrides;
460
461 if (extensions || include || exclude) {
462 warnOnce(this, 'The "include", "exclude" and "extensions" options are ignored when transforming the output.');
463 }
464
465 if (!allowAllFormats && outputOptions.format !== 'es' && outputOptions.format !== 'cjs') {
466 this.error(`Using Babel on the generated chunks is strongly discouraged for formats other than "esm" or "cjs" as it can easily break wrapper code and lead to accidentally created global variables. Instead, you should set "output.format" to "esm" and use Babel to transform to another format, e.g. by adding "presets: [['@babel/env', { modules: '${getRecommendedFormat(outputOptions.format)}' }]]" to your Babel options. If you still want to proceed, add "allowAllFormats: true" to your plugin options.`);
467 }
468 },
469
470 renderChunk(code, chunk, outputOptions) {
471 /* eslint-disable no-unused-vars */
472 const _unpackOutputPluginOp = unpackOutputPluginOptions(pluginOptionsWithOverrides, outputOptions),
473 babelOptions = _objectWithoutProperties(_unpackOutputPluginOp, ["allowAllFormats", "exclude", "extensions", "externalHelpers", "externalHelpersWhitelist", "include", "runtimeHelpers"]);
474 /* eslint-enable no-unused-vars */
475
476
477 return transformCode(code, babelOptions, overrides, customOptions, this);
478 }
479
480 };
481 };
482} // export this for symmetry with output-related exports
483
484
485const getBabelInputPlugin = createBabelInputPluginFactory();
486const getBabelOutputPlugin = createBabelOutputPluginFactory();
487
488export { getBabelInputPlugin as babel, createBabelInputPluginFactory, createBabelOutputPluginFactory, getBabelInputPlugin as default, getBabelInputPlugin, getBabelOutputPlugin };