Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React-Native Release Builds, Deep Crash when changing locales #5252

Closed
fbartho opened this issue Oct 10, 2019 · 8 comments
Closed

React-Native Release Builds, Deep Crash when changing locales #5252

fbartho opened this issue Oct 10, 2019 · 8 comments

Comments

@fbartho
Copy link

fbartho commented Oct 10, 2019

When using react-native 0.59.10, and setting the locale for momentJS, we caught a super brutal crash for our release-builds only. This crash wasn't reproducible for us with the debugger attached. This crash occurred for us both on iOS & Android. try-catch statements wrapping all moment usage did not catch the crash!

To Reproduce

  1. We collect the locales/languages we want to try via Application-specific logic. Eg ["fr-CA", "en-US", "fr", "en"]
  2. We loop through these, one at a time rather than using the Array setter, so that we can call some other instrumentation, and potentially catch any JS Exceptions that are thrown and try the next candidate.
  3. Despite calling moment.locale(localeCandidate) inside a try-catch block, the Application still crashes on this line⁇

This was a crash-on-launch but only for Release builds!! This made it super tricky to extract useful error messages / logging.

We saw the following error messages via our Bugsnag integration & System Console Logging

  • iOS: Exception in HostFunction: Error loading module0from RAM Bundle: unspecified iostream_category error
  • Android: Exception in HostFunction: Module not found: 0
  • A day later, on iOS & Android we also saw Requiring unknown module "./locale/en-us". -- but strangely, this error was not being processed in a timely manner. Possibly a react-native / bugsnag issue.
  1. Eventual tracing found a single point that caused the crash:

    moment/moment.js

    Lines 1852 to 1853 in 96d0d67

    var aliasedRequire = require;
    aliasedRequire('./locale/' + name);

    Which I believe comes from here:
    var aliasedRequire = require;
    aliasedRequire('./locale/' + name);

Workaround: Commenting out those two lines halted the crash!

Expected behaviors

  • Moment shouldn't behave crashily, ever, but especially not in release-builds-only!
  • Moment's exceptions should be catchable (our catch statements would have prevented a crash). -- (could be a react-native issue when messing with require() in Release)

Smartphone (please complete the following information):

  • Device: iPhone X, iPhone 11 Pro, Samsung (? -- didn't capture exactly what), Google Pixel 3, (react-native 0.59.10)
  • OS: iOS and Android
  • iOS JavaScript Core (for the below mentioned iOS versions), and jsc-android(+intl) 245459.0.0
  • Versions: iOS 12.4 & iOS 13.1, iOS 13.1.2, and iOS 13.2 (beta?), Android 28, others.

Moment-specific environment

  • Pacific Time, and Possibly London time
  • Code bug exhibited consistently for the past 2 weeks (2019-10-09) at all times of day.
  • Other libraries in use: moment-timezone and moment-with-locales, TypeScript, react-native 0.59.10, Apollo-Client & others

Please run the following code in your environment and include the output:

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

Output:

[
  "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"
]

Additional context

Related Tickets

@fbartho
Copy link
Author

fbartho commented Oct 10, 2019

The referenced lines are "automatically" trying to require modules at runtime, but the loading locales docs indicate that if you're using a Package Manager like JSPM, you can load locales by import "moment/locale/fr. Since we need the react-native package manager to "know" that the file must be imported, we tried that style of import so that the Packager can "see" all files that have to be bundled in.

Ultimately, our import lines looked like this:

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

The exact implementation of require() is injected by the runtime you're working with, and that's definitely something that behaves significantly differently between Debug & Release builds.

In react-native, there are also several different flavors of Release-mode JavaScript Bundling, including all-in-one-file, all-in-separate-files, and RAM Bundles. Each of these also change how require works. Debug require() connects to the Metro Bundler running on a local http server. This is probably is very similar to the webpack/jspm/other debug servers, which is probably why aliasing require doesn't cause problems in that environment.

@fbartho
Copy link
Author

fbartho commented Oct 10, 2019

My Fix Proposals:

A. Delete the aliasedRequire entirely if that's not how you're supposed to do things any more + tweak install instructions about it?
B. Detect react-native vs browser (navigator isn't available in react-native, but there are other techniques here), and behave differently depending on which situation we find ourselves in? eg. if react-native && DEV then print a console.error if the locale is theoretically supported, but hasn't been required yet (+ update docs).
C. Move the aliasedRequire from a local variable in that function to a "semi-global". moment.aliasedRequire, that way we could inject a no-op/do-nothing function so that aliasedRequire can't cause react-native to crash hard anymore.

@fbartho
Copy link
Author

fbartho commented Oct 10, 2019

I'd be happy to implement any of these options if a maintainer can point me at which one they would like me to implement, and for Proposals B/C help me refine which exact implementation they would be inclined to accept!

@fbartho
Copy link
Author

fbartho commented Oct 11, 2019

@marwahaha -- not sure what the process is for Moment. Would you have an opinion on my fix proposals? I'd be happy to implement a PR once I get some advice on which route might be acceptable to contributors/maintainers?

@joe06102
Copy link

joe06102 commented Dec 22, 2020

if anyone meets such errors as "can not find module './locale/xxx' in react-native production build"

try to import the locale files manually.

eg:

import * as zh from 'moment/locale/zh-cn';

As far as I know, dynamic require is not supported in the Metro packager, which causes such errors.

@aazwar
Copy link

aazwar commented Jan 20, 2021

I wanted to drop moment because of this issue, but I need moment-hijri so I bear with it. My solution is to copy the component from node_modules to app source folder, and comment those damn two lines (aliasedRequire) in copied file. Also copy locales.js from locale directory.

`
// app/components/moment/index.js

import moment from './moment';
import hijri from './moment-hijri';
import './locales';

export { moment, hijri }
`

The import statement change to:
import moment from './app/components/moment

Do the same for all components that depend on moment.

@orlandodemauro
Copy link

@fbartho thank you so much for your resume and solution. @ichernev I noticed that your fix for it was reverted by 2e26863#diff-0d89587f1f4d4ddf3b0f2727eaed1156f378a435748c74d5440f9a79bc3f2c62 do you have a plan to fix this major issue? at least for people that use moment in react native it is. thank a lot guys

@demedos
Copy link

demedos commented Feb 27, 2023

Still happens, @ichernev do you have any plan to include this fix in a future release?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants