dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

React 16.3.0で追加されたStrictModeコンポーネントについて

2018-04-01のJS: TypeScript 2.8、React 16.3.0、TensorFlow.js - JSer.info を読んで。

React 16.3.0 から StrictMode コンポーネントというものが追加されたらしい。
公式ドキュメントを読んでみます。


StrictMode とは

StrictMode はアプリの潜在的な問題を検出するために追加されたコンポーネントコンポーネントだが Fragment などと同じく UI として画面に表示されるものはない。
<StrictMode>...</StrictMode> で囲まれた子孫コンポーネントに対し、いくつかのチェックを行う。

また development モードでのみ動作し、 production build 時には影響を与えない。


ScrictMode がチェックしてくれること

今のところ以下。今後のリリースで機能は追加予定とのこと(Additional functionality will be added with future releases of React.)

  1. 安全でないライフサイクルメソッドの使用(Identifying components with unsafe lifecycles)
  2. レガシーな string ref の使用(Warning about legacy string ref API usage)
  3. 予期せぬ副作用の検出(Detecting unexpected side effects)
    • (検出するために、特定のライフサイクルメソッドを二度実行する)


1. 安全でないライフサイクルメソッドの使用

背景として、v16.3.0 以降は非同期レンダリングなどのサポートのために一部のライフサイクルメソッド

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

が今後削除予定となった。
参考:Update on Async Rendering - React Blog (こっちはまだ読んでない)

<StrictMode> の子孫コンポーネントでこれらのライフサイクルメソッドを使用しているものがあれば、ブラウザのコンソールで warning が出力される。

(サンプル)

import React, { Component, StrictMode } from 'react';

class UnsafeComponent extends Component {
  componentWillMount() {
    console.log('componentWillMount');
  }

  componentWillReceiveProps(props) {
    console.log('componentWillReceiveProps');
  }

  render() {
    return <div>Unsafe Component</div>;
  }
}

export default function App() {
  return (
    <StrictMode>
      <UnsafeComponent />
    </StrictMode>
  );
}

(結果) f:id:dackdive:20180404015423p:plain

Warning: Unsafe lifecycle methods were found within a strict-mode tree:
    in App
    in AppContainer

componentWillMount: Please update the following components to use componentDidMount instead: UnsafeComponent

componentWillReceiveProps: Please update the following components to use static getDerivedStateFromProps instead: UnsafeComponent

Learn more about this warning here:
https://fb.me/react-strict-mode-warnings

該当のコンポーネント名と使っているライフサイクルメソッドが表示されている。


2. レガシーな string ref の使用

ref を使ってコンポーネントを参照するための方法はこれまで2通りあって

  1. ref="input" のように文字列で指定する
  2. ref={(element) => this.input = element} のように callback 関数で指定する

このうち 1 の文字列で指定する方にはいくつか問題があったらしく、ドキュメントでも 2 の方法を推奨していた。
<StrictMode> の子孫コンポーネントで 1 の string ref を使っている箇所があると、こちらも同様にブラウザのコンソールでwarning が出る。

(サンプル)

import React, { Component, StrictMode } from 'react';

class LegacyRef extends Component {
  handleClick = () => {
    const name = this.refs.name.value;
    console.log('LegacyRef', name);
  };

  render() {
    return (
      <div>
        <input ref="name" />
        <button onClick={this.handleClick}>click me</button>
      </div>
    );
  }
}

class NewRef extends Component {
  constructor(props) {
    super(props);

    this.nameRef = React.createRef();
  }

  handleClick = () => {
    const name = this.nameRef.current.value;
    console.log('NewRef', name);
  };

  render() {
    return (
      <div>
        <input ref={this.nameRef} />
        <button onClick={this.handleClick}>click me</button>
      </div>
    );
  }
}

export default function App() {
  return (
    <StrictMode>
      <LegacyRef />
      <NewRef />
    </StrictMode>
  );
}

(結果) f:id:dackdive:20180404021000p:plain

Warning: A string ref, "name", has been found within a strict mode tree. String refs are a source of potential bugs and should be avoided. We recommend using createRef() instead.

    in div (created by LegacyRef)
    in LegacyRef (created by App)
    in App
    in AppContainer

Learn more about using refs safely here:
https://fb.me/react-strict-mode-string-ref


余談: createRef() を使った新しい ref の方法

上のサンプルで、 NewRef コンポーネントがやっているのは v16.3.0 から追加された新しい ref の実装方法で、 createRef() という関数を使う。
従来の string ref のような書き方で、かつ string ref のときの問題点は解決されている。らしい。

参考:React v16.3.0: New lifecycles and context API - React BlogcreateRef API の項

なお createRef() の導入後も 2 の callback を使った方法はサポートされるので、置き換える必要はない。


3. 予期せぬ副作用の検出

背景として、React の動作時には大きく2つのフェーズがある。

  • render フェーズ:DOM に適用する必要のある変更を決定する。 render メソッドが呼ばれ、結果を直前の render の結果と比較する
  • commit フェーズ:React がすべての変更を DOM に適用する。 componentDidMountcomponentDidUpdate などのライフサイクルメソッドもこのフェーズで呼ばれる

一般に commit フェーズは速いが render は遅い。そのため、非同期レンダリングによってレンダリング処理を複数の小さな処理に分割し、ブラウザをブロックしないように停止と再開をしながらレンダリングを行う。
これにより、commit 前に render フェーズのライフサイクルメソッドが複数回実行される可能性が生じる。
(このあたりはよくわかっていない)

ので、これらのライフサイクルメソッドに副作用がないことが重要となる。
これらのライフサイクルメソッドとは具体的には以下。

  • Class コンポーネントconstructor メソッド
  • render
  • setState の第一引数に関数を渡したときの関数(updater と呼ぶらしい)
  • getDerivedStateFromProps (v16.3.0 から componentWillReceiveProps の代替として追加)

ただ、これらのメソッドに副作用がないことを自動的に検出することは難しいため、StrictMode ではこれらのメソッドを2回ずつ実行する。

(サンプル)

class SideEffect extends Component {
  constructor(props) {
    super(props);

    this.state = { count: 0 };
    console.log('SideEffect constructor');
  }

  increment = () => {
    this.setState((prevState, props) => {
      console.log('SideEffect updater', prevState);
      return {
        count: prevState.count + 1,
      };
    });
  };

  render() {
    console.log('SideEffect render');
    return <div onClick={this.increment}>{this.state.count}</div>;
  }
}

export default function App() {
  return (
    <StrictMode>
      <SideEffect />
    </StrictMode>
  );
}

(結果)

f:id:dackdive:20180404023839p:plain


コード

一応上げておく。

https://github.com/zaki-yama/react-strict-mode-example

Node.js製CLIフレームワークoclifを試す

はじめに

Heroku が oclif という CLI フレームワークオープンソースとして公開したという記事を読みました。

Heroku CLISalesforce DX のベースにもなっているらしい。
どんなもんか触ってみます。

(oclif は (The) Open CLI Framework の略のようです。読み方がわからない。。。)


oclif の特徴

手を動かす前に、どういった特徴があるのか公式ドキュメントに目を通してみます。
Features · oclif: The Open CLI Framework

  • Super Speed
    • コマンド実行時のオーバーヘッド(?)がほとんどなく、また依存パッケージもほとんどない
    • 実行されるコマンドだけ require されるので、たくさんのコマンドからなる巨大 CLI でも単一コマンドの CLI と速度が変わらない
  • CLI Generator
    • コマンド一発で scaffold が生成できる generator がある
  • Testing Helpers
    • テストが書きやすい。 stdout/stderr を簡単にモックできる
    • generator がテストの scaffold も自動生成する
  • Auto-documentation
    • --help オプションで表示するヘルプテキストが自動生成される
    • ↑は CLI が publish されるときに README にも自動的に記載される(※最後で軽く触れる)
  • (未確認)Plugins
  • (未確認)Hooks
    • コマンド実行時などのライフサイクルイベントや独自にカスタムイベントを定義し、そこにフックする処理を書ける
  • TypeScript
    • TypeScript と JavaScript 両方をサポート
    • oclif 自体も TypeScript で書かれている
  • Coming soon: man pages, Autocomplete

テストしやすく、ドキュメントが自動生成されるのはいいですね。


Single-command と Multi-command

oclif で作成できる CLI には大きく分けて 2 種類あります。
Single-command とは lscurl のように、コマンド自体は1つで引数やオプションを取るものです。
Multi-command とは githeroku のように、後にサブコマンドが続くものです。


試してみる

今回は、自分が過去に Node.js で作った parse-salesforce-object という CLI に oclif を導入してみます。
Salesforce 開発で使うメタデータファイル(XML)をパースしてよしなに表示してくれるという、ごくごく一部の方にしか需要がないやつです)
この CLI は先ほどの分類で言うと Single-command です。


generator で CLI のひな形(scaffold)作成

Quickstart を参考に、generator を使って必要なファイルを生成します。

$ npx oclif single [コマンド名]

を実行すると途中で色々聞かれるので適宜入力します。

$ npx oclif single parse-salesforce-object
npx: 293個のパッケージを17.261秒でインストールしました。

     _-----_     ╭──────────────────────────╮
    |       |    │      Time to build a     │
    |--(o)--|    │  single-command CLI with │
   `---------´   │   oclif! Version: 1.7.9
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

? npm package name parse-salesforce-object
? command bin name the CLI will export parse-salesforce-object
? description
? author Shingo Yamazaki @zaki-yama
? version 0.0.5
? license MIT
? node version supported >=8.0.0
? github owner of repository (https://github.com/OWNER/repo) zaki-yama
? github name of repository (https://github.com/owner/REPO) parse-salesforce-object
? optional components to include
❯◉ yarn (npm alternative)
 ◉ mocha (testing framework)
 ◉ typescript (static typing for javascript)
 ◉ tslint (static analysis tool for typescript)
 ◯ semantic-release (automated version management)

最後のオプションで yarn, mocha, TypeScript を使うかどうかは好みです。

インストールが完了すると、 [コマンド名]ディレクトリが作成され、その下に必要なファイルが揃っています。
npm install (or yarn )も実行されているため、必要なパッケージもインストール済みです。

$ tree -I node_modules parse-salesforce-object
parse-salesforce-object
├── README.md
├── appveyor.yml
├── bin
│   ├── run
│   └── run.cmd
├── package.json
├── src
│   └── index.ts
├── test
│   ├── helpers
│   │   └── init.js
│   ├── index.test.ts
│   ├── mocha.opts
│   └── tsconfig.json
├── tsconfig.json
├── tslint.json
└── yarn.lock

bin/run を実行するとコマンドを実行できます。

$ cd parse-salesforce-object
$ ./bin/run
hello world from /Users/yamazaki/workspace/nodejs/parse-salesforce-object/src/index.ts!


コマンドファイルの構成

src/index.ts がコマンド本体です。中身を見てみます。
((1) ~ (3) は便宜的にこちらで番号を振りました)

import {Command, flags} from '@oclif/command'

class ParseSalesforceObject extends Command {
  // (3)
  static description = 'describe the command here'

  // (3)
  static examples = [
    `$ parse-salesforce-object
hello world from ./src/parse-salesforce-object.ts!
`,
  ]

  // (2)
  static flags = {
    // add --version flag to show CLI version
    version: flags.version({char: 'v'}),
    // add --help flag to show CLI version
    help: flags.help({char: 'h'}),

    // flag with a value (-n, --name=VALUE)
    name: flags.string({char: 'n', description: 'name to print'}),
    force: flags.boolean({char: 'f'}),
  }

  // (2)
  static args = [{name: 'file'}]

  // (1)
  async run() {
    const {args, flags} = this.parse(ParseSalesforceObject)

    const name = flags.name || 'world'
    this.log(`hello ${name} from ${__filename}!`)
    if (args.file && flags.force) {
      this.log(`you input --force and --file: ${args.file}`)
    }
  }
}

export = ParseSalesforceObject
  • (1) コマンドの実処理は run() メソッドに記述します
  • (2) 引数やオプション(flags)はこのように static 変数として定義します。この後ここをカスタマイズしてみます
  • (3) description, examples も同様に static 変数として定義すると、ヘルプテキストに反映されます

最後の (3) について、実際にコマンドを --help オプションつきで実行すると

$ ./bin/run --help
describe the command here

USAGE
  $ parse-salesforce-object [FILE]

OPTIONS
  -f, --force
  -h, --help       show CLI help
  -n, --name=name  name to print
  -v, --version    show CLI version

EXAMPLE
  $ parse-salesforce-object
  hello world from ./src/parse-salesforce-object.ts!

のように、description および examples に記述した文字列がヘルプの先頭と EXAMPLE セクションに記載されているのがわかります。


引数を処理する

さて、ここからひな形をベースに元の CLI としての機能を実装していきます。
まずは引数の処理から。
参考:Command Arguments · oclif: The Open CLI Framework

元の CLI では

const argv = require('minimist')(process.argv.slice(2));
const filePath = argv._[0];

...

if (!filePath) {
  console.log(chalk.red('ERROR: You must specify a path to .object file.'));
  process.exit(1);
}

fs.readFile(filePath, (err, data) => {
  ...
});

のように、とあるファイルへのパスを必須の引数として受け取るようになっていました。
またその処理のために minimist というライブラリを使っていました。

oclif だと以下のように書けます。

class ParseSalesforceObject extends Command {

  ...

  static args = [{
    name: 'path',
    description: 'path to .object file',
    required: true,
  }]

  async run() {
    const {args, flags} = this.parse(ParseSalesforceObject)

    fs.readFile(args.path, (err, data) => {
      ...
    })
  }

引数には { name: 'foo' } という形で名前を付けておくことができ、run() メソッド内で(パース後に) args.foo でアクセスできます。

その他のオプションは https://oclif.io/docs/args.html を参照するといいです。
必須かどうかもオプションで指定できるようになったので判定処理が不要になりました。

# 引数なしで実行するとエラーになる
$ ./bin/run
 ›   Error: Missing 1 required arg:
 ›   path  path to .object file
 ›   See more help with --help


フラグ(オプション)を処理する

続いて、いくつかのフラグを受け取れるようにします。
フラグとは -f foo--file=foo のようなものを指します。
参考:Command Flags · oclif: The Open CLI Framework

フラグは version と help 以外は

static flags = {
  force: flags.boolean({char: 'f'}),
  file: flags.string(),
}

のように、

  • 引数を受け取るもの: flags.string()
  • 引数を受け取らず、boolean として使うもの: flags.boolean()

の 2 種類あります。

また両者に共通して、{char: 'f'} のように char オプションを指定すると短縮形も扱えるようになります。

その他のオプションは https://oclif.io/docs/flags.html を参照します。

元の CLI には、-f xxx または --format=xxx オプションで出力フォーマットを指定でき、その選択肢は markdown, csv, soql のいずれかとなっていたので

format: flags.string({
  char: 'f',
  description: 'output format',
  options: ['markdown', 'csv', 'soql'],
  default: 'markdown',
}),

のように optionsdefault を利用しました。便利。

# -f で許可されているフォーマット以外を指定するとエラー
$ ./bin/run objects/Expense__c.object -f foo 
 ›   Error: Expected --format=foo to be one of: markdown, csv, soql
 ›   See more help with --help


ヘルプを出力してみる

引数やオプションを一通り定義した後で、 --help によりヘルプを表示してみます。

$ ./bin/run --help
USAGE
  $ parse-salesforce-object PATH

ARGUMENTS
  PATH  path to .object file

OPTIONS
  -f, --format=markdown|csv|soql  [default: markdown] output format
  -h, --help                      show CLI help
  -n, --namespace=namespace       namespace prefix (for SOQL format)
  -v, --version                   show CLI version

EXAMPLE
  $ parse-salesforce-object src/objects/Expense__c.object
  | label       | fullName      | type     | required |
  | ----------- | ------------- | -------- | -------- |
  | Amount      | Amount__c     | Number   | false    |
  | Client      | Client__c     | Text     | false    |
  | Date        | Date__c       | DateTime | false    |
  | Reimbursed? | Reimbursed__c | Checkbox | null     |

ARGUMENTS および OPTIONS のセクションのところが、定義した引数・フラグの内容から自動生成されました。


GitHub

今回 oclif での置き換えを試した CLIリポジトリはここにあります。

PR は https://github.com/zaki-yama/parse-salesforce-object/pull/6


まとめと TODO

今回は oclif の導入手順と基本構成についてなんとなくわかったという程度ですが、個人的には

  • npx oclif single/multi foo で scaffold から始められるのは楽
  • 引数やオプションからヘルプ自動生成は便利

といった点が、フレームワークというだけあって良いなと思いました。
また将来的に Autocomplete もサポートしてくれるのは期待したい。

テストを書くところや Plugins、Hooks については試せてないので、今後の TODO ということで。


おまけ:Auto-documentation について

Features#Auto-documentation には

This information is also automatically placed in the README whenever the npm package of the CLI is published. See the multi-command CLI example

と記載がありますが、自動生成したヘルプを README に埋め込む方法はドキュメントに記載がありませんでした。
リンクされてるリポジトリを見ると oclif-dev という CLI を使ってる っぽいので、これかな...?


あわせて読みたい

Salesforce のエンジニアブログにも記事があった。

Vim+ALEでファイル保存時にPrettierを実行する

メモ。
Prettier という JavaScript のフォーマッターをファイル保存時に自動的に実行する、というのを Vim でやりたい。
特に自分は ESLint や Flow のチェックに ALE というプラグインを使っているため
(参考:VimでESLintとFlowを使うためにNeomakeからALEに乗り換える - Qiita
Prettier も同じように ALE で設定できないのか調べた。

すると、ちゃんと Prettier の公式ドキュメントに ALE での設定方法が載ってた。

Prettier 単体で使う場合は上記を読むのが一番早いが、自分は ESLint と併用するために prettier-eslint-cli)を使っているのでその前提で手順を記載する。


設定手順

Vim

ALE の設定として以下を追加する。

let g:ale_fixers = {}
let g:ale_fixers['javascript'] = ['prettier-eslint']

" ファイル保存時に実行
let g:ale_fix_on_save = 1

" ローカルの設定ファイルを考慮する
let g:ale_javascript_prettier_use_local_config = 1

プラグイン管理に dein.vim を使っており、toml で管理している場合は以下のようになる

# rc/dein.toml

[[plugins]]
repo = 'w0rp/ale'
hook_add = '''
let g:ale_statusline_format = ['E%d', 'W%d', 'OK']

nmap <silent> <C-w>j <Plug>(ale_next_wrap)
nmap <silent> <C-w>k <Plug>(ale_previous_wrap)

let g:ale_fixers = {}
let g:ale_fixers['javascript'] = ['prettier-eslint']

" ファイル保存時に実行
let g:ale_fix_on_save = 1

" ローカルの設定ファイルを考慮する
let g:ale_javascript_prettier_use_local_config = 1
'''

let g:ale_fixers['javascript'] = ['prettier-eslint'] の部分、prettier 単体では ['prettier'] でいいが ESLint と併用している場合は prettier-eslint-cli を使うため上記の設定になる。


プロジェクト側

prettier-eslint-cli をインストールしておくだけ。

$ yarn add -D prettier-eslint-cli


様子

f:id:dackdive:20180330013445g:plain

あんまり早くない。。。
非同期で行われるためそこまで気にならないかもしれないけど。
VSCode とかでもこんなもんなんだろうか。


GitHub

以上を反映した Redux アプリ用テンプレートリポジトリがこちらです。
Prettier 導入の Issue は https://github.com/zaki-yama/redux-express-template/issues/16


リファレンス

冒頭の Prettier のドキュメントのほか、ALE の README でも fixer については記載があった。
https://github.com/w0rp/ale#2ii-fixing

[Salesforce]ApexでPermissionSetの"PermissionsXXX"項目の一覧を取得

ちょいメモ。

参考:PermissionSet | SOAP API 開発者ガイド | Salesforce Developers

f:id:dackdive:20180324030725p:plain

Permissions... から始まる項目がいくつかあるらしいので、Apex で項目一覧を取得してみる。

調べ方

以下を開発者コンソールの Execute Anonymous で実行。ログをダウンロードして USER_DEBUG だけ抽出。

Map<String, Schema.SObjectField> M = Schema.SObjectType.PermissionSet.fields.getMap();

SObjectType t = Schema.getGlobalDescribe().get('PermissionSet');
Map<String,Schema.SObjectField> fields = t.getDescribe().fields.getMap();

for (Schema.SObjectField field : fields.values()) {
    Schema.DescribeFieldResult dfr = field.getDescribe();
    if (dfr.getName().contains('Permissions')) {
        System.debug(dfr.getName() + ' | ' + dfr.getLabel());
    }
}

結果

API 参照名 ラベル
PermissionsEmailSingle メールの送信
PermissionsEmailMass 一括メール送信
PermissionsEditTask ToDo の編集
PermissionsEditEvent 行動の編集
PermissionsExportReport レポートのエクスポート
PermissionsImportPersonal 個人データのインポート
PermissionsDataExport ウィークリーデータのエクスポート
PermissionsManageUsers ユーザの管理
PermissionsEditPublicFilters 公開リストビューの管理
PermissionsEditPublicTemplates 公開テンプレートの管理
PermissionsModifyAllData すべてのデータの編集
PermissionsManageCases ケースの管理
PermissionsMassInlineEdit リストからの一括編集
PermissionsEditKnowledge 記事の管理
PermissionsManageKnowledge Salesforce ナレッジの管理
PermissionsManageSolutions 公開ソリューションの管理
PermissionsCustomizeApplication アプリケーションのカスタマイズ
PermissionsEditReadonlyFields 参照のみ項目の編集
PermissionsRunReports レポート実行
PermissionsViewSetup 設定・定義を参照する
PermissionsTransferAnyEntity 所有権の移行
PermissionsNewReportBuilder レポートビルダー
PermissionsActivateContract 契約の有効化
PermissionsActivateOrder 注文の有効化
PermissionsImportLeads リードのインポート
PermissionsManageLeads リードの管理
PermissionsTransferAnyLead リード所有者の移行
PermissionsViewAllData すべてのデータの参照
PermissionsEditPublicDocuments 公開ドキュメントの管理
PermissionsViewEncryptedData 暗号化されたデータの参照
PermissionsEditBrandTemplates レターヘッドの管理
PermissionsEditHtmlTemplates HTML テンプレートの編集
PermissionsChatterInternalUser Chatter 内部ユーザ
PermissionsManageTranslation 翻訳の管理
PermissionsDeleteActivatedContract 有効契約の削除
PermissionsChatterInviteExternalUsers Chatter に顧客を招待する
PermissionsSendSitRequests 登録情報照会要求の送信
PermissionsManageRemoteAccess 接続アプリケーションを管理する
PermissionsCanUseNewDashboardBuilder ドラッグアンドドロップ ダッシュボードビルダー
PermissionsManageCategories カテゴリの管理
PermissionsConvertLeads リードの取引の開始
PermissionsPasswordNeverExpires パスワード無期限
PermissionsUseTeamReassignWizards チーム再割り当てウィザードの使用
PermissionsEditActivatedOrders 有効化された注文の編集
PermissionsInstallPackaging AppExchange パッケージのダウンロード
PermissionsPublishPackaging AppExchange パッケージのアップロード
PermissionsChatterOwnGroups 新規 Chatter グループの作成および所有
PermissionsEditOppLineItemUnitPrice 商談商品の販売価格の編集
PermissionsCreatePackaging AppExchange パッケージの作成
PermissionsBulkApiHardDelete Bulk API の物理削除
PermissionsSolutionImport ソリューションのインポート
PermissionsManageCallCenters コールセンターの管理
PermissionsManageSynonyms シノニムの管理
PermissionsViewContent ポータルのコンテンツの参照
PermissionsManageEmailClientConfig メールクライアント設定の管理
PermissionsEnableNotifications アウトバウンドメッセージの送信
PermissionsManageDataIntegrations データインテグレーションの管理
PermissionsDistributeFromPersWksp コンテンツ配信の作成
PermissionsViewDataCategories データカテゴリの表示
PermissionsManageDataCategories データカテゴリの管理
PermissionsAuthorApex Apex 開発
PermissionsManageMobile モバイル設定を管理する
PermissionsApiEnabled API の有効化
PermissionsManageCustomReportTypes カスタムレポートタイプの管理
PermissionsEditCaseComments ケースコメントの編集
PermissionsTransferAnyCase ケース所有者の移行
PermissionsContentAdministrator Salesforce CRM Content の管理
PermissionsCreateWorkspaces ライブラリの作成
PermissionsManageContentPermissions コンテンツ権限の管理
PermissionsManageContentProperties コンテンツプロパティの管理
PermissionsManageContentTypes ファイルのレコードタイプおよびレイアウトの管理
PermissionsManageExchangeConfig Lightning Sync を管理
PermissionsManageAnalyticSnapshots レポート作成スナップショットを管理
PermissionsScheduleReports レポートのスケジュール
PermissionsManageBusinessHourHolidays 営業時間の休日の管理
PermissionsManageDynamicDashboards 動的ダッシュボードの管理
PermissionsCustomSidebarOnAllPages すべてのページにカスタムサイドバーを表示
PermissionsManageInteraction フローの管理
PermissionsViewMyTeamsDashboards 私のチームのダッシュボードの参照
PermissionsModerateChatter Chatter のモデレート
PermissionsResetPasswords ユーザパスワードのリセットおよびユーザのロック解除
PermissionsFlowUFLRequired フローユーザ機能ライセンスが必要
PermissionsCanInsertFeedSystemFields Chatter フィードにシステム項目値を挿入
PermissionsManageKnowledgeImportExport ナレッジ記事のインポート/エクスポートの管理
PermissionsEmailTemplateManagement メールテンプレートの管理
PermissionsEmailAdministration メール管理
PermissionsManageChatterMessages Chatter メッセージとダイレクトメッセージを管理
PermissionsAllowEmailIC メールベースの ID 検証オプション
PermissionsChatterFileLink 公開リンクの作成
PermissionsForceTwoFactor ユーザインターフェースログインの 2 要素認証
PermissionsViewEventLogFiles イベントログファイルを参照
PermissionsManageNetworks コミュニティを作成および設定
PermissionsManageAuthProviders 認証プロバイダの管理
PermissionsRunFlow フローを実行
PermissionsCreateCustomizeDashboards ダッシュボードの作成とカスタマイズ
PermissionsCreateDashboardFolders ダッシュボードフォルダを作成
PermissionsViewPublicDashboards 公開フォルダのダッシュボードを参照
PermissionsManageDashbdsInPubFolders 公開フォルダのダッシュボードを管理
PermissionsCreateCustomizeReports レポートの作成とカスタマイズ
PermissionsCreateReportFolders レポートフォルダを作成
PermissionsViewPublicReports 公開フォルダのレポートを参照
PermissionsManageReportsInPubFolders 公開フォルダのレポートを管理
PermissionsEditMyDashboards 私のダッシュボードを編集
PermissionsEditMyReports 私のレポートを編集
PermissionsViewAllUsers すべてのユーザの参照
PermissionsAllowUniversalSearch Knowledge One
PermissionsConnectOrgToEnvironmentHub 環境ハブに組織を接続
PermissionsWorkCalibrationUser Work.com 調整を有効化
PermissionsCreateCustomizeFilters リストビューを作成およびカスタマイズ
PermissionsWorkDotComUserPerm Work.com を有効化
PermissionsGovernNetworks コミュニティを管理する
PermissionsSalesConsole セールスコンソール
PermissionsTwoFactorApi API ログインの 2 要素認証
PermissionsDeleteTopics トピックを削除
PermissionsEditTopics トピックを編集
PermissionsCreateTopics トピックを作成
PermissionsAssignTopics トピックを割り当てる
PermissionsIdentityEnabled Identity 機能を使用
PermissionsIdentityConnect Identity Connect を使用
PermissionsAllowViewKnowledge ナレッジの参照を許可
PermissionsContentWorkspaces ライブラリへのアクセス
PermissionsManageSearchPromotionRules 昇格済み検索語の管理
PermissionsCustomMobileAppsAccess カスタムモバイルアプリケーションにアクセス
PermissionsViewHelpLink ヘルプリンクを参照
PermissionsManageProfilesPermissionsets プロファイルおよび権限セットを管理
PermissionsAssignPermissionSets 権限セットの割り当て
PermissionsManageRoles ロールを管理
PermissionsManageIpAddresses IP アドレスを管理
PermissionsManageSharing 共有を管理
PermissionsManageInternalUsers 内部ユーザを管理
PermissionsManagePasswordPolicies パスワードポリシーを管理
PermissionsManageLoginAccessPolicies ログインアクセスポリシーを管理
PermissionsManageCustomPermissions カスタム権限を管理
PermissionsCanVerifyComment Chatter の質問への回答の確認
PermissionsManageUnlistedGroups 「リストに記載しない」グループを管理
PermissionsModifySecureAgents セキュアエージェントを変更
PermissionsManageTwoFactor API で 2 要素認証を管理
PermissionsChatterForSharePoint Chatter For SharePoint へのアクセス
PermissionsLightningExperienceUser Lightning Experience ユーザ
PermissionsConfigCustomRecs カスタムおすすめの設定
PermissionsSubmitMacrosAllowed ユーザが元に戻せないマクロを管理
PermissionsBulkMacrosAllowed 複数のレコードに対してマクロを実行
PermissionsShareInternalArticles 内部のナレッジ記事を外部と共有
PermissionsManageSessionPermissionSets セッション権限セットの有効化を管理
PermissionsSendAnnouncementEmails お知らせメールを送信
PermissionsChatterEditOwnPost 自分の投稿を編集
PermissionsChatterEditOwnRecordPost 自分の所有レコードへの投稿を編集
PermissionsImportCustomObjects カスタムオブジェクトのインポート
PermissionsDelegatedTwoFactor ユーザインターフェースで 2 要素認証を管理
PermissionsChatterComposeUiCodesnippet UI からのコードスニペットの挿入を許可
PermissionsSelectFilesFromSalesforce Salesforce からファイルを選択
PermissionsModerateNetworkUsers コミュニティユーザモデレート
PermissionsMergeTopics トピックのマージ
PermissionsSubscribeToLightningReports レポートを登録
PermissionsManagePvtRptsAndDashbds すべての非公開レポートおよびダッシュボードを管理
PermissionsAllowLightningLogin Lightning Login ユーザ
PermissionsCampaignInfluence2 キャンペーンインフルエンス
PermissionsViewDataAssessment データ評価の参照アクセス権
PermissionsRemoveDirectMessageMembers ダイレクトメッセージから人を削除
PermissionsCanApproveFeedPost フィード投稿とコメントを承認可能
PermissionsAddDirectMessageMembers ダイレクトメッセージに人を追加
PermissionsAllowViewEditConvertedLeads 取引開始済みのリードを表示および編集
PermissionsShowCompanyNameAsUserBadge コミュニティロールとして会社名を表示
PermissionsAccessCMC コミュニティ管理にアクセス
PermissionsViewHealthCheck 状態チェックを表示
PermissionsManageHealthCheck 状態チェックを管理
PermissionsPackaging2 第二世代パッケージの作成と更新
PermissionsManageCertificates 証明書を管理
PermissionsCreateReportInLightning レポートビルダー (Lightning Experience)
PermissionsPreventClassicExperience Salesforce Classic に切り替えるオプションを非表示
PermissionsHideReadByList [表示先] リストを非表示
PermissionsListEmailSend リストメールの送信を許可
PermissionsFeedPinning フィードでの投稿の固定
PermissionsChangeDashboardColors ダッシュボードの色を変更
PermissionsIotUser IoT ユーザ
PermissionsUseWebLink カスタマイズしたアクションへのアクセスを許可
PermissionsViewAllActivities すべての活動を表示
PermissionsSubscribeReportToOtherUsers レポートを登録: 受信者を追加
PermissionsLightningConsoleAllowedForUser Lightning コンソールユーザ
PermissionsSubscribeReportsRunAsUser レポートを登録: 実行ユーザを設定
PermissionsSubscribeToLightningDashboards ダッシュボードへの登録
PermissionsApexRestServices Apex REST サービス
PermissionsEnableCommunityAppLauncher コミュニティでアプリケーションランチャーを表示
PermissionsManageSurveys アンケートを管理
PermissionsViewRoles ロールおよびロール階層を表示

いっぱいある。

[Salesforce]代理承認者機能の使い方

まとまってるドキュメントが見つからなかったのでメモ。


代理承認者とは

  • Salesforce の承認プロセス機能において、承認者の代わりに申請を承認/却下できる人を設定できる機能
  • ユーザに対して一人だけ代理承認者を設定しておくことができる
  • また承認プロセスの各承認ステップごとに代理承認者による承認を許可するかどうかが設定できる

承認プロセスそのものについては以下の Trailhead モジュールをやるといい。


設定手順

ユーザ設定より代理承認者を設定する

代理承認者を設定したいユーザのユーザ設定画面を開き、下の方の「承認者の設定」セクションに移動する。
「代理承認者」項目に代理承認者にしたいユーザを検索してセットする。

f:id:dackdive:20180317025929p:plain:w320

また、代理承認者自身のユーザ設定で、「承認申請メールを受信」項目を「自分が承認者である場合のみ」から「承認者または代理承認者である場合」に変更しておくとよい。理由は後述。

f:id:dackdive:20180317025925p:plain:w320


承認プロセス設定より、承認ステップで代理承認を許可する

今度は承認プロセスの設定画面を開き、任意の承認ステップの編集画面を開く。

f:id:dackdive:20180317031300p:plain

ステップ 3/3 で、一番下に「代理承認者もこの申請を承認可能にする」という項目があるのでチェックする。

f:id:dackdive:20180317031303p:plain


結果

自分が代理承認者になっている承認ステップに申請が回ってきたとき、承認者同様にメールが届く。
メールの件名には「(◯◯の代理承認者)」と書かれているよう。

f:id:dackdive:20180317032513p:plain


制限事項

自身が代理承認可能な申請は今のところ「未承認申請一覧」には表示されない。メール以外に確認する手段がないのでつらい。
これについてはずいぶん前から Idea に投稿されてるよう。

また、以下のヘルプ記事ではメール以外のワークアラウンドも紹介されてる(英語)。

Why can't a delegated approver see anything in the Items to approve list?

どうやら「レコードが現在承認プロセスの申請中かどうか」を表すチェックボックス項目をオブジェクトに追加し、それを条件にリストビューを作るという方法らしい。
チェックボックスの ON/OFF は承認プロセスの開始時や最終承認/却下時のアクションとして項目自動更新により自動的に制御できる。

が、これは別に「自身が代理承認になっているものリスト」ではないし、もっと言うと自分が承認者じゃないやつも含まれるのでは。。。

(2018/3/23追記)

と思ったら、標準レポートに「代理承認申請一覧」というのがあった。。。

f:id:dackdive:20180323234404p:plain

これで一覧表示できます。

f:id:dackdive:20180326143254p:plain

※ただし、標準フォルダは LEX では表示されないため、コピーする必要あり
(参考:レポートおよびダッシュボード: Lightning Experience で使用できない機能とその新機能


「代理承認者」に設定する以外に、他人の申請を承認/却下する方法

「代理承認者」はユーザにつき一人しか指定できない。もう少し柔軟に代理承認可能なユーザを増やすにはどうするか。

代理承認ではないが、申請を行うオブジェクトに対して「すべての編集」権限があれば任意の申請を承認/却下することができる。
(「すべてのデータの編集」権限もそう)

参考:承認の管理に関する考慮事項

管理者権限
次の権限のいずれかを持つユーザは承認管理者とみなされます。

  • 指定のオブジェクトに対するオブジェクトレベルでの「すべての編集」権限
  • 「すべてのデータの編集」ユーザ権限

承認管理者には次の権限があります。

  • 承認プロセスの一環としてではなく、未承認申請を承認または却下
  • 承認がロックされたレコードの編集


わかっていないこと

  • 承認者としてキューやグループを指定したときにどういう挙動になるのか

electron-react-boilerplateでCSSフレームワーク(Lightning Design System)をインストールするとエラー

Chatter Desktop という Electron アプリ を作っていて真っ先にハマったところのメモ。

Lightning Design System という CSS フレームワークを入れようとしたところ、以下のエラーとなった。

$ git clone --depth=1 https://github.com/chentsulin/electron-react-boilerplate.git your-project-name
$ cd your-project-name
$ yarn
$ yarn add @salesforce-ux/design-system
...
ERROR in dll renderer
Module not found: Error: Can't resolve '@salesforce-ux/design-system' in '/Users/yamazaki/workspace/electron/electron-react-boilerplate'
 @ dll renderer

ERROR in ./node_modules/bootstrap/dist/js/bootstrap.js
Module not found: Error: Can't resolve 'jquery' in '/Users/yamazaki/workspace/electron/electron-react-boilerplate/node_modules/bootstrap/dist/js'
 @ ./node_modules/bootstrap/dist/js/bootstrap.js 7:81-98
 @ dll renderer

ERROR in ./node_modules/bootstrap/dist/js/bootstrap.js
Module not found: Error: Can't resolve 'popper.js' in '/Users/yamazaki/workspace/electron/electron-react-boilerplate/node_modules/bootstrap/dist/js'
 @ ./node_modules/bootstrap/dist/js/bootstrap.js 7:100-120
 @ dll renderer
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! electron-react-boilerplate@0.13.2 build-dll: `cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.dev.dll.js --colors`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the electron-react-boilerplate@0.13.2 build-dll script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/yamazaki/.npm/_logs/2018-03-04T21_19_46_858Z-debug.log
error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.

https://github.com/chentsulin/electron-react-boilerplate/blob/master/package.json#L23
どうやら postinstall スクリプトが走っており、この中で build-dll という DLL ファイルの生成処理をやろうとして失敗しているよう。

また build-dllwebpack.config.renderer.dev.dll.js を見て webpack によるビルドを行っているので、設定ファイルの中身を見る。そうすると

https://github.com/chentsulin/electron-react-boilerplate/blob/master/webpack.config.renderer.dev.dll.js#L175-L181

entry: {
  renderer: (
    Object
      .keys(dependencies || {})
      .filter(dependency => dependency !== 'font-awesome')
  )
},

ここで、元から入っていた font-awesome については明示的にビルド対象から外しているようだ。
なので手動で

entry: {
  renderer: (
    Object
      .keys(dependencies || {})
      .filter(dependency => {
        // also ignore installed CSS library
        return dependency !== 'font-awesome' && dependency !== '@salesforce-ux/design-system';
      })
  )
},

というふうにインストールする CSS フレームワークをビルド対象から除外してやると、ビルドに成功するようになった。

が、正しい手順なのかがよくわからず、Issue で質問してみることにした。


おまけ: webpack.config.renderer.dev.dll.js がやっていること

開発時のビルドパフォーマンス向上のために webpack.DLLPlugin というのを使っている。
DLLPlugin についてはきちんと理解したわけではないが、外部ライブラリなど毎回毎回ビルドする必要がないモジュールを DLL バンドルという形で別ファイルに切り出し、require() 時に読み込むらしい。
こちらの記事が非常に参考になった。

LDSとdesign-system-reactをBabel&webpackな環境に導入する

メモ。
ちょっと前から Lightning Design System(LDS)の React 実装が公式から出てます。

Lightning Design System for React

で、ようやく入れてみようとドキュメントを頼りにやってみたところ色々手こずったので、手順をまとめておきます。


はじめに

design-system-react の設定に関するドキュメントは、以下のように点在しています。

  1. Getting Started
  2. リポジトリREADME#Usage
  3. リポジトリdocs/webpack.md (Usage with Webpack)

1 が一番参考にしたドキュメント。webpack.config.js の設定例なども載っています。基本はここを見ながらやりました。
ここに書かれてる内容は private repo で管理してるらしい) 2 は 1 と被る部分もあり、ただ Icon の設定に関してはここにしか記載されてません。
また 1, 2 は design-system-react のインストール手順であり、これだけだと LDS はインストールされず、スタイルがあたりません。
LDS を webpack から使えるようにするための情報が 3 に載っています。

以下では、設定手順を大きく「1. design-system-react の設定」と「2. LDS の設定」に分けて記載します。


サンプルコード

Redux & Express でのアプリのひな形リポジトリに導入したときの PR です。

ディレクトリ構成

クライアント側のファイルは public というディレクトリに配置するような構成になっています。

- public
  - assets
    - lds --- LDS はここに展開
    - bundle.js --- ビルド後のファイル
  - components --- React & Redux の
  - containers
  - ...
  - index.js --- エントリーポイント
  - index.html
- package.json
- webpack.config.base.babel.js
- webpack.config.dev.babel.js
- webpack.config.prod.babel.js


1. design-system-react の設定

まずは design-system-react のインストールから設定までの手順。主に先ほどの 1. Getting Started を参考にします。

インストール

npm でインストールできます。

$ npm install @salesforce-ux/design-system @salesforce/design-system-react


webpack の設定

Getting Started のページに webpack.config.js の設定例があるので、それを参考に設定していきます。

 // webpack.config.babel.js (抜粋)
 export default {
   context: path.resolve(path.join(__dirname, 'public')),
   entry: [
     './index',
   ],
+  resolve: {
+    extensions: ['.js', '.jsx'],
+  },
   output: {
     filename: 'bundle.js',
     path: path.resolve(path.join(__dirname, 'public', 'assets')),
     publicPath: '/assets/',
   },
   module: {
     rules: [
       {
-        test: /\.js$/,
-        exclude: /node_modules/,
+        test: /\.jsx?$/,
+        include: [
+          path.resolve(path.join(__dirname, 'public')),
+          path.resolve(path.join(__dirname, 'node_modules/@salesforce/design-system-react')),
+        ],
         use: 'babel-loader',
       },
       {
         ...

(ファイル全体は こちら を参照)

ポイントとしては

  • design-system-react のファイルは .jsx なので、 .jsx もビルドできるように resolve.extensions および babel-loader を適用する正規表現を修正
  • 元々 node_modules 以下はすべて babe-loader の対象外としていたが、design-system-react は対象に含めるよう修正

です。

また、この記事を書いた時点ではドキュメントの方では

path.join(__dirname, 'node_modules/design-system-react')

というように @salesforce が入ってなかったんですが、入れるのが正しいです。
Issue で報告したのでそのうち直るはず。


Babel の設定

.babelrc を修正します。
これには @salesforce/babel-preset-design-system-react という preset を使えとドキュメントにはありますが、中身

https://github.com/salesforce/design-system-react/blob/master/preset/index.js

を見てみると

  • babel-preset-env
  • babel-preset-react
  • babel-plugin-transform-object-rest-spread
  • babel-plugin-transform-class-properties
  • babel-plugin-transform-export-extensions

を指定しているだけなので、既に設定されている .babelrc の内容に合わせて必要な preset/plugin を個別に追加するという対応でもいいのかもしれません。
(実際自分のリポジトリでも babel-plugin-transform-export-extensions 以外は使っていた)

今回は素直にこの preset を使うようにします。

$ npm install -D @salesforce/babel-preset-design-system-react
// .babelrc
{
  "presets": ["@salesforce/babel-preset-design-system-react"]
}


使う

このような形で import して使います。

// components/App.js
import React, { Component } from 'react';
import Button from '@salesforce/design-system-react/components/button';

export default class App extends Component {
  render() {
    return (
      <Button
        iconName="download"
        iconPosition="left"
        label="Neutral Icon"
      />
    );
  }
}


2. LDS の設定

さて、今度は LDS を読み込むための設定をします。
これは docs/webpack.md がある程度参考になります。


インストール
$ npm install @salesforce-ux/design-system style-loader css-loader file-loader

必要になる loader も一緒にインストールしときます。


webpack の設定
 // webpack.config.babel.js
 ...
 export default {
   ...
   module: {
     rules: [
       ...
+      {
+        test: /\.min\.css$/,
+        use: ['style-loader', 'css-loader'],
+      },
+      {
+        test: /\.(eot|svg|ttf|woff|woff2)$/,
+        loader: 'file-loader',
+        options: {
+          name: '[name].[ext]',
+          outputPath: 'lds/',
+        },
+      },
       ...


postinstall 時の処理

次に、npm install 後に LDS を静的リソース配信用のディレクトリ(ここでは public/assets/lds/)にコピーしておきます。
ドキュメントでは postinstall.js スクリプトを作ってシンボリックリンクを貼る処理をやっているようですが、単にコピーするだけなら package.json 内に記述しても十分だと思います。

 // package.json
 {
   "scripts": {
+    "postinstall": "rm -rf public/assets/lds && cp -r node_modules/@salesforce-ux/design-system/assets public/assets/lds",
   ...
 }


読み込み

アプリのエントリーポイントで

 // public/index.js
 import { render } from 'react-dom';
 import { AppContainer } from 'react-hot-loader';
 
+import '@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.min.css';
+
 import configureStore from './store/configureStore';
 import App from './containers/App';
 ...

とすれば OK です。


3. Icon パスの設定

最後にもう一点だけ。Icon を使う場合、アプリのルートで <IconSettings> コンポーネントを使い、パスを正しく設定しておく必要があります。

README#icon-usageSLDS React | Icon Settings あたりに記載があります。

 // public/index.js
 import { AppContainer } from 'react-hot-loader';
 
 import '@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.min.css';
+import IconSettings from '@salesforce/design-system-react/components/icon-settings';
 
 import configureStore from './store/configureStore';
 import App from './containers/App';
 
 import './styles/index.scss';
 
 const store = configureStore();
 
 const rootEl = document.getElementById('root');
+const LDS_ROOT = 'assets/lds';
 
 render(
   <AppContainer>
     <Provider store={store}>
-      <App />
+      <IconSettings
+        standardSprite={`${LDS_ROOT}/icons/standard-sprite/svg/symbols.svg`}
+        utilitySprite={`${LDS_ROOT}/icons/utility-sprite/svg/symbols.svg`}
+        actionSprite={`${LDS_ROOT}/icons/action-sprite/svg/symbols.svg`}
+        doctypeSprite={`${LDS_ROOT}/icons/doctype-sprite/svg/symbols.svg`}
+        customSprite={`${LDS_ROOT}/icons/custom-sprite/svg/symbols.svg`}
+      >
+        <App />
+      </IconSettings>
     </Provider>
   </AppContainer>,
   rootEl,
 );

ここに関してはよくわかってないことが 2 点あって、

1) <IconSettings> には iconPath を指定する方法と、xxxSprite というアイコンの種類ごとのパスを指定する方法とあるんですが、webpack を使ってると後者を推奨してるようです。

Individual sprites If you are using webpack it is advised to use the sprite properties {actionSprite, standardSprite...} to specify the individual sprite paths so that webpack can easily re-write the paths.

この PR で追加されたようで、そこでも webpack のためにということが書かれていますが詳しいことはよくわからず。

2) xxxSprite の指定のしかたとして、Example にあるように

import actionSprite from '@salesforce-ux/design-system/assets/icons/action-sprite/svg/symbols.svg';

のようにすればいいのかなと思ったんですが、なぜかうまくいかず。。。
結局 postinstall でコピーした方のパスを指定してうまくいったのでそうしています。


結果

f:id:dackdive:20180206041536p:plain

<Button> だけですが、無事に LDS のスタイルがあたった状態でコンポーネントが描画できていることが確認できました。アイコンも表示されています。


おわりに

書いてみるとなんてことないんですが、 @salesforce がちょいちょい抜けてたりとドキュメントの整備が追いついてないみたいで結構ハマりました。。。
とりあえず使えるところまでできてよかった。


トラブルシューティング

遭遇したエラー達。

ERROR in ./node_modules/@salesforce/design-system-react/components/button/index.jsx
Module parse failed: Unexpected token (228:3)
You may need an appropriate loader to handle this file type.
ERROR in ./public/components/App.js
Module not found: Error: Can't resolve '@salesforce/design-system-react/components/button' in '/Users/yamazaki/workspace/react/redux-express-template/public/components'

→ webpack の設定不備。 .jsx がビルド対象になっていない、 node_modules/@salesforce/design-system-react が include されていない、などなど。

/Users/yamazaki/workspace/react/redux-express-template/node_modules/webpack/lib/webpack.js:19
                throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
        ^
WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
 - configuration.resolve.extensions[0] should not be empty.
   -> A non-empty string
    at webpack (/Users/yamazaki/workspace/react/redux-express-template/node_modules/webpack/lib/webpack.js:19:9)
    at Object.<anonymous> (/Users/yamazaki/workspace/react/redux-express-template/app.js:13:20)
    at Module._compile (module.js:643:30)
    at babelWatchLoader (/Users/yamazaki/workspace/react/redux-express-template/node_modules/babel-watch/runner.js:50:13)
    at Object.require.extensions.(anonymous function) [as .js] (/Users/yamazaki/workspace/react/redux-express-template/node_modules/babel-watch/runner.js:61:7)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Function.Module.runMain (module.js:684:10)
    at process.on (/Users/yamazaki/workspace/react/redux-express-template/node_modules/babel-watch/runner.js:108:21)

→ webpack の resolve.exntensions に空文字 ['', '.js', '.jsx'] は webpack 2 系以降 NG になったぽい。使うなら'*'