Rollup で CSS や JSON を読み込むプラグイン活用法

Rollup は JavaScript のモジュールバンドラですが、実際のプロジェクトでは CSS や JSON といった非 JavaScript ファイルも扱う必要があります。Rollup 単体ではこれらのファイルを処理できませんが、プラグインを追加することで import 文ひとつで読み込めるようになります。

なぜプラグインが必要なのか

Rollup のコアは ESM の JavaScript だけを理解します。たとえば次のようなコードを書いても、Rollup はそのままでは CSS ファイルの中身をどう扱えばよいかわかりません。

import './styles.css';
import config from './config.json';

ここで Rollup のプラグインシステムが役立ちます。プラグインはビルドパイプラインの各段階にフックし、非 JavaScript ファイルを Rollup が理解できる形に変換する役割を担います。

JSON を読み込む:@rollup/plugin-json

JSON の読み込みは最もシンプルなプラグイン活用例です。@rollup/plugin-json を導入すると、JSON ファイルを ESM のオブジェクトとして直接 import できるようになります。

まずプラグインをインストールします。

npm install --save-dev @rollup/plugin-json

rollup.config.js に追加します。

import json from '@rollup/plugin-json';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  },
  plugins: [json()]
};

これだけで、アプリケーションコードから JSON を直接読み込めます。

import pkg from '../package.json';

console.log(`${pkg.name} v${pkg.version}`);

このプラグインが優れている点は、JSON の内容を ESM の named export に変換してくれるところです。つまり、必要なフィールドだけを取り出す書き方ができます。

import { name, version } from '../package.json';

console.log(`${name} v${version}`);

この named export への変換により、Rollup のTree Shakingが JSON にも効くようになります。巨大な JSON ファイルでも、使われていないフィールドはバンドルから除外されます。

未使用のエクスポートをバンドルから自動的に除去する仕組み。

CSS を読み込む:rollup-plugin-postcss

CSS の読み込みには rollup-plugin-postcss がよく使われます。このプラグインは PostCSS をベースにしており、CSS のバンドル、抽出、CSS Modules、SCSS や Less のプリプロセスまで対応しています。

npm install --save-dev rollup-plugin-postcss

基本的な設定は次のとおりです。

import postcss from 'rollup-plugin-postcss';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  },
  plugins: [postcss()]
};

デフォルトでは、import した CSS は JavaScript を通じて <style> タグとしてページに注入されます。

import './button.css';

const btn = document.createElement('button');
btn.className = 'btn-primary';
document.body.appendChild(btn);

CSS を外部ファイルとして抽出する

<style> タグによる注入ではなく、独立した CSS ファイルとして出力したい場面も多いでしょう。extract オプションを使えば、バンドルされた CSS を別ファイルに書き出せます。

import postcss from 'rollup-plugin-postcss';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  },
  plugins: [
    postcss({
      extract: 'styles.css'
    })
  ]
};

この設定で dist/styles.css が生成されます。HTML 側で <link> タグを使って読み込む従来の方式に適しており、CSS のキャッシュ戦略を独立して管理できるメリットがあります。

inject 方式(デフォルト)

CSS を JavaScript に埋め込み、実行時に style タグで注入する。ファイル数が減り配信がシンプルになるが、JavaScript が実行されるまでスタイルが適用されない。

extract 方式

CSS を別ファイルとして出力する。ブラウザが HTML のパース中に CSS を並行して読み込めるため、初期描画のパフォーマンスが向上する。

CSS Modules を使う

コンポーネント単位でスタイルのスコープを閉じたい場合、CSS Modules が有効です。rollup-plugin-postcss は modules オプションで対応しています。

import postcss from 'rollup-plugin-postcss';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  },
  plugins: [
    postcss({
      modules: true
    })
  ]
};

CSS Modules を有効にすると、import した CSS のクラス名がオブジェクトのプロパティとして取得できます。

/* button.module.css */
.primary {
  background: #3498db;
  color: white;
  padding: 8px 16px;
}
import styles from './button.module.css';

const btn = document.createElement('button');
btn.className = styles.primary;
// 実際のクラス名は "button_primary_x7k2a" のようにハッシュ付きになる

クラス名が自動でユニーク化されるため、グローバルな名前衝突を気にする必要がなくなります。

プラグインの実行順序を理解する

Rollup のプラグインは plugins 配列に並べた順に実行されます。複数のプラグインを組み合わせるとき、この順序が重要になる場面があります。

import resolve from '@rollup/plugin-node-resolve';
import json from '@rollup/plugin-json';
import postcss from 'rollup-plugin-postcss';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  },
  plugins: [
    resolve(),
    json(),
    postcss({ extract: 'styles.css' })
  ]
};
resolve を先頭に置く理由

node_modules 内のパッケージを解決する @rollup/plugin-node-resolve は、他のプラグインがファイルを変換する前にモジュールの場所を特定する必要があるため、先頭付近に配置します。

変換系プラグインの順序

json や postcss のような変換系プラグインは、それぞれ独立したファイルタイプを扱うため順序の依存は少ないものの、TypeScript のトランスパイル(@rollup/plugin-typescript)がある場合はそれを json より前に置くのが一般的です。

プラグインの仕組みを覗いてみる

Rollup プラグインが内部でどう動いているかを簡単に見てみましょう。プラグインは特定のフック関数を持つオブジェクトを返す仕組みになっています。

// 最小限の JSON プラグインのイメージ
function simpleJson() {
  return {
    name: 'simple-json',

    transform(code, id) {
      if (!id.endsWith('.json')) return null;

      const parsed = JSON.parse(code);
      const esm = `export default ${JSON.stringify(parsed)};`;
      return { code: esm, map: null };
    }
  };
}

transform フックはファイルの中身(code)とファイルパス(id)を受け取り、JavaScript として有効なコードに変換して返します。JSON ファイルが import されると、このフックが呼ばれ、中身を export default {...} という ESM に変換するわけです。実際の @rollup/plugin-json はこれに加えて named export の生成や Tree Shaking 対応を行っていますが、基本原理はこのとおりです。

非 JS ファイルの import を Rollup が検出する

該当するプラグインの transform フックが呼ばれる

プラグインがファイル内容を有効な JavaScript に変換して返す

Rollup が変換後のコードを通常のモジュールとしてバンドルに組み込む

このように、Rollup のプラグインシステムは非常にシンプルな規約で成り立っています。CSS であれ JSON であれ、最終的に JavaScript の export 文に変換できればよいという設計思想が、多様なファイル形式への対応を可能にしています。公式の @rollup/plugin-* シリーズとコミュニティ製プラグインを組み合わせることで、Rollup は軽量なコアのまま幅広いユースケースに対応できるバンドラとなっています。