ES6でクラス名を動的に変更する方法

ES7のdecoratorsでクラスを生成するとクラス名がそのデコレータのものになってしまいます。
例えば:

import React from 'react'

function addAwesomeProperty (Spec, Component = Spec) {
  class AwesomeClass extends React.Component {
    render () {
      return React.createElement(
        Component,
        Object.assign({ awesome: 'yes!' }, this.props)
      )
    }
  }
  return AwesomeClass
}

@addAwesomeProperty
class OriginalClass extends React.Component {
  ...
}

こうすると生成後のクラスの名前は AwesomeClass になってしまいます。これは不便。
だからといってクラス名を以下のように変えようとしても出来ません:

AwesomeClass.name = 'OriginalClass'

なぜなら name プロパティは writable ではないからです:

Object.getOwnPropertyDescriptor(AwesomeClass, 'name')
    { value: 'func',
      writable: false,
      enumerable: false,
      configurable: true }

しかしES6の仕様によると以下の方法で変更できるとの記述を見つけました:

> Object.defineProperty(func, 'name', {value: 'foo', configurable: true});
> func.name
  'foo'

という事は、前述の例を以下のように修正すればクラス名に影響を与えずに済みます:

import React from 'react'

function addAwesomeProperty (Spec, Component = Spec) {
  class AwesomeClass extends React.Component {
    render () {
      return React.createElement(
        Component,
        Object.assign({ awesome: 'yes!' }, this.props)
      )
    }
  }
  // 追加
  Object.defineProperty(AwesomeClass, 'name', { value: Component.name, configurable: true })

  return AwesomeClass
}

まぁ、これが設計として正しいのかは謎です。

蛇足

動的なクラス名を定義する方法として、

const classes = { [ dynamicClassName ]: class { ... } }
classes[dynamicClassName].name  // => dynamicClassName

みたいな方法もあるみたいだけど、自分の環境では常に _class となって上手く行かなかった。
自分のbabelの設定が悪いのかもしれないけど。

remark+ReactでMarkdownをレンダリングする

本記事は React Advent Calendar 2016 9日目の記事です。

本稿では、remarkというプラグインベースのMarkdownプロセッサを用いて、ReactのdangerouslySetInnerHTMLを使わずにMarkdownをレンダリングする方法をご紹介します。
InkdropというMarkdown専用ノートアプリを作っていて、その中でこのremarkを使っています。

XSSを回避してReact方式でDOM操作したい

ReactでMarkdownをレンダリングしようと思うと、markedとかmarkdown-itを使ってHTMLの文字列に変換してからdangerouslySetInnerHTMLで無理やりねじ込む実装方法が多いと思います。

続きを読む remark+ReactでMarkdownをレンダリングする

atomエディタのソースコードを再利用してアプリのカスタマイズ性を高めよう

本記事はElectron Advent Calendar 2016 5日目の記事です。

InkdropというElectron製ノートアプリを作っています。
このアプリにはプラグイン機構による拡張性を備えているのですが、これはatomからコードを拝借して実装しました。
atomはMITライセンスによるオープンソースのテキストエディタです。
その際に得た知見を共有したいと思います。
これをきっかけにあなたのElectronアプリ改善のお役に立てれば幸いです。
atomのパッケージを作ったことのある方ならすんなり理解できると思います。

続きを読む atomエディタのソースコードを再利用してアプリのカスタマイズ性を高めよう

Markdown-Itで独自レンダリングする方法

軽量でPluggableなJS製Markdownパーサ&レンダラのMarkdown-It
例えばaタグは全てtarget="_blank"にしたいなどの要求を実現するにはレンダラの処理を一部変更する必要がある。
その変更方法について説明する。

md.renderer.rulesを書き換える

以下のようにlink_openという名前のルールを書き換えることで、target="_blank"をデフォルトに出来る。

続きを読む Markdown-Itで独自レンダリングする方法

ElectronでデスクトップからD&Dでファイルを受け取る方法

FinderやエクスプローラなどからElectronアプリケーションにファイルをドラッグ&ドロップする操作を実現する方法について説明する。
すごく簡単。

まず以下のようにデフォルトの挙動をキャンセルする。

document.ondragover = document.ondrop = function (e) {
  e.preventDefault()
}

続きを読む ElectronでデスクトップからD&Dでファイルを受け取る方法

jshintで”Redefinition of Promise (W079)”が出た時の対処方法

jshint v2.5.x 以降で、以下のようなコードを書くと、jshintさんに叱られます:

var Promise = require('bluebird');

そんな時は、.jshintrcファイルに以下の項目を追記しましょう:

{
  "predef": [ "-Promise" ]
}

参考

[JavaScript] Getter/Setterをオブジェクト初期化子で定義する方法

JS

koaのソースコード を読んで知ったのでメモ。

一般的な定義方法

JavaScriptではオブジェクトにSetter/Getterを定義できます。
Setter/Getterとは、プロパティの設定時・参照時に呼び出されるメソッドのことです。

一般的には以下のように定義します:

var o = function() {};
o.prototype.__defineGetter__("b", function() { return this.a + 1; });
o.prototype.__defineSetter__("c", function(x) { this.a = x / 2; });

var i = new o();
i.c = 10;
console.log(i.b);

オブジェクト初期化子を使った定義

このSetter/Getterは、オブジェクト初期化子を使っても定義できます。

var o = {
  a: 7,
  get b() { return this.a + 1; },
  set c(x) { this.a = x / 2; }
};

var i = Object.create(o);

このように、get, setプレフィックスをつけて関数を定義します。

ブラウザ側で使う際の注意

MDNのドキュメント によると、ブラウザ実装状況は以下の通りです:

機能 Firefox (Gecko) Chrome Internet Explorer Opera Safari
基本サポート 2.0 (1.8.1) 1 9 9.5 3

サポートされていない場合 (特にIE6-8において) 、スクリプトはシンタックスエラーを引き起こします。

参考資料