vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

コンパイルを自動化したい話 + (Pikaday + TypeScript) で IE でも DatePicker を表示したい話

はじめに

ここまで tsc による TypeScript -> JavaScript 変換、 webpack によるバンドルや PostCSS -> CSS 変換など、ツールによるコンパイルをいくつか試してきたわけですが、正直これを手動でやり続けるのは大変です。

ということで、これを自動で実行してくれるようにしてほしかった話と、前回の DatePicker の話の続きです。

npm scripts で自動的にコンパイル

npm scripts を使って面倒なコンパイル処理をもう少し簡素化できるようです。

npm scripts 以外にも自動で処理を実行してくれるツールは色々あるようですが、全く別のツールを導入しなくて良い、ということと、今回やりたいことは十分実現できそう、ということで今回はこれでいくことにしました。

とりあえず npm scripts でコンパイルを実行

まずはこれまで実行してきた下記二つのコマンドを npm scripts で実行してみます。

  • npx webpack
  • npx postcss pcss/*.css -c postcss.config.js -d src

これらを package.json に scripts として追加します。

package.json

{
    
    "scripts": {
        "css": "npx postcss pcss/*.css -c postcss.config.js -d src",
        "webpack": "npx webpack"
    },
    
    "devDependencies": {
        ~省略~
    }
}

で、あとはターミナルから下記のように実行すれば OK です。

npm run css

webpack の方は

npm run webpack

まとめて実行

今は二つですが、今後増えてくるとやっぱり面倒なので、一括で実行したいところ。

npm-run-all を使ってみます。

npm install --save-dev npm-run-all 

package.json

{
    "scripts": {
        
        "all": "run-s css webpack",
        
        "css": "npx postcss pcss/*.css -c postcss.config.js -d src",
        "webpack": "npx webpack"
    },
    "devDependencies": {
        ~省略~
    }
}

これで npm run all とすれば両方実行してくれます。

自動で実行してほしい

2 回実行するものが 1 つにまとまって、少しは楽になったもののやっぱり変更のたびに都度実行するのはちょっと。。。

Rider で tscコンパイルを、ファイルの変更を感知して自動で実行してくれたように、 webpack や PostCSS も自動実行してほしいですね。

通常 npm scripts で実現するためには、 watch を使うことができます。

npm install --save-dev watch

ですが、 webpack 、 PostCSS の場合、 watch を使わなくてもオプションとして watch の機能を持っています。

ということで、 package.json を下記のように変更するだけで、ファイルの変更の監視、コンパイルの自動実行をしてくれるのでした。

package.json

{
    "scripts": {
        "all": "run-s css webpack",
        "css": "npx postcss pcss/*.css -c postcss.config.js -d src -w",
        "webpack": "npx webpack -w"
    },
    "devDependencies": {
        ~省略~
    }
}

別でターミナルを立ち上げて実行

今回の開発は Visual Studio Code (以下 VS Code )を使っているのですが、 VS Code ではメニューからターミナル( PowerShell )を立ち上げることができます。

コンパイルや npm install をする場合はこれを使うのが便利なのですが、上記の処理については別で PowerShell などを立ち上げて実行するのが良いかと思います。

理由は明示的に処理を終了するまでずーっと監視し続けるため、他のコマンドが打てなくなるためです。

IE11 での input type='date'

前回の話で、 DatePicker を表示するのは input の type を date にするだけ。

えらい簡単だな~凄いな~と思っていたわけですよ。

IE で確認するまでは。

まぁ当然のように動かない、と/(^o^)\

自分で作るのは厳しそうなので、良さげなものないかな~と思っていたところ、 Pikaday を見つけました。

シンプルに使えそうとか良いところは色々あるわけですが、とりわけ IE の対応が ver.7 からというのがありがたい (..)_ (今回は IE7 には対応していませんが)

ということで試してみます。

準備

とにかくインストールから。

色々方法はあるようですが、 npm install することにしました。

npm install --save-dev pikaday

CSS の読み込み

Pikaday を使うにあたり、必要なファイルは 2 つです。

  • Pikaday.js
  • Pikaday.css

JavaScript の方は import なりなんなりすれば利用できるので問題ありません。

ただ、 CSS をどうするか。

ファイル自体は node_modules > pikaday > css に置かれています。

HTML から node_modules 以下のパスを指定して読み込む、または手動あるいは npm scripts でコピーという手もなくはないですが、自分で設定する CSS ファイル程更新は頻繁ではないでしょうし、かといって放置というわけにもいかずいまいち。

などと思っていたら、 postcss-import を使う方法が見つかりました。

これを使うことで、 JavaScript の import / export で分割したファイルを webpack でひとまとめにするのと同じようなことができます。

postcss-import を使う

上記リンク参照というところではありますが、一応ここにも書き残しておきます。

まずはインストール。

npm install --save-dev postcss-import

PostCSS から使用するので、 postcss.config.js に追加します。

postcss.config.js

module.exports = {
    plugins: [
        
      require('postcss-import')(),
      
      require('autoprefixer')({
        "grid": true,
        "browsers": [
          "last 2 versions"
        ]
      }),
      require('precss'),
    ]
  }

で、表示するページで使用する CSS に import を追加してやります。

page.css

@import '../node_modules/pikaday/css/pikaday.css';

~省略~

Pikaday の README と違ってパスが ../ になっているのは、 page.css が pcss というディレクトリ内にあるからです(想定されている CSS ファイルとパスが異なる)。

これでコンパイルすると、 page.css の内容に pikaday.css が追加されたものが出力されます。

HTML から node_modules 以下のパスを指定する場合、 ASP.NET Core (というか Web アプリ側)の静的ファイルの公開範囲をいじる必要が出てきますが、 PostCSS のコンパイル時だけ参照するのであれば問題はぐっと減ってくれそうです。

なお、何らかの理由で pikaday.css の内容と他のファイルを分けたい場合は、 @import ~ だけ書いた

Pikaday を使う

Pikaday を使う場合、対象の要素を指定する、日付のフォーマットを指定する、といった初期化処理を行う必要があります。

< script src="pikaday.js">< /script>
< script>
    var picker = new Pikaday({ 
        field: document.getElementById('datepicker') 
    });
< /script>

注意点としては document.getElementById('datepicker') が実行されるより前に該当の DOM 要素がロードされている必要があるため、この処理は HTML の下部に書く必要がある、ということです。

フォーマットを変更する

デフォルトだと下記のデモのように曜日や月が英語で表示されます。

例えばこれを日本語に変更したい場合、初期化処理時に指定することができます( 項目は pikaday.js の defaults で初期値が設定されています)。

で、これらを DatePicker 使うたびに設定するのはツラすぎるので、クラスにまとめてみました。

import Pikaday from 'pikaday';

class CustomDatePicker{
    public static getPikaday(elementName: string): Pikaday{
        const today = new Date();
        const yearFrom = 1999;
        const yearTo = today.getFullYear() + 1;

        return new Pikaday({
            field: document.getElementById(elementName),
            firstDay: 1, // 月曜始まりにする
            minDate: new Date(yearFrom.toString() + '-1-1'), // 選択できる最小範囲
            maxDate: new Date(yearTo + '-12-31'),
            yearRange: [yearFrom, yearTo], // 選択できる年の範囲。 1999 - 一年後までとする
            yearSuffix: '年', // 2019年 と表示されるようにする
            showMonthAfterYear: true, // 2019年 四月 と表示されるようにする
            i18n: {
                previousMonth : '前月',
                nextMonth     : '次月',
                months        : ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
                weekdays      : ['日曜','月曜','火曜','水曜','木曜','金曜','土曜'],
                weekdaysShort : ['日','月','火','水','木','金','土'],
            }
        })
    }
}
export {CustomDatePicker}

Pikaday を import する

@types/pikaday のインストールが必要そうです。

npm install --save-dev @types/pikaday

field で渡せる要素

getElementById となっている通り、一つの Pikaday インスタンスで設定できる要素は一つだけのようです。

ということで、 ID は外から渡せるようにしています。

複数ファイルをバンドルして出力する

例えば今回の CustomDatePicker を ページのスクリプトとは別にバンドルしたい場合。

webpack.config.js の entry で複数パスを渡してやることと、 output で名前が重複しないよう名前を指定する必要があります。

webpack.config.js

var path = require('path');

module.exports = {
    mode: 'development',

    entry: {
        'main':'./ts/mainPage.ts',
        'datePicker':'./ts/customDatePicker.ts',
    },
    
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    },
    resolve: {
        extensions: [ '.tsx', '.ts', '.js' ]
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'src/js'),
        library: 'Page',
        libraryTarget: 'umd'
    }
};

課題としては、(今回使っていませんが) SourceMap を生成すると一つ分しか生成されない、ということと、 npm run all で TypeScript を変更した場合に処理がうまく動いていない気がする、という二点は把握しています。

この辺りは解決したら追記、または別記事で投稿します。