mcpField
S
sf-lwc

sf-lwc

github.com/clientell-ai開発20101 回インストール32 週間前に更新
リポジトリ

sf-lwc について

sf-lwcは、Salesforce開発者向けのLightning Web Componentsスキャフォールディングスキルです。コンポーネントファイルや設定、Jestテストを生成し、SLDSスタイル、データ取得、ナビゲーション、メッセージングのパターンも扱います。

主な機能

  1. 01HTML、JS、CSS、メタデータファイルを生成
  2. 02モックデータを使ったJestテストを作成
  3. 03SLDSスタイルの指針を提示
  4. 04LDSとApexのデータパターンを採用
  5. 05ナビゲーションとLightning Message Serviceを扱う

活用シーン

  1. 01新しいLWCバンドルを作成
  2. 02コンポーネントのテストを追加
  3. 03レコードCRUD画面を構築
  4. 04DOMをまたぐコンポーネント通信を実装

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/clientell-ai/salesforce-skills --skill sf-lwc

インストール後、Claude Code で/sf-lwcを入力して読み込まれたか確認します。

同梱ファイルreferences/lwc-patterns.md

詳細

LWC スキャフォルダー

Salesforce Lightning Web Component のスペシャリストとして、完全で本番環境に対応した LWC バンドルを生成します。

LWC バンドルの構成

すべての LWC は、force-app/main/default/lwc/componentName/ にある以下のファイルで構成されます。

myComponent/
├── myComponent.html          # Template
├── myComponent.js            # Controller
├── myComponent.css           # Styles (SLDS-compliant)
├── myComponent.js-meta.xml   # Configuration
└── __tests__/
    └── myComponent.test.js   # Jest tests

命名規則

  • バンドルフォルダー:camelCase(例:accountList)
  • HTML マークアップ:c- 名前空間を付けた kebab-case(例:<c-account-list>)
  • JS クラス:PascalCase(例:AccountList)
  • CSS:コンポーネント名に準拠

JavaScript コントローラーパターン

import { LightningElement, api, wire, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { NavigationMixin } from 'lightning/navigation';
import getRecords from '@salesforce/apex/MyController.getRecords';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';

export default class MyComponent extends NavigationMixin(LightningElement) {
    @api recordId;
    @track records = [];
    error;
    isLoading = false;

    @wire(getRecords, { recordId: '$recordId' })
    wiredRecords({ error, data }) {
        if (data) {
            this.records = data;
            this.error = undefined;
        } else if (error) {
            this.error = error;
            this.records = [];
        }
    }

    handleAction() {
        this.isLoading = true;
        imperativeMethod({ param: this.recordId })
            .then(result => {
                this.dispatchEvent(new ShowToastEvent({
                    title: 'Success',
                    message: 'Operation completed',
                    variant: 'success'
                }));
            })
            .catch(error => {
                this.dispatchEvent(new ShowToastEvent({
                    title: 'Error',
                    message: error.body?.message || 'An error occurred',
                    variant: 'error'
                }));
            })
            .finally(() => {
                this.isLoading = false;
            });
    }
}

Meta XML 設定

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>62.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <objects>
                <object>Account</object>
            </objects>
            <property name="title" type="String" default="My Component"/>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

Jest テストパターン

import { createElement } from 'lwc';
import MyComponent from 'c/myComponent';
import getRecords from '@salesforce/apex/MyController.getRecords';

// Mock Apex method
jest.mock('@salesforce/apex/MyController.getRecords', () => ({
    default: jest.fn()
}), { virtual: true });

const MOCK_DATA = [
    { Id: '001xx000003ABCDEF', Name: 'Test Account' }
];

describe('c-my-component', () => {
    afterEach(() => {
        while (document.body.firstChild) {
            document.body.removeChild(document.body.firstChild);
        }
        jest.clearAllMocks();
    });

    it('renders records when data is returned', async () => {
        getRecords.mockResolvedValue(MOCK_DATA);

        const element = createElement('c-my-component', { is: MyComponent });
        element.recordId = '001xx000003ABCDEF';
        document.body.appendChild(element);

        await Promise.resolve();

        const items = element.shadowRoot.querySelectorAll('.record-item');
        expect(items.length).toBe(1);
    });

    it('shows error when apex call fails', async () => {
        getRecords.mockRejectedValue(new Error('Test error'));

        const element = createElement('c-my-component', { is: MyComponent });
        document.body.appendChild(element);

        await Promise.resolve();

        const errorEl = element.shadowRoot.querySelector('.error-message');
        expect(errorEl).toBeTruthy();
    });
});
Lightning Data Service(LDS)

Apex を使わずに CRUD を行うには、lightning/uiRecordApi を使用します。

  • getRecord ワイヤーアダプター — 項目レベルセキュリティを適用してレコードを読み取ります
  • createRecord、updateRecord、deleteRecord — 命令型の CRUD 操作です
  • getObjectInfo、getPicklistValues — メタデータにアクセスします
  • refreshApex() — 更新後にワイヤーキャッシュを無効化します
  • 使用する場面:シンプルな CRUD。複雑なクエリやビジネスロジックには、Apex ワイヤーを使用します。
ライフサイクルフック
フックタイミング一般的な用途
constructor()コンポーネントの作成時状態の初期化
connectedCallback()DOM への挿入時データの取得、リスナーの追加
renderedCallback()各レンダリングの後DOM 操作(フラグでガードしてください!)
disconnectedCallback()DOM からの削除時リスナーのクリーンアップ、LMS の購読解除
errorCallback(error, stack)子コンポーネントのエラー発生時エラーバウンダリー、ロギング
ナビゲーション

ページ参照タイプには NavigationMixin を使用します。

  • standard__recordPage — レコードの表示/編集/複製(recordId、actionName が必要です)
  • standard__objectPage — オブジェクトのホーム/リスト/新規ページ(objectApiName、actionName が必要です)
  • standard__namedPage — 標準ページ(home、chatter、filePreview)
  • standard__webPage — 外部 URL(url が必要です)
Lightning Message Service(LMS)

LWC、Aura、Visualforce 間の DOM をまたぐ通信です。

  • .messageChannel-meta.xml でメッセージチャネルを定義します
  • publish(messageContext, channel, payload) で送信します
  • subscribe(messageContext, channel, handler, {scope: APPLICATION_SCOPE}) で受信します
  • メモリリークを防ぐため、必ず disconnectedCallback() で unsubscribe() を呼び出します
Shadow DOM と Light DOM
  • Shadow DOM(デフォルト):CSS の分離、DOM のカプセル化 — ほとんどのコンポーネントで使用します
  • Light DOM(lwc:dom="light"):カプセル化なし — コンポーネント間の ARIA 参照、グローバル CSS、サードパーティライブラリからの DOM アクセスが必要な場合に使用します
  • Shadow DOM は外部からの document.querySelector() をブロックします — 内部では this.template.querySelector() を使用します

ルール

  • スタイリングには常に SLDS クラスを使用します — SLDS にユーティリティがある場合は、カスタム CSS を避けます
  • 公開プロパティには @api を使用します。プロパティはデフォルトでリアクティブです
  • 宣言的なデータ取得には @wire を使用します
  • ユーザーが開始するアクションには、命令型の Apex 呼び出しを使用します
  • すべてのコンポーネントでローディング状態とエラーを処理します
  • シンプルな CRUD には lightning-record-form/lightning-record-edit-form を使用します
  • 子から親への通信にはカスタムイベントをディスパッチします
  • DOM をまたぐ通信には MessageChannel を使用します

注意点

  • @track は非推奨です — API v40 以降では、すべてのプロパティがデフォルトでリアクティブです
  • renderedCallback() はレンダリングのたびに実行されます — 無限ループを防ぐため、常にブール値フラグでガードしてください
  • LDS キャッシュは自動では更新されません — 命令型の更新後に refreshApex(wiredProperty) を呼び出します
  • メモリリークを防ぐため、LMS の購読は disconnectedCallback() で必ず解除する必要があります
  • Shadow DOM はコンポーネント間の ID ベースの ARIA 参照(aria-labelledby)をブロックします — アクセシビリティのためには Light DOM を使用します
  • CSP は eval()、new Function()、インラインの <script> をブロックします — サードパーティライブラリは Static Resources から loadScript() を使って読み込みます
  • @api プロパティはコンポーネント内で読み取り専用です — 親が値を設定し、子は変更できません
  • リアクティブなパラメーターが変わるとワイヤーアダプターが再実行されます — 不必要なパラメーターの変更は避けます

ワークフロー

  1. コンポーネントの要件を把握します
  2. 拡張できる既存のコンポーネントがないか確認します
  3. バンドルファイル一式(HTML、JS、CSS、meta.xml)を生成します
  4. モックデータを使って Jest テストファイルを生成します
  5. デプロイします:sf project deploy start -d force-app/main/default/lwc/componentName/

参考資料

  • LWC パターン — LDS、ナビゲーション、LMS、データテーブル、カスタムイベント、スロット、アクセシビリティ、SLDS、サードパーティーライブラリ、動的コンポーネント、Experience Cloud

コメント