Webpack and Wet Dependencies

    There are two factions in the JavaScript world. The first of them is techies who try to solve all problems “technically”. In general, techies are harsh guys, I would even say strict, and therefore love the same harsh and strict typing, and everywhere they type TypeScript, Dependency Injection and other IoC.

    The second is magicians. Someone considers them charlatans, and no one really understands how their code works. But it works. They have a taboo on strict typing, but they have a simple excuse about (from) DI:

    “Why should I mutilate my code by mixing it with a hedgehog if it is necessary exclusively for tests?”

    And in fact, to add DI to the project solely to get the dependencies in the tests wet is not the smartest idea. Especially if DI is actually a rare beast outside the Angular ecosystem.

    There is only one thing - if techies do not suffer from their professional deformation, then magicians ... well, how to say ...

    In general, a couple of months ago, a kind person created a proxyquire-webpack-alias issue for me . The bottom line was simple - "does not work." It took me a day to change WHAT doesn't work, WHERE.



    PS: Why do you need to replace (get wet) dependencies in tests? That tests were more "unit", more isolated, and did not pull real teams (endpoints), which can be very slow and very one-time. In general, do not touch them in the tests.

    The essence of the problem is very simple - VERY many libraries have been invented to get dependencies in nodejs: proxyquire, rewire, mockery and so on. They all parasitize on the internal representation of nodejs about modules and their tentacles make their way somewhere into the require stuffing.

    If your tests run not in nodejs, but in the browser, then everything changes. Trite - there is no nodejs environment, only the surrogate that provided the used bundler. But, since there are generally unlimited number of Bandera - we will consider only one - webpack.

    Moreover, some bundlers, such as browseryfy or (especially) rollup "modular" systems do not have at all. And it is not necessary.

    Webpack


    Historically, there is only one approach to getting a bunch of dependencies in webpack - use inject-loader or rewire , which is also “loader”.

    Loaders simply "change" the requested file at the source level. In principle, there are no special claims to such bootloaders - you just say

    const stuff = require('inject-loader!stuff')({
      'fs': mockFS,
      'someOtherDep': mock
    });
    

    And dependencies will be locked up. Well, it's just a bit of a stone age, and it's not always easy to use the whole thing. The "big brothers" from nodejs (especially mockery) can do much more.

    Rewire is harder. I would personally break the fingers of those who use it - “getting wet” using rewire is the same as “getting wet” using sinon.

    On the other hand, rewire-webpack is the only (!) Correct source-level plugin (not a loader) for webpack. Just because the author of rewire flipped this plugin to write, and it was written by the webpack author. Although this plugin in the end just adds a loader. I'm confused too.
    A big plus of rewire, in spite of its cracking ability, is the same interface for webpack and node environments. He was one so good.

    Rewiremock


    A few months ago I wrote a little more “correct” than the rest, a tool for getting addicted - Rewiremock ( github , an article on the hub ). And it was directly “sports fun” for me to get rewiremock not only for nodejs (which isn’t a problem at all), but also for webpack.

    And so that the API does not change, and all the tests work. Now one test does not work, because it should not. And all the rest are green.

    1. How does it work?


    All the work came down to just three pounds:

    1. Add “at least some” brains to the webpack modular system. Namely, you need to add two plug-ins, and both with some probability already exist - NamedModulesPlugin (which will return the names of the files) and HotModuleReplacementPlugin (which will provide some kind of modular system substitute), plus connect the plug-in from rewiremock (which will replace require with your version).
    2. Add the missing tuning - clearing the cache, working with a slightly different “module”.
    3. Actually draw a plugin that somehow implements the require overload capability.

    The problem arose only with the third point - I could not find any hell, docks or an example of how to do what I wanted. The webpack documentation often sends smoke sorts, which was also done, but it did not clarify.

    Further familiarization with the source code of rewire-webpack, which, as I said, is the only correct one, the solution prompted. More precisely, it became clear that everything is just very bad.

    Webpack is mostly based on Tappable , a small library that calls hooks in a certain sequence. It’s kind of like a lifecycle ... but what and when it calls, what arguments to pass, and (most important!) What can be done with them - information is zero. In general, webpacks were written by magicians, not techies.

    Now I’m thinking everything - I left my plugin implementation as is, or replaced with a more “correct” one from rewire. It’s just 5 times longer and I don’t see any sense in this yet.

    2. What is the result?


    In the end - it just works. Somewhere inside, a bit too much magic is wired to normalize file names so that everything works transparently in both ecosystems, but a rather simple and convenient API grinds out. More precisely, a whole bouquet that has not changed.

    One of the “problems” of rewiremock is the versatility of the API.

    It can work like mockery (the basic syntax is like mockery, including isolation mode):

    rewiremock('fs')
        .with({
            readFile: yourFunction
        });
    rewiremock.enable();
    

    Maybe like proxyquire (proxy and module healers):

    rewiremock.proxy('somemodule', {
       'dep1': { name: 'override' },
       'dep2': { name: 'override' }
     }));
    

    Able to use different Jest parts (for example, dynamic moka creation):

    rewiremock('fs')
        .by(({requireActual}) => requireActual('fs'));
    

    And there are some of their tricks (extended proxy syntax):

    const mock = await rewiremock.module(() => import('somemodule'), r => ({
       'dep1': r.with({ name: 'override' }).calledFromMock(),
    }));

    In general, as I wrote above, rewiremock is a little better tool than everyone else. And the first one, from the “normal” ones, which is equally able to work both under nodejs and webpack.



    Though. Who needs it under webpack then? Honestly - raise your hands, otherwise I have acquaintances who are sitting on Karma / Headless / Webpack / Angular, and who can check it all in business - somehow it did not start.

    PS: It is not especially demanded under the node either. The good old proxyquire, despite all its limitations, copes with 99% of the tasks. Only I know about the significant difference ...

    Also popular now: