Renderer.js` exists at the default path (auto-discovery). If yes, import and assign it. If no, add `renderer: null` or create an inline renderer
+ - For string declaration: Convert to module import
+ - For missing apiVersion: Add `apiVersion: 2` and convert render methods
+ - For IconPool: Add the import to sap.ui.define dependencies
+ - For rerender override: Move logic to lifecycle hooks
+ - For non-static: Add `static` keyword (ES6 classes)
+
+5. **Verify the fix** by re-running the linter
+
+## Example Fix Session
+
+Given linter output:
+```
+npx @ui5/linter --details
+
+MyControl.js:5:1 error Deprecated declaration of renderer 'my.app.control.MyControlRenderer' for control 'my.app.control.MyControl' no-deprecated-control-renderer-declaration
+ Details: Defining the control renderer by its name may lead to synchronous loading of the control renderer module.
+MyControl.js:20:5 error Use of deprecated renderer detected. Define explicitly the {apiVersion: 2} parameter in the renderer object no-deprecated-api
+ Details: See: https://ui5.sap.com/#/topic/c9ab34570cc14ea5ab72a6d1a4a03e3f
+```
+
+**Before:**
+```javascript
+sap.ui.define([
+ "sap/ui/core/Control"
+], function(Control) {
+ "use strict";
+
+ return Control.extend("my.app.control.MyControl", {
+ metadata: {
+ properties: {
+ text: { type: "string", defaultValue: "" }
+ }
+ },
+
+ renderer: "my.app.control.MyControlRenderer"
+ });
+});
+
+// MyControlRenderer.js (separate file)
+sap.ui.define([], function() {
+ "use strict";
+
+ var MyControlRenderer = {};
+
+ MyControlRenderer.render = function(oRm, oControl) {
+ oRm.write("");
+ oRm.writeEscaped(oControl.getText());
+ oRm.write("
");
+ };
+
+ return MyControlRenderer;
+});
+```
+
+**After:**
+```javascript
+sap.ui.define([
+ "sap/ui/core/Control",
+ "./MyControlRenderer"
+], function(Control, MyControlRenderer) {
+ "use strict";
+
+ return Control.extend("my.app.control.MyControl", {
+ metadata: {
+ properties: {
+ text: { type: "string", defaultValue: "" }
+ }
+ },
+
+ renderer: MyControlRenderer
+ });
+});
+
+// MyControlRenderer.js (separate file) - updated
+sap.ui.define([], function() {
+ "use strict";
+
+ var MyControlRenderer = {
+ apiVersion: 2
+ };
+
+ MyControlRenderer.render = function(oRm, oControl) {
+ oRm.openStart("div", oControl);
+ oRm.class("myControl");
+ oRm.openEnd();
+ oRm.text(oControl.getText());
+ oRm.close("div");
+ };
+
+ return MyControlRenderer;
+});
+```
+
+## Notes
+
+- Controls that extend these base classes do NOT need a renderer declaration:
+ - `sap/ui/core/mvc/View`
+ - `sap/ui/core/XMLComposite`
+ - `sap/ui/core/webc/WebComponent`
+ - `sap/uxap/BlockBase`
+
+- `apiVersion: 4` is also valid and provides additional optimizations for modernized UI5 code
+
+- When converting from apiVersion 1 to 2, ensure all `write()` calls are properly converted to semantic methods
+
+- The IconPool import is needed at module load time for icon font registration, even if `IconPool` variable is not used in code
+
+## Related Skills
+
+- **fix-js-globals**: For `no-globals` errors in non-renderer JavaScript files (e.g., controllers, utilities), use fix-js-globals — it handles `sap.ui.define` dependency additions and global access replacement
+- **fix-pseudo-modules**: If renderer code also has enum or DataType pseudo module imports, use fix-pseudo-modules for those specific issues
diff --git a/plugins/ui5-modernization/skills/fix-control-renderer/references/renderer-api-mapping.md b/plugins/ui5-modernization/skills/fix-control-renderer/references/renderer-api-mapping.md
new file mode 100644
index 0000000..22badce
--- /dev/null
+++ b/plugins/ui5-modernization/skills/fix-control-renderer/references/renderer-api-mapping.md
@@ -0,0 +1,94 @@
+# Renderer API Mapping Reference
+
+This reference contains the complete mapping from apiVersion 1 to apiVersion 2 renderer methods.
+
+## apiVersion 1 to apiVersion 2 Method Conversions
+
+| Old Method (apiVersion 1) | New Method (apiVersion 2) | Notes |
+|---------------------------|---------------------------|-------|
+| `oRm.write("")` | `oRm.openEnd()` | After openStart |
+| `oRm.write("/>")` | `oRm.voidEnd()` | After voidStart |
+| `oRm.write("")` | `oRm.close("tag")` | Close non-void element |
+| `oRm.write(text)` | `oRm.text(text)` | For text content |
+| `oRm.writeEscaped(text)` | `oRm.text(text)` | Auto-escapes in v2 |
+| `oRm.writeControlData(oCtrl)` | `oRm.openStart("div", oControl)` | Pass control as 2nd arg |
+| `oRm.writeAttribute("name", val)` | `oRm.attr("name", val)` | Attribute |
+| `oRm.addClass("cls")` | `oRm.class("cls")` | CSS class |
+| `oRm.writeClasses()` | (automatic) | Called by openEnd() |
+| `oRm.addStyle("prop", val)` | `oRm.style("prop", val)` | Inline style |
+| `oRm.writeStyles()` | (automatic) | Called by openEnd() |
+| `oRm.renderControl(oChild)` | `oRm.renderControl(oChild)` | Unchanged |
+| `oRm.write(oRm.getAccessibilityState(...))` | `oRm.accessibilityState(oControl, {...})` | ARIA attributes |
+| `oRm.writeIcon(src, classes, attrs)` | `oRm.icon(src, classes, attrs)` | Icon rendering |
+
+## Complete Example: apiVersion 1 to 2 Migration
+
+### Before (apiVersion 1)
+```javascript
+renderer: function(oRm, oControl) {
+ oRm.write("");
+
+ oRm.write("");
+ oRm.writeEscaped(oControl.getText());
+ oRm.write("");
+
+ oRm.renderControl(oControl.getAggregation("content"));
+
+ oRm.write("
");
+}
+```
+
+### After (apiVersion 2)
+```javascript
+renderer: {
+ apiVersion: 2,
+ render: function(oRm, oControl) {
+ oRm.openStart("div", oControl);
+ oRm.class("myControl");
+ oRm.class(oControl.getStyleClass());
+ oRm.style("width", oControl.getWidth());
+ oRm.attr("title", oControl.getTitle());
+ oRm.openEnd();
+
+ oRm.openStart("span");
+ oRm.class("myControl-text");
+ oRm.openEnd();
+ oRm.text(oControl.getText());
+ oRm.close("span");
+
+ oRm.renderControl(oControl.getAggregation("content"));
+
+ oRm.close("div");
+ }
+}
+```
+
+## Key Differences
+
+1. **Structure**: apiVersion 2 uses object with `apiVersion` and `render` properties
+2. **Control ID**: Pass control as second argument to `openStart()` instead of `writeControlData()`
+3. **Classes/Styles**: No need to call `writeClasses()`/`writeStyles()` - automatic with `openEnd()`
+4. **Text escaping**: `text()` auto-escapes, no need for `writeEscaped()`
+5. **Void elements**: Use `voidStart()`/`voidEnd()` for self-closing elements
+
+## Valid apiVersions
+
+| Version | Description |
+|---------|-------------|
+| `apiVersion: 2` | Semantic rendering, UI5 1.67+ |
+| `apiVersion: 4` | Same as 2, with performance optimizations for modernized UI5 code |
+
+Both versions use the same method names; version 4 adds internal optimizations.
diff --git a/plugins/ui5-modernization/skills/fix-csp-compliance/SKILL.md b/plugins/ui5-modernization/skills/fix-csp-compliance/SKILL.md
new file mode 100644
index 0000000..b6e6137
--- /dev/null
+++ b/plugins/ui5-modernization/skills/fix-csp-compliance/SKILL.md
@@ -0,0 +1,433 @@
+---
+name: fix-csp-compliance
+description: |
+ Fix Content Security Policy (CSP) compliance issues that UI5 linter reports but cannot auto-fix. Use this skill when linter outputs:
+ - `csp-unsafe-inline-script` with message "Use of unsafe inline script"
+ Trigger on: inline JavaScript in HTML files, script tags without src attribute, onclick/onload handlers in HTML.
+ Also use when user mentions 'security headers', 'inline script warning', 'CSP policy', 'unsafe-inline', or 'Content-Security-Policy'.
+ Provides guidance on moving inline scripts to external files for CSP compliance.
+---
+
+# Fix CSP Compliance - Unsafe Inline Scripts
+
+This skill fixes Content Security Policy (CSP) compliance issues that the UI5 linter detects but cannot auto-fix because they require restructuring code into external files.
+
+## Linter Rule Handled
+
+| Rule ID | Message Pattern | Severity | This Skill's Action |
+|---------|-----------------|----------|---------------------|
+| `csp-unsafe-inline-script` | Use of unsafe inline script | Warning | Move to external JS file |
+
+## When to Use
+
+Apply this skill when you see linter output like:
+```
+index.html:15:5 warning Use of unsafe inline script csp-unsafe-inline-script
+test.html:20:5 warning Use of unsafe inline script csp-unsafe-inline-script
+```
+
+## Background: Why CSP Matters
+
+Content Security Policy (CSP) is a security feature that helps prevent:
+- Cross-Site Scripting (XSS) attacks
+- Data injection attacks
+- Unauthorized script execution
+
+Inline scripts are considered unsafe because an attacker who manages to inject HTML can also inject malicious JavaScript. CSP-compliant apps use `script-src 'self'` which blocks inline scripts.
+
+Documentation: [Content Security Policy](https://ui5.sap.com/#/topic/fe1a6dba940e479fb7c3bc753f92b28c)
+
+## Detection
+
+The linter flags `
+
+
+
+
+```
+
+**Not flagged:**
+```html
+
+
+
+
+
+```
+
+## Fix Strategy
+
+### 1. Basic Inline Script → External File
+
+**Problem**: Inline JavaScript in HTML.
+
+```html
+
+
+
+
+
+ My App
+
+
+
+
+
+
+
+```
+
+**Fix Strategy**: Move inline scripts to external files.
+
+```html
+
+
+
+
+
+ My App
+
+
+
+
+
+
+```
+
+```javascript
+// config.js
+window.myConfig = {
+ apiUrl: "/api/v1",
+ debug: true
+};
+```
+
+```javascript
+// my/app/init.js
+sap.ui.define([], function() {
+ "use strict";
+
+ return {
+ start: function() {
+ // Initialization code
+ }
+ };
+});
+```
+
+### 2. UI5 Bootstrap with Inline Init → data-sap-ui-on-init
+
+**Problem**: Inline script after UI5 bootstrap.
+
+```html
+
+
+
+```
+
+**Fix Strategy**: Use `data-sap-ui-on-init` attribute.
+
+```html
+
+
+```
+
+```javascript
+// webapp/init.js
+sap.ui.define([
+ "sap/m/Shell",
+ "sap/ui/core/ComponentContainer"
+], function(Shell, ComponentContainer) {
+ "use strict";
+
+ new Shell({
+ app: new ComponentContainer({
+ name: "my.app",
+ async: true
+ })
+ }).placeAt("content");
+});
+```
+
+### 3. Configuration Data → JSON or Module
+
+**Problem**: Inline configuration object.
+
+```html
+
+
+```
+
+**Fix Strategy A**: External JSON file loaded at runtime.
+
+```html
+
+
+```
+
+```javascript
+// config.js - loads JSON
+(function() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "config.json", false); // Sync for config
+ xhr.send();
+ window.APP_CONFIG = JSON.parse(xhr.responseText);
+})();
+```
+
+```json
+// config.json
+{
+ "apiEndpoint": "https://api.example.com",
+ "features": {
+ "darkMode": true,
+ "analytics": false
+ }
+}
+```
+
+**Fix Strategy B**: UI5 module with configuration.
+
+```javascript
+// my/app/config.js
+sap.ui.define([], function() {
+ "use strict";
+
+ return {
+ apiEndpoint: "https://api.example.com",
+ features: {
+ darkMode: true,
+ analytics: false
+ }
+ };
+});
+
+// Usage in other modules
+sap.ui.define(["my/app/config"], function(config) {
+ console.log(config.apiEndpoint);
+});
+```
+
+### 4. Inline Event Handlers → External Scripts
+
+**Problem**: Inline event handlers in HTML attributes.
+
+```html
+
+
+
+
+```
+
+**Fix Strategy**: Use external script with event listeners.
+
+```html
+
+
+
+
+```
+
+```javascript
+// app.js
+document.addEventListener("DOMContentLoaded", function() {
+ document.getElementById("myButton").addEventListener("click", handleClick);
+ document.getElementById("logo").addEventListener("error", function() {
+ handleError(this);
+ });
+ init();
+});
+
+function handleClick() {
+ // Click handler
+}
+
+function handleError(element) {
+ // Error handler
+}
+
+function init() {
+ // Initialization
+}
+```
+
+### 5. Test HTML Files
+
+**Problem**: QUnit test files with inline scripts.
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+```
+
+**Fix Strategy**: Use Test Starter (also fixes `prefer-test-starter`).
+
+```html
+
+
+
+
+
+
+
+
+
+
+```
+
+### 6. Dynamic Script Content
+
+**Problem**: Script content generated dynamically.
+
+```html
+
+
+```
+
+**Fix Strategy**: Use data attributes or meta tags.
+
+```html
+
+
+
+
+```
+
+```javascript
+// app.js
+var userId = document.querySelector('meta[name="user-id"]').content;
+var token = document.querySelector('meta[name="csrf-token"]').content;
+```
+
+## Implementation Steps
+
+1. **Identify all inline scripts** from linter output
+
+2. **Categorize each script**:
+ - UI5 initialization → Use `data-sap-ui-on-init`
+ - Configuration → External JSON or JS module
+ - Event handlers → External script with `addEventListener`
+ - Test boilerplate → Use Test Starter
+
+3. **Create external files** for the script content
+
+4. **Update HTML** to reference external files
+
+5. **Test the application** to ensure functionality is preserved
+
+6. **Configure CSP headers** on your server:
+ ```
+ Content-Security-Policy: script-src 'self'; object-src 'none'
+ ```
+
+## Common Patterns
+
+| Inline Pattern | CSP-Compliant Solution |
+|----------------|------------------------|
+| `` | `
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+**Fix Strategy**: Use Test Starter's `runTest.js`.
+
+```html
+
+
+
+
+
+ Unit Tests
+
+
+
+
+
+
+
+
+```
+
+### 2. Create Testsuite Configuration
+
+**Problem**: No testsuite configuration file.
+
+**Fix Strategy**: Create `testsuite.qunit.js` (or `.html` for HTML-based suite).
+
+```javascript
+// testsuite.qunit.js
+sap.ui.define(function() {
+ "use strict";
+
+ return {
+ name: "Unit Tests for my.app",
+ defaults: {
+ page: "test-resources/my/app/test/unit/{name}.qunit.html",
+ qunit: {
+ version: 2
+ },
+ sinon: {
+ version: 4
+ },
+ ui5: {
+ theme: "sap_horizon"
+ },
+ coverage: {
+ only: ["my/app"]
+ }
+ },
+ tests: {
+ "AllTests": {
+ title: "All Unit Tests",
+ module: "./AllTests.qunit"
+ },
+ "controller/Main": {
+ title: "Main Controller Tests",
+ module: "./controller/Main.qunit"
+ },
+ "model/formatter": {
+ title: "Formatter Tests",
+ module: "./model/formatter.qunit"
+ }
+ }
+ };
+});
+```
+
+### 3. Convert Test JS File
+
+**Problem**: Test file using Core.ready() or without sap.ui.define.
+
+```javascript
+// Before - formatter.qunit.js (old style)
+sap.ui.getCore().attachInit(function() {
+ "use strict";
+
+ sap.ui.require([
+ "my/app/model/formatter",
+ "sap/ui/thirdparty/sinon",
+ "sap/ui/thirdparty/sinon-qunit"
+ ], function(formatter) {
+
+ QUnit.module("formatter");
+
+ QUnit.test("formatValue", function(assert) {
+ assert.equal(formatter.formatValue(1), "One");
+ });
+ });
+});
+```
+
+**Fix Strategy**: Use sap.ui.define with QUnit module.
+
+```javascript
+// After - formatter.qunit.js (Test Starter style)
+sap.ui.define([
+ "my/app/model/formatter",
+ "sap/ui/qunit/utils/createAndAppendDiv"
+], function(formatter, createAndAppendDiv) {
+ "use strict";
+
+ // Create fixture div if needed
+ createAndAppendDiv("content");
+
+ QUnit.module("formatter");
+
+ QUnit.test("formatValue", function(assert) {
+ assert.equal(formatter.formatValue(1), "One");
+ });
+});
+```
+
+### 4. Convert Testsuite HTML to Test Starter
+
+**Problem**: Testsuite HTML with manual jsUnitTestSuite.
+
+```html
+
+
+
+
+
+ Test Suite
+
+
+
+
+
+
+
+
+```
+
+**Fix Strategy**: Use Test Starter's `createSuite.js`.
+
+```html
+
+
+
+
+
+ Test Suite
+
+
+
+
+
+
+```
+
+### 5. Convert jsUnitTestSuite JS File
+
+**Problem**: Using jsUnitTestSuite in JavaScript.
+
+```javascript
+// Before - testsuite.qunit.js (old style)
+sap.ui.define(function() {
+ "use strict";
+
+ var oSuite = new jsUnitTestSuite();
+ oSuite.addTestPage("test-resources/my/app/test/unit/AllTests.qunit.html");
+
+ return oSuite;
+});
+```
+
+**Fix Strategy**: Return configuration object instead.
+
+```javascript
+// After - testsuite.qunit.js (Test Starter style)
+sap.ui.define(function() {
+ "use strict";
+
+ return {
+ name: "Test Suite for my.app",
+ defaults: {
+ qunit: {
+ version: 2
+ },
+ sinon: {
+ version: 4
+ },
+ ui5: {
+ theme: "sap_horizon"
+ }
+ },
+ tests: {
+ "unit/AllTests": {
+ title: "All Unit Tests"
+ },
+ "integration/AllJourneys": {
+ title: "All Integration Tests"
+ }
+ }
+ };
+});
+```
+
+## Complete Migration Example
+
+### Directory Structure
+```
+webapp/
+├── test/
+│ ├── testsuite.qunit.html # Main test suite entry
+│ ├── testsuite.qunit.js # Test suite configuration
+│ ├── unit/
+│ │ ├── AllTests.qunit.js # Unit test entry
+│ │ └── model/
+│ │ └── formatter.qunit.js # Individual test file
+│ └── integration/
+│ └── AllJourneys.qunit.js # Integration test entry
+```
+
+### testsuite.qunit.html
+```html
+
+
+
+
+ Test Suite
+
+
+
+
+
+```
+
+### testsuite.qunit.js
+```javascript
+sap.ui.define(function() {
+ "use strict";
+
+ return {
+ name: "Test Suite for my.app",
+ defaults: {
+ qunit: {
+ version: 2
+ },
+ sinon: {
+ version: 4
+ },
+ ui5: {
+ theme: "sap_horizon",
+ resourceroots: {
+ "my.app": "../../"
+ }
+ },
+ coverage: {
+ only: ["my/app"],
+ never: ["my/app/test"]
+ }
+ },
+ tests: {
+ "unit/AllTests": {
+ title: "All Unit Tests"
+ },
+ "integration/AllJourneys": {
+ title: "All Integration Tests",
+ ui5: {
+ preload: "async"
+ }
+ }
+ }
+ };
+});
+```
+
+### unit/AllTests.qunit.js
+```javascript
+sap.ui.define([
+ "./model/formatter.qunit"
+], function() {
+ "use strict";
+ // This file just loads all unit tests
+});
+```
+
+### unit/model/formatter.qunit.js
+```javascript
+sap.ui.define([
+ "my/app/model/formatter"
+], function(formatter) {
+ "use strict";
+
+ QUnit.module("Formatter Functions");
+
+ QUnit.test("should format value correctly", function(assert) {
+ // Test implementation
+ assert.ok(true, "Test passed");
+ });
+});
+```
+
+## Implementation Steps
+
+1. **Create testsuite.qunit.js** with test configuration object
+
+2. **Update testsuite.qunit.html** to use `createSuite.js`
+
+3. **Update individual test HTML files** to use `runTest.js`
+
+4. **Convert test JS files**:
+ - Wrap in `sap.ui.define`
+ - Remove `Core.ready()` / `Core.attachInit()` wrappers
+ - Remove `sap.ui.require` wrappers (use sap.ui.define dependencies)
+
+5. **Remove redundant code**:
+ - Remove QUnit/Sinon script tags (handled by Test Starter)
+ - Remove coverage setup (configured in testsuite)
+ - Remove manual UI5 bootstrapping
+
+6. **Verify** by running the tests
+
+## Notes
+
+- Testsuite files (`testsuite.*.qunit.js`) are allowed to use `Core.ready()` - they're exempt from the warning
+- The Test Starter automatically handles QUnit, Sinon, and coverage setup
+- Resource roots and other UI5 config go in the testsuite configuration
+- Individual test files should only contain test code, not boilerplate
+- The `page` property in defaults determines the URL pattern for test pages
diff --git a/plugins/ui5-modernization/skills/fix-xml-globals/SKILL.md b/plugins/ui5-modernization/skills/fix-xml-globals/SKILL.md
new file mode 100644
index 0000000..8e149a2
--- /dev/null
+++ b/plugins/ui5-modernization/skills/fix-xml-globals/SKILL.md
@@ -0,0 +1,444 @@
+---
+name: fix-xml-globals
+description: |
+ Fix XML view/fragment issues that UI5 linter reports but cannot auto-fix. Use this skill when linter outputs these rules:
+ - `no-globals` - For accessing global variables in XML views (e.g., my.app.formatter.method, sap.ui.model.type.Currency, factory functions)
+ - `no-ambiguous-event-handler` - For event handlers without dot prefix or local name
+ - `no-deprecated-api` - For legacy template:require syntax (space-separated)
+ Trigger on XML view/fragment files with errors about global variables, event handlers, formatters, type references in bindings, factory functions, or template:require.
+ Automatically adds core:require declarations, fixes event handler prefixes, and handles .bind($control) for functions that use 'this'.
+ For native HTML or SVG in XML views, use the fix-xml-native-html skill instead.
+---
+
+# Fix XML Views/Fragments Global Access
+
+This skill fixes XML view and fragment issues that the UI5 linter detects but cannot auto-fix because they require understanding of module paths and handler locations.
+
+## Linter Rules Handled
+
+| Rule ID | Message Pattern | This Skill's Action |
+|---------|-----------------|---------------------|
+| `no-globals` | Access of global variable '...' (...) | Add `core:require` and use local name |
+| `no-ambiguous-event-handler` | Event handler '...' must be prefixed by a dot '.' or refer to a local name | Add `.` prefix for controller methods or add `core:require` for modules |
+| `no-deprecated-api` | Usage of space-separated list '...' in template:require | Convert to object notation |
+
+## When to Use
+
+Apply this skill when you see linter output like:
+```
+Main.view.xml:15:5 error Access of global variable 'formatter' (my.app.model.formatter) no-globals
+Main.view.xml:20:5 warning Event handler 'onPress' must be prefixed by a dot '.' or refer to a local name no-ambiguous-event-handler
+Main.view.xml:25:5 error Usage of space-separated list 'formatter helper' in template:require no-deprecated-api
+```
+
+## Fix Strategy
+
+### 1. `no-globals` - Fix Global Variable Access with core:require
+
+When a formatter, type, or utility function is accessed via global namespace (e.g., `my.app.formatter.formatDate`), add a `core:require` declaration and use the local name.
+
+**Step 1: Add xmlns:core namespace if not present**
+```xml
+
+```
+
+**Step 2: Add core:require on the nearest control that uses the module**
+
+Place `core:require` on the **control that uses the global reference**. If multiple controls use the same module, place it on their **nearest common ancestor**. See the "core:require Placement Rules" section below for details.
+
+```xml
+
+
+```
+
+```xml
+
+
+
+
+
+```
+
+**Step 3: Update references to use local name**
+```xml
+
+
+
+
+
+```
+
+**Step 4: Add `.bind($control)` if the function uses `this`**
+
+If the formatter, event handler, or factory function accesses `this` internally, add `.bind($control)` to preserve the control context. Always use `$control` (not `$controller`).
+
+```xml
+
+
+```
+
+### 2. `no-ambiguous-event-handler` - Fix Event Handlers
+
+Event handlers must either:
+- Start with a dot (`.`) to indicate controller method
+- Use a locally required module name
+
+**Controller method (add dot prefix):**
+```xml
+
+
+
+
+
+```
+
+**Module method (use core:require):**
+```xml
+
+
+
+
+
+
+
+```
+
+### 3. `no-deprecated-api` - Fix Legacy template:require Syntax
+
+Convert space-separated module list to object notation.
+
+```xml
+
+
+
+
+
+```
+
+### 4. Handle Multiple Global References
+
+When multiple globals are used, combine them in a single core:require on their nearest common ancestor.
+
+```xml
+
+```
+
+### 5. `no-globals` - Fix Type Property in Bindings
+
+When a global is used in the `type` property of a binding object, it also needs `core:require`.
+
+```xml
+
+
+
+
+
+```
+
+This applies to all UI5 types (`Currency`, `Date`, `Float`, `Integer`, `String`, etc.) and custom type classes.
+
+### 6. `no-globals` - Fix Factory Functions
+
+Factory functions (e.g., `factory` attribute on `List`) follow the same pattern.
+
+```xml
+
+
+
+
+
+
+
+
+```
+
+## core:require Placement Rules
+
+A module declared via `core:require` is only accessible to that element and its descendants — not siblings.
+
+- **Single control uses the module**: place `core:require` directly on that control
+- **Multiple controls use the same module**: place `core:require` on their **nearest common ancestor**
+- **All controls are direct children of View**: placing on the View root is acceptable
+
+Prefer granular placement over always using the root — it reduces unnecessary scope and makes the dependency clear at the point of use.
+
+For detailed examples (granular placement across nested elements, scope errors with sibling elements), see `references/placement-and-binding.md`.
+
+## When to Use `.bind($control)`
+
+When a formatter, event handler, or factory function is called via `core:require`, it does **not** automatically receive a `this` context. If the function accesses `this` internally, it will fail unless you add `.bind($control)`.
+
+**The rule:**
+- Check the function's implementation — if it uses `this`, add `.bind($control)`
+- Always use `$control` (NOT `$controller`) — this binds to the control instance
+- If the function does not use `this`, omit `.bind($control)`
+
+```xml
+
+
+
+
+
+```
+
+**Practical heuristic:** Standard UI5 types (`sap.ui.model.type.*`) never need `.bind($control)`. Most pure-value formatters don't either. Check the source for `this` usage when dealing with app-owned formatters that read control state, standalone event handlers, or factory functions. If in doubt, adding `.bind($control)` is safe — it provides context even if the function ignores it.
+
+For full before/after examples (formatter, handler, factory) and a grep command to find `this` usage, see `references/placement-and-binding.md`.
+
+## FragmentDefinition Handling
+
+Fragments use `` instead of ``. The same placement principle applies, with one key difference:
+
+**Place `core:require` on the child control, NOT on FragmentDefinition.** `FragmentDefinition` is a structural wrapper, not a real control. Place `core:require` on the actual root control inside it (e.g., `Dialog`, `VBox`) or on the nearest common ancestor of the controls using the module.
+
+```xml
+
+
+
+
+```
+
+**Exception:** When a fragment has multiple direct children that all need the same module, `FragmentDefinition` becomes the nearest common ancestor — placing `core:require` there is acceptable.
+
+For examples of multi-child fragments and scoped modules within fragments, see `references/placement-and-binding.md`.
+
+## Additional Edge Cases
+
+### Name Conflicts
+
+When multiple modules share the same class name, use descriptive aliases:
+
+```xml
+
+
+
+
+```
+
+### Formatters with Multiple Parameters
+
+When a binding expression uses a formatter with multiple parts, the `core:require` is the same — only the global namespace in the formatter reference changes.
+
+```xml
+
+
+
+
+
+```
+
+### Globals Accessed Inside Expression Bindings
+
+Expression bindings that reference globals via the `${...}` syntax also need `core:require`.
+
+```xml
+
+
+
+
+
+```
+
+## Implementation Steps
+
+1. Read the XML view/fragment file
+2. Parse to identify linter errors by rule ID:
+ - `no-globals`: Global namespace references in bindings (formatters, types, event handlers, factories)
+ - `no-ambiguous-event-handler`: Event handlers without proper prefix
+ - `no-deprecated-api`: Legacy template:require syntax
+3. For `no-globals` errors:
+ - Add `xmlns:core="sap.ui.core"` to the root element if not present
+ - Determine module path: convert dot notation to slash notation (e.g., `my.app.formatter` → `my/app/formatter`)
+ - Determine placement: find the **nearest control** using the module, or the **nearest common ancestor** if multiple controls use it
+ - For fragments: prefer placing `core:require` on the actual root control (e.g., `Dialog`), not on `FragmentDefinition`
+ - Check each referenced function's implementation — if it uses `this`, add `.bind($control)` to the reference
+ - Build `core:require` object with all needed modules (use aliases for name conflicts)
+ - Update all references to use the local names
+4. For `no-ambiguous-event-handler` errors:
+ - If the handler is a simple name (e.g., `onPress`), add dot prefix (`.onPress`)
+ - If the handler is a namespace path, add to core:require and use local name
+5. For `no-deprecated-api` (template:require):
+ - Convert space-separated list to object notation
+6. Write the updated file
+
+## Example Fix
+
+**Example 1: Basic — formatter + event handler**
+
+Given linter output:
+```
+Main.view.xml:15:5 error Access of global variable 'formatter' (my.app.model.formatter) no-globals
+Main.view.xml:20:5 warning Event handler 'onPress' must be prefixed by a dot '.' or refer to a local name no-ambiguous-event-handler
+```
+
+**Main.view.xml transformation:**
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+**Example 2: Advanced — granular placement, type binding, .bind($control)**
+
+Given linter output:
+```
+OrderDetail.view.xml:8:9 error Access of global variable 'formatter' (my.app.model.formatter) no-globals
+OrderDetail.view.xml:10:13 error Access of global variable 'Currency' (sap.ui.model.type.Currency) no-globals
+OrderDetail.view.xml:14:13 error Access of global variable 'Handler' (my.app.util.Handler) no-globals
+OrderDetail.view.xml:18:9 warning Event handler 'onBack' must be prefixed by a dot '.' no-ambiguous-event-handler
+```
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+**Placement decisions:**
+- `formatter` used in two `` controls across `content` — `core:require` on `Page` (nearest common ancestor)
+- `Currency` only used by one `` — `core:require` directly on `Input`
+- `Handler` only used by one `