diff --git a/src/uncurry.js b/src/uncurry.js index 9f24c6a..912f514 100644 --- a/src/uncurry.js +++ b/src/uncurry.js @@ -48,7 +48,7 @@ function makeUncurried(binding, path, id, top) { type: "FunctionExpression", id: null, params: flattened, - body: x.body, + body: $util.cloneDeep(x.body), loc: top.loc }, unique: true @@ -206,14 +206,22 @@ module.exports = function (babel) { var created = false; for (var i = uncurried.params.length - 1; i >= args.length; --i) { - // TODO make a copy of the params ? - pushAll(path, statements, flattened, uncurried.params[i]); + var params = uncurried.params[i].map(function(node){ + if (node.type === "Identifier"){ + var uid = path.scope.generateUidIdentifier(node.name); + return Object.assign({}, node, {name: uid.name}); + } else { + return node; + } + }); + + pushAll(path, statements, flattened, params); body = { type: "FunctionExpression", id: null, // TODO make a copy of the params ? - params: uncurried.params[i], + params: params, body: { type: "BlockStatement", body: [{ diff --git a/src/util.js b/src/util.js index eea0185..1412665 100644 --- a/src/util.js +++ b/src/util.js @@ -130,6 +130,30 @@ exports.print = function (node) { }).code; }; +// Stolen from babel-types +exports.cloneDeep = function cloneDeep(node) { + if (!node) return node; + const newNode = {}; + + Object.keys(node).forEach(key => { + if (key[0] === "_") return; + + let val = node[key]; + + if (val) { + if (val.type) { + val = cloneDeep(val); + } else if (Array.isArray(val)) { + val = val.map(cloneDeep); + } + } + + newNode[key] = val; + }); + + return newNode; +}; + // https://github.com/babel/babylon/blob/master/ast/spec.md // TODO Import ? diff --git a/unittest/uncurry.js b/unittest/uncurry.js new file mode 100644 index 0000000..a75db0e --- /dev/null +++ b/unittest/uncurry.js @@ -0,0 +1,123 @@ +const assert = require("assert"); +const $babel = require("babel-core"); +const uncurry = require("../src/uncurry.js"); + +const testCases = [{ + codeIn: function test(){ + const sum = function (a){ + return function(b){ + return a + b; + }; + }; + sum(5)(7); + }, + result: function test(){ + const _sum_uncurried = function (a,b){ + return a + b; + }; + const sum = function (a){ + return function(b){ + return _sum_uncurried(a,b); + }; + }; + _sum_uncurried(5,7); + } +}, { + codeIn: function test(){ + const sum = function(a){ + return function(b){ + return a + b; + }; + }; + var b = 5; + const sum5 = sum(b); + sum5(7); + }, + result: function test(){ + const _sum_uncurried = function (a, b) { + return a + b; + }; + + const sum = function (a) { + return function (b) { + return _sum_uncurried(a, b); + }; + }; + var b = 5; + const sum5 = function (_b) { + return _sum_uncurried(b, _b); + }; + sum5(7); + } +}, { + codeIn: function test(){ + var sum = function(a){ + return function(b){ + var sumInner = function(a){ + return function(b){ + return sum(a, b); + }; + }; + return sumInner(a)(b); + }; + }; + sum(5)(7); + }, + result: function test() { + const _sum_uncurried = function (a, b) { + const _sumInner_uncurried2 = function (a, b) { + return sum(a, b); + }; + + var sumInner = function (a) { + return function (b) { + return _sumInner_uncurried2(a, b); + }; + }; + return _sumInner_uncurried2(a, b); + }; + + var sum = function (a) { + return function (b) { + const _sumInner_uncurried = function (a, b) { + return sum(a, b); + }; + + return _sum_uncurried(a, b); + }; + }; + _sum_uncurried(5, 7); + } +}]; + +testCases.forEach(async function(testCase){ + var result = $babel.transform(testCase.codeIn.toString(), { + babelrc: false, + code: true, + ast: false, + sourceMaps: false, + plugins: [ + [uncurry, {debug: true}] + ] + }); + + assert(strip(result.code) === strip(testCase.result.toString()), [ + "", + "", + "------ The resulting code:", + "", + result.code, + "", + "------ is not equal to:", + "", + testCase.result.toString(), + "", + "------ Stripped: ", + strip(result.code), + strip(testCase.result.toString()) + ].join("\n")); +}); + +function strip(string){ + return string.replace(/\s/g, "").replace(/;/g, ""); +}