Search code examples
javascriptreact-nativemetro-bundler

Configure multiple transformers/resolvers using metro


I'm trying to add multiple resolvers and transformers using metro for my react native project, how do I combine them in my metro.config.js file?

Background: I want to get both a sass transformer to work as well as a svg transformer.

I've tried the configurations separately and that seems to work, but I'm confused to how I combine them so they both work at the same time. I'm assuming they need to be in the same module.exports, because when they both are in the same file I get errors for my svg's

These are the configs I'm trying to combine:

module.exports = (async () => {
  const {
    resolver: { sourceExts, assetExts }
  } = await getDefaultConfig();
  return {
    transformer: {
      babelTransformerPath: require.resolve("react-native-svg-transformer")
    },
    resolver: {
      assetExts: assetExts.filter(ext => ext !== "svg"),
      sourceExts: [...sourceExts, "svg"]
    }
  };
})();

module.exports = (async () => {
  const {
    resolver: { sourceExts }
  } = await getDefaultConfig();
  return {
    transformer: {
      babelTransformerPath: require.resolve("react-native-sass-transformer")
    },
    resolver: {
      sourceExts: [...sourceExts, "scss", "sass"]
    }
  };
})();

When I try to run it with the code as above, that is two module exports in the metro.config.js, it seems that only the sass transformer works, when I try to draw an svg I get the following error:

Invariant violation: Element type is invalid: Expected a string (for built-in components) or a class/function (for composite components) but got number.

Solution

  • I solved it by creating a custom transformer as follows:

    customTransformer.js :

    // default transformer: https://metrobundler.dev/docs/configuration/#babeltransformerpath
    var upstreamTransformer = require("metro-babel-transformer");
    var sassTransformer = require("react-native-sass-transformer");
    var svgTransformer = require("react-native-svg-transformer");
    
    module.exports.transform = function({ src, filename, options }) {
      if (filename.endsWith(".scss") || filename.endsWith(".sass")) {
        return sassTransformer.transform({ src, filename, options });
      } else if (filename.endsWith(".svg")) {
        return svgTransformer.transform({ src, filename, options });
      }  else {
        return upstreamTransformer.transform({ src, filename, options });
      }
    };
    

    And in my metro.config.js:

    const { getDefaultConfig } = require("metro-config");
    
    module.exports = (async () => {
      const {
        resolver: { sourceExts, assetExts }
      } = await getDefaultConfig();
      return {
        transformer: {
          babelTransformerPath: require.resolve("./customTransformer.js")
        },
        resolver: {
          assetExts: assetExts.filter(ext => ext !== "svg" && ext!=="scss"),
          sourceExts: [...sourceExts, "svg", "scss", "sass"]
        }
      };
    })();
    

    No idea if this is the best way, but it seems to work