Barrel files, webpack and jest

Viewed 1210

I have few questions about barrel files, webpack and jest. I've never really wondered how they work and now I'm struggling (with jest) to write tests on a bigger application that does not have ones yet.

I have barrel files in big folders (like components) and they look like this:

/components/index.js

export { default as ComponentA } from './ComponentA';
export { default as ComponentB } from './ComponentB';
export { default as ComponentC } from './ComponentC';

With a setup like this I can easily import those components like this:

import { ComponentA, Component C } from '/components';

instead of writting

import Component A from '/components/ComponentA';
import Component C from '/components/ComponentC';

My main question: In my webpack bundled files, will the ComponentB file be included just because I have it in the components/index.js (but I do not really use it)?

This came to my mind after I started writting tests with jest and it started to throw me errors about files I haven't writted tests yet for. I've tried to search why, and the only reason I can find is that I have imports from barrel files in bigger files (e.g. I import ComponentA from a barrel file when building a page - that I'm now trying to test).

2 Answers

Barrel imports should be supported, at least if you are using ts-jest transform. Ref: https://github.com/kulshekhar/ts-jest/issues/1149

but I have encouraged similar problem with barrel type definition src/types/index.d.ts referenced usually as @/types all around in codebase

I ended up with configuring extra moduleNameMapper definition

 moduleNameMapper: {
    // FIXES Could not locate module @/types mapped as: .../src/types.
    '^@/types$': '<rootDir>/src/types/index.d',
  },

Hope this will help someone :)

Actually yes, all files imported in a barrel file are currently included in the import on Jest. That happens mainly because Jest uses CommonJS and does not do any tree-shaking (which would actually just make everything even slower in this use case).

When working on a big project, it seems to be quite common to have Jest test suites run increasingly slow when using barrel files and that is related to the fact that the entire dependency tree has to be resolved before running the tests.

The quick solution for that is to not use barrel files for imports within the package, either by avoiding them completely or by mocking them using jest.mock.

I personally recommend avoiding imports completely on your modules by using dependency injection instead. If you really want to go the jest.mock way, all you have to do is use it with a factory, like this (from the Jest docs):

jest.mock('../moduleName', () => {
  return jest.fn(() => 42);
});

// This runs the function specified as second argument to `jest.mock`.
const moduleName = require('../moduleName');
moduleName(); // Will return '42';

Yes, this will magically replace the imported file before it is imported, avoiding the barrel file issue altogether.

Again: I don't like magic code and recommend you use dependency injection instead.

Related