Electron製Markdownプレビュー&コンバートツール制作中
ここ数年grunt-markdownを使った原稿整理をやっているわけですが、Grunt自体のハードルが高くて正直なところ社内ですらまったく広まってない、という事情があったりします。
そんなところへ、Electronというのを使えばインストーラー付きのWinMac兼用のツールが作れるらしい、これなら誰でも使えるじゃないか!と知って、挑戦してみているわけです。
ひとまずMarkdownファイルを開いてHTMLファイルに変換して書き出し、それを読み込んで表示する、元ファイルが更新されたら自動的にHTMLも更新するという、最低限必要なところまでできました。
Electronを使ったMarkdownビューワーというのはすでに結構存在していて、それと比べると見た目も機能もだいぶアレなのですが、そこはもうスッパリ諦めて、用が足せればいいとわりきってやっております……。
特徴は、今のところ「プレビューを表示するだけでなく、勝手にHTMLファイルを書き出す」の一点です。
最終目標がVivliostyleでの書籍形式のプレビューと、InDesign向けのXML書き出しなので、HTML出力は外せないのです。
作り方メモ
いきなりですが、こちらのチュートリアル記事が大変参考になりました。
一回その通りにやったら、Electronの基礎から、Angularの使い方、クライアントでのmarkedの使い方まで、ひととおり勉強になります。アプリの基本的な作りもそのまま参考にしました。
以降はそれを踏まえての作り方メモですが……。
markedの利用
参考にした記事では画面表示時にMarkdown変換するのでbower(フロントエンド用のパッケージマネージャ)でmarkedをインストールしていますが、こちらはHTMLファイルを書き出したいので、npm(フロントエンドじゃない用のパッケージマネージャ)でインストールします。
インストール
npm install marked -g npm install marked --save
利用コード
var marked = require('marked'); var hljs = require('highlight.js'); ……中略…… //markedのオプション設定 marked.setOptions({ renderer: new marked.Renderer(), gfm: true, tables: true, breaks: false, pedantic: false, sanitize: true, smartLists: true, smartypants: false, highlight: function(code){ return hljs.highlightAuto(code).value; } }); // markedで変換 try{ var src = fs.readFileSync(openfile, 'utf-8'); } catch (err){ require('dialog').showErrorBox('Error', 'cannot open file.'); return 'cannot open file.'; } var html = marked(src);
これでMarkdownをコードハイライト付きのHTMLに変換してくれます。grunt-markdownのコードをしばらく眺めて何とかできました。
原稿ファイルはたいてい1章分丸ごとの長さを扱うので、非同期処理とかいるのかもとも思っていたのですが、MBPなら重くもない(むしろgrunt-markdownより軽い?)のでシンプルに同期処理です。
テンプレート変換にはlodashを使う
grunt-markdownと同じように、テンプレートファイルを使ってHTMLを作成したいので、またいろいろ調べた結果lodashというものを使えばいいとわかりました。
インストール
npm install lodash --save
利用コード
var _ = require('lodash'); ……中略…… //テンプレートの読み込み try{ var template = fs.readFileSync( path.join(workfolder, '_template.html'), 'utf-8'); } catch (err){ require('dialog').showErrorBox('Error', '_template.html not found.'); return '_template.html not found.'; } ……中略…… // lodashを使ってテンプレートにはめ込む var compiled = _.template(template); try { fs.writeFileSync(htmlfile, compiled({content: html})); } catch (err){ require('dialog').showErrorBox('Error', 'cannot write file.'); return 'cannot write file.'; }
なぜテンプレートが必要かというと、任意のCSSを読み込めるようにするためです。
Markdownと同階層に_template.html
というファイルを書いておくと(存在しないとエラーになる仕様ですが)、書籍の仕様にあわせたCSSを読み込めるわけです。
_template.html
の例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>doc</title> <link rel="stylesheet" href="hljsstyles/vs.css"> <link rel="stylesheet" href="_pagestyle.css"> </head> <body> <%= content %> </body> </html>
タブ表示にはuI bootstrapを使った
タブをどうやって実装すればいいのかもしばらく悩んだのですが、参考例がAngular.jsを使ってUIを作っているので、それをマネしようということで、Angula.jsと併用する前提のuI bootstrapというものを使うことにしました。
見た目があまり気に入っていないのですが、とりあえずそこは捨てているのでイイかと。
インストール方法
bower init bower install angular --save bower install bootstrap --save bower install angular-ui-bootstrap-bower --save
ファイル監視にはchokidarを使った
開いているMarkdownファイルが更新されたときにプレビューも自動更新させたいので、ファイル監視にchokidarというパッケージを使いました。Node.js何でもありますね。
インストール方法
npm install chokidar --save
利用コード
// ファイル監視 var chokidar = require('chokidar'); ……中略…… // 監視の準備 watcher = chokidar.watch(openfile); // Markdownファイルが苦心されたらHTMLを作り直してリロードする watcher.on('change', function(path){ console.log('change: ' + path); fileUtil.convertMarkdown(openfile); document.getElementById('html-preview').contentDocument .location.reload(true); });
HTMLの表示にはiframeを使った
最終的にはVivliostyleのビューワーを表示させたいので、たぶんiframeがいいのだろうと。実装はたぶん普通のWebでの表示と同じです。
ElectronはBrowserWindowパッケージを使ってメインウィンドウを表示し、そこにUIのHTMLファイルを読み込ませる仕組みなのですが、そのUI用HTMLの中では普通のWebページと同じように書けばいいようです。
index.html
<!--生のHTMLプレビュー--> <uib-tab index="0" heading="HTMLPreview"> <iframe id="html-preview" class="row-html" src=""></iframe> </uib-tab>
index.js
// markdownからhtmlファイルを作成 var htmlfilepath = fileUtil.convertMarkdown(openfile); // iframeに読み込む var iframe = document.getElementById('html-preview'); iframe.src = 'file://' + htmlfilepath;
開発用エディタはVisual Studio Codeを使った
何かデバッグもできるというので使ってみました。ちょっとしか使ってないですが、悪くないような気がします。
ただ、ElectronはChromeのデベロッパーツールがそのまま使えるので、結局デバッグはそっちを使いました。よく見かけるツールなので安心です。
今後
とりあえず動くようにはなりましたが、今後最低限やりたいことが4点あります。
置換ファイルに沿った後処理
Markdownには範囲ブロックを指定するような仕組みがないので、それを補うために「★★ここからコラム★★」「★★ここまでコラム★★」みたいな文字列をMarkdownの中に書いておくと、任意のdivタグ(例:<div class="column></div>
)などに置換する仕組みを付けようと思っています。
これはJSON形式の置換リストを読み込んで、string.replaceするだけでいいはずなので何とかなるでしょう。
Vivliostyleを使ったプレビュー表示
Viviliostyleviewerを利用するにはAjaxが使えるWebサーバが必要ですが、Electronで簡易Webサーバを動かす記事は見つけてあるのでたぶん何とかなるでしょう。Vivliostyleは同梱しないつもりなので、Markdownファイルと同じ階層に自分で置いてね、みたいな仕様になっちゃいますが。
インストーラーを付ける
まだ調べてないですが何とかなるでしょう。
InDesign向けのXML書き出し
この機能は完全に社内でしか必要ではないので、どうしようかちょっと迷っています。とはいえXML書き出しだけGrunt使うというのもスマートではないし……。
上の3つぐらいなら、たぶん1日あればできるのではないかなと。