StencilJS Lazy Loading - Part 2: NPM Build Assets

By: Lisa Backer
Open book and lights with bokeh

Lazy loading your own assets in Stencil is possible, but can require a little extra code and configuration both for web and distributed builds. This post details the necessary steps for an npm-distributed build.

Welcome back. In part 1 of this blog series I focussed on lazy-loading assets in Stencil for use within your web deployment. In this part I’ll focus on how to lazy-load assets for your npm-distributed build. As a quick reminder, Stencil provides a standards based way to create web components that can then be used by any framework, or no framework at all.

The Scenario

You may recall that our scenario involves a component with localized text. Once the appropriate language is determined at run-time, the component needs to load a language-specific JSON file that contains the translations. While I didn’t find any advice for loading in these files at run-time for a distributed build, I was pointed towards the Ionic team’s icon library, which does make icon assets available via lazy-loading at runtime for both web and distributed builds. Much of my inspiration comes from this library.

Distributed Builds

Again, let’s assume an application namespace of “muppet-fans.” Stencil will build your output for a distributed build into the dist directory with a structure of:

|--dist
  |--cjs
  |--collection
  |--esm
  |--muppet-fans
    |-- … lazy loaded files
  |-- types
  |--index.js
  |--muppet-fans.js

In Part 1 we used the copy configuration to copy files into our web output directory using:

copy: [
  {
    src: '/locale/*.js',
    dest: 'build/muppet-fans/locales'
  },
  {
    src: '../node_modules/moment/locale/*.js',
    dest: '/build/muppet-fans/moment-locales'
  }
]

This made our locale files available within the same directory as other lazy-loaded script assets. The equivalent folders in our dist location are dist/muppet-fans/locales and dist/muppet-fans/moment-locales. Unfortunately, the copy configuration only applies to the web build folder and not the dist output, so how can we get our files in there?

Inspiration from Ionic

Once again I turned to Ionic’s own Ionicons. Reading through the code, I discovered there doesn’t appear to be a standard configuration you can set to make additional files available in your distributed builds for lazy loading. The library has a more complicated use case than the scenario described here, but it ssentially uses a NodeJS script to move the files during their build process.

The following is an example of a simple script to move our locale-specific files into the dist output created by Stencil.

const fs = require('fs-extra');
const path = require('path');

// Get a reference to the source locales folders
const buildRoot = path.join(__dirname, '..', 'www', 'build', 'muppet-fans');
const srcLocales = path.join(buildRoot, 'locales');
const srcMomentLocales = path.join(buildRoot, 'moment-locales');

// Get a reference to the output locales folders in the dist build
const destRoot = path.join(__dirname, '..', 'dist', 'muppet-fans');
const destLocales = path.join(destRoot, 'locales');
const destMomentLocales = path.join(destRoot, 'moment-locales');

// Copy from the source to the destination
fs.copySync(srcLocales, destLocales);
fs.copySync(srcMomentLocales, destMomentLocales);

There isn’t much to the code above. As the comments indicate, we just grab a reference to the source folders that we built when building the www build for locales and moment-locales. Then we get a reference to where we want to copy the files. Finally we copy our locale-specific files. One thing to note is that I chose to copy from the build output in the www directory rather than from the original source locations. This allows us to move things around and only adjust the copy configuration. Should we choose to change the way we generate our locales by installing a “muppetastic locales” npm package, we’d only need to adjust the copy configuration to:

copy: [
  {
src: '../node_modules/muppetastic-locales/locales/*.js',
dest: 'build/muppet-fans/locales'
  },
  {
src: '../node_modules/moment/locale/*.js',
dest: '/build/muppet-fans/moment-locales'
  }
]

Our copy script for the dist output would not need to change nor would our code that references the files via the resources URL (which still works to reference these files for lazy-loading now that they have been copied to the right location).

Automating the Process

Finally, we need to ensure that this script is run after every build. In order to do that we can take advantage of npm scripts. You can prepend “pre” before the name of any step in the scripts section of your package.json file to run scripts before that step. Similarly you can prepend “post” before the name of any step to run scripts after that step.

For example if the package.json contained:

{
  "scripts": {
    "premuppet": "echo 'this is'",
    "muppet": "echo 'what we call'",
    "postmuppet": "echo 'The Muppet Show'"
  }
}

Then you could execute npm run muppet and see “This is what we call The Muppet Show” written out in your terminal window. We can take advantage of this to add a “postbuild” script to the package.json to execute the contents of our copy script after the output is built.

Assuming we have saved our copy script in /scripts/copy-locales.js, we can add the following postbuild script.

   "postbuild": "node scripts/copy-locales"

Conclusion

Stencil does a great job overall of providing a simple way to write web components using only standards that can be integrated into your framework of choice. It is coming along nicely, but there is still a lot of growing room for the tool.

What I’ve detailed is one approach to solving a need to lazy-load your own custom assets at runtime for web builds and asset distribution in your npm builds with a single code base, but I’m always looking for better approaches and would love to hear from you if you have one. You can find my Twitter handle on my DockYard profile.

DockYard is a digital product agency offering exceptional strategy, design, full stack engineering, web app development, custom software, Ember, Elixir, and Phoenix services, consulting, and training. With a nationwide staff, we’ve got consultants in key markets across the United States, including Seattle, San Francisco, Los Angeles, Denver, Chicago, Austin, New York, and Boston.