読者です 読者をやめる 読者になる 読者になる
月 の 上

expo.ioを使ってリアルタイムにReact Nativeアプリを開発する

f:id:amagitakayosi:20170417212408j:plain

1年ぶり3度目のReact Native入門してるんだけど、expo.ioっていうサービスがメッチャ便利そうだったので紹介します。
何がどう便利かっていうと、

  • ケーブルなしで、実機の動作確認ができる
  • もちろんコード変更も即座に反映される
  • 開発版アプリの配布がワンタッチでできる
  • 便利なReactコンポーネントもついてくる

youtu.be

expo.io とは

React Nativeでのアプリ開発を支援するサービスです。
公式サイトはこちら。めっちゃ素朴……

expo.io

expoは create-react-native-app XDE expo client といったツールを公開しています。

  • create-react-native-app: アプリの雛形を作るCLIツール
  • XDE: アプリ開発を支援するデスクトップアプリ。実機のログをみたり、アプリを再起動したりできる
  • expo client: 開発中のアプリを実機にインストールするためのクライアント

今回はこれらを使ってReact Nativアプリケーションを開発する方法を紹介します。

事前準備

インストールするものは多いけど、設定はほぼ不要です!

開発フロー

基本的な開発の流れはこんな感じ。

  1. create-react-native-app でアプリの雛形を作成
  2. npm start して、実機で確認しつつ開発
  3. リリースビルドを作ってストアに公開

create-react-native-app

create-react-app みたいなやつです。
基本的な操作は create-react-app と同じ。
facebook公式みたいな名前なのはちょっと邪悪な感じする……;;)

$ create-react-native-app my-app とすると、 my-app ディレクトリにアプリの雛形が作成されます。
雛形はかなりシンプルになっており、 .xcodeproj ファイルすら存在しません。
expoでは、アプリケーションの配布・ビルドを独自のしくみで行なうため、これだけで充分なようです。

f:id:amagitakayosi:20170417222536p:plain

実機で確認しつつ開発

npm start すると、react-nativeのパッケージャーにより、JSのビルドが始まります。
同時に、ターミナルに巨大なQRコードが出現します。かっこいい。

f:id:amagitakayosi:20170417223418p:plain

手元のデバイスにインストールした expo client でこのQRコードを読み取ると、開発中のアプリケーションがexpo clientにインストールされます。
これによって、ケーブルなしでも実機で動作確認しつつ開発を進めることができます。

ReactでふつうにWeb開発してるような気分。

開発中のアプリを配布

XDE上にある「Publish」ボタンを押すと、開発中のアプリを配布するためのリンクが生成されます。
これにより、アプリを社内リリースしたり、友達にだけリリースしたりといったことができます。

f:id:amagitakayosi:20170417224311p:plain

ほぼ雛形の状態でPublishを押したのがこちら。
インストールにはexpo clientが必要です。

my-app — Expo

リリースビルド

Building Standalone Apps | Expo v15.0.0 documentation

expo clientなしで、普通にApp Storeから配布するためには、exp というツールでビルドする必要があります。
今回はリリースまでやってないけど、iOS版のリリースビルドを作成する手順はこんな感じみたいです。

  1. $ npm i -g exp && exp login
  2. exp.json を作成
  3. exp start
  4. 別のターミナルで exp build:ios を実行

便利コンポーネントがついてくる

create-react-native-app で作ったアプリには、最初から expo がインストールされている。
わかりやすいところだと、画像を選択するImagePickerや、地図を表示する MapViewOpenGLの描画を行なうGLViewなどがある。

感想

前回React Native入門した時は全然ビルドできなくてハマってたんだけど、今回はexpoのおかげでめっちゃ気軽に開発を開始できた。
FAQにも「いまのところ有料にする予定はない」って書いてあるし、良いんじゃないですかね。

ハーフマラソンに出場してきた

f:id:amagitakayosi:20170417173353p:plain

3月末、大阪で開催されたハーフマラソンに出場した。
ついでにスーパー銭湯でのんびりしたり、鶴橋で焼肉を食べたりした。

マラソン

淀川沿いを往復するやつ。

第7回 淀川国際ハーフマラソン|サンスポマラソン

去年なんとなくジョギングしてた時22キロくらい走ってたりしたので、まあ行けるでしょ、というノリでエントリーした。
しかし、マラソン大会の経験は当然無いし、子供の頃から体力ゼロで通してきたので、結構不安になって朝のジョギングの頻度を上げたりしていた。
目標は低く、「完走できればOK」とした。

天気予報によると当日は午後から雨だったので、前日にはスポーツショップや100均に買い出しに行った。
ウエストバッグは持っていたものの、重さや収納を考えて、こちらの商品を購入した。

FlipBelt(フリップベルト) スポーツウエストポーチ FBB ブラック M

FlipBelt(フリップベルト) スポーツウエストポーチ FBB ブラック M

当日午前中は良い感じに涼しい天気だった。
ファミリーコース?みたいなのもあり、家族連れがおおい。
主催の人が歌ってたり、NMB?かなんかの曲にあわせて準備体操が始まったり、平和な感じ。

これ司会してるのラジオDJかなんかかと思ったけど、どうやら有森裕子本人らしかった。異常にテンション高い。
有森さん、スタート地点でも「いってらっしゃーい頑張って〜!フォォォおお!!」みたいな感じで、やっぱアスリートってエネルギーすごいんだなって思った。

序盤は「エッこんなゆっくり走っててもみんなスイスイ追い抜けちゃうの?」って思うくらい楽々で、道路脇の芝生の部分を走ってたりしたんだけど、これが裏目に出たのか、半分すぎた辺りから追い抜かれることが多くなった。
追い抜かれると焦ってしまって余計しんどくなるので、後半は目をつぶって走ったりしてた。

給水所では、水のほかにも一口大にカットされたアンパンやクリームパンが配られていた。
意外と制限時間までは全然余裕だったので、ちょっと立ち止まって味わって食べた。

最後のほうは足が半分麻痺してたけど、どうにか完走出来てよかった。
順位を見ると半分より下なのでやっぱり悔しい気はする。

こちらは当日のようすです。
走りながら撮るのも楽しい。

スーパー銭湯

マラソンのあとは、会場から電車で30分ほどのスーパー銭湯へ向かった。
街なかにあり、程よい広さでいろんな種類のお湯に浸かれて便利。

www.nobuta123.co.jp

日曜の夕方だが、部活帰りの少年たちが非常に多かった。
高校生や大学生のバイトが大量に働いていたのもあり、自分が大人サイドの人間って感じがしてしまう。

銭湯なのに美容室やレストランが併設されていたり、建物内の施設では自販機含め全て独自システムで決済したりと、一つの国家のような様相を呈していた。

鶴橋で焼肉

たっぷり筋肉を痛めつけたという名目で焼肉へ。
以前から鶴橋に行きたかったんだよ

www.yakinikusora.jp

鶴橋は焼肉屋さんだらけだが、今回は一番有名っぽいとこにした。
ホルモン5種盛りがお得で旨い。

ごちそうさまでした。

Scala入門としてCLIツールを作り、Scala.js, React, ScalaCSSでサイトを作った

fand.github.io

今年2月からScalaのチームに異動し、Scala入門している。

2月中はゆっくりコップ本を読んでいたのだけど、やっぱり手を動かさないと自信が付かないので、何かツールを作る事にした。
ついでにGitHub PagesもScala.jsやScalaCSSで実装してみた。

つくったツール

github.com

!!! 実用性は度外視しています !!!

レポジトリをまるごとコピーしてくれるやつ。
ブランチを移動する度にコンパイルで時間かかるのを回避するため、ブランチ毎にコピーしたらいいのでは、という発想(上手く行ってるかはわからない……)。
手で cp -r するのが一番はやいし、ツールを作るにしても普段だったらJSで書くとこだけど、今回はせっかくなのでScalaCLIツールを作ってみた。

使い方

brewでインストール可能。

$ brew tap fand/clonepool
$ brew tap fand/clonepool

clonepool -h でかっこいいヘルプが出る。

f:id:amagitakayosi:20170407162300p:plain

仕組み

内部では motemen/ghqpeco/peco を利用している。
$ clonepool rails/rails my-branch と入力すると以下の処理が走る。

  • $ ghq get rails/rails により、 $(ghq root)/github.com/rails/rails にレポジトリがクローンされる
  • ~/.clonepool/github.com/rails/rails/my-branch にコピーを作成
  • my-branch ブランチにチェックアウト

引数なしで $ clonepool とすると ~/.clonepool 以下のディレクトリを一覧できる。
cd $(clonepool | peco) とすると絞り込みつつ移動できて便利。

f:id:amagitakayosi:20170407162312g:plain

実装について

ほとんど scala.sys.process.Process を利用してシェルコマンドを実行しているだけ。
Gitレポジトリの操作ということで、最初はJGitを利用していたんだけど、シェルコマンドを利用したほうが楽なことがわかったのでやめてしまった。

Processの実行は scala.util.control.Exception.allCatch でラップしている。
allCatch {} は、ブロックを評価した結果をSomeで返し、例外が出たらNoneを返してくれる。

seratch.hatenablog.jp

個人的には、CLIツールを実装するときは気軽に死にたい。
JSだと、大部分ををPromiseベースで実装して、一番外側で全部catchするというのをよくやっている。
今回は getOrThrow という関数を作って、気軽に死ねるようにした。

https://github.com/fand/clonepool/blob/master/src/main/scala/example/Util.scala#L9

CLIツールを作るためのライブラリは SCOPTCLIST などがあるが、あんまり好みじゃなかったので今回は利用していない。
かっこいいヘルプは ansi-interpolater を利用して実装した。

homebrewでインストールできるように

Scalaのライブラリは、maven centralに登録したり、オレオレmavenレポジトリを作って公開するらしいけど、CLIツールを配布できるか良くわからなかった。
conscript を使えば簡単に配布できるらしいけど、利用者もconscriptをインストールする必要があってなんか面倒。
sbtはhomebrewで配布されているので、今回はsbtを真似てみることにした。

今回つくったformulaはこんな感じ。

https://github.com/fand/homebrew-clonepool/blob/master/clonepool.rb

sbtのformulaと大体同じ。
jarファイルをlibexecに配置し、bin/clonepoolに java -jar clonepool.jar するシェルスクリプトを置いている。

jarファイルはsbt-assemblyというsbtプラグインを利用している。
$ sbt assembly を実行すると勝手にjarが出来て便利。

github.com

GitHub Pagesについて

http://fand.github.io/clonepool

今回の目的はScala入門、ということでGitHub PagesもScala.js + ScalaCSS + Reactで構成した。
Octocatをクリック(またはタップ)すると回転します。

f:id:amagitakayosi:20170407162432g:plain

Scala.js

www.scala-js.org

AltJSの一種。Scalaで書かれたコードをJSに変換する。
一昔前に「機能もヤバいしファイルサイズもデカ過ぎてヤバい」って話題になってたけど、今では大分マシになってる。
今回のコードはgzip圧縮済みで122 KBだ。

sbt fastOptJS または sbt fullOptJSコンパイルする。
~fastOptJS とするとファイル変更をwatchしてくれる。
fullOptJS を実行すると70秒かかる……。

この前Webpack用のローダーが公開されたりしてたけど、今回はsbtで開発した。

https://github.com/mrdziuban/scalajs-loader

Scala.js自体の内容に関しては特に言うことなくて、ふつうにScalaって感じ。
ただ、あるクラスの子オブジェクト一覧を取得したくて scala.reflect.runtime を使おうとしたけど、なんか上手くいかなかったな、そういえば。

scalajs-react

Scalaは現代的な言語なのでReactも書けます

github.com

Scala.jsからReactを利用するためのライブラリ。 JSXの代わりに、 <.div() とか ^.src := "hoge" みたいな感じでDOMを記述する。

import japgolly.scalajs.react.vdom.html_<^._

<.ol(
  ^.id     := "my-list",
  ^.lang   := "en",
  ^.margin := 8.px,
  <.li("Item 1"),
  <.li("Item 2"))

(ドキュメントより抜粋)

記法自体は慣れたらそんなに難しくない。
しかし、コンポーネント自体とは別にBackendというクラスを書く必要があったり、独自の概念がたくさんあって大変。
何より実装がほとんど読めないのがしんどい……。

ScalaCSS

CSSScalaで書ける時代……!

github.com

scalajs-reactと同じ作者が開発している。
Super type-safe CSS とのこと。???????
例えば、widthなどのピクセルを指定する場所に無効な文字列を書くと、型が合わないといって怒ってくれる。

ScalaCSSはStandalone APIとInline APIの2種類のAPIを持つ。
単なるAltCSSとして使う時は Standalone APIを、CSS in JSを行なうときはInline APIを利用する。
今回は、コンポーネント毎のスタイルはInline APIで生成し、ページ全体で共通のスタイルはStandalone APIを利用して記述した。

この辺はtakezoeさんの記事で解説されてる。

takezoe.hatenablog.com

CSS in JSライブラリとしては、生成されるクラス名が .MyComponent--button のように予想しやすいクラス名になってしまうのが難点。 (多くのCSS in JSライブラリでは、クラス名をランダムな文字列にすることで、意図しないクラス名の衝突を回避している)

他にも、コンパイル時間が長かったり、対応していないCSSプロパティが多いので、得られる恩恵も割に合わない気がする……。 が、これも慣れてしまえば、書くの自体はそんなに難しくない。

最終的なコンポーネントの定義は以下のようになった。

package io.github.fand.clonepool.docs.components
import japgolly.scalajs.react._, vdom.html_<^._
import scalacss.Defaults._
import scalacss.ScalaCssReact._

object Link {
  val component = ScalaComponent.builder[Tuple2[String, String]]("Link")
    .render_P(t =>
      <.a(
        LinkStyle.link,
        ^.href := t._2,
        t._1
      )
    )
    .build
}

object LinkStyle extends StyleSheet.Inline {
  import dsl._
  val link = style(
    color(c"#EFF"),
    pointerEvents := "auto"
  )
}

その他

Sketchでロゴをつくったり、 http://realfavicongenerator.net/favicon生成したりした

今回学んだこと

  • Scala
    • allCatchで雑にエラーを扱う方法
    • Scala.js
    • Scala.js + React
    • ScalaCSS
  • homebrewのformulaを書く方法

今度はGolang + GopherJS入門しようかな〜〜

ハンズオンを円滑に進めるテク

新人教育の時期ですね。
みなさん研修資料の準備は大丈夫ですか?

僕はこの前Reactのハンズオンをやったんだけど、そこで心がけた事や学んだ事を共有します。
ハンズオンの内容については既に記事を書いたけど、細かい所を書ききれなかった。
amagitakayosi.hatenablog.com

新人研修やインターンでも使えるテクでなので、ご参考に。

やったこと

最初に完成形を提示する

ハンズオンで作るアプリについて、動画で説明した。
最終的な目標がわかると、今やっている作業の意味がわかりやすくなる。

いくつかのステップに分けて説明する

今回のハンズオンでは、8つのステップにわけて実装を進めた。
ステップ毎にテーマを与える事ができるし、ハンズオン自体の進捗もわかりやすくなる。

ステップ毎に足並みを揃える

ハンズオンでは、一度置いていかれると追いつくのが難しい。
参加者の進捗は頻繁に確認すること。

挙手を多用する

「出来ましたか?」って聞いても、答えてくれる人は少ないので、「出来たひと〜?」って挙手してもらうとよい。
(今回は挙手してもらいすぎたかも……)

お手本ブランチを用意する

ちょっと変則的なんだけど、今回はお手本ブランチを用意して、困ったときに参照できるようにした。
このブランチはステップ毎にgitのタグを打ってある。
ちょっと遅れたとしても、 git checkout step-2 等とすれば追いつけるようになっている。

スクリーンショットを多用する

わかりやすさは 画像/動画 >>> 文字 >>>>>>>> 口頭での説明 だと考えてる。
複雑な所では、keynoteやSkitchの機能で注釈を入れると良い。

動画のつくりかた
動画つくるの、実はそんなに面倒ではない。
Macの場合の手順は次のとおり。

スライドの冒頭で、教科書や資料へのリンクを表示する

資料は事前に共有されたい!
作業のペースは人によって違うので、手元で資料を見れるとグッと進めやすくなります。

会場のWiFi情報なども載せておくとラッキー。

前日にTwitterで事前準備をお願いする

git clonenpm install といった、時間のかかる作業は事前にお願いしておくと良い。

学んだこと

参加者同士の協力をお願いする

開始時に「参加者の皆さん同士で助け合ってくださいね!(^~^)」とか言っておくと良い。
Reactハンズオンでは、何も言わずとも良い感じに助け合ってくれて、とても助かった。

来場者の前提知識をハッキリさせる

gitを知らない人がgit checkoutできなくて困ってた。
必要な知識をリストアップして、事前に共有しておくと良い。
発表資料には、前提知識のない人のために適切な教材へのリンクなどを貼っておくとよい。
(当日解説してもいいが、往々にして時間が足りなくなる)

以上です

明日はエイプリルフールですね。皆様のご入社をお待ちしております。

asyncを書くのも面倒なのでasync-nodeを作った

https://cloud.githubusercontent.com/assets/1403842/24457202/7dca1bce-14d0-11e7-9df7-d1a674b46198.png

ちょっとスクレイピングする機会があり、Nightmareでスクリプトを書いたんだけど、Promiseをベタベタ書いたりする必要があって面倒だった。 Node.js v7.6.0からはasync/awaitが使えるようになったけど、スクリプト全体をasync functionで囲んだり .catch() する必要があったりして、ノイズが多い。

というわけで、トップレベルにいきなり await を書いても実行できる async-node というコマンドを作った。

github.com

インストール

npm i -g @fand/async-node

使用例1) GIPHYから画像をダウンロード

giphy_opt

Nightmareで画像を検索し、kevva/downloadで手元にダウンロードするスクリプト

ソース: https://github.com/fand/async-node/tree/master/examples/image-downloader

使用例2) MongoDBのデータを取得/挿入

$ async-node mongo.js -s '{"name":"foo"}'  # 挿入
$ async-node mongo.js -g '{"name":"foo"}'  # 取得

ソース: https://github.com/fand/async-node/tree/master/examples/mongo

どうぞご利用ください🔪🔪🔪

FRONTEND CONFERENCE 2017でReactハンズオンを開催しました

speakerdeck.com

2017-03-18 (土) 梅田で開催された FRONTEND CONFERENCE 2017 で、Reactのハンズオンを開催しました。

kfug.jp

今回のハンズオンでは

  • Reactアプリケーションの開発を身体で覚える
  • React開発環境におけるトレンドを体験する

の2つを目標に、Next.jsを利用した簡単なReactアプリを作成しました。

この記事では、ハンズオン開催にあたって考えたことや、工夫した点などについて書いていきます。

ハンズオンの方向性

2月半ばにハンズオン講師のお誘いがあり、引き受ける事にしたものの、どのようなテーマにするかしばらく悩んでいました。
ハンズオンの時間は60分です。
Reactのメリットについてひたすら説明しても良いけど、60分でどれだけ伝えられるかわかりません。
まずは参加者層を想定し、それからテーマを考えることにしました。

昨年のFRONTEND CONFERENCEに参加した際、東京のエンジニア向けカンファレンスとは参加者層が異なると感じました。
全体的に、デザイナーやコーダーといった、エンジニア以外の職種の人が多い傾向にあります。
同じエンジニアでも、受託系のWeb制作会社やフリーランスの方が多いイメージです。

Reactの非常に強いコンポーネント指向は、大規模なWebアプリや寿命の長いWebサービスにおいて、コードの保守性を高め、長期的な開発コストを抑えられます。
しかし、いわゆるMVCフレームワークの経験のない人にとっては、設計上の利点が実感しづらくもあります。

Reactを使う以上、参加者にはコンポーネント指向の利点を体感してもらいたいです。
しかし、ハンズオンでは、60分で未経験者にReactアプリケーションを完成させてもらう必要があります。
そのため、Reactの概念や設計について時間をかけて説明するよりも、実際に手を動かすことで自然にコンポーネント開発を体験してもらう、という方向にしました。

Next.jsを採用

github.com

Next.jsはReactで簡単なSPAを開発するためのフレームワークです。
Next.jsでの開発は、ページに対応するJSファイルを pages/ に作成する所から始まります。
まるでHTMLファイルをpublicディレクトリに配置するような感覚で開発でき、初めてSPAを開発するときに躓きがちなポイントを回避できます。
また、最初からServer-Side Renderingが有効になっていたり、CSS in JS用のツールが組み込まれていたりと、Reactコミュニティで流行している技術を簡単に体験できるようになっています。

ハンズオンの流れ

ハンズオンは8つのステップに分かれていますが、学習内容としては4段階に分類できます。

まずはページを作成し、Reactコンポーネントの作り方とNext.jsの基本を覚えます。
初見時のシンプルさを重視し、コンポーネントはStateless Functional Componentで作成するようにしました。

次に、共有コンポーネントを作成し、importprops について学びます。
これにより、参加者は一般的なテンプレートエンジンと同様の機能をReactで実現できるようになります。

スタイルはCSS in JSで定義するようにしました。
他の方法よりも強いコンポーネント指向を体験でき、Reactコミュニティの流行についても触れることができるからです。

最後に状態管理について学びます。
コンポーネントをまたいだ状態管理が必要なアプリを作ることで、「状態の管理は親コンポーネントで一元的に行なう」「末端のコンポーネントは状態を管理しない」というコンポーネント指向を身に着けます。

工夫した点

ハンズオンの準備は、リポジトリ作成、教科書作成、そしてスライド作成という順序で行ないました。

これらの準備において工夫した点について説明します。

お手本ブランチを用意する

ハンズオンは、教科書リポジトリを手元に git clone し、少しずつ実装を進めるという流れで行ないます。
ブランチの構成は以下のようになっています。

f:id:amagitakayosi:20170324175624p:plain

develop ブランチにお手本を作り、ステップに対応したタグを登録しました。
これにより、参加者がどこかで詰まったとしても、 git checkout step-4 のようにすることでキャッチアップできるようになっています。

スライドにはステップ毎に画面のスクリーンショットを載せ、上手く行っているか確認できるようにしました。

コード修正の指示をdiff形式で書く

developブランチからPull Requestを作る事で、各ステップで必要な変更内容を一覧できます。
教科書には各ステップのdiffを掲載し、編集する内容が正確にわかるようにしました。

f:id:amagitakayosi:20170324175516p:plain

diff形式に馴染みのない方も多いはずなので、ハンズオンの冒頭でdiff形式について解説しています。

自己紹介よりも前にセットアップをお願いする

ハンズオンで良くあるのが「npm installが終わらない〜〜」というやつです。
git cloneやnpm installはただでさえ時間がかかりがちですが、皆で一斉に実行すると会場のネットワークに負荷がかかり、余計に時間がかかってしまいます。

今回は、自己紹介より前にセットアップを行なうことで、この問題を回避しようとしました。

f:id:amagitakayosi:20170324175541p:plain

本番では開始時間5分前には皆着席していたので、更に時間が稼げてよかったです。
前日にTwitterで事前準備をお願いしたりもしました。

https://twitter.com/amagitakayosi/status/842633938275254272

反省した点

gitが手元にない人は git checkout できない

参加者にはgitを利用していない方もいると思い、レポジトリclone時には「git clone または zip でダウンロード」と説明しています。
しかし、実装でつまづいた時に git checkout できない事を忘れていました……。

ステップ毎にzipを用意しておくと良かったかも。

お手本ブランチ作るのしんどい……

れいなコミットログを作るため、developブランチを作る前に別ブランチで試行錯誤し、developブランチで清書したのですが、それでも後から修正箇所が沢山見つかりました。
git commit --fixup git rebase git push -f を活用し、無理矢理きれいなコミットログに仕上げたのですが、これが大変だった……😇

  • 過去のコミット foo12345 に間違いが見つかる
  • git commit --fixup=foo12345 && git rebase autosquash HEAD~n
  • git push -f
  • foo12345 移行のコミットにタグを打ち直す

なんかもっと効率いい方法見つけたいですね……

皆様の反応

ハンズオン

twitter.com twitter.com twitter.com

やっぱちょっと速かったかな🙇🙇🙇

資料

twitter.com 極端な話、Reactコンポーネントの書き方しか知らない人でもWebアプリが作れるので、HTMLベタ書きの時代よりも複雑ということは無いと思います。
Node.jsが動かないとデプロイできないという問題はありますが、Next.jsの次のバージョンでは静的ファイルへのエクスポートも出来るようになるらしいので、楽しみです。

twitter.com twitter.com

動的な要素の少ないWebサイトでは、Reactのメリットは想像しづらいかもしれませんね。
それでも、コンポーネント指向のおかげでパーツの再利用が容易だったり、テストしやすい、モダンな開発環境が整備されている、といったメリットはあると思います。

感想

ハンズオン講師は初めての経験でしたが、多くの方が完走できたようで良かったです。 もしまた開催するとしたら、今度はもっとシュシュッと準備できるといいな。

参加者の皆様、お誘い頂いた id:potato4d さん並びに運営の皆様、ありがとうございました!

Atomのはてな記法モードを作ったよ!!

https://cloud.githubusercontent.com/assets/1403842/23390963/93f9712a-fdb4-11e6-82c3-67798c57e2f6.png

atom.io

はてなブログはてなグループ、増田に投稿する時に便利です。
どうぞご利用ください。

もくじ

機能

はてな記法シンタックスハイライトしてくれる

こんな感じ

https://cloud.githubusercontent.com/assets/1403842/23390963/93f9712a-fdb4-11e6-82c3-67798c57e2f6.png

Markdown文字列をはてな記法に変換できる

md2hatenaを利用した変換機能です。

language-hatena をインストールすると、Language Hatena: Convert Markdown To Hatena Syntax というコマンドが使えるようになります。
文字列を選択し、Cmd + P でコマンドパレットを表示して、 hatena とか打ち込んだら出てきます。
何も選択されていないときはファイル全体を変換します。

https://cloud.githubusercontent.com/assets/1403842/23490546/8d97a4bc-ff3c-11e6-8514-20af7e062710.gif

便利〜〜〜〜!!!!

スニペット使える

よく使うスニペットをいくつか定義しています。

f:id:amagitakayosi:20170304095644g:plain

現在使えるスニペットは以下のとおり。

  • code: スーパーpre記法
  • inlineCode: <code></code> を出力する。文中にコードを書きたい時に。
  • table: 表組み記法
  • image: [(画像URL):image] を出力する (http記法) 。
  • comment: <!-- --> を出力する

インストール方法

AtomのInstall Packages画面で検索してインストールしましょう。

f:id:amagitakayosi:20170304095022p:plain

もちろん apm でもインストールできます。

apm install language-atom

Atomで新しい言語のモードを作る方法

ここからは、Atomで新言語に対応するパッケージを開発する方法を解説します!

基礎知識

Atomlanguage-*** の作り方、公式ドキュメントにはまとまった情報がありません。
今回得た情報を要約するとこんな感じです。

言語モードはAtomのpackageとして開発する

Atomでは、ほぼ全ての機能がpackageとして実装されています。
言語の解釈は language-*** といった名前のpackageで行ないます。
これを language package と呼びます。

言語のパースは正規表現で行なう

language packageは正規表現を羅列した Grammar ファイルを持ちます。
これはTextmateで使われていたGrammarファイルと似た形式になっています。

TextmatemacOS向けのテキストエディタです。
日本では殆ど使われていませんが、英語圏ではGUIでつかえる入門向けエディターとして、かつて人気を博していました。

github.com

Textmateでは、言語のパースは正規表現で行われていました。
Grammarファイルに記法ごとの正規表現を記述し、文字列を keywordstring といったトークンに分解していました。

Atomでは、Textmateとよく似たGrammarファイルを利用して言語のパースを行ないます。
TextmateのGrammarファイルは XML 形式でしたが、Atomでは CSON 形式で記述します。

また、Atomのパッケージ管理ツール apm には、TextmateのGrammarファイルをAtomの形式に変換する機能が存在します。
Atomのlanguage packageの多くは、この変換機能を利用して作成されたようです。

Converting from TextMate

というわけで、language packageは正規表現をこねくり回して作ることになります。

実際の手順

今回の開発は、次の手順で行ないました。

  • apm init -l で雛形を作る
  • apm link して手元のAtomにインストール
  • grammars/ にGrammarファイルを書く
  • snippets/スニペットを追加
  • settings/ を編集
  • apm publish

色々と適当ですが、順番に解説していきます。

apm init -l で雛形を作る

apmAtomのパッケージ管理ツールですが、新たにパッケージを開発するためのテンプレート機能が存在します。
apm init --language (または-l) を実行すると、language packageの雛形が作成されます。

$ apm init -l hatena
$ tree language-hatena
language-hatena
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── grammars
│   └── hatena.cson
├── package.json
├── settings
│   └── language-hatena.cson
└── snippets
    └── language-hatena.cson

3 directories, 7 files

apm link を実行すると、開発中のパッケージを手元のAtomにインストールできます(apm unlink で解除)。

$ cd language-hatena
$ apm link
/Users/amagitakayosi/.atom/packages/language-hatena -> /Users/amagitakayosi/language-hatena

Window: Reload コマンドでAtomをリロードすると、選択できる言語のリストに新しい言語が追加されたのが確認できるはずです。

grammars/ にGrammarファイルを書く

早速Grammarファイルを書いていきます。

Grammarファイルの書き方は複雑なので、いきなりまっさらな状態から書き始めるのはオススメしません。
既存のlanguage packageからよく似た言語をのGrammarファイルを元に書くことをオススメします。
はてな記法の構造はMarkdownとよく似ているため、今回は laguage-gfm のGrammarファイルを参考にしました。

Grammarファイルは以下のように書きます。

'scopeName': 'source.hatena'  # 言語の識別子
'name': 'Hatena Syntax'  # 言語の名前。言語セレクターとかで出てくる
'fileTypes': [  # 拡張子
  'hatena'
  'h'
]
'patterns': [  # 記法/トークンに対応する正規表現を書いていく
  {
    'match': '^[\\+\\-]+'  # 正規表現
    'name': 'markup.list.hatena'  # クラス
  }
]

patternsには、記法/トークンに対応するパターンオブジェクトを書いていきます。
ここでは リスト記法 に対応するパターンを記述しました。
name には、 match正規表現にマッチした文字列にあてるクラス名を指定します。

ご存知のとおり、Atomではエディター内のすべての要素がHTMLとCSSで出来ています。
シンタックスハイライト機能は、language packageがHTML要素にクラスを与え、テーマのpackageがスタイルを当てる、という仕組みで実現されています。
name で指定した文字列は . で分割され、コードを表現するHTML要素のクラスに設定されます。

今回の場合だと、以下の文字列は

- foo
- bar

次のようなHTMLに変換されます(簡略化してあります)。

<span class="markup list hatena">-</span> foo
<span class="markup list hatena">-</span> bar

パターンオブジェクトには、 match name の他にも begin end captures などのプロパティを指定できます。
それらの使い方については、実際のGrammerファイルを見たほうが速いでしょう。
https://github.com/atom/language-gfm/blob/master/grammars/gfm.cson

Grammarファイルの開発は、次のような手順で進めました。

1. patternsにパターンを一つ追加する
2. Window: Reload コマンド(Cmd + Alt + Shift + L)でAtomをリロード
3. おかしい所を直す
4. 2.3.を繰り返して、直ったら git commit

snippets/ にスニペットを追加

スニペットの追加は、普段 ~/.atom/snippets.cson を書くのと全く同じ方法で行えます。
めっちゃ簡単だし日本語情報も沢山あるので割愛。

Snippets

settings/ を編集

Atomでは Cmd + / で文字列をコメントアウトできますが、そのためには settings にコメントの記法を追加する必要があります。
language-hatena の場合はこんな感じ。

'.source.hatena':
  'editor':
    'softWrap': true
    'commentStart': '<!-- '
    'commentEnd': ' -->'
GitHubにpush

Atomパッケージを公開するには、GitHubにレポジトリを公開する必要があります。
レポジトリを作成したら git push して、 package.jsonrepository フィールドを追加してください。

{
  "name": "language-hatena",
  "version": "0.0.0",
  "description": "Syntax highlighting and snippets for Hatena Syntax (?????).",
  "repository": "https://github.com/fand/language-hatena",
  "license": "MIT",
  "engines": { "atom": ">=1.0.0 <2.0.0" }
}
apm publish

いよいよ公開です!
apm publish するとパッケージが atom.io に登録され、パッケージマネージャーからインストールできるようになります。

f:id:amagitakayosi:20170304095119p:plain

公開成功です!!!!

apm publish [更新の単位] を実行すると、パッケージのバージョン更新、gitのタグ作成、Atomへの公開まで apm が自動でやってくれます。
今回は更新の単位に minor を指定したので、バージョンは 0.0.0 から 0.1.0 に更新されました。

公開したら、実際にインストールできるか試してみましょう。
apm unlink で手元のパッケージをアンインストールし、Atomをリロードします。
Install Packages画面で検索すると、公開したパッケージが見つかるはずです。

f:id:amagitakayosi:20170304095022p:plain

お疲れ様でした!!!!🎉🎉🎉🎉🎉

参考リンク