metro-bundler: Inject snippet in every function of the application

23 Views Asked by At

What do I want:

I have a react-native typescript application, and during the build process, with the help of metro-bundler I wanted to inject a code snippet like trackCall({functionName: Foo}) into every function's body, that is residing inside the application's /src directory.

So Ideally, all components, utils, helpers, and every function should have the above-mentioned snippet injected! (Atleast this would be a good start for my more specific objectives)

What have I tried:

As per my research, I found that its transformer specifically through babelTransformerPath I can traverse through each function and do necessary modifications.

The metro.config.js looks as follows

module.exports = (async () => {

 // This is my experiment config 
  const customConfig = {
    transformer: {
      babelTransformerPath: require.resolve('./debug/custom-plugin.js'),
      getTransformOptions: async () => ({
        transform: {
          experimentalImportSupport: false,
          inlineRequires: false,
        },
      }),
    },
  };

  return mergeConfig(
    { // This is the original/default config of my application
      resolver: { extraNodeModules },
      transformer: {
        getTransformOptions: async () => ({
          transform: {
            experimentalImportSupport: false,
            inlineRequires: true,
          },
        }),
      },
    },
    customConfig
  );
})();

And, the target custom plugin that I have created./debug/custom-plugin.js looks as follows. After many trials and error I was able to successfully create "get some result" without error. I might be using the wrong plugins here, but I don't know any better atm. (Open for improvements)

NOTE: In the plugin, I have tried to inject a very basic console.log to start with.

const obfuscatingTransformer = require('react-native-obfuscating-transformer-fix');
const typescriptTransformer = require('react-native-typescript-transformer');
const ts = require('typescript');

// I probably dont need this "obfuscatingTransformer" at all. 
// the "typescriptTransformer" should be enough
const filter = filename => filename.startsWith('node_modules');
const obfuscating = obfuscatingTransformer({
  upstreamTransformer: typescriptTransformer,
  emitObfuscatedFiles: false,
  enableInDevelopment: true,
  filter: filter,
  trace: true,
});

module.exports.transform = function (file) {
  if (file.filename.startsWith('src/')) {
    console.log('[BUILD-file-detection-log: ] ', file.filename);
    const updatedFile = visitNode(file);
    return typescriptTransformer.transform(updatedFile);
  }
  return obfuscating.transform(file);
};

function visitNode(node) {
  if (
    ts.isFunctionDeclaration(node) ||
    ts.isArrowFunction(node) ||
    ts.isFunctionExpression(node)
  ) {
    // Get the name of the function
    let functionName = '';
    if (node.name && ts.isIdentifier(node.name)) {
      functionName = node.name.text;
    }
    // Create a console.log statement
    const logStatement = ts.createExpressionStatement(
      ts.createCall(
        ts.createPropertyAccess(
          ts.createIdentifier('console'),
          ts.createIdentifier('log')
        ),
        undefined,
        [
          ts.createStringLiteral(
            `[Test-log: RESULT] >>  ${functionName}`
          ),
        ]
      )
    );
    // Create a new body that includes the console.log statement followed by the existing body
    const newBody = ts.createBlock(
      [logStatement, ...node.body.statements],
      true
    );
    // Update the function node with the new body
    return ts.updateFunctionDeclaration(
      node,
      node.decorators,
      node.modifiers,
      node.asteriskToken,
      node.name,
      node.typeParameters,
      node.parameters,
      node.type,
      newBody
    );
  }
  return ts.visitEachChild(node, visitNode, null);
}

Result:

During this whole experiment I was expecting to update the transformed *.js files and somehow understand how and where babel is contributing to the building process, but unfortunately, I failed find a clue. In the end, I had to work with only typescript files. Final result:

  1. During the build process I can see the [BUILD-file-detection-log: ] <filename> logs getting executed.
  2. When I run the app, [Test-log: RESULT] >> ${functionName} never appeared!

So, traversing through the files in src/ happened, but the injections didn't succeed!

Need of help

Tbh, I dont care if it's a js or a typescript transformer. In the end, I want to inject code snippets (let's say console.count) into every function inside my app's /src directory.

0

There are 0 best solutions below