blog.bouzuya.net

2020-06-27 rollup の rollup-starter-lib を試した

rollup の rollup-starter-lib を試した。

https://github.com/rollup/rollup-starter-lib の README の Getting started に従う。

$ git clone https://github.com/rollup/rollup-starter-lib
# ...
$ cd rollup-starter-lib
$ npm install
# ...
$ exa -T --git-ignore --group-directories-first
.
├── src
│  ├── lunchtime.js
│  ├── main.js
│  └── millisecondsUntil.js
├── test
│  └── test.js
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
└── rollup.config.js

package.json

まずは package.json を見る

{
  "name": "rollup-starter-lib",
  "version": "1.0.0",
  "main": "dist/how-long-till-lunch.cjs.js",
  "module": "dist/how-long-till-lunch.esm.js",
  "browser": "dist/how-long-till-lunch.umd.js",
  "dependencies": {
    "ms": "^2.0.0"
  },
  "devDependencies": {
    "@rollup/plugin-commonjs": "^11.0.1",
    "@rollup/plugin-node-resolve": "^7.0.0",
    "rollup": "^1.29.0"
  },
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "test": "node test/test.js",
    "pretest": "npm run build"
  },
  "files": ["dist"]
}

main / module / browser でエントリポイントを書き分けてあるのが印象的だ。各環境に一気に対応できるのだろう。あとで dist に生成されるものを確認する。

dependenciesms は用意されている例 src/main.js から使われているようだ。

devDependencies@rollup/plugin-* が気になるけど後回しにする。

scriptsrollup -c を何かしら動かすっぽい。 rollup -c -w-w はたぶん watch だろう。

$ (npm bin)/rollup --help

rollup version 1.29.0
=====================================

Usage: rollup [options] <entry file>

Basic options:

-c, --config <filename>     Use this config file (if argument is used but value
                              is unspecified, defaults to rollup.config.js)
-d, --dir <dirname>         Directory for chunks (if absent, prints to stdout)
-e, --external <ids>        Comma-separate list of module IDs to exclude
-f, --format <format>       Type of output (amd, cjs, esm, iife, umd)
-g, --globals <pairs>       Comma-separate list of `moduleID:Global` pairs
-h, --help                  Show this help message
-i, --input <filename>      Input (alternative to <entry file>)
-m, --sourcemap             Generate sourcemap (`-m inline` for inline map)
-n, --name <name>           Name for UMD export
-o, --file <output>         Single output file (if absent, prints to stdout)
-v, --version               Show version number
-w, --watch                 Watch files in bundle and rebuild on changes
--amd.id <id>               ID for AMD module (default is anonymous)
--amd.define <name>         Function to use in place of `define`
--assetFileNames <pattern>  Name pattern for emitted assets
--banner <text>             Code to insert at top of bundle (outside wrapper)
--chunkFileNames <pattern>  Name pattern for emitted secondary chunks
--compact                   Minify wrapper code
--context <variable>        Specify top-level `this` value
--dynamicImportFunction <name>         Rename the dynamic `import()` function
--entryFileNames <pattern>  Name pattern for emitted entry chunks
--environment <values>      Settings passed to config file (see example)
--no-esModule               Do not add __esModule property
--exports <mode>            Specify export mode (auto, default, named, none)
--extend                    Extend global variable defined by --name
--footer <text>             Code to insert at end of bundle (outside wrapper)
--no-freeze                 Do not freeze namespace objects
--no-indent                 Don't indent result
--no-interop                Do not include interop block
--inlineDynamicImports      Create single bundle when using dynamic imports
--intro <text>              Code to insert at top of bundle (inside wrapper)
--namespaceToStringTag      Create proper `.toString` methods for namespaces
--noConflict                Generate a noConflict method for UMD globals
--no-strict                 Don't emit `"use strict";` in the generated modules
--outro <text>              Code to insert at end of bundle (inside wrapper)
--preferConst               Use `const` instead of `var` for exports
--preserveModules           Preserve module structure
--preserveSymlinks          Do not follow symlinks when resolving files
--shimMissingExports        Create shim variables for missing exports
--silent                    Don't print warnings
--sourcemapExcludeSources   Do not include source code in source maps
--sourcemapFile <file>      Specify bundle position for source maps
--strictDeprecations        Throw errors for deprecated features
--no-treeshake              Disable tree-shaking optimisations
--no-treeshake.annotations  Ignore pure call annotations
--no-treeshake.propertyReadSideEffects Ignore property access side-effects
--treeshake.pureExternalModules        Assume side-effect free externals

Examples:

# use settings in config file
rollup -c

# in config file, process.env.INCLUDE_DEPS === 'true'
# and process.env.BUILD === 'production'
rollup -c --environment INCLUDE_DEPS,BUILD:production

# create CommonJS bundle.js from src/main.js
rollup --format=cjs --file=bundle.js -- src/main.js

# create self-executing IIFE using `window.jQuery`
# and `window._` as external globals
rollup -f iife --globals jquery:jQuery,lodash:_ \
  -i src/app.js -o build/app.js -m build/app.js.map

Notes:

* When piping to stdout, only inline sourcemaps are permitted

For more information visit https://rollupjs.org

-w--watch で正解だった。 -c--config rollup.config.js のことだった。

rollup.config.js

rollup.config.js を見る。

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import pkg from "./package.json";

export default [
  // browser-friendly UMD build
  {
    input: "src/main.js",
    output: {
      name: "howLongUntilLunch",
      file: pkg.browser,
      format: "umd",
    },
    plugins: [
      resolve(), // so Rollup can find `ms`
      commonjs(), // so Rollup can convert `ms` to an ES module
    ],
  },

  // CommonJS (for Node) and ES module (for bundlers) build.
  // (We could have three entries in the configuration array
  // instead of two, but it's quicker to generate multiple
  // builds from a single configuration where possible, using
  // an array for the `output` option, where we can specify
  // `file` and `format` for each target)
  {
    input: "src/main.js",
    external: ["ms"],
    output: [
      { file: pkg.main, format: "cjs" },
      { file: pkg.module, format: "es" },
    ],
  },
];

export default [ /* ... */ ]; で 2 つの要素が指定されている。

1 つめの要素は UMD ビルド。 src/main.js を入力にして package.jsonbrowser で指定された位置に umd として出力する。プラグインとして @rollup/plugin-node-resolve@rollup/plugin-commonjs を使用している。プラグインは素朴に import して関数を呼び出す形で適用するようだ。 node-resolve はおそらく TypeScript の moduleResolution: node 相当の動作を実現するもので commonjs は CJS で書かれた ms パッケージを ESM に変換するためのものだろう。

2 つめの要素は CJS および ESM ビルド。先のものとは出力先やプラグインなどが異なる。そういう基準で要素を分割しているのだろう。src/main.js を読んで package.jsonmain / module にそれぞれ CJS と ESM で出力する。さっきのもそうだが package.json の記述を流用しているのが良い。 external はバンドル対象から外して外部のままにしておくのだろう。

ドキュメントを読んでいないので推測だけどわりと雰囲気で読める設定だ。

npm run build

README.md の手順に戻って npm run build しよう。 rollup --config rollup.config.js 相当だ。

$ npm run build
# ...

src/main.js → dist/how-long-till-lunch.umd.js...
created dist/how-long-till-lunch.umd.js in 46ms

src/main.js → dist/how-long-till-lunch.cjs.js, dist/how-long-till-lunch.esm.js...
created dist/how-long-till-lunch.cjs.js, dist/how-long-till-lunch.esm.js in 6ms

$ exa -T dist/
dist
├── how-long-till-lunch.cjs.js
├── how-long-till-lunch.esm.js
└── how-long-till-lunch.umd.js

dist/ に 3 ファイルが生成されている。

ざっと中身を確認したあともとのファイルである src/ を見る。

$ exa -T src/
src
├── lunchtime.js
├── main.js
└── millisecondsUntil.js

各環境向けに 3 ファイルを 1 つにバンドルしているようだ。

src/main.js からの ↓ で参照している箇所が消えている。

import lunchtime from "./lunchtime.js";
import millisecondsUntil from "./millisecondsUntil.js";

rollup.config.js の指定どおりではあるが dist/*.cjs.jsdist/*.esm.js には ms が外部モジュールを参照する形で残っている。 dist/*.umd.js では ms もバンドルされている。

なるほど。

umd の設定を変えてみる

rollup.config.js の UMD の設定を変えてみる。

  // ...
  {
    input: "src/main.js",
    external: ["ms"], // external を追加
    output: {
      name: "howLongUntilLunch",
      file: pkg.browser,
      format: "umd",
      globals: { // output.globals を追加
        ms: "globalMs",
      },
    },
    // plugins を削除
  }
  // ...

最初 external: ["ms"] だけを追加して npm run build したら ↓ のように output.globals を指定しろと言われた。

src/main.js → dist/how-long-till-lunch.umd.js...
(!) Missing global variable name
Use output.globals to specify browser global variable names corresponding to external modules
ms (guessing 'ms')
created dist/how-long-till-lunch.umd.js in 27ms

予想通りバンドル対象から ms が外れて代わりに global.globalMs が参照されるようになった。

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('ms')) :
  typeof define === 'function' && define.amd ? define(['ms'], factory) :
  (global = global || self, global.howLongUntilLunch = factory(global.globalMs));
}(this, (function (ms) { 'use strict';
// ...

npm run dev & npm test

npm run build (rollup -c) を watch して動かすだけなので特に書くことはなさそうだ。

npm test も特に固有の要素はなさそうなので飛ばす。

TypeScript

README に https://github.com/rollup/rollup-starter-lib/tree/typescript に TypeScript の例もあるっぽいので試してみる。

違いを挙げる。

  • package.jsondevDependenciesrollup-plugin-typescriptts-nodetslibtypescript が追加された。
    • ts-nodenpm test のためのものっぽい。
  • rollup.config.js の各ビルドの pluginstypescript() が追加された。
  • tsconfig.json が追加された。
  • 拡張子が js -> ts に変更された。

うん。良さそうだ。


リングフィットアドベンチャー 10 日目 レベル 30 ワールド 4 を終えた。


ABC172 死。