依存OSSのライセンス一覧(JSON)生成ツールを作ってみた

# はじめに

[「Qiita - Angular CLIビルド時にできる利用OSSリスト(3rdpartylicenses.txt)をWebアプリ上に表示する」](https://qiita.com/kyosho/items/4d321dc5bf3593a0fd1c)で自動生成される `3rdpartylicenses.txt` を読み込んで画面に表示しました。
これは簡単にOSSのライセンスに準拠できるというメリットがあります。
ただ、テキストをそのまま表示するだけでしたので、自由なレイアウトができない、といった課題がありました。

そこでテキストではなく、JSONで出力する方法を調査してみました。
結果、、、ツールを自作したので、紹介します。

## 類似ツールの調査

まず、思い描いているツールや機能の存在を確認してみました。
私の検索能力だと2つ見つけられました。

### [license report tool](https://www.npmjs.com/package/license-report)

`node_modules`以下のpackage.jsonを解析してライセンスを含むパッケージ情報をリスト化してくれます。
実行した結果は、以下となります。

```json
> npx license-report --output=json --only=prod
npx: 62個のパッケージを4.652秒でインストールしました。
[
    {
        "department": "kessler",
        "relatedTo": "stuff",
        "name": "@oclif/command",
        "licensePeriod": "perpetual",
        "material": "material",
        "licenseType": "MIT",
        "link": "https://github.com/oclif/command.git",
        "comment": "1.5.19"
    },
    ...
    {
        "department": "kessler",
        "relatedTo": "stuff",
        "name": "tslib",
        "licensePeriod": "perpetual",
        "material": "material",
        "licenseType": "Apache-2.0",
        "link": "git+https://github.com/Microsoft/tslib.git",
        "comment": "1.11.1"
    }
]
```

これでも十分ですが、勝手な主観で申し訳ないけど、、以下が気に入りません。

* `link`要素に入るURLに"git+～"が付くことがある
* 不要な情報をフィルタできない

### [NPM License checker](https://www.npmjs.com/package/license-checker)

[「ハイブリッドアプリのライセンス一覧を自動生成しよう」](https://qiita.com/kishisuke/items/bdf9a7e83b2193108a8c)でも紹介されてますね。
使ってはいけないライセンスのOSSが含まれているか確認するために利用するみたいです。
以下の実行結果が出力されます。

```json
>npx license-checker --direct --production --json
{
  "@oclif/command@1.5.19": {
    "licenses": "MIT",
    "repository": "https://github.com/oclif/command",
    "publisher": "Jeff Dickey @jdxcode",
    "path": "D:\\～\\dependent-licenses\\src\\dependent-licenses\\node_modules\\@oclif\\command",
    "licenseFile": "D:\\～\\dependent-licenses\\src\\dependent-licenses\\node_modules\\@oclif\\command\\LICENSE"
  },

  ...

  "wrappy@1.0.2": {
    "licenses": "ISC",
    "repository": "https://github.com/npm/wrappy",
    "publisher": "Isaac Z. Schlueter",
    "email": "i@izs.me",
    "url": "http://blog.izs.me/",
    "path": "D:\\～\\dependent-licenses\\src\\dependent-licenses\\node_modules\\wrappy",
    "licenseFile": "D:\\～\\dependent-licenses\\src\\dependent-licenses\\node_modules\\wrappy\\LICENSE"
  }
}
```

イイ感じですが、以下が気に入らない…、ごめんなさい。

* 不要な情報をフィルタできない
* 深い依存関係を含む全てのライセンスを表示してしまう。ライセンスの確認のためには必要だけど、表示するだけなら直接参照しているライブラリだけにしたいんです。。。

# 自作ツール紹介(dependent-licenses)

やはりほしい機能って、ピンポイントでは中々ないですね。
ですので、作ってみました。
とありあえず、[license-checker](https://www.npmjs.com/package/license-checker)を利用したラッパーといった実装をしてみました。

ソースはGithubで公開しています。また、npmjsは以下となります。
とりあえず、動かすことを優先しているので、ドキュメント等や最適化はできていません。
本記事と同時並行でブラッシュアップしていこうかと考えています。

* [Github - dependent-licenses](https://github.com/kyosho-/dependent-licenses)
* [npmjs - dependent-licenses](https://www.npmjs.com/package/dependent-licenses)

## 使い方

package.jsonのあるプロジェクトルート上で、以下を実行してください。
ここでは`3rdpartylicenses.json`に保存しています。

```
> npx dependent-licenses > 3rdpartylicenses.json
```

以下は、出力されたJSONファイルです。
必要最低限の情報となっています。
今後はオプション等でフォーマットや情報を追加していければと考えています。

```json:3rdpartylicenses.json
{
    "@oclif/command@1.5.19": {
        "licenses": "MIT",
        "repository": "https://github.com/oclif/command"
    },
    "@oclif/config@1.14.0": {
        "licenses": "MIT",
        "repository": "https://github.com/oclif/config"
    },
    "@oclif/plugin-help@2.2.3": {
        "licenses": "MIT",
        "repository": "https://github.com/oclif/plugin-help"
    },
    "tslib@1.11.1": {
        "licenses": "Apache-2.0",
        "repository": "https://github.com/Microsoft/tslib"
    }
}
```

# Angularでの利用例

生成したJSONを[「Qiita - Angular CLIビルド時にできる利用OSSリスト(3rdpartylicenses.txt)をWebアプリ上に表示する」](https://qiita.com/kyosho/items/4d321dc5bf3593a0fd1c)と同様に画面表示してみます。

## @angular/cli インストール
適当な作業ディレクトリを作成し、作業ディレクトリ上で以下を実行してください。
1. `npm install -g @angular/cli@latest`

## プロジェクト作成
新しいプロジェクトを作成します。
1. `ng new display-license`

## Component作成
以下を実行すると、`app/license/license.component.*`ファイルが生成されます。クラスはLicenseComponentとなります。

```text:commandline
cd display-license
ng g component license
```

## ソース修正
各ソースを修正してください。

```ts:app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { LicenseComponent } from './license/license.component';

@NgModule({
  declarations: [
    AppComponent,
    LicenseComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

```html:app/app.component.html
<h2>Open Source Licenses</h2>
<div>
  <!-- LicenseComponentを挿入 -->
  <app-license></app-license>
</div>
```

```ts:app/license/license.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Observable } from 'rxjs';
import { share, map } from 'rxjs/operators';

interface License {
  package: string;
  licenses: string;
  repository: string;
}

/**
 * 3rdpartylicenses.jsonライセンスを
 * 読み込んで表示するコンポーネント
 */
@Component({
  selector: 'app-license',
  templateUrl: './license.component.html',
  styleUrls: ['./license.component.scss']
})
export class LicenseComponent implements OnInit {
  /**
   * ライセンスを読み込むストリーム
   */
  public licenses$: Observable<License[]>;

  /**
   * コンストラクタ
   * @param http HttpClient
   */
  constructor(private http: HttpClient) { }

  /**
   * 初期化イベントハンドラ
   */
  ngOnInit(): void {
    // 3rdpartylicenses.jsonを読み込むストリームを生成
    const url = 'assets/3rdpartylicenses.json';

    this.licenses$ = this.http.get(url).pipe(
      share(),
      map(json => {
        const licenses: License[] = [];
        for (const key in json) {
          if (json.hasOwnProperty(key)) {
            const element = json[key];
            const license = {} as License;
            license.package = key;
            license.licenses = element.licenses;
            license.repository = element.repository;
            licenses.push(license);
          }
        }

        // console.log(licenses);
        return licenses;
      })
    );
  }
}
```

```scss:app/license/license.component.scss
th,td {
    border: solid 1px;
    padding: 5px;
}
 
table {
    border-collapse:  collapse;
}
```

```html:app/license/license.component.html
<table>
    <tr>
        <td>package</td>
        <td>License</td>
        <td>repository</td>
    </tr>
    <tr *ngFor="let license of licenses$ | async">
        <td> {{ license.package }} </td>
        <td> {{ license.licenses }} </td>
        <td> {{ license.repository }} </td>
    </tr>
</table>
```

## ビルド&実行
`ng serve`実行後、ブラウザで`http://localhost:4200`を開いてください。
こんなふうに出力されれば、成功です。各自CSS/htmlを修正して、デザインを整えてください。

![license.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179413/939b055c-6810-b2bc-dfac-21bb10470900.png)