初めてOSSを作ってみて、そうだったの?と思ったことまとめ


はじめに

コロナで家にいて暇だったので、初めてOSSを作って公開しました。

言語はTypeScript、環境はWebpack、IE11とCDNに対応、リポジトリはGithubです。
ブラウザ、nodejs、コマンドプロンプト、bashで動きます。

作ってみて、「これって実はこうなってたの?」と思ったポイントを共有します。
もし同じようなOSSの公開を検討されている方がおられましたら、ご参考いただきたくと思います。

OSSについて

OSS(オープンソースソフトウェア)は、ソースコードが公開され、誰でも利用、改良、修正、再配布ができるソフトウェアのことです。

もし作ったOSSに興味があればこちらの記事をご参考ください。

NLP(日本語)で使えるJSのDateライブラリを公開しました
https://qiita.com/ShotaOki/items/5e4b02f795fbf88bf0b1

CDNに登録する

たとえばjQueryを使いたいとき、htmlのヘッダにこんな風に書いていると思います。

index.html(jQueryをインポートする)
<script src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script>

今回作成した僕のOSSでは、以下のように書けば取り込むことができます。

index.html
<script src="https://cdn.jsdelivr.net/gh/ShotaOki/[email protected]/modern/nlpdate-main.min.js"></script>

このCDNに登録するには、どこにユーザー登録して、何をすればいいのでしょう。

実のところ、何もしなくても登録されます。
githubにタグが上がっていればCDNとして利用できます。

jsdelivrのURLはhttps://cdn.jsdelivr.net/gh/${ユーザー名}/${レポジトリ名}@${タグ名}/${ファイル名}です。ghがgithubのことです。タグ名にはlatestと書いてもOKです。

タグを作ってpushする
# githubのメインブランチにタグを作る
git tag -a v0.0.1 -m 'リリースタグを作る'

# githubのメインブランチにタグをpushする
git push origin --tags

これでjsdelivrで使えるようになっています。

コマンドプロンプトでTypeScriptのファイルを動かす。

作ったOSSの都合で、どうしてもbashやコマンドプロンプトから動かしたいと思っていました。

コマンドプロンプト
REM 普通の使い方
nlp-date "明日の午後三時" -f "和暦の年月日時分秒"
>> 令和3年01月12日 15時00分00秒

REM 「明日」とか「今日」みたいにエポック秒を書けるので、ログを読むのに便利
nlp-date "1610341916の9時間後" -f "年月日時分秒"
>> 2021年01月11日 23時11分56秒

これはどうやれば実装できるのでしょう。

拡張子なしのJavaScriptファイルを作って、頭にShebang(#!で始まる行)を付けます。

nlp-date
#!/usr/bin/env node

// JavaScriptでソースを書けばよい
var NLPDate = require("../modern/nlpdate-main.min.js");
/** 
中略:引数をパースする
*/
// 標準出力にconsole.logで出力する
console.log(NLPDate(argv["value"]).asString(argv["format"]));

頭にShebangがついていると、その環境で実行します。
コマンドプロンプトはShebangが読めないので、cmdを拡張子につけたファイルから呼び出します。

nlp-date.cmd
@ECHO off
REM nodeでJavaScriptファイル(nlp-date)を実行する
node  "nlp-date" %*
EXIT /b %errorlevel%

このファイルの場所に環境変数を通せば、「nlp-date」コマンドが使えるようになります。

TypeScriptをIE11に対応する

IE11、フルネームで書くとInternet Explorer 11です。
TypeScripのts-loaderのままwebpackしても、過去の遺物 IE11で動きません。

開発元のMicrosoftがEdgeに移ったのだから、こちらとしても切り捨てたいところですが、さすがにWindowsに標準で入っているブラウザを切り捨てるわけにはいきません。

IE11向けのバンドルファイルの生成は、ts-loader -> babel-loaderの順で呼び出します。
こうすることで、IE11で対応していない関数(fetchとか)を作ってくれます。

webpack.config
{
            rules: [
                // JavaScriptをIE11向けにトランスパイルする
                {
                    test: /\.(js|jsx|ts)$/,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        targets: ['> 1%', 'ie 11'],
                                        useBuiltIns: 'usage',
                                        corejs: 3,
                                    },
                                ],
                            ],
                        },
                    },
                },
                // TypeScriptをJavascriptに変換する
                {
                    test: /\.ts$/,
                    exclude: /(node_modules|bower_components)/,
                    loader: 'ts-loader',
                    options: {
                        configFile: 'babel.tsconfig.json', // <- babelの時だけtsconfig.jsonを変更
                    },
                },
            ],
        }

「module変数が見つからない」と言われたら、tsconfigのmoduleをes2015に書き換えます。

babel.tsconfig.json
{
    "includes": ["src/**/*.ts", "test/**/*.ts"],
    "compilerOptions": {
        "target": "es5",
        "module": "es2015", // <- babelの時だけ"commonjs"から"es2015"に書き換える
                            // es2015だとブラウザ以外の実行(例:mochaの自動テスト)で失敗するので、
                            // webpackのoptionでbabelの時だけ変更する
        "lib": ["es6", "es2019", "dom"]
    }
}

ソースコード外にアロー関数が書かれる問題を解決するために、対応ブラウザを.browserlistrcに書きます。

.browserlistrc
> 1%
IE 11 // ここにIE 11を書けば、import文(ソースの外側)にアローが使われなくなる
not dead

これでトランスパイルすればTypeScriptのファイルがIE11対応されます。
ポリフィルも必要なものだけ取り込まれます。