jsforce-metadata-toolsを使って静的リソースをらくらくデプロイ


画面上からぽちぽち静的リソースアップロードするのつらすぎ

やること

  • CLIから静的リソースのアップロード

はじめに


jsforceという神ライブラリ使います
いつも大変お世話になっております
jsforce-metadata-tools
まだの人はコマンド使えるようにインストールしといてください

1. デプロイ環境整備 & リソース用意


用意する環境はこんな感じ
$ tree
.
├── assets
│   └── js
│       └── hoge.js
└── package
    ├── package.xml
    └── staticresources
        ├── hoge.resource
        └── hoge.resource-meta.xml
前回のやつ のJSを切り出す

hoge.js
function sayHello(helloTo) {
  new Promise((resolve, reject) => {
    Visualforce.remoting.Manager.invokeAction(
      '{!$RemoteAction.SR_Controller.sayHello}',
      helloTo,
      function(result, event) {
        if(event.status) {
          resolve(result);
        }
      }
    );
  })
  .then(function(data) {
    document.getElementById("main").innerHTML = data;
  });
  return false;
}
圧縮

$ zip package/staticresources/hoge.resource assets/js/hoge.js

「hoge」の部分がNameになるのであまり適当なものをつけないほうがいいかも
拡張子は.resourceじゃないとデプロイした時に怒られる

最小限のpackage.xml用意

package.xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>hoge</members>
        <name>StaticResource</name>
    </types>
    <version>40.0</version>
</Package>

リソース(hoge.resource)に付随するxmlが無いと怒られるので作成

hoge.resource-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
    <cacheControl>Public</cacheControl>
    <contentType>application/zip</contentType>
</StaticResource>

XMLの書き方はここ
メタデータ API 開発者ガイド - StaticResource

2. デプロイ


下記コマンドで一発デプロイ。神ライブラリすぎます!!!!!!

$ jsforce-deploy -u {username} -p {password} -D ./package/
Logged in as: ***********
Deploying to server...

Deploy Succeeded.

sandboxならlオプションでログインURL指定できます。

3. 確認


前回のやつ とおんなじ感じでファイルを用意

VF
<apex:page showHeader="false" sidebar="false" controller="SR_Controller">

  <div id="main">
    <input type="button" onClick="sayHello('Marc')" value="Salesforceへのリンク"/>
  </div>

  <script type="text/javascript" src="{!URLFOR($Resource.hoge, '/assets/js/hoge.js')}"></script>
</apex:page>
APEX
public with sharing class SR_Controller {
    @RemoteAction
    public static String sayHello(String helloTo) {
        return 'Hello ' + helloTo;
    }
}

で前回と同じようにRemoteActionできるはず、ができない。。。

余談:RemoteAction周りでちょっと詰まった話


コンソールにエラーが吐かれてるので確認すると、

VFRemote.js:121 Uncaught (in promise) TypeError: Cannot read property 'SR_Controller' of undefined
    at Object.getObject (VFRemote.js:121)
    at constructor.getController (VFRemote.js:137)
    at constructor.getAction (VFRemote.js:137)
    at constructor.invokeAction (VFRemote.js:138)
    at Promise (hoge.js:3)
    at new Promise (<anonymous>)
    at sayHello (hoge.js:2)
    at HTMLInputElement.onclick (sr:8)

で、VFRemote.jsのgetObjectの引数みてみたら

VFRemote.js:122 {!$RemoteAction.SR_Controller
VFRemote.js:123 undefined 

VFRemote.jsを少し読み進めると

VFRemote.js
    getAction: function(a) {
        if (!a || !$VFRM.Util.isString(a) || -1 == a.indexOf(".")) return null;
        var b = this.getController(a.substring(0, a.lastIndexOf("."))), 

a.substring(0, a.lastIndexOf("."))),  

ここにきてVFの記法を思い出した、外部リソースはコンパイルされないのね

hoge.js
function sayHello(helloTo) {
  new Promise((resolve, reject) => {
    Visualforce.remoting.Manager.invokeAction(
      '{!$RemoteAction.SR_Controller.sayHello}',  // ←ココ
      helloTo,
      function(result, event) {
        if(event.status) {
          resolve(result);
        }
      }
    );
  })
  .then(function(data) {
    document.getElementById("main").innerHTML = data;
  });
  return false;
}

:'{!$RemoteAction.SR_Controller.sayHello}'
:'SR_Controller.sayHello'

ちゃんと動きました
外部JSからのRemoteActionの参照はそのままテキストで渡せば(おそらく)OK

なにが入ってんのかなーと思い{!$RemoteAction}をVFのページに直書きしてみると

無効なグローバル変数「ApexPagesELAdapterContext.$RemoteAction」を使用しています。期待される項目セレクタがありません。

見たい、中身知ってる人(見方分かる人)いたら教えてください。

公式docっぽいの見てみたけど、VFに直接記述しない場合は自動で名前解決されないのでコントローラとメソッドだけでOKってことで良いのかな。
名前空間および JavaScript Remoting

おわり。

ドリームフォース行く人うらやましい。俺もミリオンドルマンになりたい。