Open hysryt opened 5 years ago
JavaScript 用のモジュールバンドラー。 ES6のモジュールシステムを使用してコードを記述し、CommonJSモジュール、AMDモジュール、およびIIFEスタイルのスクリプトに変換できる。
npm
$ npm install -D rollup
yarn
$ yarn add -D rollup
ブラウザ用にコンパイル ※ IIFE - Immediately Invoked Function Expression; 即時実行関数式
$ npx rollup main.js --file bundle.js --format iife
Common JS モジュールにコンパイル
$ npx rollup main.js --file bundle.js --format cjs
AMDモジュールにコンパイル
$ npx rollup main.js --file bundle.js --format amd
全てで使えるスクリプトにコンパイル(UMDフォーマット)
$ npx rollup main.js --file bundle.js --format umd --name "myBundle"
インポートする際、使用しないコードは除外することができる。
たとえば、CommonJSでは、以下のようにツールまたはライブラリ全体をインポートする必要がある。
// utilモジュール全体をインポート
const utils = require( './utils' );
const query = 'Rollup';
// utilモジュールのajaxを使用
utils.ajax(`https://api.example.com?search=${query}`).then(handleResponse);
ES6ではモジュール内の必要な部分のみインポートすることができる。
// util から ajax のみインポート
import { ajax } from './utils';
const query = 'Rollup';
// ajax を使用
ajax(`https://api.example.com?search=${query}`).then(handleResponse);
rollup-plugin-commonjs プラグインを使うことで、CommonJS のモジュールをインポートすることができる。
Node.jsやwebpackでESモジュールを使用したい場合、rollup.js で一旦 UMD または CommonJS モジュールに変換すれば良い。
設定ファイルは CommonJS モジュールフォマットまたは ES モジュールフォーマットで記述する。 ファイル名は rollup.config.js。
// rollup.config.js
export default { // can be an array (for multiple inputs)
// core input options
input, // required
external,
plugins,
// advanced input options
onwarn,
perf,
// danger zone
acorn,
acornInjectPlugins,
treeshake,
context,
moduleContext,
// experimental
experimentalCodeSplitting,
manualChunks,
optimizeChunks,
chunkGroupingSize,
output: { // required (can be an array, for multiple outputs)
// core output options
format, // required
file,
dir,
name,
globals,
// advanced output options
paths,
banner,
footer,
intro,
outro,
sourcemap,
sourcemapFile,
sourcemapPathTransform,
interop,
extend,
// danger zone
exports,
amd,
indent,
strict,
freeze,
namespaceToStringTag,
// experimental
entryFileNames,
chunkFileNames,
assetFileNames
},
watch: {
chokidar,
include,
exclude,
clearScreen
}
};
入力ファイルが複数ある場合は配列で記述する。
// rollup.config.js (building more than one bundle)
export default [{
input: 'main-a.js',
output: {
file: 'dist/bundle-a.js',
format: 'cjs'
}
}, {
input: 'main-b.js',
output: [
{
file: 'dist/bundle-b1.js',
format: 'cjs'
},
{
file: 'dist/bundle-b2.js',
format: 'esm'
}
]
}];
設定を非同期で設定したい場合は Promise で設定することもできる。
// rollup.config.js
import fetch from 'node-fetch';
export default fetch('/some-remote-service-or-file-which-returns-actual-config');
Promise を配列にすることも可能
// rollup.config.js (Promise resolving an array)
export default Promise.all([
fetch('get-config-1'),
fetch('get-config-2')
])
以下のことをしたい場合は設定ファイルを使用する必要がある。
以下のようなことも可能
// rollup.config.js
import defaultConfig from './rollup.default.config.js';
import debugConfig from './rollup.debug.config.js';
export default commandLineArgs => {
if (commandLineArgs.configDebug === true) {
return debugConfig;
}
return defaultConfig;
}
rollup --config --configDebugを実行すると、デバッグ設定が使用されます。
-c, --config Use this config file (if argument is used but value
is unspecified, defaults to rollup.config.js)
-i, --input Input (alternative to <entry file>)
-o, --file <output> Output (if absent, prints to stdout)
-f, --format [esm] Type of output (amd, cjs, esm, iife, umd)
-e, --external Comma-separate list of module IDs to exclude
-g, --globals Comma-separate list of `module ID:Global` pairs
Any module IDs defined here are added to external
-n, --name Name for UMD export
-m, --sourcemap Generate sourcemap (`-m inline` for inline map)
--amd.id ID for AMD module (default is anonymous)
--amd.define Function to use in place of `define`
--no-strict Don't emit a `"use strict";` in the generated modules.
--no-indent Don't indent result
--environment <values> Environment variables passed to config file
--noConflict Generate a noConflict method for UMD globals
--no-treeshake Disable tree-shaking
--intro Content to insert at top of bundle (inside wrapper)
--outro Content to insert at end of bundle (inside wrapper)
--banner Content to insert at top of bundle (outside wrapper)
--footer Content to insert at end of bundle (outside wrapper)
--no-interop Do not include interop block
ES2015 のモジュールについての概説
インポートされたオブジェクト、配列の中身は変更できるが、インポートした値は変更できない。 const と同様となる。
モジュールから特定の部分のみインポートする。
import { something } from './module.js';
モジュールから特定の部分をインポートし独自の名前をつける。
import { something as somethingElse } from './module.js';
モジュールをオブジェクトとしてインポートする。デフォルトエクスポートは除外される。
import * as module from './module.js';
モジュールに something
という関数やプロパティがある場合は module.something
でアクセスする。
モジュールのデフォルトエクスポートをインポートする。
import something from './module.js';
モジュールのコードを読み込むが、新しいオブジェクトは生成しない。
import './module.js';
import を関数として使用することで動的にインポートする
import('./modules.js').then(({ default: DefaultExport, NamedExport })=> {
// do something with modules.
})
宣言した変数をエクスポートする。
const something = true;
export { something };
別名でエクスポートする。
export { something as somethingElse };
宣言と同時にエクスポートする。
// this works with `var`, `let`, `const`, `class`, and `function`
export const something = true;
一つの値をモジュールのデフォルトとしてエクスポートする。
export default something;
モジュールが1つしかエクスポートしない場合に推奨される方法。 デフォルトエクスポートと名前付きエクスポートを混在させるのはあまり良くない。
ESモジュールはライブバインディングであるため、インポートした後でも変更される可能性がある。
// incrementer.js
export let count = 0;
export function increment() {
count += 1;
}
// main.js
import { count, increment } from './incrementer.js';
console.log(count); // 0
increment();
console.log(count); // 1
count += 1; // Error — only incrementer.js can change this
チュートリアルを始める前に、Node.js をインストールして NPM を使用可能な状態にする必要がある。コマンドラインの使用方法も押さえておくこと。
Rollup を使う場合、CLIから使うのが最も簡単な方法。グローバルでインストールしてみよう。(ローカルにインストールする方法は後述する。)
npm install rollup --global
# npm i rollup -g でも可
これで rollup
コマンドが使用可能となる。
rollup
引数を与えてないため、Rollupの使用方法が出力される。これは rollup --help
や rollup -h
の動作と同じ。
簡単なプロジェクトを作ってみよう。
mkdir -p my-rollup-project/src
cd my-rollup-project
まずはエントリーポイントが必要となる。src/main.js
を作って以下を貼り付ける。
// src/main.js
import foo from './foo.js';
export default function () {
console.log(foo);
}
エントリーポイントからインポートされている foo.js
モジュールも作成する。
// src/foo.js
export default 'hello world!';
これで準備完了。
rollup src/main.js -f cjs
-f
オプション(--format
の略)には作成するバンドルの種類を指定する。この場合だと CommonJS となる。出力ファイルを指定してないので、stdout
にそのまま出力される。
'use strict';
const foo = 'hello world!';
const main = function () {
console.log(foo);
};
module.exports = main;
以下のようにすればファイルに保存できる。
rollup src/main.js -o bundle.js -f cjs
(rollup src/main.js -f cjs > bundle.js
でもいいが、ソースマップを生成したい場合は柔軟性が損なわれる。)
コードを実行してみよう。
node
> var myBundle = require('./bundle.js');
> myBundle();
'hello world!'
おめでとう。初めてのモジュールが完成した。
今の所はまだいいが、オプションが増えるにつれて毎回コマンドを打つのが面倒になる。
同じことの繰り返しを避けるため、必要なオプションをまとめた設定ファイルを作成できる。設定ファイルは JavaScript で記述し、CLI より柔軟性がある。
プロジェクトルートに rollup.config.js
を作って以下の内容を追加する。
// rollup.config.js
export default {
input: 'src/main.js',
output: {
file: 'bundle.js',
format: 'cjs'
}
};
(module.exports = {/* config */}
のように CommonJS 形式で書くことも可能。)
設定ファイルを使用するには --config
または -c
フラグを使用する。
rm bundle.js
rollup -c
CLI からオプションを指定すれば設定ファイルの内容を上書きできる。
rollup -c -o bundle-2.js # `-o` は `--file` と同じ
余談:設定ファイルは Babel などを通さず Rollup 自体から実行されるため、Node.js でサポートされている機能しか使用出来ない。
rollup.config.js
以外のファイル名にすることも可能。
rollup --config rollup.config.dev.js
rollup --config rollup.config.prod.js
これまでのところ、エントリーポイントと相対パスでインポートしたモジュールから簡単なバンドルを生成した。さらに複雑なバンドルを生成する場合、NPM でインストールしたモジュールのインポート、Babel でのコンパイル、JSON ファイルの使用など、さらなる柔軟性が必要となるだろう。
そのために、プラグインを使用し、Rollupのビルドプロセスの中の要所要所での振る舞いを変更する。素晴らしいプラグインの数々は the Rollup Awesome List で見ることができる。
このチュートリアルでは rollup-plugin-json を使用し、JSON ファイルからデータをインポートできるようにする。
プロジェクトルートに package.json
を作成し、以下の内容を追加する。
{
"name": "rollup-tutorial",
"version": "1.0.0",
"scripts": {
"build": "rollup -c"
}
}
rollup-plugin-json を開発時の依存関係としてインストールする。
npm install --save-dev rollup-plugin-json
(実行時ではなくビルド時にプラグインに依存するため、ここでは --save
ではなく --save-dev
を使用する。)
src/main.js
を更新して、src/foo.js
ではなく package.json
からインポートする。
// src/main.js
import { version } from '../package.json';
export default function () {
console.log('version ' + version);
}
rollup.config.js
を JSON プラグインを含めるよう編集する。
// rollup.config.js
import json from 'rollup-plugin-json';
export default {
input: 'src/main.js',
output: {
file: 'bundle.js',
format: 'cjs'
},
plugins: [ json() ]
};
npm run build
で Rollup を実行する。結果は以下のようになる。
'use strict';
const version = "1.0.0";
const main = function () {
console.log('version ' + version);
};
module.exports = main;
必要なデータのみがインポートされ、name
や devDependencies
といった項目は無視される。これは tree-shaking によるもの。
新しく追加された試験的なコード分割機能を使用するには、main.js を動的に読み込む2つ目のエントリーポイント(src/main2.js
)を作成する。
// src/main2.js
export default function () {
return import('./main.js').then(({ default: main }) => {
main();
});
}
2つのエントリポイントを渡し、出力先にはファイルではなく --dir
オプションでディレクトリを指定する。(試験オプションも追加する。)
rollup src/main.js src/main2.js -f cjs --dir dist --experimentalCodeSplitting
2つのエントリポイントはコードを複製することなく、どちらも Node.js で実行できる。
node -e "require('./dist/main2.js')()"
ブラウザ用、ESモジュール、AMDローダー用、SystemJS用にビルドすることもできる。
ネイティブモジュールの場合は -f esm
をつけて、
rollup src/main.js src/main2.js -f esm --dir dist --experimentalCodeSplitting
<!doctype html>
<script type="module">
import main2 from './dist/main2.js';
main2();
</script>
SystemJS 用には -f system
をつけて、
rollup src/main.js src/main2.js -f system --dir dist --experimentalCodeSplitting
SystemJS をインストール
npm install --save-dev systemjs
どちらかまたは両方をHTMLから読み込み
<!doctype html>
<script src="node_modules/systemjs/dist/system-production.js"></script>
<script>
System.import('./dist/main2.js')
.then(({ default: main }) => main());
</script>
Rollup プラグインは関数をエクスポートするパッケージであり、その関数は1つ以上のプロパティやフックを持つオブジェクトを返す。
プラグインによって、バインディング前のコードのトランスパイルや node_modules
からのサードパーティー製モジュールの検索など、Rollupの振る舞いを変更することができる。
下記のプラグインは virtual-module
のインポートを横取りする。これは例えばブラウザで Rollup を使いたい場合に必要になる。例のようにエントリポイントを置き換えることもできる。
// rollup-plugin-my-example.js
export default function myExample () {
return {
name: 'my-example', // this name will show up in warnings and errors
resolveId ( importee ) {
if (importee === 'virtual-module') {
return importee; // this signals that rollup should not ask other plugins or check the file system to find this id
}
return null; // other ids should be handled as usually
},
load ( id ) {
if (id === 'virtual-module') {
return 'export default "This is virtual!"'; // the source code for "virtual-module"
}
return null; // other ids should be handled as usually
}
};
}
// rollup.config.js
import myExample from './rollup-plugin-my-example.js';
export default ({
input: 'virtual-module', // resolved by our plugin
plugins: [myExample()],
output: [{
file: 'bundle.js',
format: 'esm'
}]
});
rollup-plugin-
で始め、明快な名前であるべきpackage.json
に rollup-plugin
キーワードを含める\0
をつける(?)型:String プラグイン名。警告やエラーメッセージの中で使用される。
型:String または Function 文字列か、関数の場合は文字列かPromiseを返す関数。
型:Function
シグネチャ:( inputOptions ) => options
rollup.rollup
に渡されたオプションを操作する関数。nullを返した場合は何もしない。
型:Function
シグネチャ:( error ) => (void|Promise)
バンドルの終了後、generate
や write
の前に呼び出される。Promise を返すことも可能。バンドル時にエラーがあった場合は引数で渡される。
型:Function
シグネチャ:( ) => (void|Promise)
rollup.rollup
のビルド時に呼び出される。
型:String または Function 文字列か、関数の場合は文字列かPromiseを返す関数。
型:Function
シグネチャ:( outputOptions, bundle, isWrite ) => (void|Promise)
bundle.generate()
または bundle.write()
の終了後に呼び出される。bundle
で生成されたファイルの一覧と詳細が渡される。
型:String または Function 文字列か、関数の場合は文字列かPromiseを返す関数。
型:Function
シグネチャ:( id ) => (code | { code, map } | Promise)
カスタムローダーを定義する。他のローダーに譲る場合は null を返す。(最終的にはデフォルトの動作となる。)
型:String または Function 文字列か、関数の場合は文字列かPromiseを返す関数。
型:Function
シグネチャ:(code, { modules, exports, imports, fileName, isEntry }, outputOptions) => (code | { code, map} | Promise)
ここのチャンクを変換するのに使われる。各Rollup出力チャンクファイルごとに呼び出される。null を返した場合は何も起きない。
型:Function
シグネチャ:( error ) => void
bundle.generate()
または bundle.wite()
の途中でエラーが発生した時に呼び出される。バンドル成功じのフックを設定したい場合は generateBundle
の方を使う。
型:Function
シグネチャ:( ) => (void|Promise)
bundle.generate()
または bundle.write()
が呼び出されるたびに呼び出される。生成の完了を受け取りたい場合は generateBundle
または renderError
を使用する。
型:Function
シグネチャ:( importee, importer ) => (id|Promise)
カスタムリゾルバを定義する。サードパーティの依存を検索するときなどに便利。null
や undefined
を返した場合は他の resolveId
に処理を投げる。false を返した場合、バンドル対象としない。
型:Function
シグネチャ:( source, id ) => (code|{ code, map }|Promise)
各モジュールの変換に使用される。
型:Function
シグネチャ:(file) => { }
rollup 実行じに --watch
が指定されているとき、ファイルの変更を検知すると呼び出される。
フックから this でアクセスできる関数およびプロパティ
指定されたファイルをアセットとしてエミットし、アセットIDを返す。エミットされたアセットはバンドルファイルに含まれるようになる。アセットのソースは this.setAssetSource(assetId)
を使って後から設定することも可能。アセットにソースを設定していない場合、バンドル生成完了エラーが発生する。
エラーを発生させ、バンドル処理を中断する。
アセットのファイル名を取得する。
与えられたモジュールID が外部のものかどうかを判断する。
Rollup のメタデータ。this.meta.rollupVersion
など。
Rollup の acorn インスタンスを使用して、コードを AST にパースする。
モジュールIDを解決する。
アセットのソースを設定する。
ビルド時の警告をキューに追加する。ここで追加した警告はCLIで出力される。
引数のworning
には文字列か、message
プロパティを持ったオブジェクトを渡す。
this.warn( 'hmm...' );
// 上と下は同じ
this.warn({ message: 'hmm...' });
警告に付加プロパティを追加したい場合はオブジェクトで渡す。Rollup によってmessage
の他に plugin
、code
(PLUGIN_WARNING) 、id
を追加される。
引数の position
には警告が発生した場所を渡す。何も渡さなかった場合は Rollup が自動的に追加する?
JavaScript 内からアセットのURLを参照する場合は、import.meta.ROLLUP_ASSET_URL_[assetId]
を使用する。
次の例では、モジュールのCSSファイルを出力し、ターゲットの実行時環境から出力されたファイルを正しく指すように構築されたURLをエクスポートする。
load (id) {
const assetId = this.emitAsset('style.css', fs.readFileSync(path.resolve(assets, 'style.css')));
return `export default import.meta.ROLLUP_ASSET_URL_${assetId}`;
}
load
フックは任意で { code, ast }
を返しても良い。ast
は各ノードの開始プロパティと終了プロパティを持つ標準のESTree ASTでなければならない。
変換プラグインは options.include
と options.exclude
をサポートすべき。
import { createFilter } from 'rollup-pluginutils';
export default function myPlugin ( options = {} ) {
const filter = createFilter( options.include, options.exclude );
return {
transform ( code, id ) {
if ( !filter( id ) ) return;
// proceed with the transformation...
return {
code: generatedCode,
map: generatedSourceMap
};
}
};
}
コードを変換する場合は sourceMap: false
が与えられない限り、ソースマップも生成するべき。ソースマップを作らない場合は空文字を返す。
return {
code: transformedCode,
map: { mappings: '' }
};
コードを変更しない場合は null を返す。
return {
code: transformedCode,
map: null
};
rollup.config.js
は Node.js 上で実行されるため、 node_modules
下のモジュールをインポートすることができる。
https://rollupjs.org https://github.com/rollup/rollup