Skip to content

Commit 44a9566

Browse files
robhoganmeta-codesync[bot]
authored andcommitted
Babel preset: Add unstable_preserveDestructuring to experiment with disabling destructuring transform for SH (#56829)
Summary: Pull Request resolved: #56829 Disable `babel/plugin-transform-destructuring` when `customTransformOptions.unstable_preserveDestructuring` is truthy. This allows us to experiment with native destructuring support in Static Hermes. Changelog: [Internal] Reviewed By: vzaidman Differential Revision: D93013925 fbshipit-source-id: bfc2b1cb8e04c28d2ae71b596bf759e3dc0e5701
1 parent 3b76474 commit 44a9566

3 files changed

Lines changed: 330 additions & 4 deletions

File tree

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @generated
8+
* @noformat
9+
* @noflow
10+
* @nolint
11+
*
12+
* This is a snapshot of the transform output for testing purposes.
13+
* To update, run: js1 test transform-snapshot-test.js -u
14+
*
15+
* Transform configuration:
16+
* - Hermes stable transform profile in development mode with unstable_preserveDestructuring enabled
17+
* - Options: {"dev":true,"unstable_transformProfile":"hermes-stable","customTransformOptions":{"unstable_preserveDestructuring":true}}
18+
*/
19+
20+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
21+
Object.defineProperty(exports, "__esModule", {
22+
value: true
23+
});
24+
exports.LegacyComponent = exports.Dog = exports.Counter = exports.Animal = void 0;
25+
exports.ModernComponent = ModernComponent;
26+
exports.MyClass = void 0;
27+
exports.asyncNumberGenerator = asyncNumberGenerator;
28+
Object.defineProperty(exports, "default", {
29+
enumerable: true,
30+
get: function () {
31+
return _dataUtils.fetchData;
32+
}
33+
});
34+
exports.getNestedValue = getNestedValue;
35+
exports.loadModule = loadModule;
36+
exports.matchEmoji = matchEmoji;
37+
exports.mergeConfigs = mergeConfigs;
38+
exports.parseDate = parseDate;
39+
exports.processUser = processUser;
40+
exports.safeJsonParse = safeJsonParse;
41+
exports.sumPairs = sumPairs;
42+
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
43+
var _setPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/setPrototypeOf"));
44+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
45+
var _classPrivateFieldLooseBase2 = _interopRequireDefault(require("@babel/runtime/helpers/classPrivateFieldLooseBase"));
46+
var _classPrivateFieldLooseKey2 = _interopRequireDefault(require("@babel/runtime/helpers/classPrivateFieldLooseKey"));
47+
var _awaitAsyncGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/awaitAsyncGenerator"));
48+
var _wrapAsyncGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapAsyncGenerator"));
49+
var _react = _interopRequireWildcard(require("react"));
50+
var React = _react;
51+
var _jsxRuntime = require("react/jsx-runtime");
52+
var _dataUtils = require("./data-utils");
53+
var _jsxFileName = "/absolute/path/to/input.js";
54+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
55+
function _wrapRegExp() { _wrapRegExp = function (e, r) { return new BabelRegExp(e, void 0, r); }; var e = RegExp.prototype, r = new WeakMap(); function BabelRegExp(e, t, p) { var o = RegExp(e, t); return r.set(o, p || r.get(e)), (0, _setPrototypeOf2.default)(o, BabelRegExp.prototype); } function buildGroups(e, t) { var p = r.get(t); return Object.keys(p).reduce(function (r, t) { var o = p[t]; if ("number" == typeof o) r[t] = e[o];else { for (var i = 0; void 0 === e[o[i]] && i + 1 < o.length;) i++; r[t] = e[o[i]]; } return r; }, Object.create(null)); } return (0, _inherits2.default)(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (r) { var t = e.exec.call(this, r); if (t) { t.groups = buildGroups(t, this); var p = t.indices; p && (p.groups = buildGroups(p, this)); } return t; }, BabelRegExp.prototype[Symbol.replace] = function (t, p) { if ("string" == typeof p) { var o = r.get(this); return e[Symbol.replace].call(this, t, p.replace(/\$<([^>]+)(>|$)/g, function (e, r, t) { if ("" === t) return e; var p = o[r]; return Array.isArray(p) ? "$" + p.join("$") : "number" == typeof p ? "$" + p : ""; })); } if ("function" == typeof p) { var i = this; return e[Symbol.replace].call(this, t, function () { var e = arguments; return "object" != typeof e[e.length - 1] && (e = [].slice.call(e)).push(buildGroups(e, i)), p.apply(this, e); }); } return e[Symbol.replace].call(this, t, p); }, _wrapRegExp.apply(this, arguments); }
56+
function _createForOfIteratorHelperLoose(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (t) return (t = t.call(r)).next.bind(t); if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var o = 0; return function () { return o >= r.length ? { done: !0 } : { done: !1, value: r[o++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
57+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
58+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
59+
var _count = (0, _classPrivateFieldLooseKey2.default)("count");
60+
var _instances = (0, _classPrivateFieldLooseKey2.default)("instances");
61+
var _increment = (0, _classPrivateFieldLooseKey2.default)("increment");
62+
class Counter {
63+
constructor() {
64+
Object.defineProperty(this, _increment, {
65+
value: _increment2
66+
});
67+
Object.defineProperty(this, _count, {
68+
writable: true,
69+
value: 0
70+
});
71+
(0, _classPrivateFieldLooseBase2.default)(Counter, _instances)[_instances]++;
72+
}
73+
get value() {
74+
return (0, _classPrivateFieldLooseBase2.default)(this, _count)[_count];
75+
}
76+
increment() {
77+
(0, _classPrivateFieldLooseBase2.default)(this, _increment)[_increment]();
78+
}
79+
static get instanceCount() {
80+
return (0, _classPrivateFieldLooseBase2.default)(Counter, _instances)[_instances];
81+
}
82+
}
83+
exports.Counter = Counter;
84+
function _increment2() {
85+
(0, _classPrivateFieldLooseBase2.default)(this, _count)[_count]++;
86+
}
87+
Object.defineProperty(Counter, _instances, {
88+
writable: true,
89+
value: 0
90+
});
91+
function asyncNumberGenerator(_x) {
92+
return _asyncNumberGenerator.apply(this, arguments);
93+
}
94+
function _asyncNumberGenerator() {
95+
_asyncNumberGenerator = (0, _wrapAsyncGenerator2.default)(function* (max) {
96+
for (var i = 0; i < max; i++) {
97+
yield (0, _awaitAsyncGenerator2.default)(new Promise(resolve => setTimeout(resolve, 100)));
98+
yield i;
99+
}
100+
});
101+
return _asyncNumberGenerator.apply(this, arguments);
102+
}
103+
function fetchData(_x2) {
104+
return _fetchData.apply(this, arguments);
105+
}
106+
function _fetchData() {
107+
_fetchData = (0, _asyncToGenerator2.default)(function* (url) {
108+
var response = yield fetch(url);
109+
var data = yield response.json();
110+
return {
111+
data
112+
};
113+
});
114+
return _fetchData.apply(this, arguments);
115+
}
116+
function getNestedValue(obj) {
117+
var _obj$a$b$c, _obj$a;
118+
return (_obj$a$b$c = obj == null || (_obj$a = obj.a) == null || (_obj$a = _obj$a.b) == null ? void 0 : _obj$a.c) != null ? _obj$a$b$c : 42;
119+
}
120+
class Animal {
121+
#age;
122+
constructor(name, age) {
123+
this.name = name;
124+
this.#age = age;
125+
}
126+
speak() {
127+
return `${this.name} makes a sound`;
128+
}
129+
get age() {
130+
return this.#age;
131+
}
132+
}
133+
exports.Animal = Animal;
134+
class Dog extends Animal {
135+
constructor(name, age, breed) {
136+
super(name, age);
137+
this.breed = breed;
138+
}
139+
speak() {
140+
return `${this.name} barks!`;
141+
}
142+
fetchTreats() {
143+
return (0, _asyncToGenerator2.default)(function* () {
144+
yield new Promise(resolve => setTimeout(resolve, 100));
145+
return ['bone', 'biscuit', 'toy'];
146+
})();
147+
}
148+
}
149+
exports.Dog = Dog;
150+
function processUser({
151+
name,
152+
age = 18,
153+
...rest
154+
}) {
155+
var {
156+
city = 'Unknown'
157+
} = rest;
158+
return `${name} (${age}) from ${city}`;
159+
}
160+
function mergeConfigs(base, ...overrides) {
161+
return {
162+
...base,
163+
...overrides.reduce((acc, o) => ({
164+
...acc,
165+
...o
166+
}), {})
167+
};
168+
}
169+
function sumPairs(pairs) {
170+
var total = 0;
171+
for (var _iterator = _createForOfIteratorHelperLoose(pairs), _step; !(_step = _iterator()).done;) {
172+
var [a, b] = _step.value;
173+
total += a + b;
174+
}
175+
return total;
176+
}
177+
function parseDate(dateString) {
178+
var regex = _wrapRegExp(/(\d{4})-(\d{2})-(\d{2})/, {
179+
year: 1,
180+
month: 2,
181+
day: 3
182+
});
183+
var match = dateString.match(regex);
184+
if (match != null && match.groups) {
185+
return {
186+
year: match.groups.year,
187+
month: match.groups.month,
188+
day: match.groups.day
189+
};
190+
}
191+
return null;
192+
}
193+
function safeJsonParse(input) {
194+
try {
195+
return JSON.parse(input);
196+
} catch (_unused) {
197+
return null;
198+
}
199+
}
200+
function matchEmoji(text) {
201+
var match = text.match(/(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5-\uDED7\uDEDC-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDDFF\uDE70-\uDE7C\uDE80-\uDE89\uDE8F-\uDEC6\uDECE-\uDEDC\uDEDF-\uDEE9\uDEF0-\uDEF8])/);
202+
return match == null ? void 0 : match[0];
203+
}
204+
var MyClass = class {
205+
constructor(value) {
206+
this.value = value;
207+
}
208+
};
209+
exports.MyClass = MyClass;
210+
function loadModule() {
211+
return _loadModule.apply(this, arguments);
212+
}
213+
function _loadModule() {
214+
_loadModule = (0, _asyncToGenerator2.default)(function* () {
215+
var module = yield import('./some-module');
216+
return module.default;
217+
});
218+
return _loadModule.apply(this, arguments);
219+
}
220+
var LegacyComponent = exports.LegacyComponent = React.createClass({
221+
displayName: 'LegacyComponent',
222+
getInitialState() {
223+
return {
224+
count: 0
225+
};
226+
},
227+
render() {
228+
return (0, _jsxRuntime.jsx)("div", {
229+
children: this.state.count
230+
});
231+
}
232+
});
233+
function ModernComponent({
234+
initialCount = 0
235+
}) {
236+
var [count, setCount] = (0, _react.useState)(initialCount);
237+
var [status, setStatus] = (0, _react.useState)(Status.Active);
238+
(0, _react.useEffect)(() => {
239+
var timer = setInterval(() => {
240+
setCount(c => c + 1);
241+
}, 1000);
242+
return () => clearInterval(timer);
243+
}, []);
244+
function handleAsyncClick() {
245+
return _handleAsyncClick.apply(this, arguments);
246+
}
247+
function _handleAsyncClick() {
248+
_handleAsyncClick = (0, _asyncToGenerator2.default)(function* () {
249+
var data = yield fetchData('/api/data');
250+
console.log(data);
251+
});
252+
return _handleAsyncClick.apply(this, arguments);
253+
}
254+
var handleClick = function () {
255+
var _ref = (0, _asyncToGenerator2.default)(function* () {
256+
yield handleAsyncClick();
257+
setStatus(Status.Pending);
258+
});
259+
return function handleClick() {
260+
return _ref.apply(this, arguments);
261+
};
262+
}();
263+
return (0, _jsxRuntime.jsxs)("div", {
264+
children: [(0, _jsxRuntime.jsx)("span", {
265+
"data-testid": "count",
266+
children: count
267+
}), (0, _jsxRuntime.jsx)("span", {
268+
"data-testid": "status",
269+
children: String(status)
270+
}), (0, _jsxRuntime.jsx)("button", {
271+
onClick: handleClick,
272+
children: "Increment"
273+
}), (0, _jsxRuntime.jsx)(LegacyComponent, {})]
274+
});
275+
}

packages/react-native-babel-preset/src/__tests__/transform-snapshot-test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@ const testConfigs = [
141141
description:
142142
'Hermes stable transform profile in development mode with unstable_preserveBlockScoping enabled',
143143
},
144+
{
145+
name: 'hermes-stable-dev-preserve-destructuring',
146+
options: {
147+
dev: true,
148+
unstable_transformProfile: 'hermes-stable',
149+
customTransformOptions: {
150+
unstable_preserveDestructuring: true,
151+
},
152+
},
153+
description:
154+
'Hermes stable transform profile in development mode with unstable_preserveDestructuring enabled',
155+
},
144156
];
145157

146158
function transformCode(
@@ -502,5 +514,37 @@ describe('react-native-babel-preset transform snapshots', () => {
502514
expect(result).not.toContain('let ');
503515
expect(result).not.toContain('const ');
504516
});
517+
518+
it('preserves destructuring with unstable_preserveDestructuring', () => {
519+
const code = `
520+
const {a, b, ...rest} = obj;
521+
const [x, y, z = 10] = arr;
522+
const {nested: {value}} = data;
523+
`;
524+
const result = transformCode(code, {
525+
dev: false,
526+
unstable_transformProfile: 'hermes-stable',
527+
customTransformOptions: {
528+
unstable_preserveDestructuring: true,
529+
},
530+
});
531+
// Check that destructuring syntax is preserved (not transformed)
532+
expect(result).toContain('...rest');
533+
expect(result).toContain('z = 10');
534+
expect(result).toContain('nested:');
535+
expect(result).not.toContain('_objectWithoutProperties');
536+
expect(result).not.toContain('_slicedToArray');
537+
});
538+
539+
it('transforms destructuring without unstable_preserveDestructuring', () => {
540+
const code = `
541+
const {a, b, ...rest} = obj;
542+
`;
543+
const result = transformCode(code, {
544+
dev: false,
545+
unstable_transformProfile: 'hermes-stable',
546+
});
547+
expect(result).toContain('_objectWithoutProperties');
548+
});
505549
});
506550
});

packages/react-native-babel-preset/src/configs/main.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ const getPreset = (src, options, babel) => {
9292
options?.customTransformOptions?.unstable_preserveBlockScoping,
9393
);
9494

95+
// Preserve destructuring syntax if the experiment is enabled.
96+
const preserveDestructuring = TRUE_VALS.has(
97+
options?.customTransformOptions?.unstable_preserveDestructuring,
98+
);
99+
95100
const isNull = src == null;
96101
const hasClass = isNull || src.indexOf('class') !== -1;
97102

@@ -144,10 +149,12 @@ const getPreset = (src, options, babel) => {
144149
]);
145150
}
146151

147-
extraPlugins.push([
148-
require('@babel/plugin-transform-destructuring'),
149-
{useBuiltIns: true},
150-
]);
152+
if (!preserveDestructuring) {
153+
extraPlugins.push([
154+
require('@babel/plugin-transform-destructuring'),
155+
{useBuiltIns: true},
156+
]);
157+
}
151158
if (!preserveAsync && (isNull || src.indexOf('async') !== -1)) {
152159
extraPlugins.push([
153160
require('@babel/plugin-transform-async-generator-functions'),

0 commit comments

Comments
 (0)