Behrang Yarahmadi 29cd27b545 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]
2019-03-13 12:21:10 +01:00

151 lines
4.3 KiB
JavaScript

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));
},
},
};
});