Moment: Compilaciones de lanzamiento de React-Native, Deep Crash al cambiar de lugar

Creado en 10 oct. 2019  ·  4Comentarios  ·  Fuente: moment/moment

Cuando usamos react-native 0.59.10 y configuramos la configuración regional para momentJS, detectamos un bloqueo súper brutal solo para nuestras compilaciones de lanzamiento. Este bloqueo no fue reproducible para nosotros con el depurador adjunto. Este bloqueo ocurrió para nosotros tanto en iOS como en Android. ¡Las declaraciones try-catch que envuelven todo el uso del momento no detectaron el bloqueo!

Reproducir

  1. Recopilamos las configuraciones regionales/idiomas que queremos probar a través de la lógica específica de la aplicación. Por ejemplo, ["fr-CA", "en-US", "fr", "en"]
  2. Recorremos estos, uno a la vez en lugar de usar el setter de Array, para que podamos llamar a otra instrumentación y, potencialmente, detectar cualquier excepción de JS que se produzca y probar el siguiente candidato.
  3. A pesar de llamar a moment.locale(localeCandidate) dentro de un bloque try-catch, la aplicación sigue fallando en esta línea⁇

¡Este fue un bloqueo en el lanzamiento, pero solo para las versiones de lanzamiento! Esto hizo que fuera muy complicado extraer mensajes de error/registro útiles.

Vimos los siguientes mensajes de error a través de nuestra integración de Bugsnag y registro de la consola del sistema

  • iOS: Exception in HostFunction: Error loading module0from RAM Bundle: unspecified iostream_category error
  • Androide: Exception in HostFunction: Module not found: 0
  • Un día después, en iOS y Android también vimos Requiring unknown module "./locale/en-us". , pero curiosamente, este error no se procesó de manera oportuna. Posiblemente un problema de react-native/bugsnag.
  1. El rastreo eventual encontró un único punto que causó el bloqueo:
    https://github.com/moment/moment/blob/96d0d6791ab495859d09a868803d31a55c917de1/moment.js#L1852 -L1853
    Que creo que viene de aquí: https://github.com/moment/moment/blob/6a06e7a0db2c83fb92aa72bbf6bde955d4c75a16/src/lib/locale/locales.js#L55 -L56

Solución alternativa: ¡ Comentar esas dos líneas detuvo el bloqueo!

Comportamientos esperados

  • El momento no debería comportarse de manera abrupta, nunca, ¡pero especialmente no en versiones de lanzamiento solamente!
  • Las excepciones de los momentos deberían poder capturarse (nuestras declaraciones de captura habrían evitado un bloqueo). -- (podría ser un problema nativo de reacción al jugar con require() en Release)

Teléfono inteligente (por favor complete la siguiente información):

  • Dispositivo: iPhone X, iPhone 11 Pro, Samsung (? -- no capturó exactamente qué), Google Pixel 3, (react-native 0.59.10)
  • Sistema operativo: iOS y Android
  • iOS JavaScript Core (para las versiones de iOS mencionadas a continuación) y jsc-android(+intl) 245459.0.0
  • Versiones: iOS 12.4 e iOS 13.1, iOS 13.1.2 e iOS 13.2 (¿beta?), Android 28, otros.

Entorno específico del momento

  • hora del Pacífico y posiblemente hora de Londres
  • Error de código exhibido constantemente durante las últimas 2 semanas (2019-10-09) en todo momento del día.
  • Otras bibliotecas en uso: moment-timezone y moment-with-locales, TypeScript, react-native 0.59.10, Apollo-Client y otras

Ejecute el siguiente código en su entorno e incluya el resultado:

        console.log([
            new Date().toString(),
            new Date().toLocaleString(),
            new Date().getTimezoneOffset(),
            navigator && navigator.userAgent, // react-native might not have a navigator
            moment.version,
        ]);

Producción:

[
  "Wed Oct 09 2019 18:52:16 GMT-0700 (PDT)",
  "09/10/2019 à 18:52:16", // This particular device is configured as fr-FR
  420,
  null,
  "2.24.0"
]

Contexto adicional

Entradas relacionadas

  • #5214 - Diferente porque hay menos contexto aquí, y usa una API diferente y obtiene un mensaje de error diferente. Similar porque solo ocurre en las versiones de lanzamiento, lo que sugiere que también es nativo, pero solo lo reprodujeron en ios-simulator/ios.
  • #3872
  • #2979

Comentario más útil

Las líneas a las que se hace referencia intentan "automáticamente" requerir módulos en tiempo de ejecución, pero los documentos de carga de locales indican que si está utilizando un Administrador de paquetes como JSPM, puede cargar locales por import "moment/locale/fr . Dado que necesitamos el administrador de paquetes nativo de reacción para "saber" que el archivo debe importarse, probamos ese estilo de importación para que Packager pueda "ver" todos los archivos que deben incluirse.

En última instancia, nuestras líneas de importación se veían así:

import moment from "moment";
import "moment/min/locales"; // Import all moment-locales -- it's just 400kb
import "moment-timezone";

La implementación exacta de require() es inyectada por el tiempo de ejecución con el que está trabajando, y eso es definitivamente algo que se comporta de manera significativamente diferente entre las compilaciones de depuración y lanzamiento.

En react-native, también hay varios tipos diferentes de paquetes de JavaScript en modo de lanzamiento, que incluyen todo en un archivo, todo en archivos separados y paquetes de RAM. Cada uno de estos también cambia cómo funciona require. La depuración require() conecta al Metro Bundler que se ejecuta en un servidor http local. Es probable que esto sea muy similar a webpack/jspm/otros servidores de depuración, por lo que probablemente la necesidad de alias no causa problemas en ese entorno.

Todos 4 comentarios

Las líneas a las que se hace referencia intentan "automáticamente" requerir módulos en tiempo de ejecución, pero los documentos de carga de locales indican que si está utilizando un Administrador de paquetes como JSPM, puede cargar locales por import "moment/locale/fr . Dado que necesitamos el administrador de paquetes nativo de reacción para "saber" que el archivo debe importarse, probamos ese estilo de importación para que Packager pueda "ver" todos los archivos que deben incluirse.

En última instancia, nuestras líneas de importación se veían así:

import moment from "moment";
import "moment/min/locales"; // Import all moment-locales -- it's just 400kb
import "moment-timezone";

La implementación exacta de require() es inyectada por el tiempo de ejecución con el que está trabajando, y eso es definitivamente algo que se comporta de manera significativamente diferente entre las compilaciones de depuración y lanzamiento.

En react-native, también hay varios tipos diferentes de paquetes de JavaScript en modo de lanzamiento, que incluyen todo en un archivo, todo en archivos separados y paquetes de RAM. Cada uno de estos también cambia cómo funciona require. La depuración require() conecta al Metro Bundler que se ejecuta en un servidor http local. Es probable que esto sea muy similar a webpack/jspm/otros servidores de depuración, por lo que probablemente la necesidad de alias no causa problemas en ese entorno.

Mis propuestas de reparación:

A. ¿Eliminar aliasedRequire completo si ya no es así como se supone que debe hacer las cosas + ajustar las instrucciones de instalación al respecto?
B. Detectar react-native vs browser ( navigator no está disponible en react-native, pero hay otras técnicas aquí) y comportarse de manera diferente según la situación en la que nos encontremos. p.ej. si es nativo de reacción && DEV, imprima un error de consola si la configuración regional es teóricamente compatible, pero aún no ha sido required (+ documentos de actualización).
C. Mueva el aliasedRequire de una variable local en esa función a un "semi-global". moment.aliasedRequire , de esa manera podríamos inyectar una función de no operación/no hacer nada para que aliasedRequire ya no pueda causar que react-native se bloquee con fuerza.

Estaría feliz de implementar cualquiera de estas opciones si un mantenedor puede indicarme cuál les gustaría que implementara, y para las Propuestas B/C, ¡ayúdenme a refinar qué implementación exacta estarían inclinados a aceptar!

@marwahaha : no estoy seguro de cuál es el proceso para Moment. ¿Tendrías una opinión sobre mis propuestas de corrección? Estaría feliz de implementar un PR una vez que obtenga algunos consejos sobre qué ruta podría ser aceptable para los contribuyentes/mantenedores.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

Shoroh picture Shoroh  ·  3Comentarios

BCup picture BCup  ·  3Comentarios

vbullinger picture vbullinger  ·  3Comentarios

slavafomin picture slavafomin  ·  3Comentarios

benhathaway picture benhathaway  ·  3Comentarios