Skip to content

A plugin to resolve ECMAScript module bare/url import specifiers at build-time

License

Notifications You must be signed in to change notification settings

fuweichin/rollup-plugin-import-maps

Repository files navigation

rollup-plugin-import-maps

A plugin to resolve ECMAScript module bare/url import specifiers at build-time for browsers which don't support import-maps, mostly based on WICG's import-maps reference implementation.

This plugin plays a role of polyfill for browsers but is used at build-time rather than at run-time. If some day (maybe in 2023) all major browsers support import-maps then this plugin can be retired.

Contents:

Install

npm install --save-dev rollup-plugin-import-maps

Usage

Basic Usage

edit rollup.config.js, import and use the plugin

import { importMapsPlugin } from 'rollup-plugin-import-maps';
// import { readFileSync } from 'fs';

export default {
  input: './src/index.js',
  plugins: [
    importMapsPlugin({
      srcPath: './index.importmap',
      // srcText: readFileSync('./index.importmap', { encoding: 'utf8' }),
      // srcObject: JSON.parse(readFileSync('./index.importmap', { encoding: 'utf8' })),
    }),
  ],
  output: [
    {
      file: './dist/index.js',
      format: 'es'
    }
  ]
};

Plugin Options

  • srcPath:string optional

    file path to importmap

  • srcText:string optional

    plain text of importmap

  • srcObject:Object optional

    parsed object of importmap

    Note: One of srcObject, srcText, srcPath should be specified, if multiple of them specified, then precedence order is: srcObject, srcText, srcPath.

  • baseDir: string default process.cwd()

    baseDir to calculate scope paths in order to match scopes defined in importmap

  • transformingReport:string default undefined

    set a file path to save transforming report as a JSON file, will output to Console if value set to "-"

  • noTransforming:boolean default false

    if value set to true, then the plugin will mark specifiers defined in importmap as external, and won't transform those specifiers. useful if you want to build for browsers which already support import-maps and "set external list" with importmap.

  • exclude:string|RegExp|Function default undefined

    skip bare/url specifiers from resolving / transforming according to importmap.

    e.g. .json,.wasm,.css, /\.(json|wasm|css)$/, (source, importer)=> /\.(json|wasm|css)$/.test(source)

Caveats

  • This plugin doesn't yet support transforming module specifiers defined in data url. example data url:

    import {foo, bar} from "data:application/javascript;charset=utf-8,import%20%7Bdefault%20as%20foo%7D%20from%20'foo'%3B%0Aexport%20%7Bfoo%7D%3B%0Aexport%20%7Bdefault%20as%20bar%7D%20from%20'bar'%3B";

    which can be decoded as

    import {default as foo} from 'foo';
    export {foo};
    export {default as bar} from 'bar';
  • When tansforming specifiers in dynamic imports, only string literal can be transformed. example specifiers:

    import('foo/locales/en/messages.js') // Yes
    import("foo/locales/en/messages.js") // Yes
    
    let lang = 'en';
    import('foo/locales/'+lang+'/messages.js') // No
    
    let modulePath = 'foo/locales/en/messages.js'
    import(modulePath)                         // No
    
    import(`foo/locales/en/messages.js`)       // No

Use Cases

Bare Specifiers Transforming

importmap

{
  "imports": {
    "three": "/node_modules/three/build/three.module.js",
    "three/": "/node_modules/three/",
    "underscore": "data:application/javascript;charset=utf-8,export%20default%20window._%3B",
    "~/": "//mysite.com/packages/myapp/"
  }
}

input code

import * as THREE from 'three';
import GLTFLoader from 'three/examples/jsm/loaders/GLTFLoader.js';
import _ from 'underscore';
import '~/polyfills/navigator.userAgentData.js';

console.log(THREE, GLTFLoader, _);

output code

import * as THREE from '/node_modules/three/build/three.module.js';
import GLTFLoader from '/node_modules/three/examples/jsm/loaders/GLTFLoader.js';
import _ from 'data:application/javascript;charset=utf-8,export%20default%20window._%3B';
import '//mysite.com/packages/myapp/polyfills/navigator.userAgentData.js';

console.log(THREE, GLTFLoader, _);

URL Specifiers Transforming

importmap

{
  "imports": {
    "https://unpkg.com/[email protected]/build/three.module.js": "/node_modules/three/build/three.module.js",
    "node-modules:/": "/node_modules/",
    "data:application/javascript;charset=utf-8,export%20default%20window._%3B": "/underscore/underscore-esm-min.js",
    "app-home:/": "//mysite.com/packages/myapp/"
  }
}

input code

import * as THREE from 'https://unpkg.com/[email protected]/build/three.module.js';
import GLTFLoader from 'node-modules:/three/examples/jsm/loaders/GLTFLoader.js';
import _ from 'data:application/javascript;charset=utf-8,export%20default%20window._%3B';
import 'app-home:/polyfills/navigator.userAgentData.js';

console.log(THREE, GLTFLoader, _);

output code

import * as THREE from '/node_modules/three/build/three.module.js';
import GLTFLoader from '/node_modules/three/examples/jsm/loaders/GLTFLoader.js';
import _ from '/underscore/underscore-esm-min.js';
import '//mysite.com/packages/myapp/polyfills/navigator.userAgentData.js';

console.log(THREE, GLTFLoader, _);

No Transforming

You may use rollup to build two distributions, for browsers with or without import-maps support, and load corresponding distribution conditionally. e.g.

// rollup.config.js
export default [
  {
    input: './src/index.js',
    plugins: [
      importMapsPlugin({
        srcPath: './index.importmap',
        transformingReport: '-',
      }),
    ],
    output: [
      {
        file: './dist/index.js',
        format: 'es'
      }
    ]
  },
  {
    input: './src/index.js',
    plugins: [
      importMapsPlugin({
        srcPath: './index.importmap',
        noTransforming: true,
      }),
    ],
    output: [
      {
        file: './dist/index-experimental.js',
        format: 'es'
      }
    ]
  }
];
<script type="importmap">
put content of ./index.importmap here
</script>

<script type="module">
if (HTMLScriptElement.supports && HTMLScriptElement.supports('importmap')) {
  console.log('Your browser supports import maps.');
  import('/dist/index-experimental.js');
}else{
  console.log('Your browser doesn\'t support import maps.');
  import('/dist/index.js');
}
</script>

Related Efforts

  • import-maps - Reference implementation playground for import maps proposal

Maintainers

@fuweichin

License

MIT

Other licenses of dependencies

About

A plugin to resolve ECMAScript module bare/url import specifiers at build-time

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published