LoginSignup
7
1

AngularのextractLicensesみたいなやつをVite + Reactでもやりたい

Last updated at Posted at 2023-12-04

概要

Vite + Reactで開発をしている時にOSSのライセンスを出す必要があり、AngularのextractLicensesみたいに出せたらいいなと思いました。

AngularのextractLicensesとは

Angular CLIのビルドオプションで、Webバンドルと一緒に依存パッケージのライセンス情報(3rdpartylicenses.txt)を出力してくれます。ビルド時に ng build --extract-licenses=true と指定したりangular.jsonで設定したりもできますが、production buildではデフォルトで有効になっています。
生成したライセンス情報をHTTPリクエストでバンドルから取得し、UI上に表示することができます。

以下のように実装することでコンポーネント上にライセンスを表示することができます。

app/license/license.component.ts
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' })
    );
  }
}

Screenshot 2023-12-04 at 21.56.54.png

Vite + Reactでやる

1. やり方を決める

ササッと確認したかった + Viteのビルド周りに手を入れたくなかったので以下の方法にしました。

  1. ビルド前にライセンス情報を public/ に出力する。
  2. vite buildpublic/ の中のライセンス情報を dist/ に内包する。
  3. アプリケーションから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 というファイル名にします。

custom-format.json
{
  "name": "",
  "licenses": "",
  "repository": "",
  "publisher": "",
  "version": "",
  "description": "",
  "copyright": "",
  "licenseText": "",
  "licenseModified": "no",
  "licenseFile": false,
  "path": false
}
package.json
"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 が含まれていますね。

Screenshot 2023-12-04 at 22.55.48.png

4. HTTPリクエストで取得した 3rdpartylicenses.json の内容を表示する

実際にHTTPリクエストでJSONの中身を取得し、アプリ上で表示します。useEffect + fetchのやっつけ実装ですが、適宜データフェッチライブラリを使うなどして下さい。

App.tsx
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;

こんな感じで表示されます。

Screenshot 2023-12-04 at 23.51.04.png

参考

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1