UNPKG

2.62 kBJavaScriptView Raw
1/**
2 * @type {import('@babel/core')}
3 */
4
5/**
6 * Transform ...rest parameters to [].slice.call(arguments,offset).
7 * Demo: https://astexplorer.net/#/gist/70aaa0306db9a642171ef3e2f35df2e0/576c150f647e4936fa6960e0453a11cdc5d81f21
8 * Benchmark: https://jsperf.com/rest-arguments-babel-pr-9152/4
9 * @param {object} opts
10 * @param {babel.template} opts.template
11 * @param {babel.types} opts.types
12 * @returns {babel.PluginObj}
13 */
14export default function fastRestTransform({ template, types: t }) {
15 const slice = template`var IDENT = Array.prototype.slice;`;
16
17 const VISITOR = {
18 RestElement(path, state) {
19 if (path.parentKey !== 'params') return;
20
21 // Create a global _slice alias
22 let slice = state.get('slice');
23 if (!slice) {
24 slice = path.scope.generateUidIdentifier('slice');
25 state.set('slice', slice);
26 }
27
28 // _slice.call(arguments) or _slice.call(arguments, 1)
29 const args = [t.identifier('arguments')];
30 if (path.key) args.push(t.numericLiteral(path.key));
31 const sliced = t.callExpression(
32 t.memberExpression(t.clone(slice), t.identifier('call')),
33 args,
34 );
35
36 const ident = path.node.argument;
37 const binding = path.scope.getBinding(ident.name);
38
39 if (binding.referencePaths.length !== 0) {
40 // arguments access requires a non-Arrow function:
41 const func = path.parentPath;
42 if (t.isArrowFunctionExpression(func)) {
43 func.arrowFunctionToExpression();
44 }
45
46 if (binding.constant && binding.referencePaths.length === 1) {
47 // one usage, never assigned - replace usage inline
48 binding.referencePaths[0].replaceWith(sliced);
49 } else {
50 // unknown usage, create a binding
51 const decl = t.variableDeclaration('var', [
52 t.variableDeclarator(t.clone(ident), sliced),
53 ]);
54 func.get('body').unshiftContainer('body', decl);
55 }
56 }
57
58 path.remove();
59 },
60 };
61
62 return {
63 name: 'transform-fast-rest',
64 visitor: {
65 Program(path, state) {
66 const childState = new Map();
67 const useHelper = state.opts.helper === true; // defaults to false
68
69 if (!useHelper) {
70 let inlineHelper;
71 if (state.opts.literal === false) {
72 inlineHelper = template.expression.ast`Array.prototype.slice`;
73 } else {
74 inlineHelper = template.expression.ast`[].slice`;
75 }
76 childState.set('slice', inlineHelper);
77 }
78
79 path.traverse(VISITOR, childState);
80
81 const name = childState.get('slice');
82 if (name && useHelper) {
83 const helper = slice({ IDENT: name });
84 t.addComment(helper.declarations[0].init, 'leading', '#__PURE__');
85 path.unshiftContainer('body', helper);
86 }
87 },
88 },
89 };
90}