Rollupの使い方

ブラウザ向けのJavascriptアプリやライブラリをビルドするためのバンドラーツールとして、webpackや rollup.jsが普及していますが、ブラウザの世代交代が進んできた結果、Rollupが有力になってきています。
rollupのビルドコマンドは以下のとおり、実行じたいは簡素です。

$ rollup -c [rollup.config.js]

なお、configファイル名がデフォルトのrollup.config.jsの場合は、ファイル名を省略しrollup -cで動作します。
デフォルト出力ファイル形式はesm(ECMAScript modules)のため、ブラウザ向けHTMLのインクルード書式は以下のようになります。

<script src="bundle.js" type="module"></script>

type=module機能のブラウザサポートは シェア90%超であり、多くのケースでreadyでしょう。

rollupとプラグインのインストール

rollupは node環境で動作し、インストールはnpm installで入ります。インストール操作そのものにはrollup特有の手順はなく、npmの熟練度をそのまま生かせます。

rollupもwebpack同様、関連プラグインのセット導入が必要になります。(TypeScriptの補足は後述)
代表的なものをpackage.jsonの抜粋で挙げると以下のようなリストになります。なお、jsonにはコメントを書けないので、以下のコードはそのままでは動作しません。
タイムリーなバージョン設定の点でも、標準的な手順に沿ってnpm install --save-devコマンドでインストールするのが良いでしょう。

{
  "dependencies": {
    // 開発に利用するJSライブラリを定義
  },
  "devDependencies": {
    "rollup": "^2.26.11",
    "@rollup/plugin-commonjs": "^15.0.0",   // require() 形式の互換性レイヤ
    "@rollup/plugin-node-resolve": "^9.0.0",  // node_modules内のnpmパッケージの解決
    "@rollup/plugin-typescript": "^6.0.0",  // typescriptのトランスパイル
    "rollup-plugin-terser": "^7.0.2"    // コード量を削減
  },
  "scripts": {
    "build": "rollup -c"
  }
}

公式プラグインは、 GitHubのpluginsリポジトリに集まっています。なお、 commonjsnode-resolveについては、幅広いnpmライブラリを使ううえでほぼ必須です。

rollup.config.js

rollup.config.jsの基礎的な記述例は以下のとおりです。構成はwebpack.config.jsと似ており、主に起点の入力ファイル、ビルド後の出力ファイル、使用するプラグインを設定します。

import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";
import {terser} from "rollup-plugin-terser";

export default {
    input: 'src/main.ts',
    output: {
        file: 'dist/bundle.js'
    },
    plugins: [
	    typescript(),
	    resolve(),
	    commonjs(),
	    terser()
    ]
}

pluginsのリストの通り、プラグインがデフォルト設定でかなり適切に動作する点は便利です。挙動をカスタマイズする場合は、プラグインのドキュメントを参照して、各関数の引数に設定オブジェクトを指定します。

TypeScriptとJSX

TypeScriptとJSX(reactやpreactのテンプレート)は、公式の @rollup/plugin-typescriptでトランスパイル可能です。この目的では babelは不要です。
@rollup/plugin-typescriptはtscを使用しているため、別途 TypeScriptのインストールが必要です。npmでインストールする場合、typescriptとtslibをインストールします。また、Debianのパッケージを利用する場合には、node-typescriptパッケージが提供しています。

なお、TypeScript->Javascriptのコンパイルプロセスはtscが担当するため、追加設定はtsconfig.jsonで行います。 preactの.tsx(TypeScriptで書いたJSX)向けの設定例は以下のとおりです。

{
	"compilerOptions": {
		"sourceMap": true,
		"target": "es6",
		"module": "esNext",
		"jsx": "react",
		"jsxFactory": "h",
		"allowSyntheticDefaultImports": true
	}
}

TypeScriptからCommonJSのライブラリを利用する場合

npmの中にはブラウザ向けのimportと非互換のCommonJS形式で書かれているライブラリも多く、@rollup/plugin-commonjsを利用することでrequire()が適切に解決されます。
ただし、TypeScriptのコードでCommonJSのライブラリをインポートする場合の書き方は以下のとおりです。

import some_function from "some-commonjs-lib";

require()でインポートした場合、期待と異なり何も解決されず、元のコードがそのまま出力ファイルに残ります(コンパイルは正常終了します)。
そしてブラウザが当然require()未定義のエラーを出力します。commonjsやnode-resolveプラグインの設定を変えても挙動は変わらず、非常に分かりづらいバグになります。

またこの場合、先ほどのallowSyntheticDefaultImportsの設定が必要になるでしょう。デフォルト設定ではおそらくコンパイルエラーで指摘されます。

もともとのCommonJSの使われ方を考慮すると複雑ですが、TypeScriptとして自然なインポート形式を利用できると考えれば便利な仕様です。

Babelは

トランスパイラとして実績のあるBabelも @rollup/plugin-babel経由で利用でき、TypeScript、JSXを変換できます。
この場合、一応Typescriptなしで動作するもののBabelのプラグインを追加していく必要があり、TypeScriptの新しい機能を利用したい場合、構成が複雑になりがちです。
JSXのみのプロジェクトであれば比較的簡素ですが、TypeScriptへの発展性に難がある点には注意が必要です。

WebAssembly

rollupは、より高度な機能もカバーしています。

WebAssemblyについては考慮すべき点が多いのですが、実用上有力であるRustを前提とすると、 rollup-plugin-rustでブラウザからロードする部分を含めて一貫したビルドを実現できました。

rollup.config.jsへの追加は簡素です(抜粋)。

import rust from "@wasm-tool/rollup-plugin-rust";

export default {
    plugins: [
	    rust(),
    ]
}

rollup-plugin-rustの特徴的な点は、以下のようにRustのビルドconfigをimportする点です。

import wasm from './rust-src/Cargo.toml';

rollupコマンド実行時にrustのビルドも走り、仕上がりのwasmを出力フォルダに生成します。 なお、rust側でwasmをビルドするためのセットアップは別途必要です。

rollup-plugin-rustを用いずwasm-bindgenを組込む方法もあります。ただし、現状かなり注意が必要な状況と言えます。

PostCSS

Sass, SCSSなどCSSを含めたビルドには、 rollup-plugin-postcssを使えます。
プラグインの該当部分の設定例は以下のとおりです。

import postcss from 'rollup-plugin-postcss';

export default {
    plugins: [
        postcss({
            extensions: ['.css', '.sass', '.scss'],
            inject: false,
        })
    ]
}

JSのコード例は以下のとおりです。形式はいくつか選べますが、一例として文字列形式でインポートする場合には、コンポーネントのstyleタグに指定します。
SCSSのインポートもプラグインによりCSSで置換されます。

import styles from './some.scss';

render() {
  return html`<div>
    <style>${styles}</style>
    <a class="button">Button</a>
    </div>`
}

ServiceWorker

ServiceWorkerも高度な機能で、書き方を間違えるとブラウザの挙動を破壊するので注意を要します。
ライブラリキャッシュ機能については、 Workboxがメジャーで rollup-plugin-workboxを利用すると手軽に組込めます。

Workboxは挙動が特殊で、プラグインの副作用を利用してServiceWorkerのファイルを生成します。利用できるモードがいくつかありますが、カスタムコードにキャッシュ機能を追加する用途のrollup.config.jsは以下のような記述になります。

const { injectManifest } = require('rollup-plugin-workbox');

export default {
    input: './src/dummy-entry.js',
    output: {
        file: '/dev/null',
    },
    plugins: [
        injectManifest({
            swSrc: 'src/serviceworker.js',
            swDest: 'dist/serviceworker.js',
            globDirectory: 'precache/',
            globPatterns: ['**/*.{js,css}'],
            mode: 'production'
        })
    ]
}
中馬崇尋
Chuma Takahiro