React NativeでOSSのライセンス表記を楽にやりたい

最初に断っておきますが、僕自身はOSSのライセンスを理解しようとし、尊重していますが、ライセンスそのものの詳細に詳しいわけではないのでもしかしたら間違っていることがあるかもしれません。その際は優しく指摘してください。優しくね。

昨今、アプリケーションを開発するにはWebであれモバイルであれ、オープンソースOSS)のライブラリを利用することが多いかと思います。

その際に問題になってくるのが各ライブラリのライセンス表記でして、多くの場合はそのライブラリを利用して開発したものに関して著作権表記等を求められるライセンスで配布されていることが多いと思います。比較的制限のゆるいとされているMITライセンスなんかもそうですね。

WebなんかだとJSのソース見れるのでどこかに記載されていれば問題ないのかと思います。一方でiOSAndroidといったいわゆるネイティブアプリというのはそうはいかないよねってことでライセンス一覧のページなりをアプリ内で表示できるように用意していることが多いです。

というわけで、用意していこうと思うのですが一から自分で調べて用意するのも大変です。iOSとかAndroid向けのツールはいろいろとあるのですが自分の環境はReact Nativeです。なので今回はReact Nativeで同じようなことができるツールを探して試してみます。

候補

npmで入れているものをチェックして何らかの方法で出力してくれるものがいいな、ということでreact native oss license generatorとかで検索してみると思った以上に少ない。

とりあえず出てきたのはこの3種類くらい。

license-checker - npm

react-native-oss-license - npm

npm-license-crawler - npm

license-checkerが一番ダウンロード数が多いみたいだけど、最終更新が2年前とちょっと古い。次にダウンロード数が多いのはnpm-license-crawlerでこれも最後に更新されたのが2年前。react-native-oss-licenseはダウンロード数は一番少ないのだけれども最後の更新は4ヶ月前。

なお、react-native-oss-licenseについては作者のページがQiitaにあった。

React Native用のOSSライセンス表記生成ツールを作った - Qiita

さて、どれにするか悩ましいけどダウンロード数が一番多いやつか、更新が新しいものかのどちらかにすることにしよう。というわけでlicense-checkerかreact-native-oss-licenseを比較。

license-checker

  • 2021年6月29日時点での週間ダウンロード数は233,388
  • node_modulesをなめてすべてのpackage.jsonをチェック、SPDX形式のライセンスが含まれてればそれを使い、なければLICENSE, LICENCE, COPYING, READMEといったファイルを見る
  • ファイルを直接生成するのではなくJSONファイルを生成
  • したがってそれを読み込んで表示する画面を用意する必要がある

react-native-oss-license

  • 2021年6月29日時点での週間ダウンロード数は790
  • package.jsonから利用しているパッケージの情報を取得し、node_modules以下からLICENSEファイルを取得
  • iOSはsettings-bundle形式、Androidはlicense-tools-plugin形式もしくはabout-libraries形式で出力することが可能。自分でマージする必要あり
  • JSON出力も可能

どちらにするか

今のところネイティブなライブラリは使っていないのでiOSAndroidでの出力は不要なので個々の形式に合わせて出力するのではなく、JSONで出力して両プラットフォーム向けの画面を用意することにします。

そうするとどちらもJSON出力可能なので、長いものに巻かれようってことで使われている数が多いliense-checkerを使うことにします。

早速やってみる

ますはインストール。

yarn global add license-checker

実行します。

license-checker --production  --json --out ./licenses.json

--productionでdev dependenciesは取り除きます。実はCSV形式でも出力できるのですが--jsonJSON形式を指定します。そして--outで出力先のファイルを指定します。ここではカレントディレクトリにlinceses.jsonというファイル名で保存します。

こんな感じで出力されました。

{
  "@babel/code-frame@7.10.4": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "publisher": "Sebastian McKenzie",
    "email": "sebmck@gmail.com",
    "path": "/workspaces/patrolcommunity/node_modules/@expo/json-file/node_modules/@babel/code-frame",
    "licenseFile": "/workspaces/patrolcommunity/node_modules/@expo/json-file/node_modules/@babel/code-frame/LICENSE"
  },
  "@babel/code-frame@7.12.11": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "publisher": "Sebastian McKenzie",
    "email": "sebmck@gmail.com",
    "path": "/workspaces/patrolcommunity/node_modules/eslint/node_modules/@babel/code-frame",
    "licenseFile": "/workspaces/patrolcommunity/node_modules/eslint/node_modules/@babel/code-frame/LICENSE"
  },
  "@babel/code-frame@7.12.13": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "publisher": "Sebastian McKenzie",
    "email": "sebmck@gmail.com",
    "path": "/workspaces/patrolcommunity/node_modules/@babel/code-frame",
    "licenseFile": "/workspaces/patrolcommunity/node_modules/@babel/code-frame/LICENSE"
  },
  "@babel/compat-data@7.14.0": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "publisher": "The Babel Team",
    "url": "https://babel.dev/team",
    "path": "/workspaces/patrolcommunity/node_modules/@babel/compat-data",
    "licenseFile": "/workspaces/patrolcommunity/node_modules/@babel/compat-data/LICENSE"
  },

以下略

インストールされている異なるライブラリがそれぞれ依存している同じライブラリであってもそれぞれ別のバージョンに依存していると各バージョンが出力されてしまい、エグい量になるのでオプションとして--directをつけて再実行してみます。

{
  "@babel/code-frame@7.12.13": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "publisher": "Sebastian McKenzie",
    "email": "sebmck@gmail.com",
    "path": "/workspaces/patrolcommunity/node_modules/@babel/code-frame",
    "licenseFile": "/workspaces/patrolcommunity/node_modules/@babel/code-frame/LICENSE"
  },
  "@babel/compat-data@7.14.0": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "publisher": "The Babel Team",
    "url": "https://babel.dev/team",
    "path": "/workspaces/patrolcommunity/node_modules/@babel/compat-data",
    "licenseFile": "/workspaces/patrolcommunity/node_modules/@babel/compat-data/LICENSE"
  },
  "@babel/core@7.14.0": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "publisher": "Sebastian McKenzie",
    "email": "sebmck@gmail.com",
    "path": "/workspaces/patrolcommunity/node_modules/@babel/core",
    "licenseFile": "/workspaces/patrolcommunity/node_modules/@babel/core/LICENSE"
  },
  "@babel/generator@7.14.1": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "publisher": "Sebastian McKenzie",
    "email": "sebmck@gmail.com",
    "path": "/workspaces/patrolcommunity/node_modules/@babel/generator",
    "licenseFile": "/workspaces/patrolcommunity/node_modules/@babel/generator/LICENSE"
  },

以下略

うーん、それでも結構な量になるな。7000行超えてる。

ていうかこれbabelとかがdependenciesに含まれてるからか。でもpackage.jsonではdev dependenciesに指定してるんだけどな。--productionが効いてないのかな? 確認したら--productionがちゃんと有効になった状態でこれだった。

え、一般的にこんなに多いのかな。。。

と思いつつもまずはこのJSONを読み込んで表示する画面を先に作ることにする。

なお、表示にあたってライセンスの中身も出力したかったのでフォーマット指定を行うことにした。以下のようなファイルを用意する。名前はなんでもいいがcustomFormat.jsonとした。

{
    "name": "",
    "version": "",
    "description": "",
    "licenses": "",
    "copyright": "",
    "licenseText": ""
}

このフォーマットを指定してコマンドを実行する。

license-checker --production --direct --customPath ./src/customFormat.json --json --out ./src/licenses.json

ライセンス情報のJSONを読み込んで表示する

ぶっちゃけここは好きに作ればいい。ここではひとまず1枚の画面にベタっと表示するようにした。

import { Container } from 'native-base'
import licenseFile from '../../licenses.json'
import { Text } from 'native-base'
import { StyledScrollView, StyledView, NameText } from './styled'

export const LicenseScreen: React.FC = () => {
  const licenseKeys = Object.keys(licenseFile)
  return (
    <Container>
      <StyledScrollView>
        {licenseKeys.map((value) => {
          const license = licenseFile[value].licenses
          const publisher = licenseFile[value].publisher
          const licenseText = licenseFile[value].licenseText
          const copyright = licenseFile[value].copyright
          return (
            <StyledView>
              <NameText>
                {value} published by {publisher}
              </NameText>
              <Text>{licenseText}</Text>
              <Text>{copyright}</Text>
            </StyledView>
          )
        })}
      </StyledScrollView>
    </Container>
  )
}

export default LicenseScreen

styleは別にstyled.tsとして以下のように用意している。

import styled from 'styled-components/native'

export const StyledScrollView = styled.ScrollView.attrs(() => ({
  contentContainerStyle: {
    alignItems: 'center',
    justifyContent: 'center',
  },
}))``

export const StyledView = styled.View`
  margin-top: 10px;
`
export const NameText = styled.Text`
  font-size: 20px;
  font-weight: bold;
`

というわけでこんな感じでできあがったけど、なんかダサいし重いのでもうちょっと考えよう。。。

f:id:Keisuke69:20210630000457p:plain

©Keisuke Nishitani, 2020   プライバシーポリシー