Partial application plugin (#9474)

* add partial application syntax and some tests

remove unnecessary error message and hasPartial function from parseNewArguments

add types for PartialExpression

Update the tests

rename PartialExpression to Partial

move Partial from expressions to types and rename to ArgumentPlaceholder

add tests for ArgumentPlaceholder in babel-generator

rename Partial to ArgumentPlaceholder

update the tests

remove alias from the type and undo changes in generated folder

adds a nice error message

better definition for the type

auto-generated files

update the conditional for allowPlaceholder message and tests

update CallExpression definition to accept ArgumentPlaceholder

change description

clean up

indent ArgumentPlaceholder entry and revert unwanted changes

* add Partial Application Plugin

* update unwrapArguments function and add replacePlaceholders.

* update the tests

* remove unnecessary clone

* update readme and description an package.json

* put options.json one level up and add test for not assigned and chained

* add await to fix the test

* use Promise in the test instead of async await

* remove unnecessary method test [#9474]

* push sequenceExpressions to an array [#9474]
This commit is contained in:
Behrang Yarahmadi 2019-03-13 12:21:10 +01:00 committed by Nicolò Ribaudo
parent a6cb24fa1a
commit 29cd27b545
44 changed files with 454 additions and 5 deletions

View File

@ -96,4 +96,4 @@
],
"directives": []
}
}
}

View File

@ -302,4 +302,4 @@
],
"directives": []
}
}
}

View File

@ -257,4 +257,4 @@
],
"directives": []
}
}
}

View File

@ -227,4 +227,4 @@
],
"directives": []
}
}
}

View File

@ -227,4 +227,4 @@
],
"directives": []
}
}
}

View File

@ -0,0 +1,3 @@
src
test
*.log

View File

@ -0,0 +1,19 @@
# @babel/plugin-proposal-partial-application
> Introduces a new ? token in an argument list which allows for partially applying an argument list to a call expression.
See our website [@babel/plugin-proposal-partial-application](https://babeljs.io/docs/en/next/babel-plugin-proposal-partial-application.html) for more information.
## Install
Using npm:
```sh
npm install --save-dev @babel/plugin-proposal-partial-application
```
or using yarn:
```sh
yarn add @babel/plugin-proposal-partial-application --dev
```

View File

@ -0,0 +1,25 @@
{
"name": "@babel/plugin-proposal-partial-application",
"version": "7.2.0",
"description": "Introduces a new ? token in an argument list which allows for partially applying an argument list to a call expression",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-partial-application",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "lib/index.js",
"keywords": [
"babel-plugin"
],
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-syntax-partial-application": "^7.2.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
},
"devDependencies": {
"@babel/core": "^7.2.0",
"@babel/helper-plugin-test-runner": "^7.0.0"
}
}

View File

@ -0,0 +1,150 @@
import { declare } from "@babel/helper-plugin-utils";
import syntaxPartialApplication from "@babel/plugin-syntax-partial-application";
import { types as t } from "@babel/core";
export default declare(api => {
api.assertVersion(7);
/**
* a function to figure out if a call expression has
* ArgumentPlaceholder as one of its arguments
* @param node a callExpression node
* @returns boolean
*/
function hasArgumentPlaceholder(node) {
return node.arguments.some(arg => t.isArgumentPlaceholder(arg));
}
function unwrapArguments(node, scope) {
const init = [];
for (let i = 0; i < node.arguments.length; i++) {
if (
!t.isArgumentPlaceholder(node.arguments[i]) &&
!t.isImmutable(node.arguments[i])
) {
const id = scope.generateUidIdentifierBasedOnNode(
node.arguments[i],
"param",
);
scope.push({ id });
if (t.isSpreadElement(node.arguments[i])) {
init.push(
t.assignmentExpression(
"=",
t.cloneNode(id),
t.arrayExpression([t.spreadElement(node.arguments[i].argument)]),
),
);
node.arguments[i].argument = t.cloneNode(id);
} else {
init.push(
t.assignmentExpression("=", t.cloneNode(id), node.arguments[i]),
);
node.arguments[i] = t.cloneNode(id);
}
}
}
return init;
}
function replacePlaceholders(node, scope) {
const placeholders = [];
const args = [];
node.arguments.forEach(arg => {
if (t.isArgumentPlaceholder(arg)) {
const id = scope.generateUid("_argPlaceholder");
placeholders.push(t.identifier(id));
args.push(t.identifier(id));
} else {
args.push(arg);
}
});
return [placeholders, args];
}
return {
name: "proposal-partial-application",
inherits: syntaxPartialApplication,
visitor: {
CallExpression(path) {
if (!hasArgumentPlaceholder(path.node)) {
return;
}
const { node, scope } = path;
const functionLVal = path.scope.generateUidIdentifierBasedOnNode(
node.callee,
);
const sequenceParts = [];
const argsInitializers = unwrapArguments(node, scope);
const [placeholdersParams, args] = replacePlaceholders(node, scope);
scope.push({ id: functionLVal });
if (node.callee.type === "MemberExpression") {
const receiverLVal = path.scope.generateUidIdentifierBasedOnNode(
node.callee.object,
);
scope.push({ id: receiverLVal });
sequenceParts.push(
t.assignmentExpression(
"=",
t.cloneNode(receiverLVal),
node.callee.object,
),
t.assignmentExpression(
"=",
t.cloneNode(functionLVal),
t.memberExpression(
receiverLVal,
node.callee.property,
false,
false,
),
),
...argsInitializers,
t.functionExpression(
node.callee.property,
placeholdersParams,
t.blockStatement(
[
t.returnStatement(
t.callExpression(
t.memberExpression(
functionLVal,
t.identifier("call"),
false,
false,
),
[receiverLVal, ...args],
),
),
],
[],
),
false,
false,
),
);
} else {
sequenceParts.push(
t.assignmentExpression("=", t.cloneNode(functionLVal), node.callee),
...argsInitializers,
t.functionExpression(
node.callee,
placeholdersParams,
t.blockStatement(
[t.returnStatement(t.callExpression(functionLVal, args))],
[],
),
false,
false,
),
);
}
path.replaceWith(t.sequenceExpression(sequenceParts));
},
},
};
});

View File

@ -0,0 +1,16 @@
class Partial {
constructor() {
this.compare = this.compare(?, ?);
}
compare(a, b) {
if(a > b){
return a;
}
}
}
const foo = new Partial;
expect(foo.compare(3,1)).toEqual(3);
expect(foo.compare.length).toEqual(2);
expect(foo.compare.name).toEqual("compare");

View File

@ -0,0 +1,5 @@
class Partial {
constructor() {
this.compare = this.compare(?, ?);
}
}

View File

@ -0,0 +1,10 @@
class Partial {
constructor() {
var _this$compare, _this;
this.compare = (_this = this, _this$compare = _this.compare, function compare(_argPlaceholder, _argPlaceholder2) {
return _this$compare.call(_this, _argPlaceholder, _argPlaceholder2);
});
}
}

View File

@ -0,0 +1,8 @@
"use strict";
function add(x, y) { return x + y; }
const addOne = add(1, ?);
expect(addOne(2)).toEqual(3);
expect(addOne.length).toEqual(1);
expect(addOne.name).toEqual("add");

View File

@ -0,0 +1,3 @@
"use strict";
const foo = bar(?, 1);

View File

@ -0,0 +1,7 @@
"use strict";
var _bar;
const foo = (_bar = bar, function bar(_argPlaceholder) {
return _bar(_argPlaceholder, 1);
});

View File

@ -0,0 +1,8 @@
"use strict";
function add(x, y) { return x + y; }
const addTen = add(?, 10);
expect(addTen(2)).toEqual(12);
expect(addTen.length).toEqual(1);
expect(addTen.name).toEqual("add");

View File

@ -0,0 +1,3 @@
"use strict";
const foo = bar(1, ?);

View File

@ -0,0 +1,7 @@
"use strict";
var _bar;
const foo = (_bar = bar, function bar(_argPlaceholder) {
return _bar(1, _argPlaceholder);
});

View File

@ -0,0 +1,7 @@
"use strict";
const obj = { add: (x, y) => x + y };
const addOne = obj.add(1, ?);
expect(addOne(5)).toEqual(6);
expect(addOne.length).toEqual(1);
expect(addOne.name).toEqual("add");

View File

@ -0,0 +1,7 @@
"use strict";
const g = o.f(?, x, 1);
const h = p.b(1, y, x, 2, ?);
const j = a.b.c.d.e.foo(?, 1);

View File

@ -0,0 +1,13 @@
"use strict";
var _x, _o$f, _o, _y, _x2, _p$b, _p, _a$b$c$d$e$foo, _a$b$c$d$e;
const g = (_o = o, _o$f = _o.f, _x = x, function f(_argPlaceholder) {
return _o$f.call(_o, _argPlaceholder, _x, 1);
});
const h = (_p = p, _p$b = _p.b, _y = y, _x2 = x, function b(_argPlaceholder2) {
return _p$b.call(_p, 1, _y, _x2, 2, _argPlaceholder2);
});
const j = (_a$b$c$d$e = a.b.c.d.e, _a$b$c$d$e$foo = _a$b$c$d$e.foo, function foo(_argPlaceholder3) {
return _a$b$c$d$e$foo.call(_a$b$c$d$e, _argPlaceholder3, 1);
});

View File

@ -0,0 +1,18 @@
function add(a, b) {
return a + b;
}
function square(x){
return x * x;
}
const foo = add(?, 1);
const bar = square(?);
expect(bar(foo(2))).toEqual(9);
expect(foo(2)).toEqual(3);
expect(bar(4)).toEqual(16);
expect(foo.length).toEqual(1);
expect(foo.name).toEqual("add");
expect(bar.length).toEqual(1);
expect(bar.name).toEqual("square");

View File

@ -0,0 +1,4 @@
"use strict";
const baz = ber(2, 4, ?, ?, 1);
const foo = bar(1, ?, x, 3, ?);

View File

@ -0,0 +1,10 @@
"use strict";
var _ber, _x, _bar;
const baz = (_ber = ber, function ber(_argPlaceholder, _argPlaceholder2) {
return _ber(2, 4, _argPlaceholder, _argPlaceholder2, 1);
});
const foo = (_bar = bar, _x = x, function bar(_argPlaceholder3, _argPlaceholder4) {
return _bar(1, _argPlaceholder3, _x, 3, _argPlaceholder4);
});

View File

@ -0,0 +1,7 @@
"use strict";
function add(x, y) { return x + y; }
const addOne = add(1, ?);
const addTen = add(?, 10);
expect(addOne(addTen(1))).toEqual(12);

View File

@ -0,0 +1,3 @@
"use strict";
const foo = bar(baz(?, 1));

View File

@ -0,0 +1,7 @@
"use strict";
var _baz;
const foo = bar((_baz = baz, function baz(_argPlaceholder) {
return _baz(_argPlaceholder, 1);
}));

View File

@ -0,0 +1,9 @@
"use strict";
const obj = {a : {b : {c : {add: (x, y) => x + y}}}};
const addOne = obj.a.b.c.add(1, ?);
expect(addOne(4)).toEqual(5);
expect(addOne.length).toEqual(1);
expect(addOne.name).toEqual("add");

View File

@ -0,0 +1,3 @@
"use strict";
const g = a.b.fn().c["d"].e(?, 1, ?, 3, x, y, z);

View File

@ -0,0 +1,7 @@
"use strict";
var _x, _y, _z, _a$b$fn$c$d$e, _a$b$fn$c$d;
const g = (_a$b$fn$c$d = a.b.fn().c["d"], _a$b$fn$c$d$e = _a$b$fn$c$d.e, _x = x, _y = y, _z = z, function e(_argPlaceholder, _argPlaceholder2) {
return _a$b$fn$c$d$e.call(_a$b$fn$c$d, _argPlaceholder, 1, _argPlaceholder2, 3, _x, _y, _z);
});

View File

@ -0,0 +1,7 @@
"use strict";
function square(n){
return new Promise(resolve => resolve(n * n));
}
square(?)(3).then(res => expect(res).toBe(9));

View File

@ -0,0 +1,3 @@
"use strict";
foo(?)(b).then(() => {});

View File

@ -0,0 +1,7 @@
"use strict";
var _foo;
(_foo = foo, function foo(_argPlaceholder) {
return _foo(_argPlaceholder);
})(b).then(() => {});

View File

@ -0,0 +1,3 @@
{
"plugins": ["proposal-partial-application"]
}

View File

@ -0,0 +1,11 @@
"use strict";
function square(x){
return x * x;
}
const foo = square(?);
expect(foo(3)).toBe(9);
expect(foo.length).toEqual(1);
expect(foo.name).toEqual("square");

View File

@ -0,0 +1,3 @@
"use strict";
const foo = bar(?);

View File

@ -0,0 +1,7 @@
"use strict";
var _bar;
const foo = (_bar = bar, function bar(_argPlaceholder) {
return _bar(_argPlaceholder);
});

View File

@ -0,0 +1,7 @@
"use strict";
const slice = Array.prototype.slice.call(?, ?, ?);
expect(slice({ 0: "a", 1: "b", length: 2 }, 1, 2)).toEqual(["b"]);
expect(slice.length).toEqual(3);
expect(slice.name).toEqual("call");

View File

@ -0,0 +1 @@
const slice = Array.prototype.slice.call(?, ?, ?);

View File

@ -0,0 +1,5 @@
var _Array$prototype$slic, _Array$prototype$slic2;
const slice = (_Array$prototype$slic2 = Array.prototype.slice, _Array$prototype$slic = _Array$prototype$slic2.call, function call(_argPlaceholder, _argPlaceholder2, _argPlaceholder3) {
return _Array$prototype$slic.call(_Array$prototype$slic2, _argPlaceholder, _argPlaceholder2, _argPlaceholder3);
});

View File

@ -0,0 +1,13 @@
"use strict";
const num = [2,3];
function multiplyXYZ(x, y, z){
return x * y * z;
}
const multiplyBySix = multiplyXYZ(?, ...num);
expect(multiplyBySix(6)).toBe(36);
expect(multiplyBySix.length).toEqual(1);
expect(multiplyBySix.name).toEqual("multiplyXYZ");

View File

@ -0,0 +1,7 @@
"use strict";
const q = foo(a, ...b, ?);
const w = bar(1, x, ?, ...y, ?, 2);
const z = obj.baz(?, ...d);

View File

@ -0,0 +1,13 @@
"use strict";
var _a, _param, _foo, _x, _param2, _bar, _param3, _obj$baz, _obj;
const q = (_foo = foo, _a = a, _param = [...b], function foo(_argPlaceholder) {
return _foo(_a, ..._param, _argPlaceholder);
});
const w = (_bar = bar, _x = x, _param2 = [...y], function bar(_argPlaceholder2, _argPlaceholder3) {
return _bar(1, _x, _argPlaceholder2, ..._param2, _argPlaceholder3, 2);
});
const z = (_obj = obj, _obj$baz = _obj.baz, _param3 = [...d], function baz(_argPlaceholder4) {
return _obj$baz.call(_obj, _argPlaceholder4, ..._param3);
});

View File

@ -0,0 +1,3 @@
import runner from "@babel/helper-plugin-test-runner";
runner(__dirname);