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 syntaxPipelineOperator from "@babel/plugin-syntax-pipeline-operator";
|
||||||
import minimalVisitor from "./minimalVisitor";
|
import minimalVisitor from "./minimalVisitor";
|
||||||
import smartVisitor from "./smartVisitor";
|
import smartVisitor from "./smartVisitor";
|
||||||
|
import fsharpVisitor from "./fsharpVisitor";
|
||||||
|
|
||||||
const visitorsPerProposal = {
|
const visitorsPerProposal = {
|
||||||
minimal: minimalVisitor,
|
minimal: minimalVisitor,
|
||||||
smart: smartVisitor,
|
smart: smartVisitor,
|
||||||
|
fsharp: fsharpVisitor,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default declare((api, options) => {
|
export default declare((api, options) => {
|
||||||
|
|||||||
@ -1,51 +1,21 @@
|
|||||||
import { types as t } from "@babel/core";
|
import { types as t } from "@babel/core";
|
||||||
|
import buildOptimizedSequenceExpression from "./buildOptimizedSequenceExpression";
|
||||||
|
|
||||||
const minimalVisitor = {
|
const minimalVisitor = {
|
||||||
BinaryExpression(path) {
|
BinaryExpression(path) {
|
||||||
const { scope } = path;
|
const { scope, node } = path;
|
||||||
const { node } = path;
|
const { operator, left, right } = node;
|
||||||
const { operator, left } = node;
|
|
||||||
let { right } = node;
|
|
||||||
if (operator !== "|>") return;
|
if (operator !== "|>") return;
|
||||||
|
|
||||||
let optimizeArrow =
|
const placeholder = scope.generateUidIdentifierBasedOnNode(left);
|
||||||
t.isArrowFunctionExpression(right) &&
|
|
||||||
t.isExpression(right.body) &&
|
|
||||||
!right.async &&
|
|
||||||
!right.generator;
|
|
||||||
let param;
|
|
||||||
|
|
||||||
if (optimizeArrow) {
|
const call = t.callExpression(right, [t.cloneNode(placeholder)]);
|
||||||
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)]);
|
|
||||||
path.replaceWith(
|
path.replaceWith(
|
||||||
t.sequenceExpression([
|
buildOptimizedSequenceExpression({
|
||||||
t.assignmentExpression("=", t.cloneNode(placeholder), left),
|
assign: t.assignmentExpression("=", t.cloneNode(placeholder), left),
|
||||||
call,
|
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);
|
expect(result).toBe(31);
|
||||||
|
|
||||||
var inc = x => x + 1;
|
var inc = x => x + 1;
|
||||||
@ -8,8 +8,8 @@ var inc = x => x + 1;
|
|||||||
var double = x => x * 2;
|
var double = x => x * 2;
|
||||||
|
|
||||||
var result2 = [4, 9].map(x => {
|
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]);
|
expect(result2).toEqual([10, 20]);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
var _a;
|
var _array;
|
||||||
|
|
||||||
var array = [10, 20, 30];
|
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);
|
expect(last).toBe(30);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { declare } from "@babel/helper-plugin-utils";
|
import { declare } from "@babel/helper-plugin-utils";
|
||||||
|
|
||||||
export const proposals = ["minimal", "smart"];
|
export const proposals = ["minimal", "smart", "fsharp"];
|
||||||
|
|
||||||
export default declare((api, { proposal }) => {
|
export default declare((api, { proposal }) => {
|
||||||
api.assertVersion(7);
|
api.assertVersion(7);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user