hysryt / wiki

https://hysryt.github.io/wiki/
0 stars 0 forks source link

rollup.js #93

Open hysryt opened 5 years ago

hysryt commented 5 years ago

https://rollupjs.org https://github.com/rollup/rollup

hysryt commented 5 years ago

JavaScript 用のモジュールバンドラー。 ES6のモジュールシステムを使用してコードを記述し、CommonJSモジュール、AMDモジュール、およびIIFEスタイルのスクリプトに変換できる。

hysryt commented 5 years ago

インストール

npm

$ npm install -D rollup

yarn

$ yarn add -D rollup
hysryt commented 5 years ago

コンパイル

ブラウザ用にコンパイル ※ 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"
hysryt commented 5 years ago

Tree-Shaking

インポートする際、使用しないコードは除外することができる。

たとえば、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);
hysryt commented 5 years ago

互換性

CommonJS のインポート

rollup-plugin-commonjs プラグインを使うことで、CommonJS のモジュールをインポートすることができる。

ESモジュールの使用

Node.jsやwebpackでESモジュールを使用したい場合、rollup.js で一旦 UMD または CommonJS モジュールに変換すれば良い。

hysryt commented 5 years ago

設定ファイル

設定ファイルは 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を実行すると、デバッグ設定が使用されます。

hysryt commented 5 years ago

コマンドラインフラグ

-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
hysryt commented 5 years ago

ES モジュール構文

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
hysryt commented 5 years ago

チュートリアル

チュートリアルを始める前に、Node.js をインストールして NPM を使用可能な状態にする必要がある。コマンドラインの使用方法も押さえておくこと。

Rollup を使う場合、CLIから使うのが最も簡単な方法。グローバルでインストールしてみよう。(ローカルにインストールする方法は後述する。)

npm install rollup --global
# npm i rollup -g でも可

これで rollup コマンドが使用可能となる。

rollup

引数を与えてないため、Rollupの使用方法が出力される。これは rollup --helprollup -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;

必要なデータのみがインポートされ、namedevDependencies といった項目は無視される。これは 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>
hysryt commented 5 years ago

プラグイン

概要

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'
  }]
});

慣例

プロパティ

name

型:String プラグイン名。警告やエラーメッセージの中で使用される。

banner

型:String または Function 文字列か、関数の場合は文字列かPromiseを返す関数。

options

型:Function シグネチャ:( inputOptions ) => options rollup.rollup に渡されたオプションを操作する関数。nullを返した場合は何もしない。

フック

buildEnd

型:Function シグネチャ:( error ) => (void|Promise) バンドルの終了後、generatewrite の前に呼び出される。Promise を返すことも可能。バンドル時にエラーがあった場合は引数で渡される。

buildStart

型:Function シグネチャ:( ) => (void|Promise) rollup.rollup のビルド時に呼び出される。

footer

型:String または Function 文字列か、関数の場合は文字列かPromiseを返す関数。

generateBundle

型:Function シグネチャ:( outputOptions, bundle, isWrite ) => (void|Promise) bundle.generate() または bundle.write() の終了後に呼び出される。bundle で生成されたファイルの一覧と詳細が渡される。

intro

型:String または Function 文字列か、関数の場合は文字列かPromiseを返す関数。

load

型:Function シグネチャ:( id ) => (code | { code, map } | Promise) カスタムローダーを定義する。他のローダーに譲る場合は null を返す。(最終的にはデフォルトの動作となる。)

outro

型:String または Function 文字列か、関数の場合は文字列かPromiseを返す関数。

renderChunk

型:Function シグネチャ:(code, { modules, exports, imports, fileName, isEntry }, outputOptions) => (code | { code, map} | Promise) ここのチャンクを変換するのに使われる。各Rollup出力チャンクファイルごとに呼び出される。null を返した場合は何も起きない。

renderError

型:Function シグネチャ:( error ) => void bundle.generate() または bundle.wite() の途中でエラーが発生した時に呼び出される。バンドル成功じのフックを設定したい場合は generateBundle の方を使う。

renderStart

型:Function シグネチャ:( ) => (void|Promise) bundle.generate() または bundle.write() が呼び出されるたびに呼び出される。生成の完了を受け取りたい場合は generateBundle または renderError を使用する。

resolveId

型:Function シグネチャ:( importee, importer ) => (id|Promise) カスタムリゾルバを定義する。サードパーティの依存を検索するときなどに便利。nullundefined を返した場合は他の resolveId に処理を投げる。false を返した場合、バンドル対象としない。

transform

型:Function シグネチャ:( source, id ) => (code|{ code, map }|Promise) 各モジュールの変換に使用される。

watchChange

型:Function シグネチャ:(file) => { } rollup 実行じに --watch が指定されているとき、ファイルの変更を検知すると呼び出される。

コンテキスト

フックから this でアクセスできる関数およびプロパティ

this.emitAsset( assetName, source )

指定されたファイルをアセットとしてエミットし、アセットIDを返す。エミットされたアセットはバンドルファイルに含まれるようになる。アセットのソースは this.setAssetSource(assetId) を使って後から設定することも可能。アセットにソースを設定していない場合、バンドル生成完了エラーが発生する。

this.error( error, [, position] )

エラーを発生させ、バンドル処理を中断する。

this.getAssetFileName( assetId )

アセットのファイル名を取得する。

this.isExternal( id, parentId, isResolved )

与えられたモジュールID が外部のものかどうかを判断する。

this.meta

Rollup のメタデータ。this.meta.rollupVersion など。

this.parse( code, acornOptions )

Rollup の acorn インスタンスを使用して、コードを AST にパースする。

this.resolveId( importee, importer )

モジュールIDを解決する。

this.setAssetSource( assetId, source )

アセットのソースを設定する。

this.warn( warning [, position] )

ビルド時の警告をキューに追加する。ここで追加した警告はCLIで出力される。 引数のworning には文字列か、message プロパティを持ったオブジェクトを渡す。

this.warn( 'hmm...' );
// 上と下は同じ
this.warn({ message: 'hmm...' });

警告に付加プロパティを追加したい場合はオブジェクトで渡す。Rollup によってmessage の他に plugincode(PLUGIN_WARNING) 、id を追加される。

引数の position には警告が発生した場所を渡す。何も渡さなかった場合は Rollup が自動的に追加する?

アセットURL

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.includeoptions.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
};
hysryt commented 5 years ago

rollup.config.js

rollup.config.js は Node.js 上で実行されるため、 node_modules 下のモジュールをインポートすることができる。