2nd try: Add loose option for es2015-parameters transformation (#5943)

* Import changes to parameters package from previous branch

* Refactor plugin option access via state
This commit is contained in:
Mauro Bringolf 2017-07-12 23:36:44 +02:00 committed by Justin Ridgewell
parent a0f0411abf
commit b83e0ec7b0
20 changed files with 195 additions and 1 deletions

View File

@ -72,3 +72,23 @@ require("babel-core").transform("code", {
plugins: ["transform-es2015-parameters"]
});
```
## Options
### `loose`
`boolean`, defaults to `false`.
In loose mode, parameters with default values will be counted into the arity of the function. This is not spec behavior where these parameters do not add to function arity.
The `loose` implementation is a more performant solution as JavaScript engines will fully optimize a function that doesn't reference `arguments`. Please do your own benchmarking and determine if this option is the right fit for your application.
```javascript
// Spec behavior
function bar1 (arg1 = 1) {}
bar1.length // 0
// Loose mode
function bar1 (arg1 = 1) {}
bar1.length // 1
```

View File

@ -11,6 +11,16 @@ const buildDefaultParam = template(`
DEFAULT_VALUE;
`);
const buildLooseDefaultParam = template(`
if (ASSIGMENT_IDENTIFIER === UNDEFINED) {
ASSIGMENT_IDENTIFIER = DEFAULT_VALUE;
}
`);
const buildLooseDestructuredDefaultParam = template(`
let ASSIGMENT_IDENTIFIER = PARAMETER_NAME === UNDEFINED ? DEFAULT_VALUE : PARAMETER_NAME ;
`);
const buildCutOff = template(`
let $0 = $1[$2];
`);
@ -51,6 +61,45 @@ export const visitor = {
// ensure it's a block, useful for arrow functions
path.ensureBlock();
const params = path.get("params");
if (this.opts.loose) {
const body = [];
for (let i = 0; i < params.length; ++i) {
const param = params[i];
if (param.isAssignmentPattern()) {
const left = param.get("left");
const right = param.get("right");
const undefinedNode = scope.buildUndefinedNode();
if (left.isIdentifier()) {
body.push(
buildLooseDefaultParam({
ASSIGMENT_IDENTIFIER: left.node,
DEFAULT_VALUE: right.node,
UNDEFINED: undefinedNode,
}),
);
param.replaceWith(left.node);
} else if (left.isObjectPattern() || left.isArrayPattern()) {
const paramName = scope.generateUidIdentifier();
body.push(
buildLooseDestructuredDefaultParam({
ASSIGMENT_IDENTIFIER: left.node,
DEFAULT_VALUE: right.node,
PARAMETER_NAME: paramName,
UNDEFINED: undefinedNode,
}),
);
param.replaceWith(paramName);
}
}
}
path.get("body").unshiftContainer("body", body);
return;
}
const state = {
iife: false,
scope: scope,
@ -77,7 +126,6 @@ export const visitor = {
const lastNonDefaultParam = getFunctionArity(node);
//
const params = path.get("params");
for (let i = 0; i < params.length; i++) {
const param = params[i];

View File

@ -0,0 +1,3 @@
function f(a, b = a, c = b) { return c; }
assert.equal(3, f(3));

View File

@ -0,0 +1 @@
function test({a: b} = {}) {}

View File

@ -0,0 +1,4 @@
function test(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
b = _ref.a;
}

View File

@ -0,0 +1 @@
function t([,,a] = [1,2,3]) { return a }

View File

@ -0,0 +1,4 @@
function t([,,a] = [1,2,3]) { return a }
assert.equal(t(), 3);
assert.equal(t([4,5,6]), 6);

View File

@ -0,0 +1,7 @@
function t(_temp) {
var _ref = _temp === void 0 ? [1, 2, 3] : _temp,
_ref2 = babelHelpers.slicedToArray(_ref, 3),
a = _ref2[2];
return a;
}

View File

@ -0,0 +1,3 @@
function f(a, b = a, c = b) { return c; }
assert.equal(3, f(3));

View File

@ -0,0 +1,9 @@
const bar = true;
function foo(a = bar, ...b) {
const bar = false;
assert.equal(b[0], 2);
assert.equal(b[1], 3);
}
foo(1, 2, 3);

View File

@ -0,0 +1,9 @@
class Ref {
static nextId = 0
constructor(id = ++Ref.nextId, n = id) {
this.id = n
}
}
assert.equal(1, new Ref().id)
assert.equal(2, new Ref().id)

View File

@ -0,0 +1,7 @@
class Ref {
constructor(ref = Ref) {
this.ref = ref
}
}
assert.equal(Ref, new Ref().ref)

View File

@ -0,0 +1,7 @@
var t = function (e = "foo", f = 5) {
return e + " bar " + f;
};
var a = function (e, f = 5) {
return e + " bar " + f;
};

View File

@ -0,0 +1,19 @@
var t = function (e, f) {
if (e === void 0) {
e = "foo";
}
if (f === void 0) {
f = 5;
}
return e + " bar " + f;
};
var a = function (e, f) {
if (f === void 0) {
f = 5;
}
return e + " bar " + f;
};

View File

@ -0,0 +1,19 @@
function required(msg) {
throw new Error(msg);
}
function sum(
{ arr = required('arr is required') } = { arr: arr = [] },
length = arr.length
) {
let i = 0;
let acc = 0;
for (let item of arr) {
if (i >= length) return acc;
acc += item;
i++;
}
return acc;
}
assert.equal(sum({arr:[1,2]}), 3);

View File

@ -0,0 +1,15 @@
const a = 1;
function rest(b = a, ...a) {
assert.equal(b, 1);
}
rest(undefined, 2)
function rest2(b = a, ...a) {
assert.equal(a[0], 2);
}
rest2(undefined, 2)
function rest3(b = a, ...a) {
assert.equal(a.length, 1);
}
rest3(undefined, 2)

View File

@ -0,0 +1,3 @@
var t = function (f = "foo") {
return f + " bar";
};

View File

@ -0,0 +1,7 @@
var t = function (f) {
if (f === void 0) {
f = "foo";
}
return f + " bar";
};

View File

@ -0,0 +1,3 @@
{
"plugins": ["transform-class-properties", "external-helpers", "syntax-flow", ["transform-es2015-parameters", { "loose": true } ], "transform-es2015-block-scoping", "transform-es2015-spread", "transform-es2015-classes", "transform-es2015-destructuring", "transform-es2015-arrow-functions", "syntax-async-functions", "transform-es2015-for-of"]
}

View File

@ -0,0 +1,5 @@
function t(undefined = 17, a = 3) {
return a;
}
assert.equal(t(), 3);