@react-native/babel-preset applies transform-destructuring unconditionally under Hermes — semantic divergence breaks three.js TSL (crashes every InstancedMesh on WebGPU)
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- C++
- Estrellas
- 127k
- Forks
- 25.3k
- Merge medio
- 1 d 23 h
- PR fusionados (30 d)
- 4
Descripción
Description
@react-native/babel-preset pushes @babel/plugin-transform-destructuring unconditionally — including for Hermes targets (packages/react-native-babel-preset/src/configs/main.js, the always-on plugin list; verified in 0.81.4 at src/configs/main.js:122). Hermes supports array/object destructuring natively, so for Hermes builds the transform is (a) redundant bytecode and (b) a semantic hazard, because downleveled destructuring is not perfectly equivalent to native destructuring.
The concrete divergence: native array destructuring always goes through the iterator protocol, while Babel's _slicedToArray helper short-circuits via _arrayWithHoles — if (Array.isArray(r)) return r; — and then indexes positionally. For a Proxy over an array (Array.isArray pierces to the target) whose get trap serves a custom Symbol.iterator but does not answer positional reads, the two compile outputs produce different values: iterator yields the real elements, indexing yields undefined.
That exotic-sounding object is shipping in one of the most popular 3D libraries: three.js's TSL shader system passes Fn arguments through exactly such a Proxy, and three's own sources destructure it (src/nodes/accessors/Instance.js: Fn( ( [ instancedMesh ] ) => { const { instanceMatrix, instanceColor } = instancedMesh; ... } )). Metro rewrites three's code with this transform, so on device the body receives undefined and every React Native app using three.js WebGPU (react-native-webgpu, Dawn/Vulkan) with an InstancedMesh crashes on the first rendered frame:
TypeError: Cannot read property 'instanceMatrix' of undefined
at ... setupOutput / getOutputNode / build / flowNodeFromShaderStage / ... / render
The same pattern arms skinning(), batch() and morphReference() — i.e. skinned meshes, batched meshes and morph targets are equally affected. Nothing surfaces in Node/browser tests because those run untransformed code; the failure exists only in the Metro-bundled app.
Ask
Consider making transform-destructuring conditional on the engine target (skipped for Hermes, as several other transforms already are engine-aware), or documenting why it must stay unconditional. Cross-filed with the two neighboring projects, since each owns a slice of the collision:
- three.js (Proxy should honor positional reads): https://github.com/mrdoob/three.js/issues/34227
- Babel (
_arrayWithHolesfast path vs custom iterators on array-backed Proxies): link to follow in a comment
Steps to reproduce
- Bare RN 0.81.4 app (Hermes, new arch) +
react-native-webgpu0.8.2 +three0.185.1 - Render any scene containing a
THREE.InstancedMeshwithWebGPURenderer - First frame throws
TypeError: Cannot read property 'instanceMatrix' of undefined
A toolchain-only demonstration (no device needed): run @react-native/babel-preset over const fn = ([a]) => a; and observe the _slicedToArray output; feed it an array-backed Proxy with a custom Symbol.iterator in its get trap and no index handling — native semantics return the element, transformed semantics return undefined.
React Native Version
0.81.4
Affected Platforms
Runtime: Android (verified on device: Android 15, Hermes release bundle, Dawn/Vulkan). iOS untested but the transform is platform-independent.
Output of npx @react-native-community/cli info
Not attached — the report is toolchain-level (@react-native/babel-preset 0.81.4, @babel/plugin-transform-destructuring 7.29.7, @babel/runtime 7.29.7); happy to add on request.
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Comienza en packages/react-native-babel-preset/src/configs/main.js, especialmente en la lista de plugins siempre activos alrededor de la línea 122, y reproduce la transformación con el ejemplo de Proxy respaldado por un array del issue. Compara los patrones de configuración de Hermes y no-Hermes que ya están presentes; se considera terminado cuando el preset evita la divergencia semántica para Hermes o documenta una razón confirmada para conservar la transformación, con el comportamiento de la toolchain cubierto por una prueba adecuada.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- javascript, react-native, three.js
- Área
- build-system, mobile, tooling
- Tipo de issue
- Error
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Estado de actividad
- Activo
- Claridad
- Bastante claro
- Aptitud para principiantes
- 55/100