概要
Vite + Reactで開発をしている時にOSSのライセンスを出す必要があり、AngularのextractLicensesみたいに出せたらいいなと思いました。
AngularのextractLicensesとは
Angular CLIのビルドオプションで、Webバンドルと一緒に依存パッケージのライセンス情報(3rdpartylicenses.txt
)を出力してくれます。ビルド時に ng build --extract-licenses=true
と指定したりangular.jsonで設定したりもできますが、production buildではデフォルトで有効になっています。
生成したライセンス情報をHTTPリクエストでバンドルから取得し、UI上に表示することができます。
以下のように実装することでコンポーネント上にライセンスを表示することができます。
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { Component, OnInit, inject } from '@angular/core';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'app-license',
standalone: true,
imports: [HttpClientModule],
styleUrl: './license.component.scss',
template: `
@if (licenseText !== undefined) {
<p style="white-space: pre-wrap">
{{ licenseText }}
</p>
} @else {
<div>Loading...</div>
}
`,
})
export class LicenseComponent implements OnInit {
licenseText: string | undefined;
private readonly http = inject(HttpClient);
ngOnInit(): void {
this.getLicenseText();
}
private async getLicenseText(): Promise<void> {
// development buildで見るためにassetsにコピーしました(ごめんなさい)
this.licenseText = await firstValueFrom(
this.http.get('assets/3rdpartylicenses.txt', { responseType: 'text' })
);
}
}
Vite + Reactでやる
1. やり方を決める
ササッと確認したかった + Viteのビルド周りに手を入れたくなかったので以下の方法にしました。
- ビルド前にライセンス情報を
public/
に出力する。 -
vite build
でpublic/
の中のライセンス情報をdist/
に内包する。 - アプリケーションからHTTPリクエストで内容を取得する。
vite-react-project
├── public // <- ライセンスをここに出力したい
├── src
│ ├── assets
│ ├── App.css
│ ├── App.tsx
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
2. ライセンス情報の出力
欲しいのは license-extractor.ts のような機能です。
package.json -> node_modules -> LICENSE...
の内容をテキストファイルに出すスクリプトを用意するのが面倒だったので、今回はlicense-checkerというnpmライブラリを使用しています。
3. ライブラリのインストールと諸々の準備
Vite + Reactのプロジェクトにlicense-checkerをインストールします
% npm i -D license-checker
ライセンス情報のフォーマットファイルを作成し、出力用のscriptを定義します。license-checkerはJSONで出力できるので 3rdpartylicenses.json
というファイル名にします。
{
"name": "",
"licenses": "",
"repository": "",
"publisher": "",
"version": "",
"description": "",
"copyright": "",
"licenseText": "",
"licenseModified": "no",
"licenseFile": false,
"path": false
}
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"extract-licenses": "license-checker --customPath ./custom-format.json --production --json --out ./public/3rdpartylicenses.json"
}
実際にビルドしてバンドルの中身を確認します。
% npm run extract-licenses && npm run build
3rdpartylicenses.json
が含まれていますね。
4. HTTPリクエストで取得した 3rdpartylicenses.json
の内容を表示する
実際にHTTPリクエストでJSONの中身を取得し、アプリ上で表示します。useEffect + fetchのやっつけ実装ですが、適宜データフェッチライブラリを使うなどして下さい。
import { useEffect, useState } from "react";
interface License {
name: string;
licenses: string;
repository: string;
publisher: string;
version: string;
description: string;
copyright: string;
licenseText: string;
licenseModified: string;
}
function App() {
const [licenses, setLicenses] = useState<License[]>();
useEffect(() => {
fetch("/3rdpartylicenses.json")
.then((res) => res.json())
.then((data) =>
setLicenses(
Object.keys(data as Record<string, License>).map((name) => data[name])
)
);
}, []);
return (
<>
<h1>3rd Party Licenses</h1>
{licenses !== undefined &&
licenses.map(
(
{
name,
licenses,
repository,
publisher,
version,
description,
copyright,
licenseText,
licenseModified,
},
i
) => (
<div key={i} style={{ whiteSpace: "pre-wrap" }}>
{[
name,
licenses,
`repository: ${repository}`,
`publisher: ${publisher}`,
`version: ${version}`,
`description: ${description}`,
`copyright: ${copyright}`,
licenseText,
`licenseModified: ${licenseModified}`,
"------------------------------------------------------------------------------------------",
].join("\n")}
</div>
)
)}
</>
);
}
export default App;
こんな感じで表示されます。
参考