Are ES6 modules in browsers going to get loaded level-by-level?

Randy Buchholz work at randybuchholz.com
Sat Oct 17 20:28:27 UTC 2020


I think some form of bundling will always be necessary. I use classes and took a name-spaced and typed approach to modules and classes, putting each class in its own module in a file hierarchy (namespace). This is an enterprise level LOB application with dozens of classes. Many classes are used cross-domain, limiting static/design-time bundling approaches. Also, an issue I encountered with static bundling is that classes aren't hoisted, so there are ordering concerns/issues with class bundles.

I have multiple workers as background services that use these. Each class usually has a few imports for the classes it uses. Using normal imports, I was soon generating 100's of requests for the files. Even with caching, there is a lot of overhead. As classes are used more, this can become a real issue.

I ended up with an approach where I added dependency metadata to each class to support bundling. The metadata helps with the "what to send" issue. When I need a class/type do an import for it. The server walks the dependencies, and bundles the request class with the full tree of dependencies. When the client receives the bundle it adds the new classes to a type library. It's designed for enterprise use where you have more control of things and can enforce standards.

Class looks like this:

// File Bar.cmjs
```
//::Requires: Foo.Package.Class1 Foo.Package.Class2
class Bar {
    const a = new Class1(); // Actually usually the qualified Foo.Package.Class1
    ...
}
```
// File /Foo/Package/Class1.cmjs
```
//::/Requires: Common.Util.Whatever
class Class1{

}
```

The basic idea is that when the server gets a request it reads the "Requires" and gets those files, recursively reading requires. I keep a list of all files and the depth so I know if I already captured a required, and how to order the results. Once I have all of the files I write them to a single bundle. I don't need to parse the files (Requires is just a comment), but can if I want more control. The bundled file looks like:

```
class Whatever {...}

class Class1 {...}

class Class2 {...}

class Bar {...}
```

It's more complex, because of name collisions and import scoping. What I do is process the bundle and promote the classes out of the scope.
```
globalThis
   .Foo
      .Package
         .Class1 = Class1; // (The "newable" class)
         .Class2 = Class2;
   .Common
      .Util
         .Whatever = Whatever;
```

Now I can just do `new Foo.Package.Class1()` anywhere in the context, not just in the import scope. On the client I use `inject` in many places instead of `import` - inject('A.B.Class'); const x = new A.B.Class();`. This checks for the type, and if it doesn't exist on the client it requests it from the server.  The server creates a bundle for it and its dependencies. I add these to the type-tree. My server isn't really import-aware, I just use middleware to intercept the request. This is why I could use a way to identify "import requests". I know when I'm doing an "import" through injection, but with a regular import I have to do some inspection of the fetch to know to initiate the process.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20201017/2b7a2b60/attachment.html>


More information about the es-discuss mailing list