dynamic-import

Documentation of Meteor's dynamic-import package.

Note: Dynamic imports require Meteor 1.5 or higher.

The dynamic-import package provides an implementation ofModule.prototype.dynamicImport, an extension of the module runtime whichpowers the dynamic import(…)statement, an up-and-coming (ECMA2020) addition to theECMAScript standard.

The dynamic import(…) statement is a complementary method to the staticimport technique of requiring a module. While a statically import-edmodule would be bundled into the initial JavaScript bundle, adynamically import()-ed module is fetched from the server atruntime.

Once a module is fetched dynamically from the server, it is cached permanentlyon the client and additional requests for the same version of the module willnot incur the round-trip request to the server. If the module is changed then afresh copy will always be retrieved from the server.

Usage

The import(…) statement returns a Promisewhich is resolved with the exports of the module when it has been successfullyfetched from the server and is ready to be used.

Because it’s a Promise, there are a couple methods developers can use todictate what will happen upon the availability of the dynamically loaded module:

The .then() method of the Promise

  1. import("tool").then(tool => tool.task());

By await-ing in an asynchronous function

Meteor supports async and await,which provide a straightforward approach to asynchronously wait for themodule to be ready without the need to provide a callback:

  1. async function performTask() {
  2. const tool = await import("tool");
  3. tool.task();
  4. }

Default exports

The import(…) Promise is resolved with the exports of the module.If it’s necessary to use the “default” export from a module, it will beavailable on the default property of the resulting object. In the aboveexamples, this means it will be available as tool.default. It can behelpful to use parameter de-structuring to provide additional clarity:

  1. import("another-tool").then(({ default: thatTool }) => thatTool.go());

Using import() with dynamic expressions

If you try to import using any computed expression, such as:

  1. let path = 'example';
  2. const module = await import(`/libs/${path}.js`);

You’ll get an error like so:

  1. Error: Cannot find module '/libs/example.js'

Meteor’s build process builds a graph of all files that are imported or requiredusing static analysis. It then creates exact bundles of the referenced filesand makes them available to the client for import().

Without a complete import statement (static, dynamic or require), Meteor won’tmake that module available for import().

The solution to make dynamic expressions work is to create a module “whitelist”that can be read by the build process, but does not actually run. For example:

  1. if (false) {
  2. import("/libs/example.js");
  3. import("/libs/another-example.js");
  4. import("/libs/yet-another-example.js");
  5. }

Make sure the whitelist is imported from both the client and server entry points.

Difference to other bundling systems

In Meteor’s implementation, the client has perfect information about whichmodules were in the initial bundle, which modules are in the local cache, andwhich modules still need to be fetched. There is never any overlap betweenrequests made by a single client, nor will there be any unneeded modules in theresponse from the server. You might call this strategy exact code splitting,to differentiate it from bundling.

Moreover, the initial bundle includes the hashes of all available dynamicmodules, so the client doesn’t have to ask the server if it can use a cachedversion of a module, and the same version of the module never needs to bedownloaded again by the same client. This caching system has all the benefits ofimmutable caching.

Meteor also allows dynamic expressions as long as the dependency is expressedstatically somewhere else in your code. This is possible because Meteor’sclient-side module system understands how to resolve dynamic strings at runtime(which is not true in webpack or browserify, because they replace moduleidentifier strings with numbers). However, the set of available modules isconstrained by the string literals that you, the programmer, explicitly decidedto allow to be imported (either directly or in a whitelist).