We provide a rollup configuration to help you get started using rollup with web components and modules.
Our configuration lets you write code using modern javascript syntax and features, providing the necessary syntax transformation and polyfills for older browsers. See 'config features' for more details.
See the extending section for more customization, such as supporting non-standard syntax or adding babel plugins.
npm init @open-wc building-rollup
- Install the required dependencies:
npm i -D @open-wc/building-rollup rollup rimraf http-server
- Create a
rollup.config.js
file and pass in your app'sindex.html
:
import createDefaultConfig from '@open-wc/building-rollup/modern-config';
export default createDefaultConfig({ input: './src/index.html' });
Our rollup config will look through your index.html
and extract all module scripts and feed them to rollup.
- Create an
index.html
:
<!doctype html>
<html>
<head></head>
<body>
<your-app></your-app>
<script type="module" src="./your-app.js"></script>
</body>
</html>
Note: our config will not handle inline module such as:
<script type="module">
import { MyApp } from './my-app';
</script>
- Add the following commands to your
package.json
:
{
"scripts": {
"build": "rimraf dist && rollup -c rollup.config.js",
"start:build": "http-server dist -o",
"watch:build": "rimraf dist && rollup --watch -c rollup.config.js & http-server dist -o",
}
}
build
builds your app and outputs it in yourdist
directorystart:build
runs your built app fromdist
directorywatch:build
builds and runs your app, rebuilding when input files change
The modern-config.js
based config we setup above works for browsers which support dynamic imports (Chrome 63+, Safari 11.1+, Firefox 67+)
If you need to support older browsers or Edge and IE11 you use our modern-and-legacy-config.js
in your rollup.config.js
:
import createDefaultConfig from '@open-wc/building-rollup/modern-and-legacy-config';
export default createDefaultConfig({ input: './src/index.html' });
In addition to outputting your app as a module, it outputs a legacy build of your app and loads the appropriate version based on browser support. Depending on your app's own code, this will work on Chrome, Safari, Firefox, Edge and IE11.
modern-config.js
:
- compatible with (Chrome 63+, Safari 11.1+, Firefox 67+)
- babel transform based on browser support (no es5)
- output es modules using native dynamic import
- resolve bare imports (
import { html } from 'lit-html'
) - preserve
import.meta.url
value from before bundling - minify + treeshake js
- minify html and css in template literals
modern-and-legacy-config.js
:
Modern build:
- compatible with latest 2 versions of chrome, safari, firefox and edge
- babel transform based on browser support (no es5)
- es modules
- dynamic import polyfill
Legacy build
- compatible down to IE11
- babel transform down to IE11 (es5)
- core js babel polyfills (
Array.from
,String.prototype.includes
etc.) - systemjs modules
Both
- resolve bare imports (
import { html } from 'lit-html'
) - web component polyfills
- preserve
import.meta.url
value from before bundling - minify + treeshake js
- minify html and css in template literals
Our config accepts two options:
export default createDefaultConfig({
// your app's index.html. required
input: './src/index.html',
// the directory to output files into, defaults to 'dist'. optional
outputDir: '',
});
See 'extending' to add more configuration.
You can define your own babel plugins by adding a .babelrc
or babel.config.js
to your project. See babeljs config for more information.
For example to add support for class properties:
{
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
A rollup config is just a plain object. It's easy to extend it using javascript:
import createDefaultConfig from '@open-wc/building-rollup/modern-config';
const config = createDefaultConfig({ input: './src/index.html' });
export default {
...config,
output: {
...config.output,
sourcemap: false,
},
plugins: [
...config.plugins,
myAwesomePlugin(),
],
};
If you use modern-and-legacy-config.js
, it is actually an array of configs so that rollup outputs a modern and a legacy build. Simply map
over the array to adjust both configs:
import createDefaultConfig from '@open-wc/building-rollup/modern-and-legacy-config';
const configs = createDefaultConfig({ input: './src/index.html' });
export default configs.map(config => ({
...config,
output: {
...config.output,
sourcemap: false,
},
plugins: [
...config.plugins,
myAwesomePlugin(),
],
}));
::: warning Many extensions add non-native syntax to your code, which can be bad for maintenance longer term. We suggest sticking to native syntax. If you really need it, scroll below to see some usage examples. :::
CommonJS is the module format for NodeJS, and not suitable for the browser. Rollup only handles es modules by default, but sometimes it's necessary to be able to import a dependency. To do this, you can add rollup-plugin-commonjs:
import createDefaultConfig from '@open-wc/building-rollup/modern-and-legacy-config';
import commonjs from 'rollup-plugin-commonjs';
const configs = createDefaultConfig({ input: './src/index.html' });
// map if you use an array of configs, otherwise just extend the config
export default configs.map(config => ({
...config,
plugins: [
...config.plugins,
commonjs(),
],
});
Web apps often include assets such as css files and images. These are not part of your regular dependency graph, so they need to be copied into the build directory.
Rollup-plugin-cpy is a plugin that can be used, but there are other options too.
import cpy from 'rollup-plugin-cpy';
import createDefaultConfig from '@open-wc/building-rollup/modern-and-legacy-config';
const config = createDefaultConfig({
input: './src/index.html',
});
// if you use an array of configs, you don't need the copy task to be executed for both builds.
// we can add the plugin only to the first rollup config:
export default [
// add plugin to the first config
{
...config[0],
plugins: [
...config[0].plugins,
cpy({
// copy over all images files
files: ['**/*.png'],
dest: 'dist',
options: {
// parents makes sure to preserve the original folder structure
parents: true
}
}),
],
},
// leave the second config untouched
config[1],
];
To import a typescript file, use the .ts
extension in your index.html
:
<html>
<head></head>
<body>
<my-app></my-app>
<script type="module" src="./my-app.ts"></script>
</body>
</html>
Make sure you set your tsconfig.json
target
and module
fields to ESNext
. This way tsc
, the typescript compiler, won't do any compilation so that a plugin can take care of it.
Within rollup there are two options to add typescript support.
We recommend using the babel typescript plugin. Add it to your babel config file (.babelrc
or babel.config.js
):
{
"presets": [
"@babel/preset-typescript"
],
}
This is the fastest method, as it strips away types during babel transformation of your code. It will not perform any type checking though. We recommend setting up the type checking as part of your linting setup, so that you don't need to run the typechecker during development for faster builds.
It is also possible to add the rollup typescript plugin, which does typechecking and compiling for you:
import typescript from 'rollup-plugin-typescript2';
import createDefaultConfig from '@open-wc/building-rollup/modern-and-legacy-config';
const configs = createDefaultConfig({
input: './src/index.html',
});
export default configs.map(config => ({
...config,
plugins: [
...config.plugins,
typescript(),
],
}));
We already mentioned this above, but this is really important: Make sure to prevent any compilation done by the typescript compiler (tsc
). If you use one of the options above, you put babel or rollup in charge of the compilation of typescript. In no case do you want multiple compilers to interfere with each other.
You can do this by setting the following options in tsconfig.json
:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
}
}
To separate your lit-html styles in css files, you can use rollup-plugin-lit-css:
import createDefaultConfig from '@open-wc/building-rollup/modern-and-legacy-config';
import litcss from 'rollup-plugin-lit-css';
const configs = createDefaultConfig({ input: './src/index.html' });
// map if you use an array of configs, otherwise just extend the config
export default configs.map(config => ({
...config,
plugins: [
...config.plugins,
litcss({ include, exclude, uglify })
],
});