Transform for F# Pipeline (#9984)
* Transform for F#-style await Inludes support for optimizing single-parameter arrow functions * Wait until optimization before pushing placeholder into scope
This commit is contained in:
parent
6b8a37c413
commit
a4170b5e32
@ -0,0 +1,54 @@
|
||||
import { types as t } from "@babel/core";
|
||||
|
||||
// tries to optimize sequence expressions in the format
|
||||
// (a = b, ((c) => d + e)(a))
|
||||
// to
|
||||
// (a = b, a + e)
|
||||
const buildOptimizedSequenceExpression = ({ assign, call, path }) => {
|
||||
const { left: placeholderNode, right: pipelineLeft } = assign;
|
||||
const { callee: calledExpression } = call;
|
||||
|
||||
let optimizeArrow =
|
||||
t.isArrowFunctionExpression(calledExpression) &&
|
||||
t.isExpression(calledExpression.body) &&
|
||||
!calledExpression.async &&
|
||||
!calledExpression.generator;
|
||||
let param;
|
||||
|
||||
if (optimizeArrow) {
|
||||
const { params } = calledExpression;
|
||||
if (params.length === 1 && t.isIdentifier(params[0])) {
|
||||
param = params[0];
|
||||
} else if (params.length > 0) {
|
||||
optimizeArrow = false;
|
||||
}
|
||||
} else if (t.isIdentifier(calledExpression, { name: "eval" })) {
|
||||
const evalSequence = t.sequenceExpression([
|
||||
t.numericLiteral(0),
|
||||
calledExpression,
|
||||
]);
|
||||
|
||||
call.callee = evalSequence;
|
||||
|
||||
path.scope.push({ id: placeholderNode });
|
||||
|
||||
return t.sequenceExpression([assign, call]);
|
||||
}
|
||||
|
||||
if (optimizeArrow && !param) {
|
||||
// Arrow function with 0 arguments
|
||||
return t.sequenceExpression([pipelineLeft, calledExpression.body]);
|
||||
}
|
||||
|
||||
path.scope.push({ id: placeholderNode });
|
||||
|
||||
if (param) {
|
||||
path.get("right").scope.rename(param.name, placeholderNode.name);
|
||||
|
||||
return t.sequenceExpression([assign, calledExpression.body]);
|
||||
}
|
||||
|
||||
return t.sequenceExpression([assign, call]);
|
||||
};
|
||||
|
||||
export default buildOptimizedSequenceExpression;
|
||||
@ -0,0 +1,25 @@
|
||||
import { types as t } from "@babel/core";
|
||||
import buildOptimizedSequenceExpression from "./buildOptimizedSequenceExpression";
|
||||
|
||||
const fsharpVisitor = {
|
||||
BinaryExpression(path) {
|
||||
const { scope, node } = path;
|
||||
const { operator, left, right } = node;
|
||||
if (operator !== "|>") return;
|
||||
|
||||
const placeholder = scope.generateUidIdentifierBasedOnNode(left);
|
||||
|
||||
const call =
|
||||
right.type === "AwaitExpression"
|
||||
? t.awaitExpression(t.cloneNode(placeholder))
|
||||
: t.callExpression(right, [t.cloneNode(placeholder)]);
|
||||
const sequence = buildOptimizedSequenceExpression({
|
||||
assign: t.assignmentExpression("=", t.cloneNode(placeholder), left),
|
||||
call,
|
||||
path,
|
||||
});
|
||||
path.replaceWith(sequence);
|
||||
},
|
||||
};
|
||||
|
||||
export default fsharpVisitor;
|
||||
@ -2,10 +2,12 @@ import { declare } from "@babel/helper-plugin-utils";
|
||||
import syntaxPipelineOperator from "@babel/plugin-syntax-pipeline-operator";
|
||||
import minimalVisitor from "./minimalVisitor";
|
||||
import smartVisitor from "./smartVisitor";
|
||||
import fsharpVisitor from "./fsharpVisitor";
|
||||
|
||||
const visitorsPerProposal = {
|
||||
minimal: minimalVisitor,
|
||||
smart: smartVisitor,
|
||||
fsharp: fsharpVisitor,
|
||||
};
|
||||
|
||||
export default declare((api, options) => {
|
||||
|
||||
@ -1,51 +1,21 @@
|
||||
import { types as t } from "@babel/core";
|
||||
import buildOptimizedSequenceExpression from "./buildOptimizedSequenceExpression";
|
||||
|
||||
const minimalVisitor = {
|
||||
BinaryExpression(path) {
|
||||
const { scope } = path;
|
||||
const { node } = path;
|
||||
const { operator, left } = node;
|
||||
let { right } = node;
|
||||
const { scope, node } = path;
|
||||
const { operator, left, right } = node;
|
||||
if (operator !== "|>") return;
|
||||
|
||||
let optimizeArrow =
|
||||
t.isArrowFunctionExpression(right) &&
|
||||
t.isExpression(right.body) &&
|
||||
!right.async &&
|
||||
!right.generator;
|
||||
let param;
|
||||
const placeholder = scope.generateUidIdentifierBasedOnNode(left);
|
||||
|
||||
if (optimizeArrow) {
|
||||
const { params } = right;
|
||||
if (params.length === 1 && t.isIdentifier(params[0])) {
|
||||
param = params[0];
|
||||
} else if (params.length > 0) {
|
||||
optimizeArrow = false;
|
||||
}
|
||||
} else if (t.isIdentifier(right, { name: "eval" })) {
|
||||
right = t.sequenceExpression([t.numericLiteral(0), right]);
|
||||
}
|
||||
|
||||
if (optimizeArrow && !param) {
|
||||
// Arrow function with 0 arguments
|
||||
path.replaceWith(t.sequenceExpression([left, right.body]));
|
||||
return;
|
||||
}
|
||||
|
||||
const placeholder = scope.generateUidIdentifierBasedOnNode(param || left);
|
||||
scope.push({ id: placeholder });
|
||||
if (param) {
|
||||
path.get("right").scope.rename(param.name, placeholder.name);
|
||||
}
|
||||
|
||||
const call = optimizeArrow
|
||||
? right.body
|
||||
: t.callExpression(right, [t.cloneNode(placeholder)]);
|
||||
const call = t.callExpression(right, [t.cloneNode(placeholder)]);
|
||||
path.replaceWith(
|
||||
t.sequenceExpression([
|
||||
t.assignmentExpression("=", t.cloneNode(placeholder), left),
|
||||
buildOptimizedSequenceExpression({
|
||||
assign: t.assignmentExpression("=", t.cloneNode(placeholder), left),
|
||||
call,
|
||||
]),
|
||||
path,
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
const y = 2;
|
||||
|
||||
const f = (x) => (x |> (y) => y + 1)
|
||||
|> (z) => z * y
|
||||
|
||||
const _f = (x) => x
|
||||
|> (y) => y + 1
|
||||
|> (z) => z * y
|
||||
|
||||
const g = (x) => x
|
||||
|> (y) => (y + 1 |> (z) => z * y)
|
||||
|
||||
const _g = (x) => x
|
||||
|> (y => (y + 1 |> (z) => z * y))
|
||||
|
||||
const __g = (x) => x
|
||||
|> (
|
||||
y => {
|
||||
return (y + 1 |> (z) => z * y);
|
||||
}
|
||||
)
|
||||
|
||||
expect( f(1)).toBe(4);
|
||||
expect( _f(1)).toBe(4);
|
||||
expect( g(1)).toBe(2);
|
||||
expect( _g(1)).toBe(2);
|
||||
expect(__g(1)).toBe(2);
|
||||
14
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/arrow-functions/exec.js
vendored
Normal file
14
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/arrow-functions/exec.js
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
var result = [5,10]
|
||||
|> (_ => _.map(x => x * 2))
|
||||
|> (_ => _.reduce( (a,b) => a + b ))
|
||||
|> (sum => sum + 1)
|
||||
|
||||
expect(result).toBe(31);
|
||||
|
||||
|
||||
var inc = (x) => x + 1;
|
||||
var double = (x) => x * 2;
|
||||
|
||||
var result2 = [4, 9].map( x => x |> inc |> double )
|
||||
|
||||
expect(result2).toEqual([10, 20]);
|
||||
14
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/arrow-functions/input.js
vendored
Normal file
14
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/arrow-functions/input.js
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
var result = [5,10]
|
||||
|> (_ => _.map(x => x * 2))
|
||||
|> (_ => _.reduce( (a,b) => a + b ))
|
||||
|> (sum => sum + 1)
|
||||
|
||||
expect(result).toBe(31);
|
||||
|
||||
|
||||
var inc = (x) => x + 1;
|
||||
var double = (x) => x * 2;
|
||||
|
||||
var result2 = [4, 9].map( x => x |> inc |> double )
|
||||
|
||||
expect(result2).toEqual([10, 20]);
|
||||
15
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/arrow-functions/output.js
vendored
Normal file
15
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/arrow-functions/output.js
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
var _ref, _ref2, _ref3;
|
||||
|
||||
var result = (_ref = (_ref2 = (_ref3 = [5, 10], _ref3.map(x => x * 2)), _ref2.reduce((a, b) => a + b)), _ref + 1);
|
||||
expect(result).toBe(31);
|
||||
|
||||
var inc = x => x + 1;
|
||||
|
||||
var double = x => x * 2;
|
||||
|
||||
var result2 = [4, 9].map(x => {
|
||||
var _ref4, _x;
|
||||
|
||||
return _ref4 = (_x = x, inc(_x)), double(_ref4);
|
||||
});
|
||||
expect(result2).toEqual([10, 20]);
|
||||
13
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/await/exec.js
vendored
Normal file
13
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/await/exec.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
const triple = (x) => x * 3;
|
||||
|
||||
async function myFunction(n) {
|
||||
return n
|
||||
|> Math.abs
|
||||
|> Promise.resolve
|
||||
|> await
|
||||
|> triple;
|
||||
}
|
||||
|
||||
return myFunction(-7).then(result => {
|
||||
expect(result).toBe(21);
|
||||
});
|
||||
6
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/await/input.js
vendored
Normal file
6
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/await/input.js
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
async function myFunction(n) {
|
||||
return n
|
||||
|> Math.abs
|
||||
|> Promise.resolve
|
||||
|> await;
|
||||
}
|
||||
9
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/await/options.json
vendored
Normal file
9
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/await/options.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"plugins": [
|
||||
["proposal-pipeline-operator", { "proposal": "fsharp" }]
|
||||
],
|
||||
"parserOpts": {
|
||||
"allowReturnOutsideFunction": true
|
||||
},
|
||||
"minNodeVersion": "8.0.0"
|
||||
}
|
||||
5
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/await/output.js
vendored
Normal file
5
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/await/output.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
async function myFunction(n) {
|
||||
var _ref, _ref2, _n;
|
||||
|
||||
return _ref = (_ref2 = (_n = n, Math.abs(_n)), Promise.resolve(_ref2)), await _ref;
|
||||
}
|
||||
3
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/basic/exec.js
vendored
Normal file
3
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/basic/exec.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
var inc = (x) => x + 1
|
||||
|
||||
expect(10 |> inc).toBe(11);
|
||||
3
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/basic/input.js
vendored
Normal file
3
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/basic/input.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
var inc = (x) => x + 1
|
||||
|
||||
expect(10 |> inc).toBe(11);
|
||||
5
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/basic/output.js
vendored
Normal file
5
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/basic/output.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
var _;
|
||||
|
||||
var inc = x => x + 1;
|
||||
|
||||
expect((_ = 10, inc(_))).toBe(11);
|
||||
7
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/indirect-eval/exec.js
vendored
Normal file
7
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/indirect-eval/exec.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
var result = '(function() { return this; })()'
|
||||
|> eval;
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
})();
|
||||
7
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/indirect-eval/input.js
vendored
Normal file
7
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/indirect-eval/input.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
var result = '(function() { return this; })()'
|
||||
|> eval;
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
})();
|
||||
@ -0,0 +1,8 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var _functionReturn;
|
||||
|
||||
var result = (_functionReturn = '(function() { return this; })()', (0, eval)(_functionReturn));
|
||||
expect(result).not.toBeUndefined();
|
||||
})();
|
||||
@ -0,0 +1,5 @@
|
||||
var array = [10,20,30];
|
||||
|
||||
var last = array |> (a => a[a.length-1]);
|
||||
|
||||
expect(last).toBe(30);
|
||||
@ -0,0 +1,5 @@
|
||||
var _array;
|
||||
|
||||
var array = [10, 20, 30];
|
||||
var last = (_array = array, _array[_array.length - 1]);
|
||||
expect(last).toBe(30);
|
||||
@ -0,0 +1,8 @@
|
||||
var a = 1,
|
||||
b = 2,
|
||||
c = 3;
|
||||
var result = a
|
||||
|> (() => b)
|
||||
|> (() => c);
|
||||
|
||||
expect(result).toBe(c);
|
||||
@ -0,0 +1,8 @@
|
||||
var a = 1,
|
||||
b = 2,
|
||||
c = 3;
|
||||
var result = a
|
||||
|> (() => b)
|
||||
|> (() => c);
|
||||
|
||||
expect(result).toBe(c);
|
||||
@ -0,0 +1,5 @@
|
||||
var a = 1,
|
||||
b = 2,
|
||||
c = 3;
|
||||
var result = ((a, b), c);
|
||||
expect(result).toBe(c);
|
||||
8
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/options.json
vendored
Normal file
8
packages/babel-plugin-proposal-pipeline-operator/test/fixtures/fsharp/options.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"plugins": [
|
||||
["proposal-pipeline-operator", { "proposal": "fsharp" }]
|
||||
],
|
||||
"parserOpts": {
|
||||
"allowReturnOutsideFunction": true
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
const y = 2;
|
||||
const f = (x) => x
|
||||
|> (y => y + 1)
|
||||
|> (z => z * y)
|
||||
|
||||
const g = (x) => x
|
||||
|> (y =>
|
||||
y + 1
|
||||
|> (z => z * y)
|
||||
)
|
||||
|
||||
const h = (x) => x
|
||||
|> (y => (
|
||||
y + 1
|
||||
|> (z => z * y)
|
||||
))
|
||||
|
||||
expect(f(1)).toBe(4);
|
||||
expect(g(1)).toBe(2);
|
||||
expect(h(1)).toBe(2);
|
||||
@ -0,0 +1,20 @@
|
||||
const y = 2;
|
||||
const f = (x) => x
|
||||
|> (y => y + 1)
|
||||
|> (z => z * y)
|
||||
|
||||
const g = (x) => x
|
||||
|> (y =>
|
||||
y + 1
|
||||
|> (z => z * y)
|
||||
)
|
||||
|
||||
const h = (x) => x
|
||||
|> (y => (
|
||||
y + 1
|
||||
|> (z => z * y)
|
||||
))
|
||||
|
||||
expect(f(1)).toBe(4);
|
||||
expect(g(1)).toBe(2);
|
||||
expect(h(1)).toBe(2);
|
||||
@ -0,0 +1,23 @@
|
||||
const y = 2;
|
||||
|
||||
const f = x => {
|
||||
var _ref, _x;
|
||||
|
||||
return _ref = (_x = x, _x + 1), _ref * y;
|
||||
};
|
||||
|
||||
const g = x => {
|
||||
var _x2, _ref2;
|
||||
|
||||
return _x2 = x, (_ref2 = _x2 + 1, _ref2 * _x2);
|
||||
};
|
||||
|
||||
const h = x => {
|
||||
var _x3, _ref3;
|
||||
|
||||
return _x3 = x, (_ref3 = _x3 + 1, _ref3 * _x3);
|
||||
};
|
||||
|
||||
expect(f(1)).toBe(4);
|
||||
expect(g(1)).toBe(2);
|
||||
expect(h(1)).toBe(2);
|
||||
@ -1,6 +1,6 @@
|
||||
var _sum, _ref, _ref2;
|
||||
var _ref, _ref2, _ref3;
|
||||
|
||||
var result = (_sum = (_ref = (_ref2 = [5, 10], _ref2.map(x => x * 2)), _ref.reduce((a, b) => a + b)), _sum + 1);
|
||||
var result = (_ref = (_ref2 = (_ref3 = [5, 10], _ref3.map(x => x * 2)), _ref2.reduce((a, b) => a + b)), _ref + 1);
|
||||
expect(result).toBe(31);
|
||||
|
||||
var inc = x => x + 1;
|
||||
@ -8,8 +8,8 @@ var inc = x => x + 1;
|
||||
var double = x => x * 2;
|
||||
|
||||
var result2 = [4, 9].map(x => {
|
||||
var _ref3, _x;
|
||||
var _ref4, _x;
|
||||
|
||||
return _ref3 = (_x = x, inc(_x)), double(_ref3);
|
||||
return _ref4 = (_x = x, inc(_x)), double(_ref4);
|
||||
});
|
||||
expect(result2).toEqual([10, 20]);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
var _a;
|
||||
var _array;
|
||||
|
||||
var array = [10, 20, 30];
|
||||
var last = (_a = array, _a[_a.length - 1]);
|
||||
var last = (_array = array, _array[_array.length - 1]);
|
||||
expect(last).toBe(30);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
|
||||
export const proposals = ["minimal", "smart"];
|
||||
export const proposals = ["minimal", "smart", "fsharp"];
|
||||
|
||||
export default declare((api, { proposal }) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user