Skip to content

ES2015 Modules & Browser Bundles

Matt Gaunt edited this page Dec 12, 2017 · 1 revision

This document covers how the source code is set up for V3 of Workbox and how it's intended to be released / published.

Source Code Structure for a Package

Each package will have a structure of:

workbox-example/
    package.json
    README.md
    index.mjs
    _private.mjs
    FileName1.mjs
    exampleDirectory/
        FileName2.mjs
  • package.json is a normal NPM package file with the following pieces of information:
    • "main": "./builds/browser/index.js" will point to a browser friendly build of the module.
    • "module": "./index.mjs": will be used by Rollup to find the ES2015 module code.
    • "workbox": { "browserNamespace": "....", "packageType": "...." }: These fields are used during the build process to generate the bundles release on NPM and the CDN. The namespace value affects how the module exposed on the global scope, if we set "browserNamespace": "xyz" this would be accessed via workbox.xyz.
  • index.mjs is the default ES2015 export of the module. This will have a default export and named exports.
  • browser.mjs is the default browser export of the module. This will export a single object which provides the default and named exports.

"_*.mjs" Files

These files have a symbolic importance and should be similar across each package.

  • _default.mjs This should define the default export of the module that index.mjs and browser.mjs will proivde.
  • _private.mjs This should be the central place where all private classes and objects are exposed to be used by other workbox-* modules.
  • _types.mjs Is used to define custom JSDoc types. This should contain no code - just JSDocs comments.
  • _version.mjs These files are autogenerated and added to each file. Build tools are smart enough to only include this once, but it provides version of a module - helpful for debugging.

Generating Different Browser Bundles

All of the browser libraries for Workbox are written in ES2015 modules and files end with .mjs extension.

When generating a browser friendly bundle we generate dev and prod bundles where prod is aggressively stripped of any unnecessary code and minified.

We use process.env.NODE_ENV === 'production' checks in our code to allow Rollup to evaluate this code and perform dead code elimination.

// Our source code looks like this
if (process.env.NODE_ENV !== 'production') {
  console.log(`Hey this is super helpful but also increases file size.`);
} else {
  console.log(`Hi.`);
}

//  For dev builds, the above code will be outputted as
{
  console.log(`Hey this is super helpful but also increases file size.`);
}

// For prod builds, the output is
{
  console.log(`Hi.`);
}

You can extend this further to alter the export of a class

Our module can look like:

const ourExport = (process.env.NODE_ENV === 'production') ? slimExport : largeExport;
export default ourExport

Again, Rollup will eliminate dead code and only include the appropriate code that is exported.

Importing in a Package

You must import files within a package by referencing a specific file, you MUST NOT rely on a modules default / named exports.

For example, this is bad:

import {bar} from 'workbox-foo';

Instead you must reference the specific file that would supply bar.

import {bar} from 'workbox-foo/bar.mjs';

This is a restriction we have in place to try and reduce unnecessary bloat in builds.

How Do Workbox-* Modules Reference Each Other

When one Workbox module wants to use another module's code, it will need to reference the code via a normal import.

For example, workbox-routing wanting to throw a WorkboxError which is defined in workbox-core would do the following:

// Inside of a file in workbox-routing
import {WorkboxError} from 'workbox-core/_private/WorkboxError.mjs';

throw new WorkboxError('error-code');

There are a few scenarios to consider in terms of how this works as an ES2015 module and how it works as a browser bundle:

Running as an ES2015 Module: workbox-routing will need to add workbox-core as a dependency. Lerna will manage the npm -linking of workbox-core and this is how the ES2015 set up will work (it'll get the code from node_modules). Running as a browser iife: Our build process will replace the import with the appropriate browser namespace for each module, so for code above Rollup will replace the import with: const WorkboxError = workbox.core._private.WorkboxError;.

This means we'll have shared code between modules and a more consistent explanation for API's and module code.

If you are curious to learn more regarding the .mjs extension please read: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md