facebook/metro

Constant folding breaks some already minified code

chpill opened this issue · 2 comments

Do you want to request a feature or report a bug?

Bug

What is the current behavior?

See https://github.com/chpill/repro-bug-metro

The index.js file contains JS compiled from Clojurescript and JS optimized by google closure compiler. The minimalist app boots without issue when __DEV__ == false or when minify == true.
But when __DEV == false && minify == true, the app crashes with a JS error because of an undefined variable (ReferenceError: $G__1978__22$$ is not defined).

It turns out the issue comes from the constant folding optimization when applied on our already optimized/minified index.js

If the current behavior is a bug, please provide the steps to reproduce and a minimal repository on GitHub that we can yarn install and yarn test.

To reproduce the bug:

  • git clone https://github.com/chpill/repro-bug-metro
  • react-native start --reset-cache
  • react-native run-android then check that the app boots (you should see a green screen)
  • Shake the device and go to "Dev settings", adjust options so that __DEV == false && minify == true and reload
  • You should now see the exception ReferenceError: $G__1978__22$$ is not defined

To see that the problems comes from constant folding on our index.js:

if (!options.dev) {
  if (filename === "index.js")
    console.log("======= CUSTOM PATCH @chpill ======== IGNORING CONSTANT FOLDING FOR", filename);
  else
    plugins.push([constantFoldingPlugin, opts]);

  plugins.push([inlinePlugin, opts]);
}
  • stop the running packager if necessary, and launch react-native start --reset-cache
  • reload the app
  • Make sure you see the custom console log statement being issued while the packager is bundling the production minified artefact
  • You should now see the application booting without issue.

What is the expected behavior?

The constant folding optimization should not produce invalid JS code, or there should be a way to opt out of it for specific files.

Please provide your exact Metro configuration and mention your Metro, node, yarn/npm version and operating system.

{
  "metro": "^0.48.1",
  "metro-babel-register": "^0.48.1",
  "metro-core": "^0.48.1",
  "metro-memory-fs": "^0.48.1"
}

yarn: 1.7.0
node: v8.11.3

OS: Linux 4.18.0-1-amd64

I confirm this is a problem. I ran into it as well with minified Google Closure code.

Thanks @chpill for the patch, it worked as a temp solution.

leops commented

I can confirm this issue seems to still exist in the latest version of Metro, I did not hit this with minified code but with hand written library code so I managed to reduce it to a simple test case, and it seems to be caused by a function declaring a variable shadowing its own name:

function foo() {
    let foo;
}

// Side Effect
console.log(foo);

With dev=true the module is outputted correctly, however with dev=false only the console.log statement is emitted and the function is removed. I used console.log as a side-effect here but the bug is also reproducible when the function is exported. This was discovered on React Native 0.63.4 (Metro 0.58.0) and tested on Metro 0.65.2.