Metro: Dead code elimination

Created on 3 Sep 2018  路  4Comments  路  Source: facebook/metro

I'm trying to share some code between a RN project and a Node.js project. In the Node.js specific code I'm importing modules which RN lacks. I'm therefore trying to do dynamically imports, only importing/requiring the Node.js modules in a if/else branch which RN will never hit. However, Metro tries to bundle the modules, and therefore throws errors.

Is it possible for Metro to detect unreachable code and avoid bundling those branches? Kinda like NODE_ENV === "production"/"development" in Webpack and similar tools.

Most helpful comment

Thanks @Salakar for the suggestion!

If you want to actually have dead code elimination based on the platform, you can also have a babel plugin that replaces some defined global variables (similar to Webpack's DefinePlugin).

Such plugin would look like: https://gist.github.com/rafeca/e06e88feed05eaa526bd96744337c29f

Then you can configure it from you babel config like:

plugins: [[
  'babel-plugin-replace-js-globals',
  {__IS_NODEJS__: false},
]]

This way Metro will replace the following code and do dead code elimination and strip any require that is inside a dead branch (in production), e.g:

if (__IS_NODEJS__) {
 // foo
} else {
 // bar
}

Note that the dead code elimination only works for production mode, so having the hook on the resolver that @Salakar will still be helpful to make development builds work.

All 4 comments

@torkelrogstad you could configure metro with a custom metro.config.js file in your root to get rid of these.

For example, with a bit of meta programming, you can redirect specific dependencies by name to instead require an empty JS file: (untested code 馃檲 )

// <PROJECT_ROOT>/metro.config.js
const { join } = require('path');
const NODEJS_ONLY = ['expressjs', 'koa'];

// `process.env.NODE_ENV` is also available here

module.exports = {
  resolver: {
    extraNodeModules: new Proxy(
      {},
      {
        get: (target, name) => {
          if (NODEJS_ONLY.includes(name)) {
            // <PROJECT_ROOT>/noop.js
            return join(process.cwd(), `noop.js`);
          }
          // <PROJECT_ROOT>/node_modules/${name}
          return join(process.cwd(), `node_modules/${name}`);
          // return; // <-- should also work
        },
      }
    ),
  }
};
// <PROJECT_ROOT>/noop.js
module.exports = {};

You can see the rest of the config options here: https://facebook.github.io/metro/docs/en/configuration

There's also an issue/discussion here in regards to tree shaking.

Hope that helps!

That seems to solve my problem quite well. Thank you!

@torkelrogstad you're welcome!

Thanks @Salakar for the suggestion!

If you want to actually have dead code elimination based on the platform, you can also have a babel plugin that replaces some defined global variables (similar to Webpack's DefinePlugin).

Such plugin would look like: https://gist.github.com/rafeca/e06e88feed05eaa526bd96744337c29f

Then you can configure it from you babel config like:

plugins: [[
  'babel-plugin-replace-js-globals',
  {__IS_NODEJS__: false},
]]

This way Metro will replace the following code and do dead code elimination and strip any require that is inside a dead branch (in production), e.g:

if (__IS_NODEJS__) {
 // foo
} else {
 // bar
}

Note that the dead code elimination only works for production mode, so having the hook on the resolver that @Salakar will still be helpful to make development builds work.

Was this page helpful?
0 / 5 - 0 ratings