fix-js-globals について
fix-js-globals は、UI5 linter が検出して自動修正できない JavaScript のグローバル参照を直すコーディングスキルです。パターン別のモジュールインポートやレガシーコード移行のルールを示し、関連するグローバル参照を一度に処理します。
主な機能
- 01UI5 のグローバル参照エラー修正
- 02非推奨 Core API の置き換え
- 03jQuery.sap ユーティリティの移行
- 04レガシーモジュールとコントローラーの変換
- 05未使用のグローバル代入の削除
活用シーン
- 01UI5 linter の no-globals エラー対応
- 02レガシーコントローラーの sap.ui.define 化
- 03コントローラー内のコントロール ID 検索の置き換え
- 04jQuery.sap API の使用箇所の確認
MCP 対応
追加の Core メソッド置き換えには、UI5 MCP Server の get_api_reference ツールを使うよう案内しています。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ui5/ plugins-coding-agents --skill fix-js-globalsインストール後、Claude Code で/fix-js-globalsを入力して読み込まれたか確認します。
同梱ファイルreferences/core-api-replacements.mdreferences/example-fix-session.md
詳細
JavaScript のグローバルアクセス(no-globals)を修正する
UI5 リンターが検出するものの、自動修正できない JavaScript ファイルの no-globals エラーを修正します。置き換え候補とドキュメントへのリンクを確認するには、npx @ui5/linter --details を実行してください。
重要なルール — 修正を適用する前にお読みください
-
jQuery/$ グローバル — jQuery API 呼び出しを維持する:jQuery/$ グローバルを修正するときは、
sap/ui/thirdparty/jqueryの依存関係を追加して、$をjQueryに置き換えるだけにしてください。標準の jQuery API 呼び出し(jQuery.each、jQuery.extend、jQuery.proxy、jQuery.isEmptyObjectなど)を、ネイティブ JavaScript の同等機能に置き換えてはいけません。これらは標準の jQuery メソッドであり、非推奨の SAP API ではありません。 -
ケース 9/10 — 1 回の作業ですべてのグローバルを修正する:ファイルを
jQuery.sap.declare/requireからsap.ui.defineに変換する場合(ケース 9 または 10)、ファイル本体の他のグローバルアクセスパターンも、同じ作業ですべて修正する必要があります。2 回目の作業はありません。下記の「該当するすべてのケースを 1 回の作業で適用する」セクションをお読みください。 -
未使用コード — インポートせずに削除する:グローバルへの代入で格納した値が、ファイル内の他の場所で一度も読み取られない場合(例:
this.BarColor = sap.ui.core.BarColorの後にthis.BarColorが一度も登場しない場合)は、文全体を削除してください。インポートを追加してはいけません。 -
コントローラーの byId で中間形式を使わない:コントローラー内の
sap.ui.getCore().byId("prefix--id")またはjQuery("#prefix--id").control(0)は、直接this.byId("id")に置き換えてください。Element.getElementById("prefix--id")の形で残してはいけません。置き換えた後、未使用のElementまたはjQueryのインポートを削除してください。 -
deepExtend ではなく merge を使う:
jQuery.sap.extend(true, ...)は、sap/base/util/mergeのmerge()に置き換えてください。sap/base/util/deepExtendというモジュールは存在しません。
ケース別の修正方法
1. グローバル名前空間への代入
問題:コードがグローバルな sap オブジェクトにカスタム名前空間を作成しています。
// Before
sap.ui.demo = sap.ui.demo || {};
sap.ui.demo.myApp = { formatter: function() { ... } };
// After — convert to AMD module
sap.ui.define("sap/ui/demo/myApp", [], function() {
"use strict";
return { formatter: function() { ... } };
});
1b. sap.ui.define 内でのグローバル名前空間への代入
問題:ファイルはすでに sap.ui.define を使用していますが、グローバル名前空間に代入し、グローバル参照を返しています(jQuery.sap.declare の削除後に残ったコード)。
リンターでは報告されません — 手動で検索してください:grep -rl "your\.project\.namespace\." webapp/ --include="*.js"
// Before
sap.ui.define([], function() {
"use strict";
com.example.app.utils.MyScripts = { runTests: function() { ... } };
return com.example.app.utils.MyScripts;
});
// After — local variable replaces global assignment
sap.ui.define([], function() {
"use strict";
var MyScripts = { runTests: function() { ... } };
return MyScripts;
});
重要なルール:名前空間の末尾から短い名前を取り出します。var ShortName を使ってください(スコープ設定が不可欠です)。ファイル内の完全な名前空間への参照をすべて置き換えてください。
1c. sap.ui.define 内のグローバル名前空間への読み取り専用参照
問題:モジュールを依存関係としてインポートせず、変数への代入によってグローバル名前空間からファイルを読み込んでいます。
リンターでは報告されません — 検索:grep -rn "var .* = your\.project\.namespace\." webapp/ --include="*.js"
// Before
sap.ui.define(["sap/ui/core/mvc/Controller"], function(Controller) {
var Helper = com.example.app.utils.Helper;
// ...
});
// After — add as dependency, remove local var assignment
sap.ui.define([
"com/example/app/utils/Helper",
"sap/ui/core/mvc/Controller"
], function(Helper, Controller) {
// Helper is now available via dependency parameter
});
主なルール:
- ドット記法をスラッシュ記法に変換します:
com.example.app.utils.Helper→"com/example/app/utils/Helper" - 依存関係配列の先頭に追加し(「注意事項」を参照)、対応するパラメーターを追加します
var X = global.namespace.X;の行を削除します- グローバル参照が複数ある場合は、すべてまとめて依存関係として追加します
- パラメーター名がモジュールの短い名前と一致していることを確認します
- アトミック性:グローバル参照からローカル参照への置き換えには、必ず
sap.ui.defineの依存関係を組み合わせる必要があります。ここで発生した循環参照は、後でfix-cyclic-depsによって解決します - 修正後の検証:追加した変数名をすべて Grep で検索し、パラメーター、var/let/const、または
sap.ui.require呼び出しとして解決されることを確認します
置き換える前に、対象モジュールの return 文を確認してください:
モジュールの return | 依存関係パラメーターの使い方 |
|---|---|
| クラスを返す | new MyClass() または MyClass.staticMethod() |
| ラッパーを返す | MyModule.getInstance() |
| インスタンスを返す | myInstance.method() を直接呼び出します |
| 何も返さない(副作用のみ) | まず対象モジュールを修正し、return を追加します |
副作用のみのモジュール:対象モジュールの末尾が return を含まず }); で終わっている場合は、そのモジュールを開き、グローバル名前空間への代入をローカル変数に置き換えて、return varName; を追加します。その後、使用側のモジュールで通常どおりインポートします。
2. sap.ui.getCore() の呼び出し
問題:sap.ui.getCore() は非推奨です。そのメソッドは専用モジュールに移動しています。
// Before — standalone init script (NOT a controller)
sap.ui.getCore().attachInit(function() { ... });
// After
sap.ui.define(["sap/ui/core/Core"], function(Core) {
Core.ready().then(function() { ... });
});
注意:Core.ready() は起動フェーズの初期化スクリプト用です。コントローラー内では、UI5 はすでに初期化されているため、使用しないでください。
主な置き換え(表の全項目は references/core-api-replacements.md を参照):
| 非推奨 | モジュール | 呼び出し |
|---|---|---|
sap.ui.getCore().attachInit(fn) | sap/ui/core/Core | Core.ready().then(fn) |
sap.ui.getCore().byId(id) | sap/ui/core/Element | Element.getElementById(id) — コントローラーでは this.byId() を優先します |
sap.ui.getCore().getEventBus() | sap/ui/core/EventBus | EventBus.getInstance() |
sap.ui.getCore().getLibraryResourceBundle(lib) | sap/ui/core/Lib | Lib.getResourceBundleFor(lib) |
Core メソッドの置き換えについて詳しくは、UI5 MCP Server の get_api_reference ツールを使用してください。
3. sap.ui.core.Core への直接アクセス
依存関係配列に sap/ui/core/Core を追加し、グローバルアクセスを削除します:
// Before: var Core = sap.ui.core.Core;
// After: add "sap/ui/core/Core" to deps, use Core parameter directly
4. jQuery/$ のグローバルアクセス
重要:修正方法はインポートの追加であり、jQuery API 呼び出しの置き換えではありません。jQuery.sap.*(.sap. を含む)は非推奨のため、置き換える必要があります(ケース 4b)。jQuery.*(.sap. を含まない)または jQuery(...) は標準の jQuery であり、そのまま使用します。
// Before
jQuery("#el").addClass("x");
$(".container").css("display", "block");
jQuery.each(items, function(i, item) { ... });
// After — add dependency, rename $ to jQuery, keep all API calls unchanged
sap.ui.define([..., "sap/ui/thirdparty/jquery"], function(..., jQuery) {
jQuery("#el").addClass("x");
jQuery(".container").css("display", "block");
jQuery.each(items, function(i, item) { ... });
});
次の標準 jQuery メソッドは絶対に置き換えないでください — UI5 では非推奨ではありません:jQuery.each、jQuery.extend、jQuery.proxy、jQuery.isEmptyObject、jQuery.isArray、jQuery.inArray、jQuery.grep、jQuery.map、jQuery.type、jQuery.trim。
4a. UI5 コントロールの jQuery DOM 検索 → this.byId()
問題:コントローラー内の UI5 コントロールを取得するために、jQuery("#prefix--id").control(0) または Element.closestTo() を使用しています。
検出パターン — すべて this.byId("<local-id>") に置き換えます:
jQuery("#<anything>--<id>").control(0)Element.closestTo(jQuery("#<anything>--<id>")[0])- ID にビューのプレフィックスが含まれる
sap.ui.getCore().byId("<full-id>") - ID に
--が含まれるElement.getElementById("<full-id>")
ローカル ID は、最後の -- より後の部分です。置き換え後、未使用の jQuery/Element インポートを削除します。
4b. jQuery.sap.* ユーティリティへのアクセス
問題:jQuery.sap.* の呼び出しは、専用の置き換えモジュールが用意されている非推奨の UI5 ユーティリティです。
// Before
jQuery.sap.log.info("msg");
var sId = jQuery.sap.uid();
// After
sap.ui.define([..., "sap/base/Log", "sap/base/util/uid"], function(..., Log, uid) {
Log.info("msg");
var sId = uid();
});
置き換え候補を確認するには、npx @ui5/linter --details を実行してください。全表は references/core-api-replacements.md にあります。
jQuery.sap.extend の判断基準:
- ディープコピー(最初の引数が
true)→sap/base/util/merge→merge({}, obj1, obj2) - フラットなオブジェクト(プロパティが単一階層)→
Object.assign({}, obj1, obj2)(インポート不要) jQuery.extend(...)に変換するのは絶対に避けてください(不要な依存関係が発生します)sap/base/util/deepExtendは絶対に使用しないでください(存在しません)
5. 条件付き/存在確認のグローバルアクセス
問題:コードでグローバルが存在するか確認しています:if (sap.ui.fl && sap.ui.fl.Utils) { ... }
修正:常に利用可能なモジュールは、sap.ui.define の依存関係に追加します。本当にオプションのモジュールには、同期 sap.ui.require を使用します。
var FlUtils = sap.ui.require("sap/ui/fl/Utils");
if (FlUtils) { FlUtils.getComponentClassName(this); }
遅延読み込みには、非同期の sap.ui.require(["module/path"], function(Mod) { ... }) を使用します。
6. カスタム名前空間の定義
ケース 1 と同じパターンですが、SAP 以外の名前空間(window.mycompany.myapp = {...})が対象です。sap.ui.define モジュールに変換し、オブジェクトを返します。利用側では依存関係としてインポートします。
7. インポートなしのバインディング型文字列
// Before — global reference as string
value: { path: "/amount", type: "sap.ui.model.type.Float" }
// After — import type module, use class reference
value: { path: "/amount", type: new FloatType() } // FloatType from "sap/ui/model/type/Float"
8. delete 式
delete sap.ui.core.someTempProperty は通常、コードの問題を示しています。完全に削除するか、ローカルオブジェクトを使用してください。
9. sap.ui.controller() — グローバルファクトリーによるコントローラー定義
対象範囲:プレーンなコントローラー定義。Fiori Elements V2 拡張機能は対象外です(そちらには fix-fiori-elements-extensions を使用してください)。
検出方法:grep -rn 'sap\.ui\.controller(' webapp/ --include="*.js"
- 2 つの引数
sap.ui.controller("name", {...})= 定義 → ここで修正 - 1 つの引数
sap.ui.controller("name")= インスタンスの検索 →MODERNIZATION-ISSUES.mdに記載
パターン A:既存の sap.ui.define 内
// Before
sap.ui.define(["sap/m/MessageBox"], function(MessageBox) {
return sap.ui.controller("my.app.controller.Main", { ... });
});
// After — add Controller dep, replace factory with extend
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageBox"
], function(Controller, MessageBox) {
return Controller.extend("my.app.controller.Main", { ... });
});
パターン B:sap.ui.define がない場合(レガシーモジュールシステム)
// Before
jQuery.sap.declare("my.app.controller.Detail");
jQuery.sap.require("sap.ui.core.mvc.Controller");
sap.ui.controller("my.app.controller.Detail", { onInit: function() { ... } });
// After
sap.ui.define(["sap/ui/core/mvc/Controller"], function(Controller) {
"use strict";
return Controller.extend("my.app.controller.Detail", { onInit: function() { ... } });
});
手順:jQuery.sap.declare/require を削除します。sap.ui.define で囲みます。ドット表記の依存関係をスラッシュ表記に変換します。sap.ui.controller を Controller.extend に置き換えます。return を追加します。"use strict" を追加します。ファイル本体にあるインラインの修正をすべて適用してください(「Apply ALL」セクションを参照)。
エッジケース
returnがない場合:Controller.extend()が返すのはクラスのみです。必ずその前にreturnを追加してください- 定義より前にモジュールレベルの変数がある場合:そのまま残し、extend 呼び出しを
returnで囲みます - コントローラー名はファイルパスと一致させる必要があります:不一致でも既存の名前を維持してください(意図的な場合があります)
- 定義とインスタンスの検索が混在するファイルの場合:定義を修正し、インスタンスの検索は
MODERNIZATION-ISSUES.mdに記載します
10. jQuery.sap.declare/require — レガシーモジュール定義
ケース 9 のパターン B と同じ構造変換ですが、コントローラー以外のモジュールが対象です。
// Before
jQuery.sap.declare("my.app.util.Formatter");
jQuery.sap.require("sap.ui.core.format.DateFormat");
my.app.util.Formatter = { formatDate: function(oDate) { ... } };
// After
sap.ui.define(["sap/ui/core/format/DateFormat"], function(DateFormat) {
"use strict";
return { formatDate: function(oDate) { ... } };
});
主なルール:jQuery.sap.declare を削除します。jQuery.sap.require を依存関係に変換します。グローバルへの代入を削除し、オブジェクトを return します。すでに sap.ui.define がある場合は、残りの require を既存の依存関係配列に統合します。動的/条件付きの require には、sap.ui.require(["..."], callback) を使用します。declare が複数ある場合やエクスポートが不明確な場合は、手動レビューの対象としてフラグを付けます。インラインの修正をすべて適用してください(「Apply ALL」セクションを参照)。
11. ランタイムのグローバルをモジュールとしてインポート
問題:sap.ushell.Container のようなランタイムモジュールに、グローバル名前空間のチェーンを介してアクセスしています。
// Before
if (sap.ushell && sap.ushell.Container) {
sap.ushell.Container.getService("CrossApplicationNavigation");
}
// After — add as dependency
sap.ui.define(["sap/ushell/Container", ...], function(Container, ...) {
if (Container && Container.getService) {
Container.getService("CrossApplicationNavigation");
}
});
テスト側:インポートしたモジュールを sinon で直接スタブします。グローバル名前空間のチェーン(window.sap.ushell = {...})は設定しないでください。sinon と QUnit は Test Starter のグローバルなので、インポートは不要です。
sap.ui.define(["sap/ushell/Container"], function(Container) {
var oSandbox = sinon.createSandbox();
QUnit.test("...", function(assert) {
oSandbox.stub(Container, "setDirtyFlag");
// ...
});
});
12. jQuery.sap.sjax のモダナイズ後における同期 XHR のガード
jQuery.sap.sjax をネイティブの XMLHttpRequest にモダナイズした後は、必ずステータスを確認してから xhr.responseText を参照してください。
var xhr = new XMLHttpRequest();
xhr.open("GET", sUrl, false);
xhr.send();
if (xhr.readyState === 4 && xhr.status === 200) {
var oData = JSON.parse(xhr.responseText);
} else {
Log.error("Failed to load: " + sUrl);
}
| コンテキスト | 失敗時のフォールバック |
|---|---|
| モックサーバーのレスポンスハンドラー | oXhr.respondJSON(200, {}, JSON.stringify({"d": {"results": []}})) |
| レスポンスの JSON.parse | {} を返す(既存の if (oResponse.data) ガードが機能します) |
| 初期化時の設定読み込み | Log.error(...) を出して早期リターンする |
重要:該当するすべてのケースを一度に適用する
ファイルがケース 9 または 10 に該当する場合は、同じ作業内ですべてのグローバルアクセスパターンを修正してください。
jQuery("#...")またはjQuery(...)の呼び出しはありますか? → ケース 4a または 4sap.ui.getCore().byId(...)の呼び出しはありますか? → ケース 2、コントローラー内の場合はケース 4ajQuery.sap.*の呼び出しはありますか? → ケース 4bsap.ui.model.*、sap.m.*、sap.ui.core.*のインラインクラス参照はありますか? → 依存関係のインポート- アプリ名前空間のグローバル参照はありますか? → 依存関係のインポート
- 置換後に未使用のインポートはありますか? → 依存関係配列とパラメーターから削除する
this.X = importedModule.Xのthis.Xがほかで参照されていませんか? → 削除する(不要なコード)
実装手順
npx @ui5/linter --detailsを実行して置換候補を取得します- エラーパターンを特定し、ケースの種類を判断します
- 適切な変換を適用します(依存関係の追加、グローバル参照の置換、不要なコードの削除)
- jQuery の DOM 検索を
this.byId()に置き換えた後、未使用のインポートを削除します - 削除したグローバル代入に依存するファイルがほかにないことを確認します
注意事項
-
依存関係の挿入位置 — 重要:新しい依存関係は、必ず配列の先頭に追加してください(対応するパラメーターも関数の先頭に追加します)。古いファイルの多くでは、末尾にある副作用のみのインポートにパラメーターがなく、依存関係とパラメーターの数が一致しません。末尾に挿入すると既存の対応関係がずれますが、先頭に挿入すれば維持できます。
// Before (3 deps, 2 params — mismatch is common in legacy code) sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/MessageToast", "some/sideEffect/Module" ], function(Controller, MessageToast) { ... }); // After — new dep added at BEGINNING sap.ui.define(["sap/ui/core/Element", "sap/ui/core/mvc/Controller", "sap/m/MessageToast", "some/sideEffect/Module" ], function(Element, Controller, MessageToast) { ... }); -
パラメーター名はモジュールのデフォルトエクスポート名と一致させます(例:
sap/base/Logの場合はLog) -
QUnitとsinonは、テストファイルで意図的にグローバルとして許可されています -
sap.ui.define、sap.ui.require、sap.ui.loader.configはグローバルとして許可されています -
オプションの依存関係には
sap.ui.require("module/path")(同期、未読み込みの場合は undefined を返す)を使用します -
遅延読み込みには
sap.ui.require(["module/path"], callback)(非同期)を使用します
修正セッションの例
複数のケースの種類を組み合わせた包括的な修正前後の例については、references/example-fix-session.md をお読みください。
関連スキル
- fix-fiori-elements-extensions:
registerControllerExtensionsまたは manifest のsap.ui.controllerExtensionsを使用する Fiori Elements V2 アプリのsap.ui.controller()向け - fix-pseudo-modules:
no-pseudo-modulesとno-implicit-globalsのエラー(列挙型のインポート、DataType のインポート、OData 式関数)向け - fix-control-renderer:レンダラー固有の問題(
no-deprecated-control-renderer-declaration、apiVersion、IconPool、rerender)向け - fix-xml-globals:XML ビュー/フラグメントの
no-globals(フォーマッター、core:requireを介したイベントハンドラー)向け - fix-linter-blind-spots:リンターが検出しない、実行時に問題を引き起こすグローバル名前空間パターン(
sap.*以外のアプリ固有の名前空間)向け。ケース 1b と 1c は、このスキルのパターン 1~4 と重複します。 - fix-cyclic-deps:ケース 1c の修正で循環依存が発生する場合は、通常の
sap.ui.define依存関係ではなく、遅延読み込みのsap.ui.requireを使用します