sfdx のインストール
まずは NodeJs をインストールするところから。
下記リンクの中で、Chocolaty を使ったインストールをしている。
この状態で
> nvm list available | CURRENT | LTS | OLD STABLE | OLD UNSTABLE | |--------------|--------------|--------------|--------------| | 19.9.0 | 18.15.0 | 0.12.18 | 0.11.16 | | 19.8.1 | 18.14.2 | 0.12.17 | 0.11.15 | ... 中略 This is a partial list. For a complete list, visit https://nodejs.org/en/download/releases
とりあえず LTS 入れておけばいいので
nvm install 18.15.0 と nvm use 18.15.0 を実行すると
> node --version v18.15.0
そしたら npm update -g npm と打って、NPM を最新化しておこう。
SFDX のインストールはこんな感じ。アップデートも同じコマンドでよい。
上でも下でも好きな方を使えば良い。
尚、下のコマンドをインストールすると、sf がコマンドインターフェースになる。
> npm install sfdx-cli --global > npm install @salesforce/cli --global
ここでは sf コマンド前提で話を進める。
sfdx プラグインジェネレータのインストール
この辺からジェネレータをインストールする。
依存するパッケージから
npm install -g yarnnpm install -g typescript
そしたらジェネレータのインストール(ここから GitBash)
sf plugins install @salesforce/plugin-dev
これでプラグイン作成の準備ができた。
最初のプラグイン
sf dev generate plugin コマンドを打つと、プラグインのテンプレートが作成される。
$ sf dev generate plugin
_-----_
| | ╭──────────────────────────╮
|--(o)--| │ Time to build an sf │
`---------´ │ plugin! Version 0.7.0 │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
? Are you building a plugin for an internal Salesforce team? No
? Enter the name of your new plugin: hello-plugin
? Enter a robust description for your plugin: hello world
? Enter the author of the plugin: azalea
? Select the % code coverage do you want to enforce: 0%
Cloning into 'D:\OneDrive\workspace\sandbox\sfdxMasterSetupPlugin\Example\hello-plugin'...
Initialized empty Git repository in D:/OneDrive/workspace/sandbox/sfdxMasterSetupPlugin/Example/hello-plugin/.git/
force hello-plugin\.nycrc
force hello-plugin\package.json
Changes to package.json were detected.
Running yarn install for you to install the required dependencies.
yarn install v1.22.19
[1/5] Validating package.json...
[2/5] Resolving packages...
[3/5] Fetching packages...
[4/5] Linking dependencies...
warning " > ts-node@10.9.1" has unmet peer dependency "@types/node@*".
warning "@salesforce/dev-scripts > typedoc@0.22.18" has incorrect peer dependency "typescript@4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x || 4.7.x".
warning "oclif > yeoman-environment@3.12.1" has unmet peer dependency "mem-fs@^1.2.0 || ^2.0.0".
warning "oclif > yeoman-environment@3.12.1" has unmet peer dependency "mem-fs-editor@^8.1.2 || ^9.0.0".
[5/5] Building fresh packages...
$ yarn husky install
yarn run v1.22.19
... (中略)
すると、hello-plugin のテンプレートが作成された。
早速使ってみる
$ cd hello-plugin/ $ bin/dev hello world Hello World at Tue Apr 11 2023. $ bin/dev hello world --name Astro Hello Astro at Tue Apr 11 2023. $ bin/dev hello world --help Say hello. USAGE $ sf hello world [--json] [-n <value>] FLAGS -n, --name=<value> [default: World] The name of the person you'd like to say hello to. GLOBAL FLAGS --json Format output as json. DESCRIPTION Say hello. Say hello either to the world or someone you know. EXAMPLES Say hello to the world: $ sf hello world Say hello to someone you know: $ sf hello world --name Astro
いい感じだ。
ソースを眺めるとこんな感じ

ソースを見てみると
messages/hello.world.md
# summary Say hello. # description Say hello either to the world or someone you know. # flags.name.summary The name of the person you'd like to say hello to. # examples - Say hello to the world: <%= config.bin %> <%= command.id %> - Say hello to someone you know: <%= config.bin %> <%= command.id %> --name Astro # info.hello Hello %s at %s.
マークダウンのトップレベルセクションがリソース名かな。
src/commands/hello/world.ts
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
Messages.importMessagesDirectory(__dirname);
const messages = Messages.load('hello-plugin', 'hello.world', [
'summary',
'description',
'examples',
'flags.name.summary',
'info.hello',
]);
export type HelloWorldResult = {
name: string;
time: string;
};
export default class World extends SfCommand<HelloWorldResult> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static flags = {
name: Flags.string({
char: 'n',
summary: messages.getMessage('flags.name.summary'),
default: 'World',
}),
};
public async run(): Promise<HelloWorldResult> {
const { flags } = await this.parse(World);
const time = new Date().toDateString();
this.log(messages.getMessage('info.hello', [flags.name, time]));
return {
name: flags.name,
time,
};
}
}
上から順に見ていこう。
まずは単純にリソースの読み込み。
Messages.importMessagesDirectory(__dirname);
const messages = Messages.load('hello-plugin', 'hello.world', [
'summary',
'description',
'examples',
'flags.name.summary',
'info.hello',
]);
hello-plugin はプラグインの名前で、hello.world はリソースファイル名かな。
最後の配列で読み込むリソースを指定しているようだ。
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
多分名称が予約されている定数。
ヘルプ表示時に利用される文言らしい。
public static flags = {
name: Flags.string({
char: 'n',
summary: messages.getMessage('flags.name.summary'),
default: 'World',
}),
};
引数定義だね。
name 引数を、略称 n で、説明は flags.name.summary リソース参照。デフォルトは「World」と読める。
思ったよりシンプルな引数構成だ。
public async run(): Promise<HelloWorldResult> {
const { flags } = await this.parse(World);
const time = new Date().toDateString();
this.log(messages.getMessage('info.hello', [flags.name, time]));
return {
name: flags.name,
time,
};
}
run というくらいだし、プラグインのエントリポイントだと思われる。
1 コマンド 1 機能と考えればコレが妥当なのかもしれませんね。
フラグをパースして、ログに吐き出すだけと
ちょっといじって反応を見よう
まずはリソースをイジってみる
# summary hello というスクリプト。 マークダウンで書けるというのも興味深い。 # description `hello world` か、任意の人名で応答するスクリプトです。 # flags.name.summary name フラグに任意の名称を設定して、hello 表示しませう。 # examples - hello world を出力します: <%= config.bin %> <%= command.id %> - 任意の人名で hello メッセージを出力します: <%= config.bin %> <%= command.id %> --name Astro # info.hello Hello %s (at %s )
するとこんな感じのヘルプとなった。
$ bin/dev hello world --help hello というスクリプト。 USAGE $ sf hello world [--json] [-n <value>] FLAGS -n, --name=<value> [default: World] name フラグに任意の名称を設定して、hello 表示しませう。 GLOBAL FLAGS --json Format output as json. DESCRIPTION hello というスクリプト。 マークダウンで書けるというのも興味深い。 `hello world` か、任意の人名で応答するスクリプトです。 EXAMPLES hello world を出力します: $ sf hello world 任意の人名で hello メッセージを出力します: $ sf hello world --name Astro
summaryには1行しか適用されない縛りみたいなものがありそうだ。FLAGSはおそらくソースと組み合わせでの出力だろう。GLOBAL FLAGSは全プラグイン標準搭載と思われる。EXAMPLESはリソースのexamplesをテンプレートに動作してるようだ。
次はソースもイジってみる。
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
Messages.importMessagesDirectory(__dirname);
const messages = Messages.load('hello-plugin', 'hello.world', [
'summary',
'description',
'examples',
'flags.name.summary',
'info.hello',
]);
export type HelloWorldResult = {
who: string;
time: string;
};
export default class World extends SfCommand<HelloWorldResult> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static flags = {
who: Flags.string({
char: 'w',
aliases: ['n', 'name'],
summary: messages.getMessage('flags.name.summary'),
default: 'World',
}),
};
public async run(): Promise<HelloWorldResult> {
const { flags } = await this.parse(World);
const time = new Date().toDateString();
this.log(messages.getMessage('info.hello', [flags.who, time]));
return {
who: flags.who,
time,
};
}
}
やったことは name 引数を who に書き換えたものだ。
ヘルプを見ると
USAGE $ sf hello world [--json] [-w <value>] FLAGS -w, --who=<value> [default: World] name フラグに任意の名称を設定して、hello 表示しませう。
思った通り、定義によって生成されているらしい。
aliases 定義は、FragDefinition を追跡してみたらこんな定義を見つけたので入れてみた。
export type FlagProps = {
name: string;
char?: AlphabetLowercase | AlphabetUppercase;
/**
* A short summary of flag usage to show in the flag list.
* If not provided, description will be used.
*/
summary?: string;
/**
* A description of flag usage. If summary is provided, the description
* is assumed to be a longer description and will be shown in a separate
* section within help.
*/
description?: string;
/**
* The flag label to show in help. Defaults to "[-<char>] --<name>" where -<char> is
* only displayed if the char is defined.
*/
helpLabel?: string;
/**
* Shows this flag in a separate list in the help.
*/
helpGroup?: string;
/**
* Accept an environment variable as input
*/
env?: string;
/**
* If true, the flag will not be shown in the help.
*/
hidden?: boolean;
/**
* If true, the flag will be required.
*/
required?: boolean;
/**
* List of flags that this flag depends on.
*/
dependsOn?: string[];
/**
* List of flags that cannot be used with this flag.
*/
exclusive?: string[];
/**
* Exactly one of these flags must be provided.
*/
exactlyOne?: string[];
/**
* Define complex relationships between flags.
*/
relationships?: Relationship[];
/**
* Make the flag as deprecated.
*/
deprecated?: true | Deprecation;
/**
* Alternate names that can be used for this flag.
*/
aliases?: string[];
/**
* Emit deprecation warning when a flag alias is provided
*/
deprecateAliases?: boolean;
/**
* Delimiter to separate the values for a multiple value flag.
* Only respected if multiple is set to true. Default behavior is to
* separate on spaces.
*/
delimiter?: ',';
};
試しに実行してみると
$ bin/dev hello world --who HAHAHA Hello HAHAHA (at Tue Apr 11 2023 ) $ bin/dev hello world --n HAHAHA Hello HAHAHA (at Tue Apr 11 2023 )
思った通りに動いてくれるようだ。
プラグインをインストールしてみる
sf plugins link . コマンドを実行すると、hello-plugin を sfdx コマンドとしてインストールし始める。
$ sf plugins link . @salesforce/cli: linking plugin hello-plugin... - [3/5] Fetching packages... warning " > ts-node@10.9.1" has unmet peer dependency "@types/node@*". warning "@salesforce/dev-scripts > typedoc@0.22.18" has incorrect peer dependency "typescript@4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x || 4.7.x". warning "oclif > yeoman-environment@3.12.1" has unmet peer dependency "mem-fs@^1.2.0 || ^2.0.0". @salesforce/cli: linking plugin hello-plugin... done
実際に動かしてみるか。
$ sf hello world --name Astro Hello Astro (at Tue Apr 11 2023 )
因みにリンクしてるとはパスが通ってるだけっぽいので、
# info.hello Hello Mr, %s (at %s )
こんな修正してやると
$ sf hello world --name Astro Hello Mr, Astro (at Tue Apr 11 2023 )
書き換わった。
リンクの解除は sf plugins unlink . できるようだ。
コマンドの追加
手動で追加もできそうではあるけど、コマンド生成のジェネレータもあるらしい。
sf dev generate command --name call:external:service
