Meteor 1.5: How to actually dynamically import modules?

Oh man, this - is - the - one - thing that most Meteor developers don’t seem to be aware of.

Another thing that most don’t grasp is that a Meteor app is an ordinary Node app in the end is no more difficult or easier than any other Node app to deploy.

As for development, it is leaps and bounds easier.

I’m now hours away from delivering an ordinary connect (read express) rest api app for an enterprise client; with unlikely features such as sql server backend and windows server deployment; and guess what, I’ve built it on Meteor which saved me loads of tooling headache and provided me with nice enhancement opportunities like the ability to bootstrap a live api monitoring dashboard!..

8 Likes

Yes, yes, yes. :thumbsup: :thumbsup: :thumbsup:

4 Likes

The 1.5 release is now available, but hasn’t yet been marked as “recommended” - it won’t update your installation automatically until that happens.

In the meantime you can do meteor update --release 1.5

1 Like

Rob, I’d like to spit my application in half – resulting in an admin side and a user side. They will of course share the same DB and Accounts system.

I’d like to upgrade to Meteor 1.5 + React for the admin side and stay on 1.3 + Blaze for the user side. The problem is they both need the same server side backend code for a services api. At some point I’d also like to use Apollo for the admin side. The way I see it my options are as follows:

  1. Create a “Package” with the services api code and “import” this into both projects. The code will be written in one spot and duplicated in both Meteor projects (via packages.json).

  2. Break out the services api code into an Express application and call that from both admin and user clients. I’d need access to the same Mongo database for this api to work correctly.

I’d like your advice.

Sounds like you would just need to make 2 app’s and hook them up to the same database to achieve what you are looking for? Shouldn’t even need dynamic imports for that, just 2 projects on the same db.

I believe that’s the only way you are going to achieve 1.5 on admin and 1.3 for users

What’s the point of staying with 1.3 for anything? Why not just use 1.5 + Blaze? That works perfectly well with no (or at least minimal) code changes.

Well, because I have a LARGE number of screens using Blaze & Autoform and SimpleSchema that do not work as expected/as they do on 1.3 – I would have to upgrade them all. A ton of work. Yet my admin side there are less screes and they do not use any of that, therefore it would be easier for me to isolate that portion of the application and upgrade to Meteor 1.5, React, Redux, and maybe even Apollo or Relay, dynamic imports, etc.

You are right, but the catch is they BOTH need a portion of the server that has nothing to do with the Database (well, this worker service code needs the database too actually). it’s a worker service that does some processing on files. I don’t want to keep two copies of the same code in two different projects. I either need to build a package that has the code and import in into both project, or maybe separate this worker service api code into it’s own web service – maybe Express or a AWS Lamda service – I don’t know.

Would you be able to clarify what exactly does not work in 1.5 like it does in 1.3?

Sorry, I haven’t gone through all the errors I saw when I did a test run. Quickly my validations and autoform get field values didn’t working right.

Were you able to get autoform working for you on 1.5 without any refactoring?

Don’t use autoform but as for Blaze and SimpleSchema I have no issues. But I can’t see what could cause autoform to be unhappy in 1.5…

What’s an example of syntax for dynamic imports if you use React instead of templates?

1 Like

I see more about it in @benjamn’s article:

import { a, b as c } from "./some/module";
doSomething(a, c)

…becomes:

import("./some/module").then(({ a, b: c }) => {
  doSomething(a, c);
});

So if you’re using React on the front end, would it make sense to wrap this around the container, e.g.:

export default composeAll(
  composeWithTracker(composer),
  useDeps(depsMapper)
)(Dial);

…becomes:

import("./some/module").then(({ a, b: c }) => {
  export default composeAll(
    composeWithTracker(composer),
    useDeps(depsMapper)
  )(Dial);
});

(Note: my React container may look a bit different because it’s in Mantra.)

1 Like

Just curious, are dynamic imports & this syntax going to become a ES7 or ES* “thing” at some point or is this something special to the Meteor 1.5 build tool?

This is a proposal currently at stage 3. The final stage is 4 - so it’s on the way - ES8???

1 Like

Can we dynamically import meteor packages yet?
e.g. import('meteor/froala:editor');

When I do this, the package is still pre-loaded in the minified bundle.

1 Like

Someday if we get JS tree shaking for dead code in the build tool, it will only work for ES6 import style code. That is IMO a big reason to start using ES6 classes.

1 Like

I’ve been working on a post about it… not sure if my article is any better than the meteor blog and this thread, but at least it’s a new take and perhaps covers a slightly different angle:

Hope this helps, and let me know if I need to correct anything. I plan on making a few more posts from different angles.

8 Likes

see my article linked above…

Excellent article - thank you :slight_smile:

I have a question about this section:

If we import Limb we get Limb - Leaf in 1 bundle.

If we later dynamically import Twig from some other page, we get Twig - Leaf in a (different) 2nd bundle.

This means that we have just duplicated code, bundled it twice.

How does that stack up with @sashko’s blog article, which states:

How it works

So what does Meteor do differently that enables some of these features? Well, here’s a summary:

The most important aspect is that the client is aware of the shape of the module tree, the names of those modules, and their content hashes ahead of time for the entire app. This means that when a dynamic import happens, the client-side app can determine exactly which modules and hashes are missing, and ask only for those from the server in one request. Then, all of the modules are cached individually on the client so that next time they can be loaded from persistent storage.

This means that, in most cases, you can load every dynamic module exactly once. Even if you push an entirely new version of your app, if that’s using the same version of a library as before that will still be loaded from local storage.

I can’t link directly to that section, unfortunately.