📜 ⬆️ ⬇️

Webpack and addictions

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

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

"Why should I mutilate my code by mixing already with a hedgehog, if it is needed solely for tests?"
')
And after all, in fact, adding to the DI project exclusively to make dependencies on tests - the idea is not the smartest. 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 me in the proxyquire-webpack-alias issue. The bottom line was simple - “not working.” It took me a day to change WHAT doesn't work, on WHERE.



PS: Why do I need to replace (mok) dependencies in tests? For tests to be more “unit”, more isolated, and not tugging on real teams (endpoints), which can be very slow and very disposable. In general, do not touch them in the tests.

The essence of the problem is very simple - for the dependency dependencies in nodejs, there are VERY many libraries: proxyquire, rewire, mockery, and so on. All of them are parasitic on the nodejs internal representation of modules and their tentacles sneak somewhere into the require thread.

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

Moreover, some bandlers, such as browseryfy or (especially) rollup, do not have a “modular” system at all. Do not need.

Webpack


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

Loaders simply "change" the requested file at the source level. By the principle of special pritenzy to such loaders do not have - you just say

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

And dependencies will zamany. Well, it's just a little stone age, and it's not always easy to use the whole thing. The elder brothers from nodejs (especially mockery) can do a lot more.

Co rewire more difficult. I would personally break the fingers of those who use it - “mopping” using rewire is the same as “mopping” using sinon.

On the other hand, rewire-webpack is the only (!) Correct source-level plugin (not a loader) for a webpack. Just because the author of rewire has written this plugin, and the author of the webpack wrote it. Although this plugin just adds a loader in the end. I'm also confused.
A big plus rewire, despite its krakozablost, - the same interface for webpack and node environment. He was the only one so good.

Rewiremock


A few months ago, I wrote a little more “correct” than the rest, a tool for addict mopping - Rewiremock ( github , article in Habré ). And it was just “sporting interesting” for me to start rewiremock not only for nodejs (with which there are no problems at all), but also for webpack.

And so that the API has not changed, and all the tests worked. Now one test does not work, because it should not. And all the others are green.

1. How does it work?


All the work has literally come down to three punt:

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

The problem arose only with the third point - no hepla, docks or an example of how to do what I wanted, I could not find. Documentation webpack often sends smoking weights, which was also done, but did not bring clarity.

Further acquaintance with the source code rewire-webpack, which, as I said, the only correct solution suggested. 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 does it cause, which arguments to pass, and (most importantly!) What can be done with them - information zero. In general, the webpack was written by magicians, not techies.

Now I think everything - leave my plug-in implementation as it is, or replace it with a more “correct” one from rewire. It is just 5 times longer and I don’t see any sense in it.

2. What is the result?


In the end, it just works. Somewhere inside there is a lot of magic on the normalization of file names, so that everything works transparently in both ecosystems, but the API is quite simple and convenient. More precisely, the whole bouquet, which has not changed.

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

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

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

Maybe as proxyquire (proxy and module checkers):

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

Aware of various things from Jest (for example, dynamic creation of mock):

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

And what are some of their own techniques (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 slightly better tool than all the others. And the first, from “normal”, which is equally able to work both under nodejs and under webpack.



Although. Who needs it under the webpack then? Honestly, raise your hands, otherwise I have acquaintances who sit on Karma / Headless / Webpack / Angular, and who can verify this all in action - somehow it did not start.

PS: He and the node is not particularly in demand. Good old proxyquire, despite all its limitations, copes with 99% of the tasks. Only I know about the significant difference ...

Source: https://habr.com/ru/post/339590/


All Articles