音楽サーバ"Mopidy"のフロントエンドを作る:01 AspCore+TSの環境作り
ここを最後に更新してから、はや2年半が過ぎました。
.Net Coreはそろそろ3.0が出るかというこの頃。初期の粗削りさは影を潜め、環境もドキュメントもリッチになり、とても便利になりました。
個人的に少し時間とモチベーションが出来たので、現在の.Net Core環境をベースに、アプリ作りの過程を記事に起こしてみようと思います。
なお、アプリ自体は既に出来上がっており、こちらに公開しております。
Mopidyとは?
Mopidyとは、Linux上で動く音楽サーバです。
純粋なサーバ実装で、UIはありません。フロントエンドは各自好きなExtensionを入れてね、というスタンスです。
フロントエンドとしては、Iris、Mopedなどが有名です。
既にIrisみたいなオシャレで出来の良い鉄板プロダクトがあるのに、なぜ新しいものを作ろうと思ったか、というと。
大量に曲を登録したときに、お目当ての曲を探し出すのにひと苦労するんですね。
アルバムやアーティストの名前や綴りを覚えてないと、検索もままならない。
というか、検索するのにキーワードを入力しなきゃいけないのが嫌!
ぽちぽちクリックしたらお目当てが出てくるようにならないの...?
これはMopidy本体の検索APIが貧弱なためで、どのフロントエンドを選んでも同じ問題に行き当たります。
ちょうど時間も出来たことだし、いっちょう作ってみるか、となりました。
何が出来たらOKとする?
まず、作るにあたって要件の整理です。クリアしたい内容としては...
- Mopidy上のアルバムや曲が探しやすいこと。一番の動機です。
- Raspberry-Pi上で動作すること。我が家のMopidyサーバがラズパイにあるので。
- お客さんからAdmin-LTEというフレームワークの話を聞いたので、実際に試す。
- TSはとっても楽しい!ので、ベース環境はAspCore+TypeScript+Vueにする。
こんなものです。
ベース環境づくり
このあたりのコミットで、おおよその土台を作っています。
Visual Studio 2017でAspCore2.2のプロジェクトを作り、AspCoreのコードを"aspCore"フォルダに全部押し込みました。
今回はSPAのつもりなので、AspCoreのViewはまるっと削除。
HomeController.Indexメソッドはdist/index.dev.htmlを返すようにしてあります。
TSのテスト環境としてmocha, chaiを導入し、TS側モデルで読み込んだlodashが動作してるよね、というところを確認。
eslintが大量のダメ出しをしてくれていますが、ひとまずスルーです。
Visual Studioでステップデバッグするために
Visual Studio 2017では、TSをwebpack出力した状態では、エディタにブレイクポイントを置いてもブレイクしてくれません。
今までにも度々試行錯誤を重ねたのですが、残念ながら方法が見つかっていません。
そこで、大変回りくどい方法で「外部ライブラリはwebpackを使用」しつつ「TSはtscが出力したものを使用」することで回避しています。
TSは、AMD形式で単一ファイルとして書き出します。
tsconfig.dev.json:
-- 一部抜粋 -- "compilerOptions": { // モジュール単位の出力形式指定: commonjs, amd, system, umd, es2015 "module": "amd", // 単一ファイル出力時のパスとファイル名 "outFile": "dist/js/tsout.js" }
CSSと外部ライブラリは、一旦一つのjsファイルで全て取得し、windowオブジェクトに書き出します。
js/libraries.js:
import '../css/site.css'; import * as es6Promise from 'es6-promise'; import * as _ from 'lodash'; import Axios from 'axios'; import * as Enumerable from 'linq'; window.__globals = { 'es6-promise': es6Promise, 'lodash': _, 'axios': Axios, 'linq': Enumerable };
windowオブジェクトに書き出された各ライブラリを、今度はAMDモジュール形式にラップします。
最後に、TSの起動クラスを読み込んでTSロジックを開始しています。
dist/boot.js:
// ダミーのCSS define('../css/site.css', ["exports"], function (exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = {}; }); // es6-promise define('es6-promise', ["exports"], function (exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = window.__globals['es6-promise']; }); // lodash define('lodash', ["exports"], function (exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var instance = window.__globals['lodash']; for (var key in instance) { var val = instance[key]; exports[key] = val; } }); // Axios define('axios', ["exports"], function (exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = window.__globals['axios']; }); // linq.js define('linq', ["exports"], function (exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = window.__globals['linq']; }); // AMD出力されたTSロジックを起動する。 var app = require(['Main']);
htmlでは、AMDモジュールを扱うためにRequire.jsを組み込んだ上で、順次取得・処理していきます。
dist/index.dev.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" /> <title></title> </head> <body> <script src="js/require.js"></script> <!-- Require.js --> <script src="js/libonly.js"></script> <!-- webpack出力 --> <script src="js/tsout.js"></script> <!-- tsc出力 --> <script src="js/boot.js"></script> <!-- 前述のAMDラップ/起動スクリプト --> </body> </html>
こうすると、Visual Studioのエディタ上で設定したブレイクポイントで、TSのロジックが止まってくれます。
生成済みの song1 オブジェクトの中身に何が入っているかも、参照出来てますね!
なお、本番ビルドの場合は、TSを含めた全てをwebpackするだけにします。
tsconfig.commonjs.json:
-- 一部抜粋 -- "compilerOptions": { // モジュール単位の出力形式指定: commonjs, amd, system, umd, es2015 "module": "commonjs" // 単一ファイル出力時のパスとファイル名 //"outFile": "dist/js/tsout.js" //無効化 }
htmlは、バンドルされた1本のjsを読み込みのみです。
dist/index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" /> <title></title> </head> <body> <script src="js/bundle.js"></script> </body> </html>
なお、この方法は決して推奨されるものではありません。
今のところ、他に良い方法が見つかっていないだけ、なのです。
良い方法があれば、どなたかぜひご教示くださいませ...。