Umi Uyuraのブログ

プログラミング関連の作業ログ

EmacsでReact(JSX)コーディングする環境について調べてみた

Reactの開発をする際、JSXで書くのか、それともJavaScriptベースにするのかと大きく2パターンありますが、コンポーネントの構造が把握しやすい点や、タイプ数も少なくなりそうなので、JSXで書くことにしています。

JSXの場合、JavaScriptのコードの中にHTMLタグが出てくるというものになりますが、Emacsでコーディングする際に、シンタックスハイライトや構文チェックをやりたいなーと思ったので、調べてみました。

※2015/10/28追記

JSXHintがDeprecatedになったため、ESLintに切り替えました。

umi-uyura.hatenablog.com

※2015/11/7追記

js2-modeがJSXの編集に対応したそうです。

Emacs で React の jsx を js2-mode で書けるようになってたメモ - 牌語備忘録 -pygo

前提

項目 バージョン
Emacs 24.4
web-mode 20150511.1326
flycheck 20150511.1432
JSXHint 0.14.0
JSHint 2.7.0

JSXのシンタックスハイライト

js2-modeではJSXはサポートしていない

EmacsJavaScriptコーディングする際によく利用されていると思われるjs2-modeですが、残念ながらJSXはサポートされていないようです。

少し前まで ReactのGitHub WikiのComplementary Toolsページ に、以下の記述があったのですが、最近見たら消えていました。

Many editors already include reasonable support for JSX (Vim, Emacs js2-mode).

f:id:umi-uyura:20150513214105p:plain

確かに、HTMLタグ部分はハイライトが効いていません。

web-modeはJSXをサポートしている

最近EmacsでHTMLを編集する際におすすめされている web-mode ですが、こちらはJSXをサポートしているようです。

web-modeの公式サイト を見ると、 compatibility with many template enginesreactjs (jsx) の記述ありました。

f:id:umi-uyura:20150513214119p:plain

(わかりにくテーマを使ってしまいましたが…) React.render() 内のJSX部分もタグとしてハイライトされていました。

HTML内にJSXを書く場合も、 <script type="text/jsx"> を認識してハイライトしてくれるので、現状JSXを扱う場合はweb-modeにしてしまった方が良さそうです。

.jsxをweb-modeで開く

JSXで書くJavaScriptファイルには .jsx拡張子を付けることにして、そのファイルを開いた場合はweb-modeになるようにしました。

(require 'web-mode)

...
(add-to-list 'auto-mode-alist '("\\.jsx\\'" . web-mode))

インデントの調整

また、web-modeの設定例を探すと以下の様な設定をよく見かけていたのですが、これだとインデントがスペース2に揃わないケースがありました。

(defun my-web-mode-hook ()
  "Hooks for Web mode."
  (setq web-mode-markup-indent-offset 2)
  (setq web-mode-html-offset   2)
  (setq web-mode-css-offset    2)
  (setq web-mode-script-offset 2)
  (setq web-mode-php-offset    2)
  (setq web-mode-java-offset   2)
  (setq web-mode-asp-offset    2)
  (setq indent-tabs-mode nil)
  (setq tab-width 2))
(add-hook 'web-mode-hook 'my-web-mode-hook)

公式サイトなどを見ていたところ、どうやら上記設定に該当する変数が新しくなっているようだったので、私の場合は以下のように設定することにしました。

(defun my-web-mode-hook ()
  "Hooks for Web mode."
  (setq web-mode-attr-indent-offset nil)
  (setq web-mode-markup-indent-offset 2)
  (setq web-mode-css-indent-offset 2)
  (setq web-mode-code-indent-offset 2)
  (setq web-mode-sql-indent-offset 2)
  (setq indent-tabs-mode nil)
  (setq tab-width 2))
(add-hook 'web-mode-hook 'my-web-mode-hook)

(余談)jsx-mode.elはDeNA製JSX用

いろいろ探していると、 jsx-mode.el という、それっぽいものが引っかかってくるのですが、これはFacebookのJSX(React)用ではなく、DeNAが開発した JSX 用のようです。

JavaScriptの歴史には詳しくありませんが、DeNAのJSXは、ECMAScript 6のベースになったECMAScript 4ベースの構文がサポートされているらしいので、ES6風に書く際のシンタックスハイライトは有効なのかもしれませんが、FacebookのJSX部分をハイライトしてくれるのかは、使っていないのでわかりません。

JSXの構文チェック

最近はFlycheckを導入して、JavaScriptの構文チェックをしていました。

JSX向けには、JSX部分も構文チェックする方法と、逆にJSX部分を無視する方法があるようです。

Flycheck + JSXHintによる構文チェック

JSXHint というJSHintのラッパーツールを利用することで、JSX部分の構文チェックもできるようです。

JSXHintのインストールと設定

npmでインストール。

$ npm install -g jsxhint

init.elに以下のように設定しておくことで、web-mode時でかつコンテンツがJSXと判定される場合、JSXHintによる構文チェックをFlycheckが実行してくれます。

(flycheck-define-checker jsxhint-checker
  "A JSX syntax and style checker based on JSXHint."
  :command ("jsxhint" source)
  :error-patterns
  ((error line-start (1+ nonl) ": line " line ", col " column ", " (message) line-end))
  :modes (web-mode))
(add-hook 'web-mode-hook
          (lambda ()
            (when (equal web-mode-content-type "jsx")
              ;; enable flycheck
              (flycheck-select-checker 'jsxhint-checker)
              (flycheck-mode))))

ただ私の環境では、.jsxではうまく動いているようでしたが、HTMLファイル内にJSXコードがある場合はチェックできていないような感じでした。

JSXHintの設定

私の環境では、2点ほどJS(X)Hintの設定を変更しました。

Mixed double and single quotes ... というエラーが、引用符を使っていないような箇所でも発生するようになってしまいました。

最近はJavaScript内の文字列にはシングルクォートを使うようにしているのですが、構文チェックをするに辺り、JSXをJavaScriptへ変換していて、その際に発生する文字列にダブルクォートが使われているのだろうと推測しました。

あまりにも多いので、 .jshintrc の "quotmark": false を設定して、引用符の種類はチェックしないようにしました。

また、 React がグローバルに出てきてしまうので、これも許容するようにしました。

{
  "globals": {
    "React": true
  },
  "quotmark": false,
  ...
}

このあたりも、使ううちにもっと調整が必要かも。

JSHintを使いつつ、JSXは無視する

これはJSHintの機能ですが、コメントに jshint ignore:〜 という指示を入れておくと、ある部分をJSHintのチェック対象外にできるようです。

複数行の場合は jshint ignore:startjshint ignore:end で囲み、1行の場合は jshint ignore:line を使うようです。

'use strict';

var Hello = React.createClass({
  render: function() {
    return (
      <h1>{this.props.message}</h1>
    );
  }
});

React.render(
  // jshint ignore:start
  <div>
    <Hello message="Hello, World!" /></Hello>
  </div>
  // jshint ignore:end
  ,
  document.getElementById('example')
);

上記のようにすると、 React.render() 内のJSX部分はチェックされないので、不要なエラーを発生させなくすることができます。

スニペット

react-snippets

johnmastro/react-snippets.el

MELPAに react-snippets というものがありましたが、正直各APIはどの程度使うものなのかまだまだ理解できていないこともあり、現時点では様子見しています。

js-mode、js2-mode、web-modeに対応しているので、package.elを使っていれば、簡単に導入はできそうです。

ちなみに、どんなものが用意されているのかを見てみたところ、以下のとおりでした。

key スニペット
cc React.createClass
cdm React#componentDidMount
cdu React#componentDidUpdate
cwm React#componentWillMount
cwrp React#componentWillReceiveProps
cwu React#componentWillUpdate
cwum React#componentWillUnmount
gdp React#getDefaultProps
gis React#getInitialState
r React#render
rc React.renderComponent
scu React#shouldComponentUpdate
sp React#setProps
ss React#setState

作られたのが1年前だけあって、すでに React.render に置き換わっている React.renderComponent が残っていたりしますね。

まとめ

ざっと調べてみて、簡単に導入できそうなものとしてはこのくらいでした。

調べている最中に、JSHintの次のLintツールと言われているESLintもJSXに対応していることに気付いた( Announcing ES6 and JSX Support - ESLint - Pluggable JavaScript linter )のですが、ESLint自体をまだ使っていないこともあり、しばらくはこの構成でいってみようと思います。

参考

Emacs実践入門 ?思考を直感的にコード化し、開発を加速する (WEB+DB PRESS plus)

Emacs実践入門 ?思考を直感的にコード化し、開発を加速する (WEB+DB PRESS plus)

入門 React ―コンポーネントベースのWebフロントエンド開発

入門 React ―コンポーネントベースのWebフロントエンド開発