mcpField
F
fix-fiori-elements-extensions

fix-fiori-elements-extensions

github.com/ui5開発44156 回インストール1 週間前に更新
リポジトリ

fix-fiori-elements-extensions について

fix-fiori-elements-extensionsは、UI5のモダナイズ時に非推奨のFiori Elements V2コントローラー拡張を扱うスキルです。マニフェスト登録は変更せず報告し、プログラム登録はControllerExtensionとマニフェスト登録へ移行します。

主な機能

  1. 012種類の拡張登録パターンを判別
  2. 02マニフェスト登録のコントローラーは変更せず報告
  3. 03プログラム登録をControllerExtensionへ移行
  4. 04登録をマニフェストに移動
  5. 05ハンドラー参照を更新しLinterで確認

活用シーン

  1. 01非推奨コントローラーAPIを使うUI5アプリのモダナイズ
  2. 02手動でのモダナイズが必要な拡張の特定
  3. 03registerControllerExtensionsの利用箇所の移行

インストール

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

$npx skills add https://github.com/ui5/plugins-coding-agents --skill fix-fiori-elements-extensions

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

詳細

Fiori Elements コントローラー拡張の修正

このスキルは、UI5 のモダナイゼーションにおける Fiori Elements V2 コントローラー拡張に対応します。対応方法が異なるケースが 2 つあります。

  • ケース B(最も一般的):manifest の controllerName で登録された拡張 → 報告のみ。コントローラーファイルは変更しないでください。 そのまま残し、手動で対応が必要なファイルをユーザーに伝えます。
  • ケース A(まれ):registerControllerExtensions を使用する拡張 → ControllerExtension.extend() と manifest 登録へのモダナイゼーションをすべて実施します。

対応する Linter ルール

ルール IDメッセージパターンこのスキルの対応
no-deprecated-api非推奨の registerControllerExtensions の使用ケース A:manifest + ControllerExtension にモダナイズ
no-deprecated-api非推奨の sap.ui.controller の使用ケース B(manifest に controllerName がある場合):報告のみ。修正しない;ケース A(registerControllerExtensions の場合):ControllerExtension クラス
no-deprecated-api非推奨の Controller(sap/ui/core/mvc/Controller からのインポート)の使用ケース B:報告のみ。修正しない

クイック判定:どちらのケースに該当しますか?

Is there a `controllerName` entry in manifest.json under
sap.ui5/extends/extensions/sap.ui.controllerExtensions with a SIMPLE key
(no "#" in the key)?
  ├── YES → Case B: DO NOT MODERNIZE. Report the file(s) and move on.
  │         (regardless of whether source uses sap.ui.controller() or Controller.extend())
  └── NO  → Is there a registerControllerExtensions call in JS?
              ├── YES → Case A: modernize to ControllerExtension.extend() + manifest with #stableId key
              └── NO  → This skill does not apply

使用するタイミング

ケース B(最も一般的) — manifest にシンプルなキーの controllerName エントリーがすでにあり、コントローラーファイルが sap.ui.controller() または Controller.extend() を使用している場合に該当します。

// manifest.json — controllerName registration already present (simple key, no "#")
"sap.ui.controllerExtensions": {
    "sap.suite.ui.generic.template.ListReport.view.ListReport": {
        "controllerName": "my.app.ext.controller.ListReportExt"
    }
}

ソースコントローラーは、次のいずれかの形式です。

// Variant 1: sap.ui.controller() — NO sap.ui.define wrapper
sap.ui.controller("my.app.ext.controller.ListReportExt", {
    onInit: function() { ... },
    onCustomAction: function() { ... }
});
// Variant 2: Controller.extend() — inside sap.ui.define
sap.ui.define(["sap/ui/core/mvc/Controller", ...], function(Controller, ...) {
    return Controller.extend("my.app.ext.controller.ListReportExt", { ... });
});

ケース B を引き起こす Linter の出力:

MyExtension.controller.js:1:1 error Use of deprecated function 'sap.ui.controller'  no-deprecated-api
MyExtension.controller.js:3:5 error Use of deprecated function 'Controller' (from 'sap/ui/core/mvc/Controller')  no-deprecated-api

ケース B での対応:これらのファイルは変更しないでください。 ユーザーに報告し、残りのモダナイゼーションを続けます。

ケース A(まれで、古いアプリのみ) — 次のような Linter の出力がある場合に適用します。

Component.js:25:5 error Use of deprecated function 'registerControllerExtensions'  no-deprecated-api

または、アプリに次のようなパターンがある場合:

// In Component.js or elsewhere — NO manifest entry exists yet
this.getExtensionComponent().registerControllerExtensions("sap.suite.ui.generic.template.ListReport.view.ListReport", {
    onInit: function() { ... },
    onAction: function() { ... }
});

修正方法

対応は、manifest で拡張がどのように登録されているかによって異なります。作業を進める前に、該当するケースを判定してください。

ケースの判定
Manifest の登録方法ソースコード(いずれか)対応
manifest の controllerName(シンプルなキー。#stableId なし)sap.ui.controller()、Controller.extend()、またはプレーンオブジェクトケース B:報告のみ — ファイルは変更しないでください
JS の registerControllerExtensions(manifest エントリーなし)sap.ui.controller() またはインラインオブジェクトケース A:ControllerExtension.extend() + override + #stableId キーで manifest エントリーを追加

重要な違いは、manifest の登録形式です。

  • ケース B(最も一般的):controllerName が、manifest のシンプルなキー(例:"sap.suite.ui.generic.template.ListReport.view.ListReport": { "controllerName": "..." })の下にすでにあります。これらのファイルをモダナイズしないでください。 報告して、次に進みます。
  • ケース A:manifest に controllerName がまだありません — 拡張は registerControllerExtensions を使ってプログラムで登録されています。モダナイゼーションでは、#stableId 形式のキーを使った manifest エントリーを追加し、ControllerExtension.extend() を使用します。

検出コマンド:

# Check manifest registration format
grep -B1 -A2 "controllerName" webapp/manifest.json

# Case B indicator: controllerName in manifest with simple key (no # in key)
# If the key does NOT contain "#", it's the merging mechanism → Case B → REPORT ONLY

# Case A indicator: registerControllerExtensions in JS
grep -rl "registerControllerExtensions" webapp/ --include="*.js"

ケース B:報告してスキップする(最も一般的)

マニフェストの controllerName で登録された拡張機能(#stableId を含まない単純なキー)の場合、コントローラーファイルは変更しないでください。慎重な手動でのモダナイズが必要なため、開発者に委ねます。

手順:

  1. manifest.json の controllerName エントリー(キーに # を含まないもの)から参照されているコントローラーファイルをすべて特定します
  2. 次の形式でユーザーに報告します:
⚠️ Fiori Elements controller extensions (Case B) — skipped, requires manual modernization:
  - webapp/ext/controller/ListReportExt.controller.js
  - webapp/ext/controller/ObjectPageExt.controller.js
These files use deprecated APIs (sap.ui.controller / Controller.extend) but are Fiori Elements
controller extensions registered via controllerName in the manifest. They require careful manual
modernization to a plain-object return pattern. The linter errors for these files will persist until
they are modernized manually.
  1. このスキルでは、次のことを行わないでください:
    • コントローラー拡張パターンを変更するためにコントローラーの .js ファイルを変更する(プレーンオブジェクトへの変換は行わない)
    • マニフェストの登録を変更する
    • リンターの無効化コメントを追加する
    • プレーンオブジェクトへのモダナイズパターンを試みる
  2. 他のスキルでは、無関係な修正のためにこれらのファイルを引き続き処理できます(例:メソッド本体内の sap.ui.getCore() 呼び出しを置き換える fix-js-globals、名前空間パターンに対応する fix-linter-blind-spots)。変更しないのは、sap.ui.controller() / Controller.extend() の構造自体のみです。
  3. 残りのモダナイズ(他のスキル、その他のリンター指摘事項)を続行します

ケース A:ステップ 1 — 登録を JS から manifest.json に移動する

ケース A(JS 内で registerControllerExtensions を使用する拡張機能)のみ対象です。

コントローラー拡張の登録を JavaScript コードから sap.ui.controllerExtensions 配下のマニフェストに移動します。

キーの形式:<FLOORPLAN_CONTROLLER>#<STABLE_ID_OF_VIEW>

// Before - no controller extensions in manifest

// After - manifest.json
{
  "sap.ui5": {
    "extends": {
      "extensions": {
        "sap.ui.controllerExtensions": {
          "sap.suite.ui.generic.template.ListReport.view.ListReport#myApp::sap.suite.ui.generic.template.ListReport.view.ListReport::EntitySet": {
            "controllerName": "my.app.ext.ListReportExtension"
          },
          "sap.suite.ui.generic.template.ObjectPage.view.Details#myApp::sap.suite.ui.generic.template.ObjectPage.view.Details::EntitySet": {
            "controllerName": "my.app.ext.ObjectPageExtension"
          }
        }
      }
    }
  }
}

正しいキーの見つけ方:

  1. キーの形式は <FloorplanController>#<StableViewId> です
  2. フロアプランコントローラー名はビュー名と一致します(例:sap.suite.ui.generic.template.ListReport.view.ListReport)
  3. 安定したビュー ID は通常、<AppId>::<FloorplanViewName>::<EntitySet> です
  4. アプリの既存のマニフェストで sap.ui.generic.app / pages の設定を確認し、エンティティセットとページ ID を見つけます
ケース A:ステップ 2 — コントローラーファイルを再構成する

⚠️ このステップはケース A(sap.ui.controller() ファクトリーを使用する拡張機能)のみに適用されます。 拡張機能がすでに Controller.extend() とマニフェストの controllerName 登録を使用している場合は、代わりに以下のケース Bを使用してください。

コントローラーファイルを sap.ui.controller スタイルから ControllerExtension クラスに変換します。

変更前:

sap.ui.controller("my.app.ext.ListReportExtension", {
    onInit: function() {
        // lifecycle code
    },
    onBeforeRebindTable: function(oEvent) {
        // framework override
    },
    onCustomAction: function(oEvent) {
        // custom method
    }
});

変更後:

sap.ui.define([
    "sap/ui/core/mvc/ControllerExtension"
], function(ControllerExtension) {
    "use strict";

    return ControllerExtension.extend("my.app.ext.ListReportExtension", {
        // Lifecycle and framework overrides go inside "override"
        override: {
            onInit: function() {
                // extensionAPI is injected by the framework into the extension instance
                this._extensionAPI = this.extensionAPI;
            },
            onBeforeRebindTable: function(oEvent) {
                // framework override
            }
        },

        // Custom methods go OUTSIDE "override"
        onCustomAction: function(oEvent) {
            // custom method
        }
    });
});

再構成の主なルール:

  • sap.ui.controller の代わりに sap/ui/core/mvc/ControllerExtension を拡張します
  • ライフサイクルメソッド(onInit、onExit、onBeforeRendering、onAfterRendering)→ override 内に配置します
  • フレームワークのコールバックメソッド(onBeforeRebindTable、onBeforeRebindChart、onListNavigationExtension、adaptNavigationParameterExtension など)→ override 内に配置します
  • カスタムアクションメソッド(カスタムボタン/アクションのハンドラー)→ override の外に配置します
  • this.extensionAPI を使って extensionAPI にアクセスします(フレームワークが拡張インスタンスに注入します)

ケース A:ステップ 3 — ハンドラー参照を更新する

マニフェストと XML アノテーションで、すべてのイベントハンドラー参照を旧形式から新しい修飾形式に更新します。

変更前:

// In manifest extensions or annotations
"Actions": {
    "MyAction": {
        "id": "MyAction",
        "text": "Do Something",
        "press": "onCustomAction"
    }
}

変更後:

"Actions": {
    "MyAction": {
        "id": "MyAction",
        "text": "Do Something",
        "press": ".extension.my.app.ext.ListReportExtension.onCustomAction"
    }
}

参照形式:.extension.<full.controller.name>.<methodName>

重要:ControllerExtension.extend("my.app.ext.ListReportExtension", ...) およびマニフェストの controllerName で宣言されているドット区切りのモジュール名を使用してください。sap.ui.define のインポートで使用されるスラッシュ区切りのパス(例:"my/app/ext/ListReportExtension")は使用しないでください。

これは次のものに適用されます:

  • マニフェストのアクション定義にある press ハンドラー
  • カスタム列/セクションのハンドラー参照
  • 以前はメソッド名のみを使用していたすべてのイベントハンドラー参照

実装手順

ケース B の実装(報告のみ)
  1. マニフェストの controllerName(単純なキーで、# を含まないもの)を通じて登録された拡張コントローラーを特定します
  2. それらをユーザーに報告します。各ファイルパスを列挙し、手動でのモダナイズが必要であることを明記します
  3. これらのファイル、マニフェストエントリー、ハンドラー参照は変更しないでください
  4. 他のモダナイズ作業を続行します
ケース A の実装(registerControllerExtensions → ControllerExtension)
  1. コードベース内のすべての registerControllerExtensions 呼び出しを特定します(通常は Component.js またはヘルパーファイル内にあります)
  2. 登録ごとに: a. フロアプランのコントローラー名とビューの安定 ID を特定します b. 拡張コントローラーの名前/パスを特定します c. manifest.json の sap.ui5/extends/extensions/sap.ui.controllerExtensions にエントリーを追加します
  3. 拡張コントローラーファイルごとに: a. sap.ui.controller(...) を sap.ui.define([ControllerExtension], ...) に置き換えます b. ライフサイクル/フレームワークメソッドを override セクションに移動します c. カスタムメソッドは override の外に置きます d. extensionAPI のアクセスパターンを更新します
  4. すべてのハンドラー参照について: a. manifest.json で、シンプルなメソッド名を使ったアクションの press ハンドラーを検索します b. XML アノテーションファイルでハンドラー参照を検索します c. .extension.<module>.<method> 形式に更新します
  5. Component.js から registerControllerExtensions 呼び出しを削除します
  6. 確認として、リンターを再実行します

完全なモダナイゼーションの例

変更前 — Component.js:

sap.ui.define([
    "sap/suite/ui/generic/template/lib/AppComponent"
], function(AppComponent) {
    "use strict";

    return AppComponent.extend("my.app.Component", {
        metadata: {
            manifest: "json"
        },
        init: function() {
            AppComponent.prototype.init.apply(this, arguments);
            this.getExtensionComponent().registerControllerExtensions(
                "sap.suite.ui.generic.template.ListReport.view.ListReport", {
                    onInit: function() {
                        // lifecycle code
                    },
                    onCustomAction: function(oEvent) {
                        // custom handler
                    }
                }
            );
        }
    });
});

変更後 — Component.js:

sap.ui.define([
    "sap/suite/ui/generic/template/lib/AppComponent"
], function(AppComponent) {
    "use strict";

    return AppComponent.extend("my.app.Component", {
        metadata: {
            manifest: "json"
        }
        // registerControllerExtensions call removed — now in manifest.json
    });
});

変更後 — manifest.json(追加内容):

{
  "sap.ui5": {
    "extends": {
      "extensions": {
        "sap.ui.controllerExtensions": {
          "sap.suite.ui.generic.template.ListReport.view.ListReport#my.app::sap.suite.ui.generic.template.ListReport.view.ListReport::Products": {
            "controllerName": "my.app.ext.ListReportExtension"
          }
        }
      }
    }
  }
}

変更後 — ext/ListReportExtension.controller.js:

sap.ui.define([
    "sap/ui/core/mvc/ControllerExtension"
], function(ControllerExtension) {
    "use strict";

    return ControllerExtension.extend("my.app.ext.ListReportExtension", {
        override: {
            onInit: function() {
                this._extensionAPI = this.extensionAPI;
            }
        },

        onCustomAction: function(oEvent) {
            // Custom action handler — accessible as
            // ".extension.my.app.ext.ListReportExtension.onCustomAction"
        }
    });
});

注意事項

  • このモダナイゼーションは、Fiori Elements V2 テンプレートアプリ(sap.suite.ui.generic.template を使用)にのみ適用されます
  • Fiori Elements V4(sap.fe.templates を使用)では異なる拡張メカニズムが使われます。このスキルは適用されません
  • 実際には ケース B のほうがはるかに一般的です。manifest の controllerName で登録された拡張が該当します。これらは自動でモダナイズされません。エージェントは該当箇所を報告して処理を続行します
  • ケース A は、registerControllerExtensions からモダナイズされていない古いアプリでのみ発生します
  • 安定ビュー ID の形式はフロアプランと設定によって異なります。実行中のアプリの DOM またはフロアプランのドキュメントを確認してください
  • アプリがハンドラー参照に templateSpecific.json を使用している場合は、それらも更新する必要があります(ケース A のみ)
  • ケース A のモダナイゼーション後、拡張コントローラーファイルはモジュール名に一致するパスに配置してください

関連スキル

  • fix-js-globals:カスタムアプリの通常の sap.ui.controller() 定義(ケース 9)、または拡張コントローラーでもグローバルアクセスパターン(sap.ui.getCore()、jQuery.sap.*)を使用している場合に使います。コントローラー拡張パターン自体とは無関係な問題(メソッド本体内のグローバル API 使用の置き換えなど)については、ケース B のコントローラーファイルにもほかのスキルを適用できます
  • fix-manifest-json:コントローラー拡張の manifest 変更は追加的なものです。fix-manifest-json によるバージョン/ルーティングの更新と競合しません
  • fix-linter-blind-spots:ケース B のコントローラーファイル内の名前空間パターンや、その他の検出対象外の問題も引き続き修正できます

コメント