dackdive's blog

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

[Salesforce]Packaging 2.0(第二世代パッケージ)を試してみた

この記事は Salesforce Platform Advent Calendar 2017 の 3 日目の記事です。

はじめに

アドベントカレンダーなので「私の Salesforce 情報収集術!(2017 年冬)」とかでお茶を濁そうと考えていたんですが
Winter'18 で Packaging 2.0 がベータ版となり、機能自体は誰でも試せるようになりました(リリースノート)。
というわけでやってみたレポです。

なお、最近 Salesforce DX 開発者ガイド が日本語版も公開されたため そこに書かれていることを一通りなぞっただけの記事になってしまいました。

開発者ガイドによると「第二世代パッケージ」と表現してるので、この記事でも以降そう呼びます。

事前準備

事前に必要な各種設定を行っていきます。

開発者ガイドで言うとここです。
組織の設定の確認 | Salesforce DX 開発者ガイド | Salesforce Developers


Salesforce DX CLI のインストール、Dev Hub 組織のサインアップ

第二世代パッケージは DX による開発を前提としています。
そのため、 sfdx コマンドおよび Dev Hub 組織を利用可能にしておく必要があります。

このあたりは Trailhead の App Development with Salesforce DX というモジュールに手順が書いてありますので割愛します。 または、先日そのハンズオン勉強会をやったときの日本語の資料が ここ にあるのでそちらを参照ください。


Dev Hub 組織で第二世代パッケージを有効化する

第二世代パッケージはベータ版機能のため、組織の設定で有効化する必要があります。 開発 > Dev Hub を開きます。

f:id:dackdive:20171204042013p:plain


Dev Hub 組織の「私のドメイン」を有効化する

Dev Hub 組織の「私のドメイン」が有効でないと、この後の名前空間のリンクができないので有効にします。
30 日間のトライアル組織でも「私のドメイン」は有効になってないので忘れずにやります。


名前空間登録用の DE 組織をサインアップ

パッケージの名前空間を登録するために、なぜか専用の Developer Edition 組織が必要になります。 https://developer.salesforce.com/signup からサインアップします。

組織にログイン後、アプリケーション > パッケージマネージャ から登録できます。

f:id:dackdive:20171204042110p:plain

ちなみに、名前空間の登録も Dev Hub 組織使えばいいじゃんって思ったんですができないようです。

f:id:dackdive:20171204042128p:plain


Dev Hub 組織で名前空間組織をリンクする

先ほど作成した名前空間組織を Dev Hub 組織にリンクします。
再度 Dev Hub 組織を開き、「Namespace Registry」タブを開きます。

SS 2017-12-04 2.26.54.png

名前空間をリンク」ボタンを押すとポップアップが開くので、そこで名前空間組織にログインしアプリケーションを許可します。
(このボタン、「私のドメイン」が無効だと表示されません)


開発

準備が整ったので、プロジェクトを作ってパッケージ作成を試してみます。


DX 用新規プロジェクトを作成

CLI でプロジェクトのひな形を作ります。

$ sfdx force:project:create -n my-first-package-2

my-first-package-2
├── README.md
├── config
│   └── project-scratch-def.json
├── force-app
│   └── main
│       └── default
│           └── aura
└── sfdx-project.json


sfdx-project.json名前空間を設定する

 {
   "packageDirectories": [
     {
       "path": "force-app",
       "default": true
     }
   ],
-  "namespace": "",
+  "namespace": "zakiyama_test",
   "sfdcLoginUrl": "https://login.salesforce.com",
   "sourceApiVersion": "41.0"
 }


Scratch Org で開発する

第二世代パッケージの作成とは直接関係ありませんが、通常はこの後 Scratch Org を作成して開発を行っていくことになります。

なお、Scratch Org を作成する際に名前空間が正しくリンクされていないと下記のエラーがでます。

$ sfdx force:org:create -f config/project-scratch-def.json -a ScratchOrg -s
ERROR:  名前空間属性 foo に無効な値が指定されました。単純な Javascript 名にしてください。.


パッケージを作成する

開発が完了したという想定で、いよいよパッケージを作成します。
開発者ガイドではこのあたりです。
パッケージの作成 | Salesforce DX 開発者ガイド | Salesforce Developers

$ sfdx force:package2:create --name new_package --description "New Package" --containeroptions Unlocked
Successfully created a second-generation package (package2). 0Ho7F0000008OIASA2 0337F000000HWJrQAO
=== Ids
NAME                   VALUE
─────────────────────  ──────────────────
Package2 Id            0Ho7F0000008OIASA2
Subscriber Package Id  0337F000000HWJrQAO

成功すると Package2 IdSubscriber Package Id という 2 つの ID 情報が表示されます。
Package2 Id はこの後すぐ使います。後者はなんだろう...


sfdx-project.jsonpackageDirectories にパッケージ ID などを追加する

ここが一番のハマりポイントでした。
sfdx-project.json ファイルでのパッケージの設定 | Salesforce DX 開発者ガイド | Salesforce Developers
によると

各パッケージの Salesforce DX プロジェクト設定ファイルにエントリを追加し、そのバージョンの詳細、連動関係、および組織設定を指定します。

各パッケージ説明には次の属性が含まれます。

{
    "path": "logic",
    "id": "0HoB00000004CFuKAM",
    "versionName": "v 1.2",
    "versionDescription": "ver 1.2 - anc = 1.1",
    "versionNumber": "1.2.0.NEXT",
    "ancestorId": "05iB00000004CIeIAM",
    "dependencies": [
        {
            "packageId": "0HoB00000004CFpKAM",
            "versionNumber": "1.2.0.LATEST"
        },
        {
            "packageId": "0HoB00000004CFkKAM",
            "versionNumber": "1.2.0.LATEST"
        }
    ],            
    "features": "MultiCurrency",
    "orgPreferences": {
        "enabled": [
            "S1DesktopEnabled",
            "Translation"
        ],
        "disabled": []
    }
}

とありますが、これ実際には sfdx-project.jsonpackageDirectories プロパティの中に記述する必要がある みたいです。
Dreamhouse アプリのリポジトリ 見てなんとかわかった...!)

 {
   "packageDirectories": [
     {
       "path": "force-app",
+      "id": "0Ho7F0000008OIASA2",
+      "versionNumber": "1.0.0.NEXT",
       "default": true
     }
   ],
   "namespace": "zakiyama_test",
   "sfdcLoginUrl": "https://login.salesforce.com",
   "sourceApiVersion": "41.0"
 }

この 2 つ以外にもバージョンや依存関係まわりのプロパティが多数あるようなんですが、今の時点では詳細はわかってません。


パッケージバージョンを作成する

作成したパッケージに対し、今度はバージョンを作成します。
パッケージを作成したときの Package2 Id を使って

$ sfdx force:package2:version:create -i 0Ho7F0000008OIASA2

とします。

成功すると

Package2 version creation request is InProgress. Run "sfdx force:package2:version:create:get -i 08c7F0000008OIKQA2" to query for status.

といったメッセージが表示されます。


(余談)

バージョン作成の部分、開発者ガイドでは

次のコマンドでパッケージバージョンを作成します。ディレクトリ名またはパッケージ ID を指定します。

と記載があり、 -d による対象ディレクトリ指定か -i による対象パッケージ Id 指定のいずれかでいけるようです。

が、どちらの場合も sfdx-project.jsonid を指定しなくて良いわけではなく、以下のエラーが出ました。

ERROR:  The --package2id (-i) value [0Ho7F0000008OIASA2], doesn’t match the id value in any packageDirectories specified in sfdx-project.json.

正しい設定方法は未だによくわかっていません。。。


バージョン作成の進行状況を確認する

GUI から作成したときと同じく、パッケージバージョンの作成は少し時間がかかるようです。
作成コマンド実行時に表示された以下のコマンドで、状況を確認することができます。

$ sfdx force:package2:version:create:get -i 08c7F0000008OIKQA2
=== Package2 Version Create Request
NAME                            VALUE
──────────────────────────────  ──────────────────
ID                              08c7F0000008OIFQA2
Status                          InProgress
Package2 Id                     0Ho7F0000008OIASA2
Package2 Version Id
Subscriber Package2 Version Id
Tag
Branch
Created Date                    2017-12-04 03:59


結果

f:id:dackdive:20171204050232p:plain

f:id:dackdive:20171204050312p:plain

ウッ😇

今日はここまで。。。


おわりに:まだまだわからないことだらけ

というわけで、何とも消化不良な結果に終わってしまいました。。。
まあエラー自体はもう少し時間をかければなんとかなるんでしょうが。

今回やってみただけでは、以下のような点がいろいろとわかっておりません。

Salesforce DXのコマンドをzshで補完する

メモ。
Salesforce DX の CLI はコマンドが長いのでシェルで補完してくれる関数ないかなーと思っていたら公式ではないけどあった。

Salesforce DX aliases and shell completions for working with sfdx in zsh and vim · GitHub

これの sfdx_completion.zsh というファイルがそう。

(2018/9/13追記)

中の人が公開しているものもあった。

オプションの補完など、こちらの方が優秀そう。
インストール方法は README の通り。

(追記ここまで)


インストール手順

普通の zsh 補完関数と同じように設定すれば OK だが、やり方を知らなかったので備忘録として。
ここを読むといい。
https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org

まず、補完関数を格納するディレクトリを作成し、 fpath という変数に追加する。( .zshrc に記載する)

$ mkdir -p ~/.zsh/completion
# ~/.zshrc
fpath=(~/.zsh/completion $fpath)

続いて、補完関数を用意する。
補間関数ファイルは、

  • 1 行目に補完対象となるコマンドを #compdef sfdx という形式で記載する
  • ファイル名は _sfdx のように、 _[補完対象のコマンド] にする

というルールがあるらしいが、今回は gist の sfdx_completion.zsh の内容をコピーし、_sfdx というファイル名でディレクトリに格納すればいい。

保存したら

$ source ~/.zshrc

で補完が効くようになる。


結果

f:id:dackdive:20171029134330g:plain

まだ本格的に試してないけどよさげ。


懸念

Salesforce が公式で提供しているものでもないし(Author も中の人ではなさそう)、どうやってこの補間関数を生成したのかわからないので
今後の CLI のバージョンアップに追従してくれるかどうかは謎。

[Salesforce]フロー(Visual Workflow)でルックアップ項目を実現する

メモ。
SWTT 2017 のミニハック で個人的に宿題になってたやつです。

課題はこれ。

f:id:dackdive:20171025051238p:plain

どういった問題があったか

課題では「候補者(Contestant)」と「投票(Vote)」という2つのオブジェクトを用意し、主従関係でひもづけた後
子にあたる「投票」オブジェクトのレコード作成画面をフローで実装した。

ここで、レコード作成には親となる「候補者」のIDが必要だが
フローの入力項目には標準画面のようなルックアップ項目が用意されておらず
ミニハックのときは「Lightning ページに埋め込み、レコード ID を直接渡す」方式で回避することにした。
参考:Salesforce World Tour Tokyo 2017 ミニハック解答速報

ただ、これはおそらく正攻法ではないので一般的なやり方を知りたいなあ、と思った次第。


フローで主従関係や参照関係のようなルックアップ項目を実現する方法

フローの「レコードの検索」機能を使えば実現できそう。
公式ドキュメントだとここかな。

Flow Record Lookup Element | Visual Workflow Guide | Salesforce Developers

以下、設定手順。
はじめにフローの全体図を載せる。

f:id:dackdive:20171025055147p:plain

上から順に以下のような処理を行っている。

  1. ユーザに、ルックアップの検索条件となる値を入力させる
  2. ユーザからの入力値を条件として、「レコードの検索」を実行する。検索結果を ID だけ変数に格納する
  3. ID を格納した変数を使い、「レコードの作成」を実行する

ステップ 1

以下のような画面入力としている。

f:id:dackdive:20171025055500p:plain

ここで重要なのは必須項目に指定した「候補者名」の値で、ここでは ContestantName という変数に格納している。


ステップ 2「レコードの検索」

f:id:dackdive:20171025055954p:plain

検索対象のオブジェクトを指定した後、「次の条件と一致します。」の下で具体的な検索条件を指定する。

今回は「候補者」オブジェクトの Name 項目に対して部分一致で検索したかったのでキャプチャのような設定になった。
演算子は「次の文字列を含む」)
値には先ほどのステップで格納した ContestantName を指定する。

Name 以外を検索条件としたければここの条件を自由にカスタマイズすれば良い。

また、「レコードの項目を変数に割り当ててフローで参照します。」のところでは
検索結果のうち、レコードの作成に必要なのは ID 項目なのでそれだけ別の変数(ここでは Contestant)に割り当てている。

ステップ 3「レコードの作成」

f:id:dackdive:20171025060340p:plain

ステップ 2 で変数に格納した ID を、子レコードの主従関係/参照関係項目(ここでは Contestant__c)にマッピングしている。

これで親レコードの参照が必要なレコード作成であっても実現できたことになる。


問題点

親レコードを検索する、という要件は満たせたが、標準画面のように「入力値に対する検索結果を自分の目で確認して、正しいレコードを選ぶ」といったことはできていないため
ユーザビリティという観点ではやや標準の項目に劣る。特に複数件ヒットしたとき。

また、レコードが見つからなかった場合の考慮がされていないので
主従関係項目の場合はハンドリングされていないエラーが発生するし、参照関係項目の場合は親にひもづかないレコードが作成されてしまっている、といったことが起きそう。

これを回避するためには、ステップ2 で「レコードが検出されない場合、Null 値を変数に割り当てます。」にチェックを入れ
ステップ2 と 3 の間に条件分岐ロジックなどをはさみ、Null だったら画面にエラー表示して終了、みたいなことを自分で構築しておく必要があると思われ。

なお「レコードが検出されない場合〜」オプションについては、ドキュメントによると

By default, the variable’s values are left unchanged.

ということなので変数が未使用ならチェックは必ずしも必要ではないかも。


余談

というところまで調べた後で、海外のデベロッパーがずいぶん前に同じトピックでブログ書いてたことに気づいた。

どうもこれによると、「レコードの検索」のかわりに「動的レコード選択肢」を使っており、
これを使うとラジオボタンや選択リストで検索結果を表示し、ユーザに選択させることができるので
こちらの方がより良い解決方法だと思う。

Chrome開発者ツールのコンソールでJavaScriptライブラリを読み込む

ちょいメモ。CDN で配信されているライブラリ限定。
moment.js とか挙動をちょっと試したいなーと思ったときに環境作るの面倒だったんですが、こちらの記事を読んでいて。

Chrome 開発者ツールのコンソールで以下を実行すると、3rd party ライブラリ(この場合 jQuery)が使えるようになる。

> (script = document.createElement('script')).src = 'http://code.jquery.com/jquery-latest.min.js'
> document.getElementsByTagName('head')[0].appendChild(script)

同じ要領で 1 行目の URL を変えれば moment.js でも React でも CDN で配信されているライブラリなら読み込んでコンソール上で使えるようになる。便利。

> (script = document.createElement('script')).src = 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.0/moment-with-locales.js';
document.getElementsByTagName('head')[0].appendChild(script)
> moment('2017-10-20T02:34:56').format('H:mm')
"2:34"

覚えていられないので関数として登録しておきたい。

第24回 Tokyo Atlassian ユーザーグループ に行ってきたメモ #augj

行ってきました。入り口でなぜかノベルティの栓抜きいただきました。

f:id:dackdive:20171011155914j:plain:w320

話聞きながら適当にメモを取ってたので、少し整理しつつ公開します。

なお、今後イベントの告知や参加申し込みは connpass でなくこちらのサイト経由になるそうです。 Atlassian User Groups Tokyo | Where Atlassian users meet and share best practices


アトラシアン ユーザコミュニティについて by Naho Inuyama @ Atlassian

中の人から Atlassian User Programs というユーザー向けプログラムについて紹介。


アトラシアン サミット 2017 USの最新情報レポートと参加レポート by Miyako Sugimoto @ GxP

  • Atlassian Summit
    • 1年で一番大きなイベント
    • 10 回目
    • 3,600 名 42 カ国
  • 基調講演の内容を共有
  • Trello
    • デスクトップアプリをリリース
    • 全 Atlassian 製品と連携(Bitbucket 内に Trello など)
  • Stride: 新しいコミュニケーションツール
    • Hipchat の後継とは言わず。ただ Hipchat はオンプレのみに
    • チャットしながら決定事項とTODOを書いていける
    • フォーカスモード(通知を一時的にオフ)
  • Jira
    • 優先度が Jira プロジェクトごとに設定可能に
    • デザイン変更(ボード上でカラムが作れる)
  • Portfolio for Jira
    • 依存関係のレポート機能に対応
  • (Jira Service Desk とか Data Center とかの話)
  • Confluence
    • Cloud, モバイルのデザインの変更
  • Bitbucket - Jira 連携強化
    • Bitbucket から Jira 課題を表示・編集可能に
  • [new] Identity Manager
    • SAML, 2段階認証
  • Atlassian Teamwork Platform
    • (独立したプロダクトではなく、製品間の統一を図る機能と思われ)
    • 3 つの柱: 人・エレメント・ホーム
  • 人: 製品間でプロファイルを共通に
  • エレメント: チームが一緒に仕事を完遂するときに共通の協働パターンがあることを発見。そこから機能のアイディアに
    • 重要な仕事を取り込む -> ToDo を Cloud 全製品へ適用中
    • 考えや性格を表現 -> リアクションにいいね! だけでなく絵文字が使えるように
    • 仕事と人の紐付け -> ワンクリックで Jira 課題を作成
  • ホーム: 複数製品の情報がホーム上に表示される
  • 2 日めの基調講演は Ask Atlassian 製品情報はほとんどなし
  • ShipIt (ハッカソン)
  • 公式のイベントレポート:Atlassian Summit U.S. 2017 – Product Keynote レポート | Atlassian Blogs
感想

サミットで発表された内容が非常に簡潔にまとまっていてわかりやすかったです。
Atlassian Summit 知らなかったんですが、イベントの様子など聞く限り Dreamforce ぽいなあなどと。

f:id:dackdive:20171011160036j:plain

また Cloud 版・Server 版だけでなく、Data Center 版というのもあるというのは知りませんでした。
懇親会でも詳しくお聞きしましたが、負荷分散とかもやってくれる Server 版のもうちょっとすごいやつ、という理解。利用規模も数千ライセンスとかになってくると Server 版ではつらいらしい。


ニフティで実践してる、アトラシアン 製品のユーザー管理・運用手法 by Takayuki Ishikawa @ Nifty

  • Atlassian Crowd とは
    • SSO とユーザーID管理
    • 各製品のユーザー管理機能を統合したようなイメージ
  • 経緯
    • 昔は Trac を利用、@nifty アカウントでユーザー認証
    • -> LDAP 認証を採用、Crowd にユーザー管理を集約
感想

この後、LDAP と連携して社内のさまざまな要件を満たすためにどういった設定を行っていったか、について説明いただいたのですが
LDAP を使ったことがなく、難しくてわからず。。。

ただ、組織内外において適切な権限設定というのはやっぱりこの手のツールを使う上で非常に重要ですね。 Atlassian 製品は権限まわりの設定項目が多いので自分も全然わかっていないところ。


Deep Automation JIRA by Narichika Kajihara @ eureka

  • Non - Programming で JiRA 操作を自動化したい管理者向けの内容
  • Automation for Jira の説明

(この後 Kajihara さんからコメントいただいて、やはり料金体系が変わって有料になっちゃったみたい)

  • eureka の環境
    • Jira Software Server 7.3.2 + Jira Service Desk
    • 250 ユーザ
  • Automation for Jira: 特定の条件をトリガとしてさまざまな処理を自動化
    • When: トリガー (Issue が作成されたとき)
    • If: 条件
    • Then: アクション

  • ユーザーの条件(プロジェクト外のユーザーだったら、とか)や添付の有無など、Condition は豊富
  • アクション、Send Slack Message などもあって便利
  • 自動化アイディア
    • 承認依頼する時に、承認者にメンションを自動化
    • サブタスクの自動作成
    • 顧客からのコメントがあったらステータスを変更する。アサインを変更する


感想

今、社内では Jira を開発チームだけで使っているので、どうしても使いたいというニーズはないかなと思ったけど、かなり便利なアドオン。無料だったら絶対使ってたなー笑

メンションしてもみんなあんまりメールは見ないので、Slack への通知がサポートされてるのはかなり魅力的。


おわりに

社内では開発チームだけで Jira, Bitbucket, Confluence を使っているので、Atlassian 製品は「開発者向けツール」というイメージが強かったのですが
Jira Service Desk など、サポートチームと連携したりユーザーからの問い合わせ窓口を作ったり...といったところまでカバーしているんだというのは初めて知りました。

冒頭で、「Atlassian 製品はインストールすれば何の説明もなく使えるようなツールではなく、各種設定を理解して自社のフローに合わせてカスタマイズしたり、逆にツールに合わせて自社のやり方を改めるみたいなことも必要」といった旨のことをおっしゃってて(誤解かも)、それは自分もほんとその通りだと思います。
そういう意味だと Jira 使ってるとこも活用方法は会社によってまちまちだったり、うまくいかなくて困ってるところもあると思うので
今後はそういった事例が LT とかでカジュアルに聞けるといいなあ、と思いました。
ウチも色々苦しんでるので助けてほしい。

あと Cloud 版/Server 版どっち使うのが一般的なんだろうというのが疑問だったんですが、懇親会でウチは全部 Cloud 版です、という話をしたら「(あんな重いのに)よく使えてますね〜」みたいなコメントをいただいてお察しという感じ。

[Salesforce]テキスト、テキストエリア、ロングテキストエリア、リッチテキストエリア項目

ちょっとしたメモ。
カスタムオブジェクトのこれらの項目の違いについて。

特にテキストエリア項目とロングテキストエリア項目の違い、あんまりよくわかってなかったので整理しておく。

f:id:dackdive:20171009002620p:plain


最初にまとめ

項目 最大文字数 UI 改行入力 項目履歴管理
テキスト 255 テキストボックス 不可
テキストエリア 255 テキストエリア
リッチテキストエリア 131,072(*1) リッチテキストエリア
(表示行数指定可(*2))
不可
ロングテキストエリア 131,072(*1) テキストエリア
(表示行数指定可(*2))
不可
  • (*1) オブジェクトごとに、ロングテキストエリア項目とリッチテキストエリア項目で使用できる文字数は合計 160 万文字
  • (*2) Classic のみ反映


違い1:UI

f:id:dackdive:20171010013917p:plain

テキストエリアとロングテキストエリアに UI 上の違いはなく、またテキスト項目と異なり 3 つとも改行入力が可能。

なおリッチテキストエリアおよびロングテキストエリアについては、項目作成時に表示行数を指定することができるがこれは LEX には反映されないらしい。
Ideas がある。
# Visible Lines in Lightning Experience - Ideas - Salesforce Success Community


違い2:最大文字数

テキスト・テキストエリアは 255 文字。リッチテキストエリア・ロングテキストエリアは 1 項目あたり 131,072 文字が最大(最小は 256 文字)。
ただしリッチテキストエリア・ロングテキストエリアについては、1 項目あたりの最大文字数のほかに、オブジェクト全体での制限がある。

参考:カスタム項目の制限 | Salesforce Developer の制限クイックリファレンス | Salesforce Developers

各オブジェクトは、ロングテキストエリア項目とリッチテキストエリア項目で 160 万文字を使用できます。

これは、項目を定義するときにも表示される。

f:id:dackdive:20171010021206p:plain

画面上は 160 万文字ではなく 1,638,400 文字となっているので、文字数でなくデータサイズで上限が決まっている可能性あり。
(1,638,400 / 32,768 = 50 と割り切れるので)

また、リッチテキストエリアには太字やリンクなどを入力できるが、データとしては HTML になっているので実際に表示されている文字数と一致しなくなる。

f:id:dackdive:20171010022815p:plain


違い3:項目履歴管理の可否

地味だけど重要な違いとして。
リッチテキストエリア・ロングテキストエリアは最小文字数が 256 文字からだが、255 文字を超える場合は項目履歴管理で元の値と新しい値の追跡ができなくなる。

参考:項目履歴管理 | Salesforce セキュリティガイド | Salesforce Developers

255 文字を超える項目に対する変更は、編集済みとして追跡され、元の値と新しい値は記録されません。

※変更されたことは追跡可能


リファレンス

thread-loaderとcache-loaderでwebpackのビルドを高速化する

はじめに

こちらのスライドを見て。
Webpackのビルド時間を1/3にした話 #gotandajs // Speaker Deck

紹介されている thread-loader も cache-loader も知らなかった!ので、使い方やどういった効果があるのか自分でも調べてみます。

なお、スライド中にもありますが、たしかに webpack 公式ドキュメントの Build Performance でもこれらの loader について言及されていました。


thread-loader

Runs the following loaders in a worker pool.

時間のかかる(expensive)loader の処理を worker pool で実行し、それにより複数スレッドでの並列ビルドを可能にする?ようです。
(worker pool という用語はどこ由来なのかわからなかった)


使い方

thread-loader を他の loaders の一番上に指定する。

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve("src"),
        use: [
          "thread-loader",
          "expensive-loader"
        ]
      }
    ]
  }
}


オプション

色々あるみたいなので #Examples の with options の項を参照。


注意

Each worker is a separate node.js process, which has an overhead of ~600ms. There is also an overhead of inter-process communication.

Use this loader only for expensive operations!

worker は独立した Node のプロセスなので、起動のオーバーヘッドはそれなりに大きいらしい。
時間のかかる処理にだけ使うように、とのこと。


その他

prewarming

worker の起動による遅延を回避するために、事前に worker pool を起動(warmup)しておくことができる。

const threadLoader = require('thread-loader');

threadLoader.warmup({
  // pool options, like passed to loader options
  // must match loader options to boot the correct pool
}, [
  // modules to load
  // can be any module, i. e.
  'babel-loader',
  'babel-preset-es2015',
  'sass-loader',
]);


で、結局どういった loader に適用すればいいの?

具体例は挙げられてなかった。
スライドや、リポジトリexample/webpack.config.js を参考にするといいと思う。

example では babel-loader を使った js のトランスパイルと sass-loader & ExtractTextPlugin による SCSS の処理に適用していた。


cache-loader

後続の loaders の結果をディスクにキャッシュする。


使い方

thread-loader と同じく、他の loaders の先頭に指定する。

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.ext$/,
        use: [
          'cache-loader',
          ...loaders
        ],
        include: path.resolve('src')
      }
    ]
  }
}


オプション

  • cacheDirectory
    • キャッシュファイルの保存先
    • デフォルトは path.resolve('.cache-loader')
  • cacheIdentifier
    • ??? "Provide an invalidation identifier which is used to generate the hashes. You can use it for extra dependencies of loaders."
    • デフォルトは cache-loader:{version} {process.env.NODE_ENV}{version} は cache-loader のバージョン?)


注意

キャッシュファイルの読み書きというオーバーヘッドが発生するので、こちらも適用は expensive loaders だけに留めるように、とのこと。

また https://webpack.js.org/guides/build-performance/#persistent-cache によると

Clear cache directory on "postinstall" in package.json.

とある。


その他

キャッシュの更新タイミングは?

明記されていなくて困った。
スライドでも

ファイルのmtimeでキャッシュしとく

とあるが、たしかに実装を見ると mtime で比較しているような箇所がなんとなくある
mtime は更新日時(Modified Time。参考


所感

どちらも複雑な設定は必要なく、追加するだけでビルドの高速化が期待できるというところは良いが
ドキュメントを読んだだけでは細かい挙動まで理解しきれていないので、いざ使ってみたらかえって重くなった、あるいはキャッシュが効きすぎて更新が反映されない、とかハマる可能性はありそう。

webpack-dev-server を使った Development Build 時と Production Build とで loader の有効無効切り替えは必要なのか?と思ったが
どちらも webpack のドキュメントでは #General という章に書かれており、

The following best practices should help whether or not you are in development or building for production.

なので、その心配はなさそうと判断。