Skip to content

Commit e5e1369

Browse files
authored
fix: Computed properties should keep original definition order (#15232)
Co-authored-by: Nicolò Ribaudo <[email protected]> Co-authored-by: liuxingbaoyu <[email protected]> Fixes #15140
1 parent 2bba4a5 commit e5e1369

File tree

22 files changed

+188
-82
lines changed

22 files changed

+188
-82
lines changed

Makefile

+9-9
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,15 @@ test-test262-update-allowlist:
189189

190190

191191
new-version-checklist:
192-
# @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
193-
# @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
194-
# @echo "!!!!!! !!!!!!"
195-
# @echo "!!!!!! Add any message here, and UNCOMMENT THESE LINES! !!!!!!"
196-
# @echo "!!!!!! !!!!!!"
197-
# @echo "!!!!!! !!!!!!"
198-
# @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
199-
# @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
200-
# @exit 1
192+
@echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
193+
@echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
194+
@echo "!!!!!! !!!!!!"
195+
@echo "!!!!!! Update the minVersion of !!!!!!"
196+
@echo "!!!!!! packages/babel-helpers/src/helpers/defineAccessor.js !!!!!!"
197+
@echo "!!!!!! !!!!!!"
198+
@echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
199+
@echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
200+
@exit 1
201201

202202
new-version:
203203
$(MAKE) new-version-checklist

packages/babel-core/src/transformation/file/file.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export default class File {
210210
});
211211

212212
nodes.forEach(node => {
213-
// @ts-expect-error Fixeme: document _compact node property
213+
// @ts-expect-error Fixme: document _compact node property
214214
node._compact = true;
215215
});
216216

packages/babel-helpers/src/helpers-generated.ts

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export default Object.freeze({
4545
"7.20.5",
4646
'export default function _checkInRHS(value){if(Object(value)!==value)throw TypeError("right-hand side of \'in\' should be an object, got "+(null!==value?typeof value:"null"));return value}',
4747
),
48+
defineAccessor: helper(
49+
"7.20.6",
50+
"export default function _defineAccessor(type,obj,key,fn){var desc={configurable:!0,enumerable:!0};return desc[type]=fn,Object.defineProperty(obj,key,desc)}",
51+
),
4852
iterableToArrayLimit: helper(
4953
"7.0.0-beta.0",
5054
'export default function _iterableToArrayLimit(arr,i){var _i=null==arr?null:"undefined"!=typeof Symbol&&arr[Symbol.iterator]||arr["@@iterator"];if(null!=_i){var _s,_e,_x,_r,_arr=[],_n=!0,_d=!1;try{if(_x=(_i=_i.call(arr)).next,0===i){if(Object(_i)!==_i)return;_n=!1}else for(;!(_n=(_s=_x.call(_i)).done)&&(_arr.push(_s.value),_arr.length!==i);_n=!0);}catch(err){_d=!0,_e=err}finally{try{if(!_n&&null!=_i.return&&(_r=_i.return(),Object(_r)!==_r))return}finally{if(_d)throw _e}}return _arr}}',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* @minVersion 7.20.6 */
2+
3+
export default function _defineAccessor(type, obj, key, fn) {
4+
var desc = { configurable: true, enumerable: true };
5+
// type should be "get" or "set"
6+
desc[type] = fn;
7+
return Object.defineProperty(obj, key, desc);
8+
}

packages/babel-plugin-transform-computed-properties/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"babel-plugin"
1818
],
1919
"dependencies": {
20-
"@babel/helper-plugin-utils": "workspace:^"
20+
"@babel/helper-plugin-utils": "workspace:^",
21+
"@babel/template": "workspace:^"
2122
},
2223
"peerDependencies": {
2324
"@babel/core": "^7.0.0-0"

packages/babel-plugin-transform-computed-properties/src/index.ts

+56-53
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { types as t } from "@babel/core";
2+
import type { PluginPass } from "@babel/core";
13
import { declare } from "@babel/helper-plugin-utils";
2-
import { template, types as t, type PluginPass } from "@babel/core";
4+
import template from "@babel/template";
35
import type { Scope } from "@babel/traverse";
46

57
export interface Options {
@@ -12,10 +14,19 @@ type PropertyInfo = {
1214
body: t.Statement[];
1315
computedProps: t.ObjectMember[];
1416
initPropExpression: t.ObjectExpression;
15-
getMutatorId: () => t.Identifier;
1617
state: PluginPass;
1718
};
1819

20+
// TODO(Babel 8): Remove this
21+
const DefineAccessorHelper = template.expression.ast`
22+
function (type, obj, key, fn) {
23+
var desc = { configurable: true, enumerable: true };
24+
desc[type] = fn;
25+
return Object.defineProperty(obj, key, desc);
26+
}`;
27+
// @ts-expect-error undocumented _compact node property
28+
DefineAccessorHelper._compact = true;
29+
1930
export default declare((api, options: Options) => {
2031
api.assertVersion(7);
2132

@@ -26,10 +37,34 @@ export default declare((api, options: Options) => {
2637
? pushComputedPropsLoose
2738
: pushComputedPropsSpec;
2839

29-
const buildMutatorMapAssign = template.statements(`
30-
MUTATOR_MAP_REF[KEY] = MUTATOR_MAP_REF[KEY] || {};
31-
MUTATOR_MAP_REF[KEY].KIND = VALUE;
32-
`);
40+
function buildDefineAccessor(
41+
state: PluginPass,
42+
type: "get" | "set",
43+
obj: t.Expression,
44+
key: t.Expression,
45+
fn: t.Expression,
46+
) {
47+
let helper: t.Identifier;
48+
if (state.availableHelper("defineAccessor")) {
49+
helper = state.addHelper("defineAccessor");
50+
} else {
51+
// Fallback for @babel/helpers <= 7.20.6, manually add helper function
52+
// TODO(Babel 8): Remove this
53+
const file = state.file;
54+
helper = file.get("fallbackDefineAccessorHelper");
55+
if (!helper) {
56+
const id = file.scope.generateUidIdentifier("defineAccessor");
57+
file.scope.push({
58+
id,
59+
init: DefineAccessorHelper,
60+
});
61+
file.set("fallbackDefineAccessorHelper", (helper = id));
62+
}
63+
helper = t.cloneNode(helper);
64+
}
65+
66+
return t.callExpression(helper, [t.stringLiteral(type), obj, key, fn]);
67+
}
3368

3469
/**
3570
* Get value of an object member under object expression.
@@ -72,31 +107,26 @@ export default declare((api, options: Options) => {
72107
);
73108
}
74109

75-
function pushMutatorDefine(
76-
{ body, getMutatorId, scope }: PropertyInfo,
110+
function pushAccessorDefine(
111+
{ body, computedProps, initPropExpression, objId, state }: PropertyInfo,
77112
prop: t.ObjectMethod,
78113
) {
79-
let key =
114+
const kind = prop.kind as "get" | "set";
115+
const key =
80116
!prop.computed && t.isIdentifier(prop.key)
81117
? t.stringLiteral(prop.key.name)
82118
: prop.key;
119+
const value = getValue(prop);
83120

84-
const maybeMemoise = scope.maybeGenerateMemoised(key);
85-
if (maybeMemoise) {
121+
if (computedProps.length === 1) {
122+
return buildDefineAccessor(state, kind, initPropExpression, key, value);
123+
} else {
86124
body.push(
87-
t.expressionStatement(t.assignmentExpression("=", maybeMemoise, key)),
125+
t.expressionStatement(
126+
buildDefineAccessor(state, kind, t.cloneNode(objId), key, value),
127+
),
88128
);
89-
key = maybeMemoise;
90129
}
91-
92-
body.push(
93-
...buildMutatorMapAssign({
94-
MUTATOR_MAP_REF: getMutatorId(),
95-
KEY: t.cloneNode(key),
96-
VALUE: getValue(prop),
97-
KIND: t.identifier(prop.kind),
98-
}),
99-
);
100130
}
101131

102132
function pushComputedPropsLoose(info: PropertyInfo) {
@@ -105,7 +135,8 @@ export default declare((api, options: Options) => {
105135
t.isObjectMethod(prop) &&
106136
(prop.kind === "get" || prop.kind === "set")
107137
) {
108-
pushMutatorDefine(info, prop);
138+
const single = pushAccessorDefine(info, prop);
139+
if (single) return single;
109140
} else {
110141
pushAssign(t.cloneNode(info.objId), prop, info.body);
111142
}
@@ -123,7 +154,8 @@ export default declare((api, options: Options) => {
123154
t.isObjectMethod(prop) &&
124155
(prop.kind === "get" || prop.kind === "set")
125156
) {
126-
pushMutatorDefine(info, prop);
157+
const single = pushAccessorDefine(info, prop);
158+
if (single) return single;
127159
} else {
128160
// the value of ObjectProperty in ObjectExpression must be an expression
129161
const value = getValue(prop) as t.Expression;
@@ -195,44 +227,15 @@ export default declare((api, options: Options) => {
195227
]),
196228
);
197229

198-
let mutatorRef: t.Identifier;
199-
200-
const getMutatorId = function () {
201-
if (!mutatorRef) {
202-
mutatorRef = scope.generateUidIdentifier("mutatorMap");
203-
204-
body.push(
205-
t.variableDeclaration("var", [
206-
t.variableDeclarator(mutatorRef, t.objectExpression([])),
207-
]),
208-
);
209-
}
210-
211-
return t.cloneNode(mutatorRef);
212-
};
213-
214230
const single = pushComputedProps({
215231
scope,
216232
objId,
217233
body,
218234
computedProps,
219235
initPropExpression,
220-
getMutatorId,
221236
state,
222237
});
223238

224-
if (mutatorRef) {
225-
body.push(
226-
t.expressionStatement(
227-
t.callExpression(
228-
state.addHelper("defineEnumerableProperties"),
229-
[t.cloneNode(objId), t.cloneNode(mutatorRef)],
230-
),
231-
),
232-
);
233-
}
234-
235-
// @ts-expect-error todo(flow->ts) `void` should not be used as variable
236239
if (single) {
237240
path.replaceWith(single);
238241
} else {
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
var _foobar, _foobar2, _test, _test2, _obj, _mutatorMap;
2-
var obj = (_obj = {}, _foobar = foobar, _mutatorMap = {}, _mutatorMap[_foobar] = _mutatorMap[_foobar] || {}, _mutatorMap[_foobar].get = function () {
1+
var _obj;
2+
var obj = (_obj = {}, babelHelpers.defineAccessor("get", _obj, foobar, function () {
33
return "foobar";
4-
}, _foobar2 = foobar, _mutatorMap[_foobar2] = _mutatorMap[_foobar2] || {}, _mutatorMap[_foobar2].set = function (x) {
4+
}), babelHelpers.defineAccessor("set", _obj, foobar, function (x) {
55
console.log(x);
6-
}, _test = "test", _mutatorMap[_test] = _mutatorMap[_test] || {}, _mutatorMap[_test].get = function () {
6+
}), babelHelpers.defineAccessor("get", _obj, "test", function () {
77
return "regular getter after computed property";
8-
}, _test2 = "test", _mutatorMap[_test2] = _mutatorMap[_test2] || {}, _mutatorMap[_test2].set = function (x) {
8+
}), babelHelpers.defineAccessor("set", _obj, "test", function (x) {
99
console.log(x);
10-
}, babelHelpers.defineEnumerableProperties(_obj, _mutatorMap), _obj);
10+
}), _obj);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var obj = {
2+
get ["x" + foo]() { return "heh"; }
3+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var obj = babelHelpers.defineAccessor("get", {}, "x" + foo, function () {
2+
return "heh";
3+
});
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
var _foo, _mutatorMap;
1+
var _foo;
22
var k = Symbol();
3-
var foo = (_foo = {}, _foo[Symbol.iterator] = "foobar", _mutatorMap = {}, _mutatorMap[k] = _mutatorMap[k] || {}, _mutatorMap[k].get = function () {
3+
var foo = (_foo = {}, _foo[Symbol.iterator] = "foobar", babelHelpers.defineAccessor("get", _foo, k, function () {
44
return k;
5-
}, babelHelpers.defineEnumerableProperties(_foo, _mutatorMap), _foo);
5+
}), _foo);
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
var _foobar, _foobar2, _test, _test2, _obj, _mutatorMap;
2-
var obj = (_obj = {}, _foobar = foobar, _mutatorMap = {}, _mutatorMap[_foobar] = _mutatorMap[_foobar] || {}, _mutatorMap[_foobar].get = function () {
1+
var _obj;
2+
var obj = (_obj = {}, babelHelpers.defineAccessor("get", _obj, foobar, function () {
33
return "foobar";
4-
}, _foobar2 = foobar, _mutatorMap[_foobar2] = _mutatorMap[_foobar2] || {}, _mutatorMap[_foobar2].set = function (x) {
4+
}), babelHelpers.defineAccessor("set", _obj, foobar, function (x) {
55
console.log(x);
6-
}, _test = "test", _mutatorMap[_test] = _mutatorMap[_test] || {}, _mutatorMap[_test].get = function () {
6+
}), babelHelpers.defineAccessor("get", _obj, "test", function () {
77
return "regular getter after computed property";
8-
}, _test2 = "test", _mutatorMap[_test2] = _mutatorMap[_test2] || {}, _mutatorMap[_test2].set = function (x) {
8+
}), babelHelpers.defineAccessor("set", _obj, "test", function (x) {
99
console.log(x);
10-
}, babelHelpers.defineEnumerableProperties(_obj, _mutatorMap), _obj);
10+
}), _obj);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var a = {
2+
get ["x"]() { return 0; },
3+
["y"]: 1,
4+
};
5+
expect(Object.keys(a)).toStrictEqual(["x", "y"]);
6+
7+
var b = {
8+
get ["x"]() { return 0; },
9+
["x"]: 1,
10+
};
11+
expect(b.x).toBe(1);
12+
13+
var x = 1;
14+
var y = { x, get x() { return 0; }, x };
15+
expect(y.x).toBe(1);
16+
y.x = 2;
17+
expect(y.x).toBe(2);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
var a = {
2+
get ["x"]() { return 0; },
3+
["y"]: 1,
4+
};
5+
6+
var b = {
7+
get ["x"]() { return 0; },
8+
["x"]: 1,
9+
};
10+
11+
var x = 1;
12+
var y = { x, get x() { return 0; }, x };
13+
y.x = 2;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"plugins": [
3+
"transform-duplicate-keys",
4+
"transform-computed-properties"
5+
]
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
var _a, _b, _y;
2+
var a = (_a = {}, babelHelpers.defineAccessor("get", _a, "x", function () {
3+
return 0;
4+
}), babelHelpers.defineProperty(_a, "y", 1), _a);
5+
var b = (_b = {}, babelHelpers.defineAccessor("get", _b, "x", function () {
6+
return 0;
7+
}), babelHelpers.defineProperty(_b, "x", 1), _b);
8+
var x = 1;
9+
var y = (_y = {
10+
x
11+
}, babelHelpers.defineAccessor("get", _y, "x", function () {
12+
return 0;
13+
}), babelHelpers.defineProperty(_y, "x", x), _y);
14+
y.x = 2;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var obj = {
2+
get ["x" + foo]() { return "heh"; }
3+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var obj = babelHelpers.defineAccessor("get", {}, "x" + foo, function () {
2+
return "heh";
3+
});
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
var _foo, _mutatorMap;
1+
var _foo;
22
var k = Symbol();
3-
var foo = (_foo = {}, babelHelpers.defineProperty(_foo, Symbol.iterator, "foobar"), _mutatorMap = {}, _mutatorMap[k] = _mutatorMap[k] || {}, _mutatorMap[k].get = function () {
3+
var foo = (_foo = {}, babelHelpers.defineProperty(_foo, Symbol.iterator, "foobar"), babelHelpers.defineAccessor("get", _foo, k, function () {
44
return k;
5-
}, babelHelpers.defineEnumerableProperties(_foo, _mutatorMap), _foo);
5+
}), _foo);

packages/babel-runtime-corejs2/package.json

+9
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@
9090
"./helpers/checkInRHS.js"
9191
],
9292
"./helpers/esm/checkInRHS": "./helpers/esm/checkInRHS.js",
93+
"./helpers/defineAccessor": [
94+
{
95+
"node": "./helpers/defineAccessor.js",
96+
"import": "./helpers/esm/defineAccessor.js",
97+
"default": "./helpers/defineAccessor.js"
98+
},
99+
"./helpers/defineAccessor.js"
100+
],
101+
"./helpers/esm/defineAccessor": "./helpers/esm/defineAccessor.js",
93102
"./helpers/iterableToArrayLimit": [
94103
{
95104
"node": "./helpers/iterableToArrayLimit.js",

packages/babel-runtime-corejs3/package.json

+9
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@
8989
"./helpers/checkInRHS.js"
9090
],
9191
"./helpers/esm/checkInRHS": "./helpers/esm/checkInRHS.js",
92+
"./helpers/defineAccessor": [
93+
{
94+
"node": "./helpers/defineAccessor.js",
95+
"import": "./helpers/esm/defineAccessor.js",
96+
"default": "./helpers/defineAccessor.js"
97+
},
98+
"./helpers/defineAccessor.js"
99+
],
100+
"./helpers/esm/defineAccessor": "./helpers/esm/defineAccessor.js",
92101
"./helpers/iterableToArrayLimit": [
93102
{
94103
"node": "./helpers/iterableToArrayLimit.js",

0 commit comments

Comments
 (0)