Fix object spread according to spec (#7034)
This commit is contained in:
parent
e732ee0c5b
commit
ee6dfd1580
@ -374,6 +374,26 @@ helpers.extends = defineHelper(`
|
||||
}
|
||||
`);
|
||||
|
||||
helpers.objectSpread = defineHelper(`
|
||||
import defineProperty from "defineProperty";
|
||||
|
||||
export default function _objectSpread(target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = (arguments[i] != null) ? arguments[i] : {};
|
||||
var ownKeys = Object.keys(source);
|
||||
if (typeof Object.getOwnPropertySymbols === 'function') {
|
||||
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
||||
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
||||
}));
|
||||
}
|
||||
ownKeys.forEach(function(key) {
|
||||
defineProperty(target, key, source[key]);
|
||||
});
|
||||
}
|
||||
return target;
|
||||
}
|
||||
`);
|
||||
|
||||
helpers.get = defineHelper(`
|
||||
export default function _get(object, property, receiver) {
|
||||
if (object === null) object = Function.prototype;
|
||||
|
||||
@ -54,18 +54,34 @@ require("@babel/core").transform("code", {
|
||||
|
||||
## Options
|
||||
|
||||
By default, this plugin will produce spec compliant code. The Babel's `objectSpread` helper will be used.
|
||||
|
||||
### `loose`
|
||||
|
||||
`boolean`, defaults to `false`.
|
||||
|
||||
Enabling this option will use Babel's `extends` helper, which is basically the same as `Object.assign` (see `useBuiltIns` below to use it directly).
|
||||
|
||||
:warning: Please take in mind that even if they're almost equivalent, there's an important difference between spread and `Object.assign`: to summarize, **spread defines new properties, while `Object.assign()` sets them**, so using this mode might produce unexpected results in some case.
|
||||
|
||||
For detailed information please check out [Spread VS. Object.assign](http://2ality.com/2016/10/rest-spread-properties.html#spreading-objects-versus-objectassign) and [Assigning VS. defining properties](http://exploringjs.com/es6/ch_oop-besides-classes.html#sec_assigning-vs-defining-properties).
|
||||
|
||||
|
||||
### `useBuiltIns`
|
||||
|
||||
`boolean`, defaults to `false`.
|
||||
|
||||
By default, this plugin uses Babel's `extends` helper which polyfills `Object.assign`. Enabling this option will use `Object.assign` directly.
|
||||
Enabling this option will use `Object.assign` directly instead of the Babel's `extends` helper. Keep in mind that this flag only applies when `loose: true`.
|
||||
|
||||
|
||||
##### Example
|
||||
|
||||
**.babelrc**
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-object-rest-spread", { "useBuiltIns": true }]
|
||||
["@babel/plugin-proposal-object-rest-spread", { "loose": true, "useBuiltIns": true }]
|
||||
]
|
||||
}
|
||||
```
|
||||
@ -86,3 +102,5 @@ z = Object.assign({ x }, y);
|
||||
|
||||
* [Proposal: Object Rest/Spread Properties for ECMAScript](https://github.com/sebmarkbage/ecmascript-rest-spread)
|
||||
* [Spec](http://sebmarkbage.github.io/ecmascript-rest-spread)
|
||||
* [Spread VS. Object.assign](http://2ality.com/2016/10/rest-spread-properties.html#spreading-objects-versus-objectassign)
|
||||
* [Assigning VS. defining properties](http://exploringjs.com/es6/ch_oop-besides-classes.html#sec_assigning-vs-defining-properties)
|
||||
|
||||
@ -2,9 +2,10 @@ import syntaxObjectRestSpread from "@babel/plugin-syntax-object-rest-spread";
|
||||
import { types as t } from "@babel/core";
|
||||
|
||||
export default function(api, opts) {
|
||||
const { useBuiltIns = false } = opts;
|
||||
if (typeof useBuiltIns !== "boolean") {
|
||||
throw new Error(".useBuiltIns must be a boolean, or undefined");
|
||||
const { useBuiltIns = false, loose = false } = opts;
|
||||
|
||||
if (typeof loose !== "boolean") {
|
||||
throw new Error(".loose must be a boolean, or undefined");
|
||||
}
|
||||
|
||||
function hasRestElement(path) {
|
||||
@ -366,6 +367,10 @@ export default function(api, opts) {
|
||||
props = [];
|
||||
}
|
||||
|
||||
if (t.isSpreadElement(path.node.properties[0])) {
|
||||
args.push(t.objectExpression([]));
|
||||
}
|
||||
|
||||
for (const prop of (path.node.properties: Array)) {
|
||||
if (t.isSpreadElement(prop)) {
|
||||
push();
|
||||
@ -377,14 +382,15 @@ export default function(api, opts) {
|
||||
|
||||
push();
|
||||
|
||||
if (!t.isObjectExpression(args[0])) {
|
||||
args.unshift(t.objectExpression([]));
|
||||
let helper;
|
||||
if (loose) {
|
||||
helper = useBuiltIns
|
||||
? t.memberExpression(t.identifier("Object"), t.identifier("assign"))
|
||||
: file.addHelper("extends");
|
||||
} else {
|
||||
helper = file.addHelper("objectSpread");
|
||||
}
|
||||
|
||||
const helper = useBuiltIns
|
||||
? t.memberExpression(t.identifier("Object"), t.identifier("assign"))
|
||||
: file.addHelper("extends");
|
||||
|
||||
path.replaceWith(t.callExpression(helper, args));
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
z = _extends({
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
z = _objectSpread({
|
||||
x
|
||||
}, y);
|
||||
z = {
|
||||
x,
|
||||
w: _extends({}, y)
|
||||
w: _objectSpread({}, y)
|
||||
};
|
||||
|
||||
@ -1 +1,7 @@
|
||||
({ x, ...y, a, ...b, c });
|
||||
|
||||
({ ...Object.prototype });
|
||||
|
||||
({ ...{ foo: 'bar' } });
|
||||
|
||||
({ ...{ get foo () { return 'foo' } } });
|
||||
|
||||
@ -1,9 +1,24 @@
|
||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
_extends({
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
_objectSpread({
|
||||
x
|
||||
}, y, {
|
||||
a
|
||||
}, b, {
|
||||
c
|
||||
});
|
||||
|
||||
_objectSpread({}, Object.prototype);
|
||||
|
||||
_objectSpread({}, {
|
||||
foo: 'bar'
|
||||
});
|
||||
|
||||
_objectSpread({}, {
|
||||
get foo() {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
z = { x, ...y };
|
||||
|
||||
z = { x, w: { ...y } };
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": [
|
||||
["proposal-object-rest-spread", { "loose": true, "useBuiltIns": true }]
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
z = Object.assign({
|
||||
x
|
||||
}, y);
|
||||
z = {
|
||||
x,
|
||||
w: Object.assign({}, y)
|
||||
};
|
||||
@ -0,0 +1,3 @@
|
||||
z = { x, ...y };
|
||||
|
||||
z = { x, w: { ...y } };
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": [
|
||||
["proposal-object-rest-spread", { "loose": true }]
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
|
||||
z = _extends({
|
||||
x
|
||||
}, y);
|
||||
z = {
|
||||
x,
|
||||
w: _extends({}, y)
|
||||
};
|
||||
@ -0,0 +1,40 @@
|
||||
Object.defineProperty(Object.prototype, 'NOSET', {
|
||||
set(value) {
|
||||
// noop
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(Object.prototype, 'NOWRITE', {
|
||||
writable: false,
|
||||
value: 'abc',
|
||||
});
|
||||
|
||||
const obj = { NOSET: 123 };
|
||||
// this wouldn't work as expected if transformed as Object.assign (or equivalent)
|
||||
// because those trigger object setters (spread don't)
|
||||
const objSpread = { ...obj };
|
||||
|
||||
const obj2 = { NOSET: 123, NOWRITE: 456 };
|
||||
// this line would throw `TypeError: Cannot assign to read only property 'NOWRITE'`
|
||||
// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties
|
||||
// (spread defines them)
|
||||
const obj2Spread = { ...obj2 };
|
||||
|
||||
assert.deepEqual(obj, objSpread);
|
||||
assert.deepEqual(obj2, obj2Spread);
|
||||
|
||||
const KEY = Symbol('key');
|
||||
const obj3Spread = { ...{ get foo () { return 'bar' } }, [KEY]: 'symbol' };
|
||||
assert.equal(Object.getOwnPropertyDescriptor(obj3Spread, 'foo').value, 'bar');
|
||||
assert.equal(Object.getOwnPropertyDescriptor(obj3Spread, KEY).value, 'symbol');
|
||||
|
||||
const obj4Spread = { ...Object.prototype };
|
||||
assert.isUndefined(Object.getOwnPropertyDescriptor(obj4Spread, 'hasOwnProperty'));
|
||||
|
||||
assert.doesNotThrow(() => ({ ...null, ...undefined }));
|
||||
|
||||
const o = Object.create(null);
|
||||
o.a = 'foo';
|
||||
o.__proto__ = [];
|
||||
const o2 = { ...o };
|
||||
assert.equal(false, Array.isArray(Object.getPrototypeOf(o2)));
|
||||
@ -1,3 +1,5 @@
|
||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
var z = _extends({}, x);
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
var z = _objectSpread({}, x);
|
||||
|
||||
@ -1 +0,0 @@
|
||||
z = { x, ...y };
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"plugins": [["proposal-object-rest-spread", { "useBuiltIns": "invalidOption" }]],
|
||||
"throws": ".useBuiltIns must be a boolean, or undefined"
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
z = { x, ...y };
|
||||
@ -1,3 +0,0 @@
|
||||
z = Object.assign({
|
||||
x
|
||||
}, y);
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"plugins": [["proposal-object-rest-spread", { "useBuiltIns": true }]]
|
||||
}
|
||||
@ -14,6 +14,10 @@ _AsyncGenerator.prototype.return = function (arg) { return this._invoke("return"
|
||||
|
||||
function _AwaitValue(value) { this.wrapped = value; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
||||
|
||||
var _x$y$a$b = {
|
||||
@ -26,7 +30,7 @@ var _x$y$a$b = {
|
||||
y = _x$y$a$b.y,
|
||||
z = _objectWithoutProperties(_x$y$a$b, ["x", "y"]);
|
||||
|
||||
var n = Object.assign({
|
||||
var n = _objectSpread({
|
||||
x: x,
|
||||
y: y
|
||||
}, z);
|
||||
|
||||
@ -6,9 +6,11 @@ require("core-js/modules/es6.symbol");
|
||||
|
||||
require("core-js/modules/es6.promise");
|
||||
|
||||
require("core-js/modules/es6.array.index-of");
|
||||
require("core-js/modules/es6.array.for-each");
|
||||
|
||||
require("core-js/modules/es6.object.assign");
|
||||
require("core-js/modules/es6.array.filter");
|
||||
|
||||
require("core-js/modules/es6.array.index-of");
|
||||
|
||||
function _awaitAsyncGenerator(value) { return new _AwaitValue(value); }
|
||||
|
||||
@ -26,6 +28,10 @@ _AsyncGenerator.prototype.return = function (arg) { return this._invoke("return"
|
||||
|
||||
function _AwaitValue(value) { this.wrapped = value; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
||||
|
||||
var _x$y$a$b = {
|
||||
@ -38,7 +44,7 @@ var _x$y$a$b = {
|
||||
y = _x$y$a$b.y,
|
||||
z = _objectWithoutProperties(_x$y$a$b, ["x", "y"]);
|
||||
|
||||
var n = Object.assign({
|
||||
var n = _objectSpread({
|
||||
x: x,
|
||||
y: y
|
||||
}, z);
|
||||
|
||||
@ -14,7 +14,9 @@ _AsyncGenerator.prototype.return = function (arg) { return this._invoke("return"
|
||||
|
||||
function _AwaitValue(value) { this.wrapped = value; }
|
||||
|
||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
||||
|
||||
@ -28,7 +30,7 @@ var _x$y$a$b = {
|
||||
y = _x$y$a$b.y,
|
||||
z = _objectWithoutProperties(_x$y$a$b, ["x", "y"]);
|
||||
|
||||
var n = _extends({
|
||||
var n = _objectSpread({
|
||||
x: x,
|
||||
y: y
|
||||
}, z);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user