5

I'm writing a code analytics webpack plugin that wants to find all instances of a function name in a webpack bundle.

I made a repo for this question: https://github.com/RonPenton/webpack-parser-fail-demo

So the parser is really simple, just looks like this:

    class ParsePlugin {
    apply(compiler) {
        compiler.plugin('compilation', function (compilation, data) {

            data.normalModuleFactory.plugin('parser', function (parser, options) {

                parser.plugin(`call $findme`, function (expr) {
                    console.log("found $findme!");
                });
            });
        });
    }

https://github.com/RonPenton/webpack-parser-fail-demo/blob/master/parse.js

All I want to do is find all instances of $findme() in code and record information about them. At a later time, I may even end up altering the calls, but that's for another day.

When I supply this source file, all is well: https://github.com/RonPenton/webpack-parser-fail-demo/blob/master/good.js

$findme("Testing");
$findme("Testing too...");

When I run webpack, the output shows both instances were found:

found $findme!
found $findme!
Hash: a6555af5036af17d9320
Version: webpack 3.6.0
Time: 69ms
  Asset     Size  Chunks             Chunk Names
good.js  2.52 kB       0  [emitted]  main
   [0] ./good.js 47 bytes {0} [built]

But when I use a different entry point, where the function is defined either locally (https://github.com/RonPenton/webpack-parser-fail-demo/blob/master/bad.js) or in an external module (https://github.com/RonPenton/webpack-parser-fail-demo/blob/master/bad2.js), suddenly the parser stops finding these methods.

function $findme(input) {
    console.log(input);
}
$findme("Testing");
$findme("Testing too...");

====

import { $findme } from './findme';
$findme("Testing");
$findme("Testing too..."); 

So what's the deal? I tried digging into the webpack source code, and as far as I can tell, this seems intentional. But there's literally no documentation about why it's done this way, and not a comment in sight.

Is this not something that can be done with plugins?

Thanks for the help in advance.

Ron Penton
  • 1,511
  • 1
  • 12
  • 24

1 Answers1

0

The webpack parser weirdly only calls those hooks when your functions are called or retrieved a certain way. That's why your hook worked when the function was invoked one way, but didn't work when it was imported from somewhere.

I was trying to find all calls to an object method and had the same issue.

This github issue made by another person goes into more detail: https://github.com/webpack/webpack/issues/9480

To resolve this you need to trick the webpack Parser into thinking that function/method calls that it doesn't want to call hooks on for some reason are Identifiers, something like what's done here: https://github.com/aurelia/webpack-plugin/blob/9f6f7983312c66f857e79b744e4ec257cc287b8b/dist/InlineViewDependenciesPlugin.js

Here are the relevant bits from webpack's code: enter image description here enter image description here

You need to use the parser 'evaluate' hook to return a custom BasicEvaluatedExpression instance that has isIdentifier set to true for all function calls that you want to find. Then the call hook will get called with the full expression. In my case I need the full CallExpression that contains both the function and its arguments.

Fabis
  • 1,932
  • 2
  • 20
  • 37