Tilted sphere with longitudinal stripes Modern Web Guides Docs Blog Toggle darkmode

Building: Rollup Plugin Import Meta Assets

Rollup plugin that detects assets references relative to modules using patterns such as new URL('./assets/my-img.png', import.meta.url).

The referenced assets are added to the rollup pipeline, allowing them to be transformed and hash the filenames.

How it works

A common pattern is to import an asset to get the URL of it after bundling:

import myImg from './assets/my-img.png';

This doesn't work in the browser without transformation. This plugin makes it possible to use an identical pattern using import.meta.url which does work in the browser:

const myImg = new URL('./assets/my-img.png', import.meta.url);

Dynamic variables

You can also use dynamic variables like so:

const myImg = new URL(`./assets/${myImg}.png`, import.meta.url);

Please consult the dynamic-import-vars plugin documentation for options and limitations.

Install

Using npm:

npm install @web/rollup-plugin-import-meta-assets --save-dev

Usage

Create a rollup.config.js configuration file and import the plugin:

import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';

export default {
  input: 'src/index.js',
  output: {
    dir: 'output',
    format: 'es',
  },
  plugins: [importMetaAssets()],
};

Then call rollup either via the CLI or the API.

Options

exclude

Type: String | Array[...String]
Default: null

A picomatch pattern, or array of patterns, which specifies the files in the build the plugin should ignore. By default no files are ignored.

include

Type: String | Array[...String]
Default: null

A picomatch pattern, or array of patterns, which specifies the files in the build the plugin should operate on. By default all files are targeted.

warnOnError

Type: Boolean
Default: false

By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched.

transform

Type: Function
Default: null

By default, referenced assets detected by this plugin are just copied as is to the output directory, according to your configuration.

When transform is defined, this function will be called for each asset with two parameters, the content of the asset as a Buffer and the absolute path. This allows you to conditionally match on the absolute path and maybe transform the content.

When transform returns null, the asset is skipped from being processed.

In this example, we use it to optimize SVG images with svgo:

import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
const svgo = new SVGO({
  // See https://github.com/svg/svgo#what-it-can-do
  plugins: [
    /* plugins here */
  ],
});

export default {
  input: 'src/index.js',
  output: {
    dir: 'output',
    format: 'es',
  },
  plugins: [
    importMetaAssets({
      transform: (assetBuffer, assetPath) => {
        return assetPath.endsWith('.svg')
          ? svgo.optimize(assetBuffer.toString()).then(({ data }) => data)
          : assetBuffer;
      },
    }),
  ],
};

preserveDynamicStructure

Type: Boolean
Default: false

When enabled, dynamic asset URLs (using template literals) are emitted to the Rollup pipeline and the URL pattern is rewritten to resolve relative to the first emitted asset.

Requirements: The output must preserve both filenames (no hashing) and the directory structure from the dynamic expression onwards. If filenames are hashed or the directory structure changes, the runtime URL resolution will fail.

This is useful when your application or CDN already has versioned URLs, so you don't need filename hashing. It also avoids generating a large switch statement in the output when you have many dynamic assets (e.g. an icon library).

import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';

const projectRoot = process.cwd();

export default {
  input: 'src/index.js',
  output: {
    dir: 'output',
    format: 'es',
    // preserve original file paths, relative to the project root
    assetFileNames: asset =>
      path.relative(projectRoot, asset.originalFileNames[0]).split(path.sep).join('/'),
  },
  plugins: [
    importMetaAssets({
      preserveDynamicStructure: true,
    }),
  ],
};

Given this source code:

const icon = new URL(`./assets/icons/${category}/${name}.svg`, import.meta.url);

The plugin will:

  1. Emit all matching assets (e.g. ./assets/icons/outline/arrow.svg, ./assets/icons/solid/check.svg, etc..)
  2. Rewrite the URL to resolve relative to the first emitted asset

Examples

Source directory:

.
├── one
│   └── two
│       └── the-image.svg
├──
└── entrypoint.js

With entrypoint.js containing this:

const imageUrl = new URL('./one/two/the-image.svg', import.meta.url).href;
console.log(imageUrl);

Output directory:

.
├── assets
│   └── the-image.svg
└── bundle.js

With bundle.js containing this:

const imageUrl = new URL(new URL('asset/the-image.svg', import.meta.url).href, import.meta.url)
  .href;
console.log(imageUrl);