DEV Community

Cover image for Dont shake it, you possibly will break it...
Eckehard
Eckehard

Posted on

Dont shake it, you possibly will break it...

Dealing with large import lists can be annoying. There is a solution to implement a "wildcard" import, but this breaks tree shaking on bundlers like Rollup. So, manual import management seems to be the only usable option today. Welcome to the stone age of programming!

Javascript is a modern and powerful programming language - just, with some conceptual oddities. Ok, there are some historical reasons, but ES6-modules are definitvely not a sinner of the past.

So, what´s odd with the JS module concept?

JS-modules work by exporting and importing keywors, which may be references to objects, variables, constants or functions. A module tells you, what it exports, and you decide, what to import.

module1.js:

function f1() { console.log("this is Function 1") }
function f2() { console.log("this is Function 2") }
export { f1, f2 }
Enter fullscreen mode Exit fullscreen mode

index.html

  <script type="module">
    import { f1 } from "./modul1.js"
    f1() // --> "this is Function 1"
  </script>
Enter fullscreen mode Exit fullscreen mode

Sounds good, isn´t it?

Well, if you have a very small number of references, it is pretty ok. But what do you do, if you need to import a larger number of references? In Javascript, you will easily get into trouble. By default, all code is imported completely. So, even if you only need a single function, you need to invite the whole party. Bad news, if you are using toolboxes like JQuery.

This is even worse, if you change an export: You will possibly break all your existing code, as the imports need to be changed also - manually!

Doing a wildcard-import, but...

It was handy to have a "wildcard-import", that simply imports all exported references, but this is prohibited to prevent naming conflicts. We can import wildcard references only into a "namespace". Nasty, but easy to overcome. The following code shows the principle:

    import * as m1 from "./modul1.js" // namespace m1
    const f1  = m1.f1 // attach to the "global" context
    f1() // --> "this is Function 1"
Enter fullscreen mode Exit fullscreen mode

The same can be achieved automatically:

    // Auto-Import all references
    function autoImport(ns){ // import ns in global namespace
         Object.keys(ns).forEach(key => window[key] = ns[key])
    }

    import * as m1 from "./modul1.js" // namespace m1
    autoImport(m1)
    f1() // --> "this is Function 1"
Enter fullscreen mode Exit fullscreen mode

So far, things look good, we managed to build a "wildcard import", just there's a catch to it: Any "automatic" solution breaks your tree shaking (dead code removal). And this is especially bad for large toolboxes!

tree shaking

Tree shaking is applied by tools like Vite (Rollup) or webpack to reduce the bundle size of your code. Therefore it eliminates all unused code. But this works only, if you use the standard ES6 module syntax. And this relies on manual coding only: Welcome back to the stone age of programming.

It is hard to understand, why the people, that designed the ES6 module syntax, wanted to force programmers to do anything manually? Dealing with large import lists could be far better done by a computer, but currently, there does not seem to be any good solution for that.

What is not working?

We could think of using "import lists" like this, to make imports easier:

    const listObj = { f1, f2 }
    import listObj from "./modul1.js" // namespace m1 
Enter fullscreen mode Exit fullscreen mode

But this is not possible. At least: gathering multiple libraries in the same namespace would be a little help to automate imports:

    import * as m1 from "./modul1.js" // namespace m1 
    import * as m1 from "./modul2.js" // namespace m1 
    import * as m1 from "./modul3.js" // namespace m1 
Enter fullscreen mode Exit fullscreen mode

Not possible too: m1 is not an object, but an "exotic object", that cannot be mutated. It seem, editing import manually is the only working option.

Final words

ES6 modules are most useful, and they provide efficient encapsulation of your code. But if you want to get the benefits of tools like Rollup.js, you will be forced to handle all the imports and exports manually. As soon as you try to add any kind of "automated imports", this will prevent dead code elemination.

You can shorten your import lists by combining multiple references into single objects. As far as I see, Rollup can deal with this kind of optimization, but it will include all functions related to an object, even if only a single function is used.

So, If you want a fine grained dead code elemination, you will need to provide all references manually. If you have any better solution for this, please let me know.

Top comments (0)