ブラウザとnode.jsに両対応したライブラリを作りたいときのWebpackレシピ集


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170905/e599e964-731a-e569-d2b8-125a2a4449cc.png)

# 概要

- Webpackを使って**Node.jsとブラウザ**に両対応した**ライブラリ**を作るときのレシピ集です

- いろいろなレシピを見てWebpackによるライブラリ生成の挙動を理解することを目的としています

## ブラウザにもNode.jsにも両対応したライブラリを作りたい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170905/a3c650bd-ae1a-7932-a8f3-921786c82399.png)

ブラウザとNode.jsに両対応したいとおもったとき、
両方同時に対応した1つのバンドルjsを作る<font color=blue>【統合型】</font>と、
ブラウザ用、Node.js用と別々にライブラリを出し分ける<font color=blue>【出し分け型】</font>の2パターンある。

本稿では主に**<font color=blue>【統合型】</font>**について説明する

# ライブラリのビルドに関するWebpack設定項目

レシピ集に行く前に、
まず、ライブラリのビルドに関する設定項目をざっくりみていく。

## webpack.config.js

以下のような典型的な**webpack.config.js**の中で**output**以下の項目でライブラリ生成のためのパラメータを決める。

```javascript:webpack.config.js
module.exports = {
    entry: {
        'mylib': [`./src/family.js`],
    },
    output: {
        path: path.join(__dirname, '/'),
        publicPath: '/',
        filename: `[name].min.js`,
        library: '',
        libraryExport: '',
        libraryTarget: 'umd',
        globalObject: 'this',
    },
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /(node_modules|bower_components)/,
            use: [{loader: 'babel-loader'}]
        }]
    },
    devtool: 'inline-source-map',
};
```

ライブラリ生成のためのパラメータは以下の部分

```javascript
    entry: {
        'mylib': [`./src/family.js`],
    },
    output: {
        filename: `[name].min.js`,
        library: '',
        libraryExport: '',
        libraryTarget: 'umd',
        globalObject: 'this',
    },
```

**entry**には、エントリーポイントになるjsファイルを指定する。ライブラリを作る時には、クラスや関数がexportされているjsファイルを指定する。

**output**以下はライブラリ生成にかかわるプロパティを設定する

<table>
<tr>
<td>設定</td>
<td>意味</td>
<td>設定値(例)</td>
</tr>
<tr>
<td>output.filename</td>
<td>生成されるバンドルのファイル名。<br>entryにキー名が指定されている。<br>それが[name]に代入される。よって、上の例では、<b>family.min.js</b>というバンドルが生成される。</td>
<td ></td>
</tr>
<tr>
<td>output.libraryTarget</td>
<td>ライブラリの公開方法。ブラウザ・Node.js両対応なら'umd'を指定する。</td>
<td x:str="''umd' 'var' 'commonjs' など">'umd' 'var' 'commonjs' など</td>
</tr>
<tr>
<td>output.library</td>
<td>ライブラリ名を指定する。<br>配列にすると a.b のような名前空間のようにできる。<br>また、モジュール形式ごとにライブラリ名を変えることもできる。</td>
<td x:str="''mylib' ['mylib','util']など">'mylib' ['mylib','util']など</td>
</tr>
<tr>
<td>output.libraryExport</td>
<td>明示的にエクスポートするモジュールを指定できる。<br>default指定されたモジュールをトップレベルにしたければ'default'を指定する</td>
<td x:str="''default'など">'default'など</td>
</tr>
<tr>
<td>output.globalObject</td>
<td>rootに設定する値。libraryTargetが'umd'の場合ライブラリ実行時の即時関数の第一引数に指定される</td>
<td x:str="''this'など">'this'など</td>
</tr>
</table>


#レシピ集

## レシピ集の目次

- [1.クラス1つをライブラリにする](#1クラス1つをライブラリにする)
 - [1-1.export defaultなクラスを1つ公開する](#1-1export-defaultなクラスを1つ公開する)
 - [1-2.export defaultなクラスをライブラリ名(≒名前空間)つきで1つ公開する](#1-2export-defaultなクラスをライブラリ名名前空間つきで1つ公開する)
 - [1-3.export defaultなクラスを1つ、ライブラリ名つきだけどdefaultプロパティを使わず公開する](#1-3export-defaultなクラスを1つライブラリ名つきだけどdefaultプロパティを使わず公開する)
 - [1-4.export defaultなクラスを1つ、ライブラリ名＝クラス名にして公開する](#1-4export-defaultなクラスを1つライブラリ名クラス名にして公開する)
 - [1-5.export defaultなクラスを1つ、再エクスポート(reexport)をつかって公開する](#1-5export-defaultなクラスを1つ再エクスポートreexportをつかって公開する)

- [2.複数のクラスを含むライブラリにする](#2複数のクラスを含むライブラリにする)
 - [2-1.複数のクラスを公開する](#2-1複数のクラスを公開する)
 - [2-2.ライブラリ名つきで複数のクラスを公開する](#2-2ライブラリ名つきで複数のクラスを公開する)
 - [2-3.export defaultなクラスを含む複数のクラスを公開する](#2-3export-defaultなクラスを含む複数のクラスを公開する)
 - [2-4.複数のクラスの中でexport defaultなクラスだけを公開する](#2-4複数のクラスの中でexport-defaultなクラスだけを公開する)

- [3.その他のオプション](#3その他のオプション)
 - [3-1.モジュール形式ごとに名前空間を分ける](#3-1モジュール形式ごとに名前空間を分ける)
 - [3-2.モジュール形式ごとにコメントを入れる](#3-2モジュール形式ごとにコメントを入れる)
 - [3-3.ライブラリ名をピリオド区切りの("org.riversun.GereatFamily"のような)名前空間のようにする](#3-3ライブラリ名をピリオド区切りのorgriversungereatfamilyのような名前空間のようにする)
 - [3-4.externalsを使って外部ライブラリを分離する](#3-4externalsを使って外部ライブラリを分離する)


## 1.クラス1つをライブラリにする
### 1-1.**export default**なクラスを1つ公開する

**export default**なクラスを公開する方法

**ライブラリ側**(機能を提供する側）のソースコードとWebpackによる**ビルド設定**、**利用側**(ライブラリの機能を使う側）に分けてコードをみていく。

**【ビルド設定】**

ライブラリ関連プロパティを以下のようにする

**family.js**というのは、これから公開したいクラスを含むjsファイル

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: '',
    libraryExport: '',
    libraryTarget: 'umd',
    globalObject: 'this',
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_1/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【ライブラリ側ソースコード】**

**family.js**には、**Tom**という**sayHello**というメソッドがあるだけのシンプルなクラスがある。
このクラス1つをライブラリとして外部公開する。

```javascript:family.js
export default class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
```

**【利用側ソースコード】**

●**ブラウザ**から利用する場合

```html
<script src="./mylib.min.js"></script>
<script>
    const Tom = window.default;
    const tom = new Tom();
    console.log(tom.sayHello());
</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_1/client/example_for_browser.html" target="_blank">デモはこちら</a>



●**Node.js**から利用する場合

```javascript:Node.jsから利用する場合①
const Lib = require('./mylib.min.js');
const Tom = Lib.default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

または、こう書いても良い

```javascript:Node.jsから利用する場合②
const Tom = require('./mylib.min.js').default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

●**ES6**の**import文**から利用する場合

```javascript:ES6のimport文から利用する①
import * as Lib from './mylib.min.js';
const Tom = Lib.default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

または、こう書いても良い

```javascript:ES6のimport文から利用する②
import {default as Tom} from './mylib.min.js';
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

これでもOK

```javascript:ES6のimport文から利用する③
import Tom from './mylib.min.js';//Pick default
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

**●このレシピのポイント**

<font color=blue>**ポイント１：**</font>**globalObject: 'this'**の意味は何か。

Webpackして実際に生成された**mylib.min.js**は以下のようになっている

```javascript:minifyしないで表示
(function webpackUniversalModuleDefinition(root, factory) {
    if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();
    else if(typeof define === 'function' && define.amd)
        define([], factory);
    else {
        var a = factory();
        for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
    }
})(this, function() {略});
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_1/mylib.develop.js" target="_blank">mylib.min.js(developビルド版)のフルソースコードを見る</a>


これは引数に`(this,function())`をとっている即時関数だが、この`this`は**globalObject: 'this'**を指定した結果として設定されている。もし**globalObject:**の指定を省略すると、この即時関数の引数は`(window,function())`となる。つまり`window`が自動的に入るため、ブラウザでなら動作するが**window**オブジェクトの無い**Node.js**で動作させると`ReferenceError: window is not defined`となり**Node.js**で動作しなくなってしまう。ゆえに、両対応したい場合は**globalObject: 'this'**を入れる。

<font color=blue>**ポイント２：**</font>**default**というキー以下にクラスがぶるさがる

Node.jsで使うときは`require('./mylib.min.js').default`、ブラウザで使うときは`window.default`(=window["default"])のように**default**というキー名以下に公開したいクラスがぶるさがる様になる


<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


### 1-2.**export default**なクラスを<font color=blue>ライブラリ名(≒名前空間)つき</font>で1つ公開する

<font color=blue>ライブラリ名（≒名前空間）</font>を指定するには、webpack.config.jsに**output.library:'MyLibrary'**のように指定する。これで、ライブラリに**MyLibrary**というライブラリ名を設定できる。

**【ビルド設定】**

ライブラリ関連プロパティを以下のようにする

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: 'MyLibrary',
    libraryExport: '',
    libraryTarget: 'umd',
    globalObject: 'this',
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_2/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【ライブラリ側ソースコード】**

```javascript:family.js
export default class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
```

**【利用側ソースコード】**

●**ブラウザ**から利用する場合

以下のように、Tomクラスは MyLibrary.default として公開される。

```html
<script src="./mylib.min.js"></script>
<script>
    const Tom = MyLibrary.default;
    const tom = new Tom();
    console.log(tom.sayHello());
</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_2/client/example_for_browser.html" target="_blank">デモはこちら</a>


●**Node.js**から利用する場合

Node.js用の場合(CommonJS2)は<font color=red>ライブラリ名は無視される</font>ので注意。
よって**output.library:'MyLibrary'**を指定しなかった場合と使い方は同じ。

```javascript
const Lib = require('./mylib.min.js');
const Tom = Lib.default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

●**ES6**の**import文**から利用する場合

```javascript:ES6のimport文から利用する①
import * as Lib from './mylib.min.js';
const Tom = Lib.default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


### 1-3.**export default**なクラスを1つ、<font color=blue>ライブラリ名つき</font>だけど<font color=blue>**default**プロパティを使わず</font>公開する

以下のように"default"を使わなくてもクラスにアクセスできるようにする。

```javascript
const Tom = MyLibrary.default;　
```


**【ビルド設定】**
`output.libraryExport: 'default'`にする

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: 'MyLibrary',
    libraryExport: 'default',
    libraryTarget: 'umd',
    globalObject: 'this',
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_3/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【ライブラリ側ソースコード】**

```javascript:family.js
export default class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
```

**【利用側ソースコード】**


●**ブラウザ**から利用する場合

この設定でライブラリをビルドすると、`MyLibrary.default`ではなく、`MyLibrary`自体が`Tom`クラスの参照を示すようになる。

```html
<script src="./mylib.min.js"></script>
<script>
    const Tom = MyLibrary;
    const tom = new Tom();
    console.log(tom.sayHello());
</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_3/client/example_for_browser.html" target="_blank">デモはこちら</a>



●**Node.js**から利用する場合

Node.js用の場合(CommonJS2)は<font color=red>ライブラリ名は無視される</font>

```javascript
const Tom = require('./mylib.min.js');
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

●**ES6**の**import文**から利用する場合

```javascript
import Tom  from './mylib.min.js';
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


### 1-4.**export default**なクラスを1つ、<font color=blue>ライブラリ名＝クラス名</font>にして公開する


**【ビルド設定】**

- `output.libraryExport: 'default'`にする
- ライブラリ名をクラス名(Tom)と同一にする→`output.library: 'Tom'`

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: 'Tom',
    libraryExport: 'default',
    libraryTarget: 'umd',
    globalObject: 'this',
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_4/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【ライブラリ側ソースコード】**

```javascript:family.js
export default class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
```

**【利用側ソースコード】**


●**ブラウザ**から利用する場合

```html
<script src="./mylib.min.js"></script>
<script>
    const tom = new Tom();
    console.log(tom.sayHello());
</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_4/client/example_for_browser.html" target="_blank">デモはこちら</a>


●**Node.js**から利用する場合

```javascript
const Tom = require('./mylib.min.js');
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

●**ES6**の**import文**から利用する場合

```javascript
import Tom from './mylib.min.js';
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

**●このレシピのポイント**

ブラウザからもNode.jsからも、**Tom**クラスだけを意識すればOKなので、使う側からみると違和感が無い。


<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


### 1-5.**export default**なクラスを1つ、<font color=blue>**再エクスポート(reexport)**</font>をつかって公開する

re-export(再エクスポート)を使って公開する。
再エクスポートとは、あるモジュールを別のモジュールの中で再度exportすること。

**【ビルド設定】**

<font color=blue>**entry**</font>を<font color=blue>**index.js**に</font>する

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/index.js`],
},
output: {
    filename: `[name].min.js`,
    library: '',
    libraryExport: '',
    libraryTarget: 'umd',
    globalObject: 'this',
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_5/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【ライブラリ側ソースコード】**

**index.js**を作り、**family.js**にある**Tomクラス**を再エクスポート(※)する。

```javascript:index.js
export {default as Tom} from './family.js';
```

<font color=gray>（※）再エクスポートの際に、**{default as Tom}**とすることで、実際には、**Tom**を「**export**」しており、この時点で「**default export**」では無くなる。</font>


```javascript:family.js
export default class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
```

**【利用側ソースコード】**


●**ブラウザ**から利用する場合

```html
<script src="./mylib.min.js"></script>
<script>
    const tom = new Tom();
    console.log(tom.sayHello());
</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_1_5/client/example_for_browser.html" target="_blank">デモはこちら</a>


●**Node.js**から利用する場合

この場合、**Tom**クラスを取得するために**{ }**で囲った[分割代入 (Destructuring assignment) 構文](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)を使っている。

```javascript
const {Tom} = require('./mylib.min.js');
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.

```

●**ES6**の**import文**から利用する場合

```javascript
import {Tom} from './mylib.min.js';
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

## 2.複数のクラスを含むライブラリにする

### 2-1.<font color=blue>複数のクラス</font>を公開する

ライブラリとして複数のクラス（クラスじゃなくても関数でも変数でも同様)を外部から使えるようにしたい場合をみていく。

**【ライブラリ側ソースコード】**

以下は**family.js**の中に、2つのクラス**Tom**と**Jack**が含まれている。

```javascript:family.js
export class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
export class Jack {
    sayHello() {
        return 'Hi, I am Jack.'
    }
}
```

**【ビルド設定】**

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: '',
    libraryExport: '',
    libraryTarget: 'umd',
    globalObject: 'this',
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_2_1/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【利用側ソースコード】**

●**ブラウザ**から利用する場合

```html
<script src="./mylib.min.js"></script>
<script>
    const tom = new Tom();//means window["Tom"]
    const jack = new Jack();//means window["Jack"]

    console.log(tom.sayHello());//-> Hi, I am Tom.
    console.log(jack.sayHello());//-> Hi, I am Jack.

</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_2_1/client/example_for_browser.html" target="_blank">デモはこちら</a>


●**Node.js**から利用する場合

```javascript
const {Tom, Jack} = require('./mylib.min.js');

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```

または、こう書いても良い

```javascript
const Lib = require('./mylib.min.js');

const Tom = Lib.Tom;
const Jack = Lib.Jack;

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```

●**ES6**の**import文**から利用する場合

```javascript
import * as Lib from './mylib.min.js';

const Tom = Lib.Tom;
const Jack = Lib.Jack;

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```

または、こう書いても良い

**{ }**で囲った[分割代入 (Destructuring assignment) 構文](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)を使って以下のようにできる。


```javascript
import {Tom, Jack} from './mylib.min.js';

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```

<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


### 2-2.<font color=blue>ライブラリ名つき</font>で複数のクラスを公開する

以下に示すとおり`library: 'GreatFamily'`のようにライブラリ名(≒名前空間)をつける

**【ビルド設定】**

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: 'GreatFamily',
    libraryExport: '',
    libraryTarget: 'umd',
    globalObject: 'this',
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_2_2/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【ライブラリ側ソースコード】**

```javascript:family.js
export class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
export class Jack {
    sayHello() {
        return 'Hi, I am Jack.'
    }
}
```

**【利用側ソースコード】**

●**ブラウザ**から利用する場合

ブラウザで動作させる場合はwindow["GreatFamily"]の下に各クラスがぶるさがる。

```html
<script src="./mylib.min.js"></script>
<script>
    const tom = new GreatFamily.Tom();
    const jack = new GreatFamily.Jack();

    console.log(tom.sayHello());
    console.log(jack.sayHello());

</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_2_2/client/example_for_browser.html" target="_blank">デモはこちら</a>


●**Node.js**から利用する場合

Node.js用の場合(CommonJS2)は<font color=red>ライブラリ名は無視される</font>。
よって**output.library:'GreatFamily'**を指定しなかった場合と使い方は同じ。

```javascript
const Lib = require('./mylib.min.js');

const Tom = Lib.Tom;
const Jack = Lib.Jack;

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```
　

●**ES6**の**import文**から利用する場合

```javascript
import * as Lib from './mylib.min.js';

const Tom = Lib.Tom;
const Jack = Lib.Jack;

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```


<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>

　

##2-3.**<font color=blue>export default</font>**なクラスを含む複数のクラスを公開する

以下のように**export default**なクラスを含む場合

**【ライブラリ側ソースコード】**

```javascript:family.js
export default class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
export class Jack {
    sayHello() {
        return 'Hi, I am Jack.'
    }
}

```

**【ビルド設定】**

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: '',
    libraryExport: '',
    libraryTarget: 'umd',
    globalObject: 'this',
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_2_3/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【利用側ソースコード】**


●**ブラウザ**から利用する場合

```html
<script src="./mylib.min.js"></script>
<script>

    const Tom = window.default;//means window["default"]

    const tom = new Tom();
    const jack = new Jack();//means window["Jack"]

    console.log(tom.sayHello());
    console.log(jack.sayHello());

</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_2_3/client/example_for_browser.html" target="_blank">デモはこちら</a>


●**Node.js**から利用する場合

```javascript
const Lib = require('./mylib.min.js');

const Tom = Lib.default;
const Jack = Lib.Jack;

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```

または、こう書いても良い

```javascript
const Tom = require('./mylib.min.js').default;
const {Jack} = require('./mylib.min.js');

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```

●**ES6**の**import文**から利用する場合

```javascript
import * as Lib from './mylib.min.js';

const Tom=Lib.default;
const Jack=Lib.Jack;

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```

または、こう書いても良い

```javascript
import {default as Tom, Jack} from './mylib.min.js';

const tom = new Tom();
const jack = new Jack();

console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
```

これでもOK

```javascript

import Tom2 from './mylib.min.js';
import {Jack as Jack2} from './mylib.min.js';

const tom2 = new Tom2();
const jack2 = new Jack2();

console.log(tom2.sayHello());//-> Hi, I am Tom.
console.log(jack2.sayHello());//-> Hi, I am Jack.
```

<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


##2-4.複数のクラスの中で**<font color=blue>export default</font>なクラスだけ**を公開する

（こういうことが必要になることはあまり無さそうだが、挙動としてみておく）

**【ビルド設定】**

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: 'Tom',
    libraryExport: 'default',
    libraryTarget: 'umd',
    globalObject: 'this'
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_2_4/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【ライブラリ側ソースコード】**

```javascript:family.js
export default class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
export class Jack {
    sayHello() {
        return 'Hi, I am Jack.'
    }
}
```


**【利用側ソースコード】**


●**ブラウザ**から利用する場合

<font color=red>Jackクラスは使えなくなる</font>

```html
<script src="./mylib.min.js"></script>
<script>
    const tom = new Tom();
    console.log(tom.sayHello());
</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_2_4/client/example_for_browser.html" target="_blank">デモはこちら</a>


●**Node.js**から利用する場合

<font color=red>Jackクラスは使えなくなる</font>

```javascript
const Tom = require('./mylib.min.js');
const tom=new Tom();
console.log(tom.sayHello());//->Hi, I am Tom.
```

●**ES6**の**import文**から利用する場合

```javascript
import Tom  from './mylib.min.js';
const tom=new Tom();
console.log(tom.sayHello());//->Hi, I am Tom.
```

**●このレシピのポイント**

ライブラリとして外部から利用できないにもかかわらず、**<font color=red>生成されたバンドルにはJackクラスも混入</font>**される。もしJackクラスが誰からも利用されないのであれば、純粋にムダなコードがバンドルに入ることになる。

<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


## 3.その他のオプション

### 3-1.モジュール形式ごとに名前空間を分ける

`libraryTarget: 'umd'`のとき、以下のようにするとRoot,AMD,CommonJS,にそれぞれ別のライブラリ名（名前空間）をつけることができる。
ただしCommonJS2(Node.js用)では無視される。

**【ビルド設定】**

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: {
         root: 'GreatFamily',
         amd: 'great-family',
         commonjs: 'common-great-family',
    },
     libraryExport: '',
     libraryTarget: 'umd',
     globalObject: 'this',
     umdNamedDefine: true,
}
```


<a href="https://riversun.github.io/make-lib-with-webpack/part_3_1/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


```javascript
    library: {
         root: 'GreatFamily',
         amd: 'great-family',
         commonjs: 'common-great-family',
    },
```

の部分でそれぞれのモジュール形式ごとの名前をつけている。

また
**umdNamedDefine:true**にすると、AMDにもライブラリ名(名前空間)が付与される

この設定でビルドすると、バンドルjsは以下のようになり、各モジュール形式ごとのライブラリ名が反映されていることがわかる。

```javascript:family.min.js
(function webpackUniversalModuleDefinition(root, factory) {
	//for CommonJS2 environment
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	//for AMD environment
	else if(typeof define === 'function' && define.amd)
		define("great-family", [], factory);
	//for CommonJS environment
	else if(typeof exports === 'object')
		exports["common-great-family"] = factory();
	//for Root
	else
		root["GreatFamily"] = factory();
})(this, function() {...}
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_3_1/mylib.develop.js" target="_blank">mylib.min.js(developビルド版)のフルソースコードを見る</a>



<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


### 3-2.モジュール形式ごとにコメントを入れる

**auxiliaryComment**を書くとビルドするバンドルjsのそれぞれのモジュール形式の定義部分にコメントを入れられる

**【ビルド設定】**

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: {
         root: 'GreatFamily',
         amd: 'great-family',
         commonjs: 'common-great-family',
    },
    libraryExport: '',
    libraryTarget: 'umd',
    globalObject: 'this',
    umdNamedDefine: true,
    auxiliaryComment: {
        root: 'Root用のコメントです',
        commonjs: 'CommonJS用のコメントです',
        commonjs2: 'CommonJS2用のコメントです',
        amd: 'AMD用のコメントです'
    }
}
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_3_2/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


以下のようにバンドルjsにコメントが入る

```javascript:family.min.js
(function webpackUniversalModuleDefinition(root, factory) {
	//CommonJS2用のコメントです
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	//AMD用のコメントです
	else if(typeof define === 'function' && define.amd)
		define("great-family", [], factory);
	//CommonJS用のコメントです
	else if(typeof exports === 'object')
		exports["common-great-family"] = factory();
	//Root用のコメントです
	else
		root["GreatFamily"] = factory();
})(this, function() {...}
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_3_2/mylib.develop.js" target="_blank">mylib.min.js(developビルド版)のフルソースコードを見る</a>


##3-3.ライブラリ名を<font color=blue>ピリオド区切りの</font>("org.riversun.GereatFamily"のような)<font color=blue>名前空間</font>のようにする

例えば、"org.riversun.GreatFamily"というライブラリ名にするときは以下の`library: ['org', 'riversun', 'GreatFamily']`のようにライブラリ名を配列にする。


**【ビルド設定】**

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: ['org', 'riversun', 'GreatFamily'],
    libraryExport: '',
    libraryTarget: 'umd',
    globalObject: 'this',
    umdNamedDefine: true,
},
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_3_3/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


**【ライブラリ側ソースコード】**

```javascript:family.js
export class Tom {
    sayHello() {
        return 'Hi, I am Tom.'
    }
}
export class Jack {
    sayHello() {
        return 'Hi, I am Jack.'
    }
}
```


**【利用側ソースコード】**


●**ブラウザ**から利用する場合

```html
<script src="./mylib.min.js"></script>
<script>
    const tom = new org.riversun.GreatFamily.Tom();
    const jack = new org.riversun.GreatFamily.Jack();
    console.log(tom.sayHello());
    console.log(jack.sayHello());
</script>
```

これまでと同様、<font color=red>Node.js(CommonJS2)ではパッケージ名は反映されない</font>


<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>


          
### 3-4.<font color=blue>externals</font>を使って外部ライブラリを分離する

自作ライブラリに外部ライブラリへの依存性があるとき、**外部ライブラリを自作ライブラリに<font color=blue>一体化</font>させて配布する方法**と、**外部ライブラリと自作ライブラリは<font color=blue>分離</font>して配布する方法**がある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170905/0834adaa-dcab-3930-be49-af19f485f7e1.png)

ふつうに作ると一体化するので、ここでは**<font color=blue>分離して</font>**する方法「<font color=blue>**外部ライブラリ分離方式**</font>」(勝手に命名しました)をみていく。

ここでは、自作ライブラリ**Tomクラス**が外部ライブラリ[**@riversun/simple-date-format**](https://www.npmjs.com/package/@riversun/simple-date-format)を参照する例でみる。

**外部ライブラリをインストール

ライブラリ開発時に参照するために、使用する外部ライブラリをインストールする

```shell
npm install --save-dev @riversun/simple-date-format
```          

**【ビルド設定】**

**externals**を以下のようにwebpack.config.jsに追加する

```javascript:webpack.config.js
entry: {
    'mylib': [`./src/family.js`],
},
output: {
    filename: `[name].min.js`,
    library: 'Tom',
    libraryExport: 'default',
    libraryTarget: 'umd',
    globalObject: 'this',

},
externals: {
    SDF: {
        commonjs: '@riversun/simple-date-format',
        commonjs2: '@riversun/simple-date-format',
        amd: '@riversun/simple-date-format',
        root: 'SimpleDateFormat'
    }
}
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_3_4/webpack.config.build-lib.js" target="_blank">webpack.config.jsのフルソースコードを見る</a>


以下の部分で"**SDF**"はライブラリ側ソースコードからライブラリを参照するためのプロパティ名。

```javascript

externals: {
    SDF: {
        commonjs: '@riversun/simple-date-format',
        commonjs2: '@riversun/simple-date-format',
        amd: '@riversun/simple-date-format',
        root: 'SimpleDateFormat'
    }
}
```

**SDF**以下には下のように"**ライブラリタイプ名**:**ライブラリ名**"(npm instlalするときの)を書く。

```javascript
commonjs: '@riversun/simple-date-format',
commonjs2: '@riversun/simple-date-format',
amd: '@riversun/simple-date-format',
```

**commonjs,commonjs2,amd**は、**umd**形式でライブラリを作るとき、それぞれCommonJS、CommonJS2、AMDにたいして**SDF**で参照されるライブラリに対してどのライブラリを適用するかを指定する。

一番下の

```javascript
root: 'SimpleDateFormat'
```

だが、これをやることで、ブラウザ上で自作ライブラリを使うとき**SDF**で指定されたライブラリとして、**SimpleDateFormat**（window.["**SimpleDateFormat**"]）を参照するようにビルドされる。

【生成されるバンドルjs】

上述したビルド設定でライブラリをビルドすると以下のようなバンドルが生成される

```javascript:family.min.js(バンドル)

(function webpackUniversalModuleDefinition(root, factory) {
	//for CommonJS2
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory(require("@riversun/simple-date-format"));
	//for AMD
	else if(typeof define === 'function' && define.amd)
		define("Tom", ["@riversun/simple-date-format"], factory);
	//for CommonJS
	else if(typeof exports === 'object')
		exports["Tom"] = factory(require("@riversun/simple-date-format"));
	//for Root
	else
		root["Tom"] = factory(root["SimpleDateFormat"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_SDF__) {...})
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_3_4/mylib.develop.js" target="_blank">mylib.min.js(developビルド版)のフルソースコードを見る</a>

ここからもわかるように、それぞれのモジュール形式用にモジュールの読み込み方が定義される。

**CommonJS**,**CommonJS2**向けには**require("@riversun/simple-date-format")**している、
**AMD**(RequireJS)向けには **define()に配列でライブラリ名を指定している**、
**ブラウザ向け**(Root)には root["SimpleDateFormat"]している。（これは、つまりthis["SimpleDateFormat)ということであり、ブラウザ上で `this=window` なのでつまり**window["SimpleDateFormat"]**)している。

この仕掛けにより、Webpackビルドでは外部ライブラリがバンドルに取り込まれなくなるので、外部ライブラリを分離することができる。

自作ライブラリ利用側で require("@riversun/simple-date-format") なり、window["SimpleDateFormat"]=SimpleDateFormatなりされていれば、自作ライブラリ内に外部ライブラリを取り込まずとも動作する。

それでは、自作ライブラリ側のソースコードをみていく。

**【ライブラリ側ソースコード】**

```javascript:family.js
import SimpleDateFormat from "SDF";

export default class Tom {
    sayHello() {
        const date = new Date();
        const sdf = new SimpleDateFormat();
        return `Hi, I am Tom. Today is ${sdf.formatWith("EEE, MMM d, ''yy", date)}`;
    }
}
```

`import SimpleDateFormat from "SDF";`は本来`import SimpleDateFormat from "@riversun/simple-date-format";`とするところを、**externals**で決めたプロパティ名としている。

次に、この**<font color=blue>外部ライブラリ分離方式</font>**で作った自作ライブラリを利用する場合についてみる。

**【利用側ソースコード】**


●**ブラウザ**から利用する場合

ブラウザから利用するときは、以下のように外部ライブラリをCDNから読み込んでいる。
先に外部ライブラリを読み込む。

```html
<script src="https://cdn.jsdelivr.net/npm/@riversun/simple-date-format@1.1.0/dist/simple-date-format.js"></script>
<script src="./mylib.min.js"></script>
<script>
    const tom = new Tom();
    document.write(tom.sayHello());
</script>
```

<a href="https://riversun.github.io/make-lib-with-webpack/part_3_4/client/example_for_browser.html" target="_blank">デモはこちら</a>


今回つかった[拙作](https://github.com/riversun/simple-date-format)外部ライブラリも、本記事の1-4のお作法でビルドした。

[1-4](#1-4export-defaultなクラスを1つライブラリ名クラス名にして公開する)のお作法によりこの外部ライブラリのバンドルは**SimpleDateFormat**クラスをwindow["SimpleDateFormat"]で参照できるようにしているので、上述のように動作させることができる。

●**Node.js**から利用する場合

```javascript
const Tom = require('./mylib.min.js');
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
```

<font color=green>**[＜目次に戻る＞](#レシピ集の目次)**</font>

# まとめ

- Webpack4をつかって**ライブラリ**つくるときの種々の設定方法と挙動についてまとめました
- ハンズオンで実際に動かして挙動の違いをみるとWebpackのライブラリ生成機能について理解が深まるとおもいます
- 長文お読みいただき、ありがとうございました。

## サンプルコード実行方法

全ソースコードは https://github.com/riversun/making-library-with-webpack に置いてあります
ソースコードをつかって実際に試す場合には以下のようにします

**ソースをクローンする**

```shell
git clone https://github.com/riversun/making-library-with-webpack.git
cd making-library-with-webpack
```

**チャプターごとのディレクトリでサンプルコードを実行する**

チャプターごとのディレクトリを切って、その下をnpmプロジェクトにしてありますので、以下のようにディレクトリ内で`npm install`および`npm start`すれば、各チャプターの例を実行して確認できます。

```shell
cd part_1_1
npm install
npm start
```

## 実際のライブラリの公開

本記事[1-4](#1-4export-defaultなクラスを1つライブラリ名クラス名にして公開する)の方式を使って作った以下のOSSライブラリを公開しています。ソースコードもシンプルな構成のライブラリなので、これからライブラリを作る場合はご参考になれば幸いです。

https://www.npmjs.com/package/@riversun/simple-date-format


## 参考資料など

- https://webpack.js.org/guides/author-libraries/
- https://webpack.js.org/configuration/output/