From a20a5e15aa4c2a268b4ff9982aeb5459716da7e8 Mon Sep 17 00:00:00 2001 From: cpojer Date: Fri, 22 May 2015 18:11:57 -0700 Subject: [PATCH] Add transform to fix up misuse of arguments in arrow functions. --- test/__tests__/transform-tests.js | 4 + test/arrow-function-arguments-test.js | 28 +++++++ test/arrow-function-arguments-test.output.js | 28 +++++++ transforms/arrow-function-arguments.js | 84 ++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 test/arrow-function-arguments-test.js create mode 100644 test/arrow-function-arguments-test.output.js create mode 100644 transforms/arrow-function-arguments.js diff --git a/test/__tests__/transform-tests.js b/test/__tests__/transform-tests.js index b950f7b..76b7b2a 100644 --- a/test/__tests__/transform-tests.js +++ b/test/__tests__/transform-tests.js @@ -56,4 +56,8 @@ describe('Transform Tests', () => { test('insert-super', 'insert-super-test'); }); + it('transforms the "arrow function arguments" tests correctly', () => { + test('arrow-function-arguments', 'arrow-function-arguments-test'); + }); + }); diff --git a/test/arrow-function-arguments-test.js b/test/arrow-function-arguments-test.js new file mode 100644 index 0000000..1b44b50 --- /dev/null +++ b/test/arrow-function-arguments-test.js @@ -0,0 +1,28 @@ +'use strict'; + +var fn1 = () => console.log(arguments); + +var fn2 = (a, b, c) => { + console.log(arguments); + + return arguments; +}; + +var fn3 = (a, b, args) => { + console.log(args); + + return arguments; +}; + +var fn4 = function(a, b, c) { + console.log(arguments); + var fn5 = () => arguments; + var fn6 = () => (function() { return arguments; }); + class A { + constructor() { + console.log(arguments); + } + } +}; + +var fn5 = (a, ...b) => arguments; diff --git a/test/arrow-function-arguments-test.output.js b/test/arrow-function-arguments-test.output.js new file mode 100644 index 0000000..16c144f --- /dev/null +++ b/test/arrow-function-arguments-test.output.js @@ -0,0 +1,28 @@ +'use strict'; + +var fn1 = (...args) => console.log(args); + +var fn2 = (a, b, c, ...args) => { + console.log([a, b, c, ...args]); + + return [a, b, c, ...args]; +}; + +var fn3 = (a, b, args) => { + console.log(args); + + return arguments; +}; + +var fn4 = function(a, b, c) { + console.log(arguments); + var fn5 = (...args) => args; + var fn6 = () => (function() { return arguments; }); + class A { + constructor() { + console.log(arguments); + } + } +}; + +var fn5 = (a, ...b) => [a, ...b]; diff --git a/transforms/arrow-function-arguments.js b/transforms/arrow-function-arguments.js new file mode 100644 index 0000000..a5ab957 --- /dev/null +++ b/transforms/arrow-function-arguments.js @@ -0,0 +1,84 @@ +// ----------------------------------------------------------------------------- +// Update arrow function arguments to args +function arrowFunctionArguments(file, api, options) { + const j = api.jscodeshift; + + const printOptions = options.printOptions || {quote: 'single'}; + const root = j(file.source); + + const ARGUMENTS = 'arguments'; + const ARGS = 'args'; + + const createArrowFunctionExpression = (fn, args) => { + const arrow = j.arrowFunctionExpression( + (fn.params || []).concat( + // https://github.com/benjamn/recast/pull/179 + // j.spreadElementPattern(args) + ), + fn.body, + fn.generator + ); + arrow.rest = args; + return arrow; + }; + + const filterArrowFunctions = path => { + while (path.parent) { + switch (path.value.type) { + case 'ArrowFunctionExpression': + if (j(path).find(j.Identifier, {name: ARGS}).size()) { + console.error( + file.path + ': arrow function uses "' + ARGS + '" already. ' + + 'Please rename this identifier first.' + ); + return false; + } + return true; + case 'FunctionExpression': + case 'MethodDeclaration': + case 'Function': + case 'FunctionDeclaration': + return false; + default: + break; + } + path = path.parent; + } + return false; + }; + + const updateArgumentsCalls = path => { + var afPath = path; + while (afPath.parent) { + if (afPath.value.type == 'ArrowFunctionExpression') { + break; + } + afPath = afPath.parent; + } + + const {value: fn} = afPath; + const {params} = fn; + const args = fn.rest || j.identifier(ARGS); + j(afPath).replaceWith(createArrowFunctionExpression(fn, args)); + + if (params.length) { + j(path).replaceWith( + j.arrayExpression(params.concat( + j.spreadElement(args) + )) + ); + } else { + j(path).replaceWith(args); + } + }; + + const didTransform = root + .find(j.Identifier, {name: ARGUMENTS}) + .filter(filterArrowFunctions) + .forEach(updateArgumentsCalls) + .size() > 0; + + return didTransform ? root.toSource(printOptions) + '\n' : null; +} + +module.exports = arrowFunctionArguments;