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

esbuild serializer causes Gradle to crash when the bundle generates a warning #2416

Closed
2 of 5 tasks
renchap opened this issue May 11, 2023 · 8 comments
Closed
2 of 5 tasks
Labels
bug Something isn't working feature: cli This is related to CLI upstream This should be fixed/reported upstream

Comments

@renchap
Copy link
Contributor

renchap commented May 11, 2023

What happened?

I configured Metro to use the ESBuild serializer, with the default options.

Using the default react-native-gradle-plugin process, Gradle runs the bundle command when building a release, changed so it runs rnx-bundle:

# app/build.gradle

react {
  bundleCommand = "rnx-bundle"
}

But this happens:

…/android/app/build/ASSETS/createBundleReleaseJsAndAssets/index.android.bundle:1:51547: warning: the variable "Promise" was not declared in function "bE1"
[22:52:00]: ▸  use strict";(()=>{var lse=Object.create;var cI=Object.defineProperty;var  [the whole 5 MB bundle]
[This repeated 10 times, including the whole bundle, as there are 10 warnings]
…
Exception in thread "pool-1-thread-1" java.lang.OutOfMemoryError: Java heap space

And then the build fails, because the Java process too all available memory trying to output those 5 MB lines.

If I disable whitespace minification in esbuild, then I each error displays the line where it happens, which are short (as there are newline characters in the emitted bundle), and the build goes fine.

Here is the output with whitespace minification disabled:

> Task :app:createBundleReleaseJsAndAssets
warning: the transform cache was reset.
                Welcome to Metro v0.73.9
              Fast - Scalable - Integrated

info esbuild bundle size: 6619674
info Writing bundle output to:, 
…/android/app/build/ASSETS/createBundleReleaseJsAndAssets/index.android.bundle
info Writing sourcemap output to:, …/android/app/build/intermediates/sourcemaps/react/release/index.android.bundle.packager.map
info Done writing bundle output
info Done writing sourcemap output
info Copying 17 asset files
info Done copying assets
…/android/app/build/ASSETS/createBundleReleaseJsAndAssets/index.android.bundle:2495:20: warning: the variable "Promise" was not declared in function "bE1"
    (yE1 || (yE1 = Promise.resolve())).then(e).catch(function(t) {
                   ^~~~~~~
…/android/app/build/ASSETS/createBundleReleaseJsAndAssets/index.android.bundle:2496:14: warning: the variable "setTimeout" was not declared in anonymous function " 3#"
      return setTimeout(function() {
             ^~~~~~~~~~
…/android/app/build/ASSETS/createBundleReleaseJsAndAssets/index.android.bundle:3916:17: warning: the variable "FileReader" was not declared in function "Bue"
    var t = new FileReader(), r = kT1(t);

When I launch the bundle directly from the CLI (react-native rnx-bundle --dev false --platform android), those errors are not displayed, so I am not sure what Gradle does that displays them. Could it be a post-process task, done with Metro once the bundle is created?

Affected Package

metro-serializer-esbuild

Version

0.1.23

Which platforms are you seeing this issue on?

  • Android
  • iOS
  • macOS
  • Windows

System Information

info Fetching system and libraries information...
System:
    OS: macOS 13.3.1
    CPU: (10) arm64 Apple M1 Max
    Memory: 67.69 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 16.15.1 - ~/.nvm/versions/node/v16.15.1/bin/node
    Yarn: Not Found
    npm: 9.5.0 - ~/.nvm/versions/node/v16.15.1/bin/npm
    Watchman: 2023.05.01.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: 1.12.1 - /var/folders/cn/qgvjcjx97mj8rc2vx37vgkvh0000gn/T/frum_69495_1683799833063/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4
    Android SDK:
      API Levels: 26, 31, 32, 33
      Build Tools: 30.0.2, 30.0.3, 31.0.0, 32.0.0, 32.1.0, 33.0.0, 33.0.2
      System Images: android-24 | Google APIs ARM 64 v8a, android-25 | Google APIs ARM 64 v8a, android-26 | ARM 64 v8a, android-26 | Google APIs ARM 64 v8a, android-28 | Google ARM64-V8a Play ARM 64 v8a, android-32 | Google APIs ARM 64 v8a
      Android NDK: Not Found
  IDEs:
    Android Studio: 2022.1 AI-221.6008.13.2211.9477386
    Xcode: 14.3/14E222b - /usr/bin/xcodebuild
  Languages:
    Java: 20.0.1 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: ^18.2.0 => 18.2.0
    react-native: ^0.71.8 => 0.71.8
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Steps to Reproduce

  • Configure a project to use metro-serializer-esbuild (changing the Metro config is enough, no need for @rnx-kit/cli here)
  • Have warnings in your bundle
  • Run ./gradlew bundleRelease to generate release
  • Check the build output, there is a huge line with the whole bundle when showing the warning location. Gradle may even crash if your bundle is big enough.

Code of Conduct

  • I agree to follow this project's Code of Conduct
@renchap renchap added the bug Something isn't working label May 11, 2023
@renchap
Copy link
Contributor Author

renchap commented May 12, 2023

After investigating a bit more, those errors are printed by hermesc when compiling the bundle during the build phase.

And the same issue also occurs during the iOS build (multi-MB lines printed) but XCode copes well with them and do not crash.

@tido64
Copy link
Member

tido64 commented May 15, 2023

Do you find the warnings useful? If not, you can suppress them by adding -w to hermesFlag in android/app/build.gradle:

react {
    hermesFlags = ["-O", "-output-source-map", "-w"]
}

Alternatively, try limiting the line width output:

react {
    hermesFlags = ["-O", "-max-diagnostic-width=80", "-output-source-map"]
}

@renchap
Copy link
Contributor Author

renchap commented May 15, 2023

I do not understand where they come from, and did not spend the time trying to figure out why they are here.

warning: the variable "Promise" was not declared in function "bE1"
the variable "setTimeout" was not declared in anonymous function

Why would those not exist? 🤔

@tido64
Copy link
Member

tido64 commented May 15, 2023

Most likely because those functions are polyfilled and Hermes can't see them unless you specify where to look: facebook/hermes#342

You can simply disable the warning if the bundle works without issues.

@tido64
Copy link
Member

tido64 commented May 15, 2023

One other thing: If you're passing this bundle to the Hermes compiler, you can also consider disabling minification altogether. It shouldn't affect the size of the bytecode output.

@tido64
Copy link
Member

tido64 commented May 22, 2023

I've just learned that you can control the output width using -max-diagnostic-width. I've updated the workaround here: #2416 (comment)

@tido64
Copy link
Member

tido64 commented May 23, 2023

Just to summarize for all the folks seeing crashes/slowdowns when switching to rnx-bundle in Gradle/Xcode: The gist of the problem is that Hermes is unable to determine the terminal width when run inside Gradle/Xcode and falls back to "unlimited". This causes issues because it essentially means that for every diagnostic/warning, Hermes will output the whole bundle.

I've submitted a fix upstream to limit the output: facebook/react-native#37531

I will close this issue when it lands. In the meantime, you can apply one of the following workarounds:

If you only use Hermes on Android, you can add -max-diagnostic-width=80 in android/app/build.gradle to limit the line width:

react {
    hermesFlags = ["-O", "-max-diagnostic-width=80", "-output-source-map"]
}

Alternatively, if you don't care about the warnings, just disable them:

react {
    hermesFlags = ["-O", "-output-source-map", "-w"]
}

iOS doesn't have an equivalent as far as I can tell. If you also use Hermes on iOS, you can instead disable minifying whitespaces in the esbuild plugin.

If you use the esbuild plugin directly in metro.config.js, add the following:

const { makeMetroConfig } = require("@rnx-kit/metro-config");
const { MetroSerializer, esbuildTransformerConfig } = require("@rnx-kit/metro-serializer-esbuild");

module.exports = makeMetroConfig({
  serializer: {
    customSerializer: MetroSerializer([], {
      minify: true,
      minifyWhitespace: false,  // disable removing whitespace
      minifyIdentifiers: true,
      minifySyntax: true,
    }),
  },
  transformer: esbuildTransformerConfig,
});

If you use rnx-bundle, you can pass a configuration to treeShake:

{
  ...
  "rnx-kit": {
    ...
    "bundle": [
      {
        ...
        "treeShake": {
          "minify": true,
          "minifyWhitespace": false,
          "minifyIdentifiers": true,
          "minifySyntax": true
        }
      }
    ]
  }
}

Either way you configure the plugin, you don't need to change your build.gradle.

Documentation: https://github.com/microsoft/rnx-kit/tree/main/packages/metro-serializer-esbuild#minifywhitespace

@tido64 tido64 added upstream This should be fixed/reported upstream and removed needs author feedback labels May 23, 2023
facebook-github-bot pushed a commit to facebook/react-native that referenced this issue May 23, 2023
Summary:
Limit diagnostics width output by `hermesc` as they may cause slowdowns or even crashes in Gradle/Xcode when a minified bundle is used as input. This occurs because Hermes is unable to determine the terminal width when executed by Gradle/Xcode, and falls back to "unlimited". If the input is a minified bundle, Hermes will output the whole bundle for each warning.

See issues filed:
- microsoft/rnx-kit#2416
- microsoft/rnx-kit#2419
- microsoft/rnx-kit#2424

## Changelog:

[GENERAL] [FIXED] - Limit diagnostics width output by `hermesc`

Pull Request resolved: #37531

Test Plan: See listed issues for repros.

Reviewed By: cipolleschi

Differential Revision: D46102686

Pulled By: cortinico

fbshipit-source-id: 1b821cad7ef0d561a5e1c13a7aedf9b10164620a
@tido64
Copy link
Member

tido64 commented May 23, 2023

Merged in facebook/react-native@260bcf7

@tido64 tido64 closed this as completed May 23, 2023
kelset pushed a commit to facebook/react-native that referenced this issue May 25, 2023
Summary:
Limit diagnostics width output by `hermesc` as they may cause slowdowns or even crashes in Gradle/Xcode when a minified bundle is used as input. This occurs because Hermes is unable to determine the terminal width when executed by Gradle/Xcode, and falls back to "unlimited". If the input is a minified bundle, Hermes will output the whole bundle for each warning.

See issues filed:
- microsoft/rnx-kit#2416
- microsoft/rnx-kit#2419
- microsoft/rnx-kit#2424

## Changelog:

[GENERAL] [FIXED] - Limit diagnostics width output by `hermesc`

Pull Request resolved: #37531

Test Plan: See listed issues for repros.

Reviewed By: cipolleschi

Differential Revision: D46102686

Pulled By: cortinico

fbshipit-source-id: 1b821cad7ef0d561a5e1c13a7aedf9b10164620a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working feature: cli This is related to CLI upstream This should be fixed/reported upstream
Projects
None yet
Development

No branches or pull requests

2 participants