From 6b7ab908b18d1e6bd590d579a1a95336e701081c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 10:58:52 +0000 Subject: [PATCH 1/3] Landing page: spend the cards on reader journeys, show the nine-line app The front page carried three cards, and two of them (GitHub, LinkedIn) duplicated the social icons that are always visible in the nav bar - while the entries a newcomer actually needs (Cookbook, Samples, Configuration, the live demo) were reachable only through menus. Now: - Hero actions: "Get Started" leads to the quickstart (the button a newcomer wants first), "Introduction" to the about page, and "Live Demo" to the browser build - try it before installing anything, which was buried in a tip on the quickstart page. - Six cards, one per journey, in the order a reader meets them: Quickstart, Cookbook, Samples, Configuration, Technical Insight, Community. "What's new?" stays reachable via the version dropdown. - Below the cards, the hello-world class: nine lines that show the promise of the tagline instead of only stating it. Centered at the site's reading width - the home layout would hand it the full grid width, where code reads as a banner. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01YM2A6eh3sX7ssGDuagbSGG --- docs/.vitepress/theme/style.css | 20 ++++++++++ docs/index.md | 69 ++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css index 70fd44fc..b74a868d 100644 --- a/docs/.vitepress/theme/style.css +++ b/docs/.vitepress/theme/style.css @@ -206,6 +206,26 @@ padding-bottom: 4px; } +/* ------------------------------------------------------ the home page --- + * + * The markdown section under the feature cards (the hello-world class). The + * home layout hands markdown content the full 1152px of the feature grid; a + * nine-line code block at that width reads as a banner, not as code. Center + * it at the width the reading column uses everywhere else, and give it air + * against the cards above so it starts a section instead of trailing one. */ +.VPHome .vp-doc > div { + max-width: 688px; + margin: 40px auto 0; +} + +/* The section heading on the home page has no sections above it to separate + * from — the top rule that earns its keep on a long cookbook page is just a + * stray line here. */ +.VPHome .vp-doc h2 { + border-top: none; + padding-top: 0; +} + /* ------------------------------------------------- the callout boxes --- * * The tip/warning/danger blocks keep the width the reading column had BEFORE diff --git a/docs/index.md b/docs/index.md index 78446c17..4794d548 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,26 +14,65 @@ hero: height: 200px actions: - theme: brand - text: Learn More - link: /get_started/about - - theme: alt - text: Quickstart + text: Get Started link: /get_started/quickstart - theme: alt - text: What's new? - link: /resources/changelog + text: Introduction + link: /get_started/about + - theme: alt + text: Live Demo + link: https://abap2ui5.github.io/web-abap2UI5-build/ +# One card per reader journey, in the order a newcomer meets them: install, +# look things up, copy from working apps, take it to production, understand +# it, join in. GitHub and LinkedIn are NOT cards — both already sit in the +# nav bar as social icons, and a card spent on a link that is always visible +# is a card not spent on a journey. features: - - title: Guide + - title: Quickstart + icon: + details: Install with abapGit and launch your first app in minutes. + link: /get_started/quickstart + - title: Cookbook icon: - details: Learn how to build, configure, and run apps with abap2UI5. - link: /get_started/about - - title: GitHub + details: Recipes for everyday tasks — views, binding, tables, events, popups, files. + link: /cookbook/overview + - title: Samples + icon: + details: Hundreds of small working apps to copy from — one per control or pattern. + link: /resources/samples + - title: Configuration + icon: + details: Setup, security, performance, launchpad — the road to production use. + link: /configuration/setup + - title: Technical Insight + icon: + details: How UI5 over the wire works, and the open-source tools it builds on. + link: /technical/concept + - title: Community icon: - details: Jump into the code — browse the repository, report issues, and contribute to the project. + details: Browse the code, report issues, contribute — the project is built in the open. link: https://github.com/abap2UI5/abap2UI5/ - - title: LinkedIn - icon: - details: Follow us on LinkedIn to stay up to date with the latest updates and changes. - link: https://www.linkedin.com/company/abap2ui5/ --- + +## A complete app in nine lines + +```abap +CLASS zcl_app_hello_world DEFINITION PUBLIC. + PUBLIC SECTION. + INTERFACES z2ui5_if_app. +ENDCLASS. + +CLASS zcl_app_hello_world IMPLEMENTATION. + METHOD z2ui5_if_app~main. + client->message_box_display( `Hello World` ). + ENDMETHOD. +ENDCLASS. +``` + +That's it — no JavaScript, no OData service, no frontend deployment. Copy the +class into your system and it runs anywhere abapGit reaches, from legacy R/3 +releases to S/4HANA Cloud and BTP. The [Hello World](/get_started/hello_world) +page explains what happens here — or try apps like it in the +[live demo](https://abap2ui5.github.io/web-abap2UI5-build/), right in your +browser, no SAP system needed. From 18075f62672c4f4982dc145902e635d589e65338 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 11:14:16 +0000 Subject: [PATCH 2/3] Move the depth pages out of Getting Started, and collect AI on one page Two pages sat in Getting Started that answer questions a newcomer does not have yet, and there they read as required steps. Both move under Advanced Topic: Use Cases (the extensibility tiers) as-is, and Your Project renamed to Working Off-Stack - what the page is actually about is developing the app repository off the SAP stack, with git, the gates and CI. The AI material lived in three places; now Building with AI is the one page. The Tooling page loses its ai-mcp section and the llms.txt tip (both restated what Building with AI already says) and points there instead; the paste-ahead prompt from the framework README - the only AI content that existed nowhere in this documentation - moves in as the zero-effort first rung of the page's ladder. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01YM2A6eh3sX7ssGDuagbSGG --- docs/.vitepress/config.mjs | 14 +++---- docs/{get_started => advanced}/use_cases.md | 0 .../working_off_stack.md} | 9 +++-- docs/get_started/ai.md | 37 ++++++++++++++++-- docs/get_started/next.md | 2 +- docs/get_started/quickstart.md | 2 +- docs/get_started/tooling.md | 39 ++++--------------- docs/technical/tools/linter.md | 2 +- 8 files changed, 55 insertions(+), 50 deletions(-) rename docs/{get_started => advanced}/use_cases.md (100%) rename docs/{get_started/project_setup.md => advanced/working_off_stack.md} (91%) diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index f60757cd..b6cda721 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -172,19 +172,11 @@ export default defineConfig({ collapsed: true, items: [ { text: "Introduction", link: "/get_started/about" }, - { text: "Use Cases", link: "/get_started/use_cases" }, { text: "Quickstart", items: [ { text: "Installation", link: "/get_started/quickstart" }, { text: "Hello World", link: "/get_started/hello_world" }, - // Between the smallest app and the complete one, because that is - // where the reader's question changes from "how do I write this" - // to "where does it live". Quickstart ends with a class typed - // into a system and nothing said the next step out of it; a - // reader who stopped once the app ran met the template, the - // linter and the agent setup on no page at all. - { text: "Your Project", link: "/get_started/project_setup" }, { text: "Full Example", link: "/get_started/full_example" }, ], }, @@ -418,9 +410,15 @@ export default defineConfig({ link: "/advanced/downporting", collapsed: true, items: [ + // Both moved out of Getting Started on purpose: the extensibility + // tiers and the git/CI project setup answer questions a newcomer + // does not have yet, and on the entry pages they read as required + // steps. Here they sit with the other depth topics. + { text: "Use Cases", link: "/advanced/use_cases" }, { text: "Add-ons", link: "/advanced/addons" }, { text: "Downporting", link: "/advanced/downporting" }, { text: "Namespaces, Renaming", link: "/advanced/renaming" }, + { text: "Working Off-Stack", link: "/advanced/working_off_stack" }, { text: "Local Setup", link: "/advanced/local" }, { text: "RFC Connector", link: "/advanced/rfc" }, { text: "HTTP Connector", link: "/advanced/http" }, diff --git a/docs/get_started/use_cases.md b/docs/advanced/use_cases.md similarity index 100% rename from docs/get_started/use_cases.md rename to docs/advanced/use_cases.md diff --git a/docs/get_started/project_setup.md b/docs/advanced/working_off_stack.md similarity index 91% rename from docs/get_started/project_setup.md rename to docs/advanced/working_off_stack.md index 95b18ac7..ca560ea2 100644 --- a/docs/get_started/project_setup.md +++ b/docs/advanced/working_off_stack.md @@ -1,11 +1,12 @@ --- outline: [2, 4] --- -# Your Project +# Working Off-Stack -The class you just built lives in your system and nowhere else. That is fine -for a first app and not fine for a second one: nothing versions it, nobody -reviews it, and the mistakes an abap2UI5 app makes — a control that does not +An abap2UI5 app usually starts as a class typed straight into a system, living +there and nowhere else. That is fine for a first app and not fine for a second +one: nothing versions it, nobody reviews it, and the mistakes an abap2UI5 app +makes — a control that does not exist on the release your users are on, a binding pointing at nothing — are invisible to the ABAP compiler and show up as a blank screen. diff --git a/docs/get_started/ai.md b/docs/get_started/ai.md index dc574cf4..05608d63 100644 --- a/docs/get_started/ai.md +++ b/docs/get_started/ai.md @@ -11,6 +11,36 @@ app, confidently, and in an API that is no longer the one to use. Everything below is a way of telling it otherwise, in rising order of effort. +## Paste the essentials + +The zero-setup version, for any assistant with web access: paste this ahead of +your task. It corrects the four things a model most reliably gets wrong about +abap2UI5: + +```text +Before writing any abap2UI5 code, read https://abap2ui5.github.io/docs/llms.txt +and follow it to the pages you need. + +Four things that override whatever you remember about abap2UI5: +1. An app is ONE ABAP class implementing z2ui5_if_app. Everything enters main( ), + which dispatches on client->check_on_navigated( ) (the display branch, true on + first start too), client->check_on_event( `X` ) and - for one-time setup only - + client->check_on_init( ). +2. Build the view with z2ui5_cl_ui5_view_builder and its verbs ele / tag / a / end / + stringify. z2ui5_cl_xml_view is the FROZEN predecessor - it is what most examples + online show, and it is not what to write. +3. Bind with client->_bind( ). It is bidirectional; only what the user edited comes back. +4. Every roundtrip is a fresh ABAP session. Nothing survives on the server except + the app class itself, which is serialized. + +Before building something from scratch, check whether it exists: the sample +catalogue lists every app with the words to search it by, at +https://github.com/abap2UI5/samples/blob/main/SAMPLES.md + +When you are done, check the result with the abap2UI5-linter +(npx abap2ui5lint) - it reads the view your ABAP builds and needs no SAP system. +``` + ## Point it at the right index Two files describe this project to a machine, and they answer different @@ -31,7 +61,7 @@ An index tells an agent what abap2UI5 is. `AGENTS.md` tells it what *your project* is — and it is read automatically, by every session, without anybody remembering to paste anything. -The [app-template](/get_started/project_setup) ships one written for +The [app-template](/advanced/working_off_stack) ships one written for app-building: the class shape, the lifecycle, the view builder, binding, events, and the gates to run before calling the work done. It also ships a `.claude/settings.json` allowlist so an agent can run `npm run check` itself @@ -93,6 +123,7 @@ every credential prompt stays an ordinary VS Code dialog the agent never sees. ## Next Steps -- [Your Project](/get_started/project_setup) — the repository all of this +- [Working Off-Stack](/advanced/working_off_stack) — the repository all of this assumes -- [Tooling](/get_started/tooling) — the same four tools, for a human +- [Tooling](/get_started/tooling) — the human side of the same loop: the + template, the linter, and the VS Code extension diff --git a/docs/get_started/next.md b/docs/get_started/next.md index 31c87117..6e1aafa6 100644 --- a/docs/get_started/next.md +++ b/docs/get_started/next.md @@ -21,7 +21,7 @@ The samples evolve all the time. Have one to share? Open a PR so others can lear ::: ## Tooling -Optional, and worth the ten minutes: a project [template](/get_started/project_setup) with the checks already wired up, a [linter](/technical/tools/linter) that finds broken views without a system, an [extension](https://github.com/abap2UI5/vscode-extension) that runs your app on `F9` next to the code, and an [MCP server](https://github.com/abap2UI5/ai-mcp) that lets an AI agent build and *look at* the app. See [Tooling](/get_started/tooling), and [Building with AI](/get_started/ai) if an assistant writes some of it. +Optional, and worth the ten minutes: a project [template](/advanced/working_off_stack) with the checks already wired up, a [linter](/technical/tools/linter) that finds broken views without a system, an [extension](https://github.com/abap2UI5/vscode-extension) that runs your app on `F9` next to the code, and an [MCP server](https://github.com/abap2UI5/ai-mcp) that lets an AI agent build and *look at* the app. See [Tooling](/get_started/tooling), and [Building with AI](/get_started/ai) if an assistant writes some of it. ## Development Build views, handle events, share data, and work with tables. The [Cookbook](/cookbook/overview) walks through the patterns you need for everyday work — start with the [Life Cycle](/cookbook/event_navigation/life_cycle) page. diff --git a/docs/get_started/quickstart.md b/docs/get_started/quickstart.md index f36e1ebc..6949f08c 100644 --- a/docs/get_started/quickstart.md +++ b/docs/get_started/quickstart.md @@ -98,5 +98,5 @@ Name your own apps in your customer namespace (`Z...`/`Y...`). The `Z2UI5_` pref ## Next Steps [Hello World](/get_started/hello_world) explains what that class actually did. -Once it is more than one class, [Your Project](/get_started/project_setup) is +Once it is more than one class, [Working Off-Stack](/advanced/working_off_stack) is where the source moves into a git repository with the checks in front of it. diff --git a/docs/get_started/tooling.md b/docs/get_started/tooling.md index 9215350b..6ad6d81d 100644 --- a/docs/get_started/tooling.md +++ b/docs/get_started/tooling.md @@ -4,19 +4,21 @@ outline: [2, 4] # Tooling Everything on this page is optional — abap2UI5 is one ABAP class and needs no -tools at all. What these four add is the loop that ABAP does not give you by +tools at all. What these three add is the loop that ABAP does not give you by itself: catching a broken view **before** it reaches a system, and seeing the app without leaving the editor. They are independent of each other. Take the first one and stop, or take all -four. +three. Building with an AI assistant? That whole side of the tooling — the +indexes, the agent conventions, the MCP server — is collected on +[Building with AI](/get_started/ai). ## Start a project from the template [**abap2UI5/app-template**](https://github.com/abap2UI5/app-template) — press *Use this template* on GitHub and you have an app repository that is already -set up: one working app class, both gates configured, a CI workflow that runs -them on every push, and a guide for AI agents. +set up: one working app class, both gates configured, and a CI workflow that +runs them on every push. The alternative is creating a class by hand, which works fine — the template only saves you from assembling the checks below yourself, and from finding out @@ -26,7 +28,7 @@ half a year later that they were never running. Use this template → clone → npm ci → npm run check ``` -[Your Project](/get_started/project_setup) walks through it — what is in the +[Working Off-Stack](/advanced/working_off_stack) walks through it — what is in the repository, the rename that makes it yours, and the way into your system. ## Check the view without a system @@ -65,33 +67,6 @@ Install it from the VS Code Marketplace or Open VSX; it needs the [ABAP remote filesystem](https://marketplace.visualstudio.com/items?itemName=murbani.vscode-abap-remote-fs) extension for the system connection. -## Let an AI agent build and see the app - -[**abap2UI5/ai-mcp**](https://github.com/abap2UI5/ai-mcp) — an MCP server that -gives a coding agent the whole loop *without* an SAP system: it validates the -view, transpiles the framework and the app to Node, boots the app in a -headless browser and hands the agent a **screenshot**. That last step is the -difference between "the code compiles" and "the app works". - -Works with any MCP client (Claude Code, Cursor, VS Code). The full loop needs -a few checkouts and a first build that takes a while — the README says which -tools need what, and validating views alone needs almost nothing. - -::: tip Building with an AI agent? -Point it at [`llms.txt`](https://abap2ui5.github.io/docs/llms.txt) — this site -generates one on every build: every chapter with a one-line summary, and -[`llms-full.txt`](https://abap2ui5.github.io/docs/llms-full.txt) for all of it -in a single fetch. There is a second one in the -[framework repository](https://github.com/abap2UI5/abap2UI5/blob/main/llms.txt); -the difference is what each maps — this site's is the map of the **prose**, the -framework's is the map of the **code**, down to the interface files an agent -should read instead of guessing at a signature. - -The [app-template](https://github.com/abap2UI5/app-template) then ships an -`AGENTS.md` that states the conventions an agent should follow in your own -project. [Building with AI](/get_started/ai) puts the whole setup in order. -::: - ## Where the samples live Three repositories, in the order they build on each other: diff --git a/docs/technical/tools/linter.md b/docs/technical/tools/linter.md index 953f5e90..e76b28ff 100644 --- a/docs/technical/tools/linter.md +++ b/docs/technical/tools/linter.md @@ -94,5 +94,5 @@ The CLI, a GitHub Action (`abap2UI5/linter@v0`), a library, inside the [VS Code extension](/get_started/tooling#run-the-app-from-the-editor) as diagnostics while you type, and as the `validate_view` tool of the [MCP server](/get_started/ai). The -[app-template](/get_started/project_setup) has the CLI and the workflow wired +[app-template](/advanced/working_off_stack) has the CLI and the workflow wired up already. From 743f3b276ba86b5af863c3e14ae6e46a6ec22a35 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 11:24:38 +0000 Subject: [PATCH 3/3] Document the three tools this project ships, and shorten their READMEs The linter, the MCP server and the VS Code extension are abap2UI5's own tools, and the only thing describing any of them was its README - written for somebody already standing in the repository. Nothing on this site said what an MCP server is, and the extension appeared as one paragraph on the Tooling page. Advanced Topic gains a Tools group with a page per repository: what the tool is, the problem it solves, how to set it up in levels, and every feature explained rather than listed. The linter page moves here from Technical Insight, where it sat among the tools this project BORROWS, and is expanded to the same depth; the entry in that list now points here. The READMEs keep what only makes sense in the repository - quick start, development, packaging, releasing - and point at the page for the rest, so there is one copy to maintain instead of two that drift. Also corrects the MCP server's repository name throughout: ai-mcp was renamed to mcp-server, and every link here still said the old one. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01YM2A6eh3sX7ssGDuagbSGG --- docs/.vitepress/config.mjs | 22 +- docs/advanced/linter.md | 403 +++++++++++++++++++++++++++++ docs/advanced/mcp_server.md | 202 +++++++++++++++ docs/advanced/vscode.md | 328 +++++++++++++++++++++++ docs/advanced/working_off_stack.md | 4 +- docs/cookbook/view/definition.md | 6 +- docs/get_started/ai.md | 18 +- docs/get_started/next.md | 2 +- docs/get_started/tooling.md | 2 +- docs/resources/samples.md | 2 +- docs/technical/tools/linter.md | 98 ------- 11 files changed, 972 insertions(+), 115 deletions(-) create mode 100644 docs/advanced/linter.md create mode 100644 docs/advanced/mcp_server.md create mode 100644 docs/advanced/vscode.md delete mode 100644 docs/technical/tools/linter.md diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index b6cda721..2984defd 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -419,6 +419,22 @@ export default defineConfig({ { text: "Downporting", link: "/advanced/downporting" }, { text: "Namespaces, Renaming", link: "/advanced/renaming" }, { text: "Working Off-Stack", link: "/advanced/working_off_stack" }, + { + // The project's own tools, each documented in full here — this is + // the documentation for those three repositories, so their READMEs + // can stay short and point at a page instead of growing a second + // copy that drifts. + text: "Tools", + link: "/advanced/linter", + collapsed: true, + items: [ + // ADVANCED copy — Technical Insight > Tool carries the same + // entry, pointing at the same page. Match on the marker. + { text: "abap2UI5 linter", link: "/advanced/linter" }, // advanced + { text: "MCP Server", link: "/advanced/mcp_server" }, + { text: "VS Code Extension", link: "/advanced/vscode" }, + ], + }, { text: "Local Setup", link: "/advanced/local" }, { text: "RFC Connector", link: "/advanced/rfc" }, { text: "HTTP Connector", link: "/advanced/http" }, @@ -467,7 +483,11 @@ export default defineConfig({ // other gate in this section is somebody else's; this one is // the only thing that can read a view that does not exist until // the app runs. - { text: "abap2UI5 linter", link: "/technical/tools/linter" }, + // + // TECHNICAL copy — the page itself lives under Advanced Topic > + // Tools with the MCP server and the extension, the project's + // other two. Match on the marker, not on the text. + { text: "abap2UI5 linter", link: "/advanced/linter" }, // technical { text: "abapGit", link: "/technical/tools/abapgit" }, { text: "ajson", link: "/technical/tools/ajson" }, { text: "S-RTTI", link: "/technical/tools/srtti" }, diff --git a/docs/advanced/linter.md b/docs/advanced/linter.md new file mode 100644 index 00000000..74a3fc4e --- /dev/null +++ b/docs/advanced/linter.md @@ -0,0 +1,403 @@ +--- +outline: [2, 4] +--- +# abap2UI5 linter + +The view an abap2UI5 app shows does not exist until the app runs. It is built +by a chain of `z2ui5_cl_ui5_view_builder` calls and handed to the browser as a +string, so the ABAP compiler never sees a control name, and no UI5 tooling ever +sees the class. Between the two sits a whole class of defect nothing catches: a +binding path the model has no field for, an event argument the control never +delivers, a control that exists in the newest UI5 and not on the release your +users are on. The app activates, the class compiles, and the screen comes up +empty. + +[`@abap2ui5/linter`](https://github.com/abap2UI5/linter) closes that gap. It +reconstructs the view from the builder chain and judges the class and the view +**together** — with no SAP system, no deployment and no UI5 project. + +## Try it in one line + +```sh +npx @abap2ui5/linter src +``` + +That is the whole of it: no install, no configuration, no system. The package +is about 240 kB and pulls in nothing else, so the line above is a fast one. + +``` +src/zcl_my_app.clas.abap + 36:14 error sap.m.Page has no aggregation contentt - typo? unknown-aggregation + 40:22 error sap.m.Button type="Emphasised" is not a valid value (allowed: ...) invalid-property-value +``` + +Files with nothing to report are not printed. + +::: tip Name the package, not the command +The executable the package installs is called `abap2ui5lint` — and so are the +config file, the baseline and the source waivers further down. But +`npx abap2ui5lint` would go looking for a *package* of that name, which is not +this one. With `npx`, write `@abap2ui5/linter`; once it is installed in a +project, `npx abap2ui5lint` is the command. +::: + +Then, when you want it to stay green: + +```sh +npm install -D @abap2ui5/linter # pin it for CI and for your machine +npx abap2ui5lint --init # write a commented abap2ui5lint.jsonc +``` + +Starting a new app repository? [Working Off-Stack](/advanced/working_off_stack) +walks through **app-template**, which ships with all of this already wired up. + +## Reading a finding + +Every finding carries four things, and all four matter: + +``` +src/zcl_my_app.clas.abap + 20:9 error a( n = `title` ) without an element to attach it to … attribute-without-element + 31:18 error text is set twice on the same control … duplicate-property + 44:22 warning sap.m.GenericTile systemInfo is @since 1.92.0 … member-too-new + 51:35 hint event NO_HANDLER is raised but never handled … event-without-handler + +4 problems (2 errors, 1 warning, 1 hint) +abap2ui5-linter: 12 file(s), 1 failing, 0 skipped (target SAPUI5 1.71, metadata from 1.151.0, failing on warning) +``` + +**Line and column** point into the ABAP source you wrote, not into the +generated view. **The message** is ready to act on. **The severity** says how +much it matters: + +| Severity | Meaning | +| --- | --- | +| `error` | the app breaks: a dump, a control that will not load, a value UI5 rejects, or a defect that silently destroys the view | +| `warning` | it works where it was written, but not necessarily on the target system (version floor, deprecation) — or the data behind it is not what the author thinks it is | +| `hint` | worth knowing, never wrong by itself | + +**The rule id** at the end of the line is the key to everything else: it is what +you name in the config to change or switch a rule off, what a source waiver +names, and the anchor of that rule's page in the full reference at +[abap2ui5.github.io/linter](https://abap2ui5.github.io/linter/) — every rule is +documented one page away, with what it means and why it exists. + +Which severities break the build is a separate decision from what is reported: +`--fail-on error|warning|hint|never` sets the exit code (default `warning`, +`--advisory` is the same as `--fail-on never`). Everything is always *reported*. + +| Exit code | | +| --- | --- | +| `0` | clean, or nothing above `--fail-on` | +| `1` | a finding at or above `--fail-on`, or a render error | +| `2` | bad usage or a broken config file | + +## What it checks + +Two gates run over every file, and they answer different questions. + +### The property gate + +Everything the view writes is resolved against a **UI5 metadata snapshot** — +988 controls with their full member lists and types, 219 enums, generated from +the OpenUI5 sources. It is instant, needs no browser, and catches the whole +family of *this name does not exist* defects: + +| | | +| --- | --- | +| a control, property, aggregation or enum value UI5 does not have | `sap.m.Shell2`, `Button typ=`, `Page contentt`, `type="Emphasised"` | +| a name UI5 does not have **yet** on your target release | a member `@since 1.92` on a 1.71 system — the permanent abap2UI5 question | +| an icon that is wrong, too new, or gone again | `sap-icon://textFormatting` matches nothing ever (it is `text-formatting`), `information` arrived in 1.80 | +| structural defects in the view | two controls in a 0..1 aggregation, a duplicate `id`, an undeclared namespace prefix, an aggregation inside an aggregation | +| layout that breaks on older releases | a `ToolbarSpacer` inside a `sap.m.Bar` deletes every sibling after it before 1.76 | + +Bindings and expressions are never value-checked — their value is a runtime +matter — custom namespaces stay out of scope, and a control whose inheritance +chain leaves the snapshot is never reported as missing a member. The gate does +not guess. + +### The render gate + +The view is then loaded with a real `XMLView.create` in headless Chromium +against a local OpenUI5 runtime, with UI5 **future mode** active. This is the +only way to catch a view that does not merely render wrongly but **fails to +load at all** — a broken expression binding, a strict property-type violation, +a control that resolves to a 404. + +It needs a real UI5 runtime, so it ships as a second package — one deliberate +install rather than a surprise attached to the first: + +```sh +npm install -D @abap2ui5/render-runtime # the UI5 runtime, once +npx playwright install chromium # and its browser +``` + +Without it the property gate still runs in full, and says so +(`--no-render` asks for that explicitly). In CI write `--render` instead: that +turns a missing runtime into an error rather than a silent fallback, so a gate +your config promised cannot quietly disappear from a green pipeline. + +### The abap2UI5 rules + +On top of the UI5 rules sit the ones that are the reason this tool exists +rather than a UI5 linter: the defects that live *between* the ABAP class and +the view it builds, and stay **silent** at runtime. A few, to give the shape of +them: + +| Rule | What it catches | +| --- | --- | +| `unknown-binding-path` | a hand-written `{/TYPO}` the derived model has no path for — the field just stays empty. Inside a bound aggregation a relative `{TYPO}` is resolved against the **row**, so a misspelled column is caught too | +| `frozen-view-builder` | the class still builds its view with `z2ui5_cl_xml_view`, the frozen predecessor. It matters more than it looks: that API is what nearly all public abap2UI5 material shows, and therefore what a language model writes when asked for an app | +| `binding-to-nonpublic` | a `PROTECTED`/`PRIVATE` attribute bound — only `PUBLIC` attributes are serialized into the model, so the first roundtrip fails with `BINDING_ERROR` | +| `binding-to-local` | a local variable bound — the instance is serialized across the roundtrip, the method stack is not, so the value is lost | +| `unconverted-abap-boolean` | an ABAP boolean written straight into the view: it arrives as `'X'`/`' '` and UI5 reads any non-empty string as true, so `visible = abap_false` makes the control **visible** | +| `missing-on-navigated-branch` | a lifecycle dispatcher with no `check_on_navigated( )` branch — the app works standalone and goes blank the first time another app hands control back | +| `view-never-displayed` | a view is built and never handed to the client: an empty page, no error | +| `source-line-too-long` | a source line over 255 characters — the class does not fail to lint, it fails to **import**, and abapGit leaves an empty class stub behind | +| `chain-indentation` | a builder call whose indentation contradicts the tree it builds. The chain is the only picture of the view's structure there is, and nothing else in the toolchain formats it | + +There are more than eighty rules in total. The full list, each with the +paragraph explaining why it exists, is the +[rule reference](https://abap2ui5.github.io/linter/) — one page, searchable, +one anchor per rule id. + +### What it cannot do, by design + +- **Event roundtrips and visual fidelity** stay with a live run — that is what + the [MCP server](/advanced/mcp_server)'s `run_app` and the + [VS Code extension](/advanced/vscode)'s `F9` are for. +- A class that builds view parts in helper methods **without the handle idiom** + cannot be statically reconstructed. The render gate is then skipped with a + notice rather than validating the wrong view; the property gate still runs on + what was reconstructed. +- A model field the class fills **in code** (a `LOOP` in `model_init`) has no + static value, so the render gate only ever sees what a literal seed sets. + +## Seeing the view without a system + +The render gate loads the view to find out whether it survives creation, then +throws it away. `--screenshot` keeps it standing and photographs it: + +```sh +abap2ui5lint zcl_my_app.clas.abap --screenshot app.png +abap2ui5lint zcl_my_app.clas.abap --screenshot app.png --screenshot-size 390x844,1280x900 +abap2ui5lint zcl_my_app.clas.abap --screenshot app.png --screenshot-theme sap_horizon_dark +``` + +Looking at an abap2UI5 view has always meant activating the class on a system +and launching the app. Here it is reconstructed from the builder calls, seeded +with a model derived from the class's own `TYPES`/`DATA`, and rendered against +the local OpenUI5 runtime in the theme and viewport you name. Several viewports +render in **one** browser session — the launch and the UI5 boot cost more than +every render together, so a phone-and-desktop matrix is barely more expensive +than one picture. + +**The empty-table problem.** The model is derived from what the class seeds +*literally*, because that is all a static reconstruction can know. A table +filled by a `SELECT` is therefore empty, and a list view — most real apps — +photographs as *No data*. So a JSON file next to the source is picked up as +preview data, by convention and without a flag: + +``` +src/zcl_travel_list.clas.abap +src/zcl_travel_list.mock.json -> { "MT_ROWS": [ { "NAME": "Berlin - Rome" } ] } +``` + +It is **merged over** the derived model rather than replacing it, so the file +only has to name the table you want filled; every other binding keeps +resolving. `--screenshot-model ` does the same without a file lying +next to the source. + +What this is not: a preview of the *app*. Nothing round-trips, no event reaches +ABAP, and the data is a mock model rather than what a system would serve. It is +the view, rendered. + +## Fixing what is mechanical + +Some rules carry an exact correction and are rewritten in place: + +```sh +npx @abap2ui5/linter src --fix # or --fix-dry-run to see it first +``` + +Among them: `client->_bind_edit( )` rewritten to `client->_bind( )`, the empty +`*_model_update( )` calls deleted, `client->_event_client( )` rewritten to +`client->follow_up_action( )`, a missing `$` inserted in an event argument, a +missing `xmlns:` declaration added at the view root. + +Nothing else is touched. A correction that would have to **guess** — which of +two duplicate attributes survives, what event a `_bind` on an event slot meant +to raise — is worse than the finding it replaces, so it is reported instead. + +## Waiving a rule + +Three scopes, from narrow to wide. + +**One line** — a comment in the source, carried by whatever comment syntax the +file has: + +```abap +" abap2ui5lint-disable-next-line unknown-binding-path -- filled in a LOOP +``` + +`-disable-line` waives the line the comment sits on, `-disable` … `-enable` +waives a block, and naming no rule waives every rule. Everything after `--` is +a reason and is ignored. + +**One repository** — the `rules` block of the config file: switch a rule off, +give it a different severity, or exclude files from it. + +**One member** — `--allow sap.m.Avatar.displaySize` keeps using a control or +member newer than your floor, without touching the rule itself. + +### Adopting it on a codebase that already exists + +Switching a linter on over a grown codebase reports everything at once, which +is the moment most adoptions stop. The **baseline** freezes that debt instead +of hiding it: + +```sh +npx abap2ui5lint src --update-baseline # writes abap2ui5lint-baseline.json +``` + +Commit that file and name it in the config. From then on the frozen findings +are counted but not listed, **new** findings fail normally, and an entry whose +finding is gone is stale and fails too — so the baseline only ever shrinks. +Its keys are line-free, so moving code around does not invalidate them. + +## Which UI5 does it check against? + +Two settings decide, and both describe **your system**, not your wish. + +`--ui5 ` is the UI5 release your system runs (default `1.71`, which is +what most systems serve). It drives both directions: a control or member +introduced *after* it is a finding, and a deprecation is only reported once it +is *in effect* at that version. + +`--distribution sapui5|openui5` says which distribution the system serves. +SAPUI5 ships libraries OpenUI5 does not — `sap.ui.comp` (Smart controls), +`sap.suite.*`, `sap.ushell`, `sap.fe` — so a SmartTable is perfectly fine on +SAPUI5 and a guaranteed runtime error on OpenUI5. With `openui5` those controls +are reported as `sapui5-only-control`. + +## The configuration file + +Pin the settings in the repository instead of repeating CLI flags — the same +idea as `abaplint.jsonc`. `abap2ui5lint --init` writes a commented starter +version; discovery is eslint-style, and precedence is CLI flag > config file > +built-in default. + +```jsonc +{ + "$schema": "./node_modules/@abap2ui5/linter/data/abap2ui5lint.schema.json", + "paths": ["src"], // used when the CLI got no positional paths + "ui5": "1.71", // the UI5 floor for the property gate + "distribution": "sapui5", // or "openui5" + "failOn": "warning", // error | warning | hint | never + "render": true, // true is a PROMISE: a missing runtime fails + "allow": [], // e.g. ["sap.m.Avatar.displaySize"] + "baseline": "abap2ui5lint-baseline.json", + "rules": { + "missing-accessibility": false, // off + "member-deprecated": "hint", // another severity + "event-without-handler": { + "severity": "warning", + "exclude": ["/test/"] // file regex, case insensitive + } + } +} +``` + +The `$schema` line gives editors completion and validation for every key and +every rule id — the schema is generated from the rule registry, so it cannot +drift from the linter. Unknown keys and unknown rule ids fail loudly, which is +typo protection rather than strictness for its own sake. + +## In your pipeline + +The GitHub Action is the whole setup: + +```yaml +jobs: + lint-views: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: abap2UI5/linter@v0 + with: + paths: src + min-ui5: '1.71' + fail-on: warning +``` + +Findings are annotated onto the pull-request diff by default. Ask for +`screenshots: build/screenshots` and the job also **photographs every checked +view** — the review artefact CI could not produce before, and it runs whether +the check passed or failed, because the failing run is where a reviewer most +wants to see the view. + +The render gate runs by default, which costs the job a UI5 runtime and a +Chromium download; `render: false` skips both and still runs every static rule. +`@v0` is a moving tag that follows the newest `0.x` release — which is the +point of a linter, and the reason to pin `@v0.1.0` where a build has to stay +reproducible. + +Other output formats fit other places: `--format markdown` for a job summary, +`--format json` for a tool, and `--format sarif` for GitHub code scanning, which +puts findings in the Security tab and as native PR annotations. + +### Badges + +A run can write two [shields.io](https://shields.io/badges/endpoint-badge) +endpoint files, because a repository has two different things to say: +`--badge-corpus` says what the repository **is** (`148 apps · 172 views · 2,176 +controls`, blue, a fact), and `--badge` says what the gate **said** +(`83 rules passed`, green/yellow/red, a verdict). Both are written on every +run, the failing one included. + +### What a clean run still tells you + +"148 files, no findings" reads identically whether two thousand controls were +judged or the reconstruction quietly produced empty views. So a run over more +than one file closes with what it **looked at**: + +``` +sources 148 app classes +views 172 documents reconstructed, nested 11 deep, 7 classes produced none +judged 2,176 controls of 106 types, 548 bindings, 69 icons, 4,164 attributes +gates properties 148 files, render 172 documents +baselined 476 findings suppressed by abap2ui5lint-baseline.json +``` + +`7 classes produced none` and a `judged` line of zeroes are the two readings +that say the gate is not seeing your corpus — the failure mode a green run +otherwise hides. + +## As a library + +The package is ESM, ships `types.d.ts` and has no dependencies: + +```js +import { checkFiles, checkAbapSource, checkXmlSource } from '@abap2ui5/linter'; + +const results = await checkFiles(['src/zcl_my_app.clas.abap']); +// -> [{ file, findings: [...], renderErrors, docs, model }] +``` + +`screenshotFiles` is the same runtime taking pictures instead of a verdict, +returning PNGs as buffers — which is what an editor holding an unsaved buffer +needs. The `findings` subpath exposes the rule registry, the severity/message +annotation and the directive handling, so a consumer never reinvents them; the +`report` subpath holds the formatters, the run summary and the badge builders. + +## Where else it runs + +The same gates reach you through three other doors, and all four agree because +they read the same `abap2ui5lint.jsonc`: + +| | | +| --- | --- | +| the **CLI** and the **GitHub Action** | this page | +| the [VS Code extension](/advanced/vscode) | as diagnostics while you type, with the quick fixes and the baseline | +| the [MCP server](/advanced/mcp_server) | as the `validate_view` tool, so an AI agent can check its own work | +| [app-template](/advanced/working_off_stack) | the CLI and the workflow, already wired up | diff --git a/docs/advanced/mcp_server.md b/docs/advanced/mcp_server.md new file mode 100644 index 00000000..b33f6c7c --- /dev/null +++ b/docs/advanced/mcp_server.md @@ -0,0 +1,202 @@ +--- +outline: [2, 4] +--- +# MCP Server + +An AI coding agent asked to write an abap2UI5 app can write ABAP. What it +cannot do is find out whether the app **works** — that has always needed a +system: activate the class, launch the app, look at the screen. So the agent +writes something plausible, and you are the one who finds out. + +[**abap2UI5/mcp-server**](https://github.com/abap2UI5/mcp-server) closes that +loop. It is an [MCP](https://modelcontextprotocol.io) server: a small program +your AI client starts in the background, which gives the agent a set of tools +it can call by itself. Those tools let it check a view, **look at a picture of +it**, deploy the class into a local sandbox, transpile the whole framework to +Node, boot the app in a headless browser and read the errors — all on your +machine, with **no SAP system involved**. + +``` +examples -> app_guide -> validate_view + screenshot_view -> deploy_app -> build_backend -> run_app -> pitfalls +(has somebody (how an app (SECONDS, no system: (write ABAP, (transpile (boot headless, (what a green + built it is built) is the view legal, lint) to Node) errors + run still + already?) and what does it LOOK like) SCREENSHOT) does not prove) +``` + +Everything it runs locally is infrastructure that already guards the abap2UI5 +ecosystem in CI: the abaplint transpiler and the open-abap runtime, the +framework's express shim, the samples-controls build and boot gates, and the +[linter](/advanced/linter)'s validation core. + +## What "MCP" means here + +MCP — Model Context Protocol — is a standard way for an AI client to talk to a +tool server. You register the server once with your client; from then on the +agent sees a list of tools with their descriptions and decides when to call +one. You do not call these tools yourself, and you do not paste their output +anywhere: the agent asks, the server answers, and you read the conversation. + +It works with any MCP client — Claude Code, Cursor, VS Code (Copilot agent +mode), Claude Desktop. + +## Setting it up + +The tools need different things, so the setup is **levelled**: stop at the +level whose tools you want. Each level adds the ones below it, and a tool whose +prerequisites are missing answers with a message naming what it needs rather +than failing — the server starts either way. + +### Level 1 — check and see views (about 3 MB, a minute) + +This is the level most work happens at: `validate_view` and `screenshot_view` +reconstruct the view your ABAP builds, check it against the UI5 API and +photograph it. Seconds per answer, no backend, no transpile. + +```sh +git clone https://github.com/abap2UI5/linter # AI_VIEW_CHECK_HOME +git clone https://github.com/abap2UI5/mcp-server +cd linter && npm ci && cd ../mcp-server && npm ci +``` + +`screenshot_view` additionally needs the linter's render runtime and a browser +— `npm i -D @abap2ui5/render-runtime && npx playwright install chromium` in the +linter checkout. `validate_view`'s property gate needs neither. + +### Level 2 — the sample catalogues and deploying (about 110 MB) + +Adds `examples`, `capabilities`, `app_guide`, `scaffold_app`, +`generation_rules`, `pitfalls`, `scope_of` and `deploy_app`. + +```sh +git clone https://github.com/abap2UI5/abap2UI5 # A2UI5_HOME +git clone https://github.com/abap2UI5/samples-controls # SAMPLES_CONTROLS_HOME +git clone https://github.com/abap2UI5/samples # SAMPLES_HOME +git clone https://github.com/abap2UI5/samples-stack # SAMPLES_STACK_HOME +cd abap2UI5 && npm ci && cd ../samples-controls && npm ci +``` + +`examples` needs only the clones — no install — and it is the cheapest useful +thing here. It answers *"has somebody already built a value help, a tree, +navigation between two apps?"* out of **614 working apps in three +repositories**, and hands back a class to read rather than a snippet to trust. +Any one of the three catalogues is enough to start; a missing clone is reported +in the answer, not fatal. See [Sample Catalogues](/resources/samples) for what +each one covers. + +### Level 3 — see the running app (a browser, and time) + +Adds `build_backend` and `run_app`: the screenshot loop. + +```sh +npx playwright install chromium +``` + +Then one `build_backend { mode: "full" }`, which transpiles the framework and +the corpus to Node. **Budget tens of minutes for that first build** — every +later one is incremental, one to two minutes. It is the slowest thing here by +far, and it is what buys an agent the ability to look at what it built. + +### Registering it with your client + +**Claude Code:** + +```sh +claude mcp add abap2ui5 -- node /path/to/mcp-server/server.mjs +``` + +**Cursor** (`.cursor/mcp.json`), **VS Code** (`.vscode/mcp.json`), **Claude +Desktop** (`claude_desktop_config.json`) and anything else reading the standard +stdio shape: + +```json +{ + "mcpServers": { + "abap2ui5": { + "command": "node", + "args": ["/path/to/mcp-server/server.mjs"], + "env": { + "AI_VIEW_CHECK_HOME": "/path/to/linter", + "A2UI5_HOME": "/path/to/abap2UI5", + "SAMPLES_CONTROLS_HOME": "/path/to/samples-controls" + } + } + } +} +``` + +The three `env` entries are only needed when the checkouts are not siblings of +`mcp-server`; drop the ones whose level you stopped short of. VS Code wants the +same object under a top-level `"servers"` key instead of `"mcpServers"`. + +::: tip Using VS Code? +The [abap2UI5 extension](/advanced/vscode) registers this server for you — no +JSON to write — and adds a second one of its own for the half this server +deliberately does not have: your real systems. +::: + +## The tools + +| Tool | What the agent gets | +| --- | --- | +| `capabilities` | Whether abap2UI5 can express a UI5 feature **at all**, from the verified capability map. The question to ask before writing a line of ABAP | +| `app_guide` | **How to build an app**, live from the framework checkout: the app class template, lifecycle, the view-builder chain, binding, events, popups, navigation, portability | +| `scaffold_app` | The files a new project starts from, live from app-template — both gate configs, the CI workflow, the abapGit metadata, an `AGENTS.md` and a working app class. `{ class: "zcl_my_app" }` renames it throughout, including the sidecar's `CLSNAME`, which is what decides whether the object activates | +| `examples` | Search the three sample catalogues for a working use of a control or a pattern. Answers with a class to read, never with a snippet to trust | +| `generation_rules` | The rulebook for porting a UI5 demo-kit sample into the samples-controls corpus — a different job from `app_guide` | +| `pitfalls` | The catalogues of defects **a green run does not catch**: `{ area: "abap" }` for abapGit import, activation, extended check, downport and runtime; `{ area: "view" }` for the oldest UI5 release. Every entry is a defect that actually shipped | +| `scope_of` | In/out-of-scope verdict for a UI5 control (since ≤ 1.71, not deprecated) | +| `validate_view` | **Seconds, not minutes**: the [linter](/advanced/linter)'s gates, from ABAP source or raw XML, judged by your project's own `abap2ui5lint.jsonc`. Findings come with severity, message, line and column — and what each rule that fired *means*, so interpreting one needs no web search | +| `screenshot_view` | **See the view in seconds**, with no build and no backend: reconstructed, rendered and returned as an image. Several viewports in one session, any theme, and preview data for the tables a `SELECT` would fill | +| `deploy_app` | Write the class plus its abapGit sidecar into a gitignored sandbox and abaplint it against the full framework context | +| `build_backend` | Rebuild the transpiled Node backend — incremental after the first full build | +| `run_app` | Boot any app class headless, return boot status, real page errors (benign UI5 noise filtered) and a full-page **screenshot as an image**. The *running* app, so it needs a `build_backend` first | +| `backend` | `status` / `start` / `stop` / `restart` of the local express backend | +| `remove_app` | Delete a dev app from the sandbox, or list what is deployed | + +Two of these look similar and are not: `screenshot_view` photographs the +**view** (seconds, no backend, mock data), `run_app` photographs the **running +app** (a build, a real roundtrip, real behaviour). They cost three orders of +magnitude apart, and most iterations should end at the first. + +## The intended loop + +1. `capabilities` — check the feature is expressible, and how, before writing + any ABAP. +2. `app_guide` — once per session, before writing any ABAP. +3. `scaffold_app` — when the user wants a project of their own rather than a + class to paste into one that exists. +4. Write the class, then `validate_view` **and** `screenshot_view` — the + findings and the picture, both in seconds. **Most iterations should end + here.** +5. `deploy_app` — abaplint against the full framework context. +6. `build_backend` — incremental after the first full build. +7. `run_app` — read the errors, look at the screenshot. Then edit, validate, + deploy, build, run again. +8. `pitfalls` before calling it done — the defects no gate here can see: what + the class does on a *real* system, and what the view does on the *oldest* + one. A green loop is not the same as a shipped app. + +## Good to know + +- **The sandbox is gitignored.** Deployed apps land in the samples-controls + checkout's `src/zz_dev/`, so nothing an agent deploys can leak into a commit. + Promote a finished app by moving it into a real package deliberately. +- **Port:** the local backend listens on 3000 (`A2UI5_MCP_PORT` overrides). +- **Timeouts:** every spawned child is killed with its whole process tree when + it exceeds its limit — lint and scope five minutes, build thirty by default. +- **Offline:** UI5 modules are served from the local `@openui5` packages, so + booting needs no network. Theme CSS is the exception: with network access it + loads from the CDN and screenshots come out styled; without, apps render + unstyled but structurally complete. `A2UI5_MCP_OFFLINE=1` forces the + hermetic behaviour. +- **Deployment to a real system** stays what it always was: abapGit. This + server is the inner development loop. + +## Next Steps + +- [Building with AI](/get_started/ai) — the whole AI setup in rising order of + effort; this server is the top rung +- [abap2UI5 linter](/advanced/linter) — the gates behind `validate_view` and + `screenshot_view` +- [VS Code Extension](/advanced/vscode) — registers this server for you, and + adds the real-system tools diff --git a/docs/advanced/vscode.md b/docs/advanced/vscode.md new file mode 100644 index 00000000..e80c0232 --- /dev/null +++ b/docs/advanced/vscode.md @@ -0,0 +1,328 @@ +--- +outline: [2, 4] +--- +# VS Code Extension + +Developing an abap2UI5 app the plain way means three windows: the editor with +the class, a browser with the app, and the activation step between them. Change +a line, save, activate, switch to the browser, reload, look, switch back. + +The [**abap2UI5 extension for VS Code**](https://github.com/abap2UI5/vscode-extension) +collapses that into one window. **F9** launches the app next to the source, +**Ctrl+F3** activates the class and reloads the preview, and the whole +[linter](/advanced/linter) runs in the editor while you type. It works with any +system running abap2UI5, on-premise or cloud — the only thing tying it to a +system is the launch URL you configure once. + +## Installing + +Install **abap2UI5** from the +[VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=abap2ui5.abap2ui5): +Extensions panel (`Ctrl/Cmd + Shift + X`) → search for *abap2UI5* → **Install**. +From the terminal: + +```sh +code --install-extension abap2ui5.abap2ui5 +``` + +It is on [Open VSX](https://open-vsx.org/extension/abap2ui5/abap2ui5) as well, +for VSCodium, Eclipse Theia and SAP Business Application Studio. Without +Marketplace access, every +[release](https://github.com/abap2UI5/vscode-extension/releases/latest) carries +the `.vsix` for *Install from VSIX…*. + +For the parts that talk to a system you also need an ABAP connection — the +[ABAP remote filesystem](https://marketplace.visualstudio.com/items?itemName=murbani.vscode-abap-remote-fs) +extension is what opens ABAP objects from the system and what the activation +step hands over to. + +## Running the app: F9 + +Put the cursor in a class implementing `z2ui5_if_app` and press **F9**. The app +opens in an embedded browser beside the source, and the cursor goes back to +where it was — even when the loading app tries to grab focus. In a class that +is *not* an abap2UI5 app, F9 keeps its usual meaning (toggle breakpoint), so +you do not lose the key. + +On the first F9 the extension asks for the **launch URL**, with `{class}` as +the placeholder: + +``` +https://host:44300/sap/bc/z2ui5?app_start={class}&sap-client=100 +``` + +Working against several systems is the normal case, so name them instead: + +```jsonc +"abap2ui5.systems": [ + { "name": "DEV", "url": "https://dev:44300/sap/bc/z2ui5?app_start={class}&sap-client=100" }, + { "name": "Sandbox", "url": "https://box:44300/sap/bc/z2ui5?app_start={class}" } +] +``` + +*"abap2UI5: Select System"* switches between them and can add one, so you never +have to find the JSON. The active system is remembered **per window** — two +windows can work against two systems at once — and credentials are stored per +host, so switching back and forth does not ask again. + +**Where the app opens** is `abap2ui5.openMode`: `tab` (default, an editor tab +next to the code), `panel` (down with Terminal and Output) or `external` (your +normal browser, reusing your existing SAP session). Two commands move a running +app between tab and panel without restarting it. + +### Why there is a local proxy + +An embedded iframe has **no** SAP session, so a direct call would end in a +**401**. In `tab` and `panel` mode the extension therefore starts a small auth +proxy on `127.0.0.1`: it asks once for your SAP user and password (the same +ones you use in ADT, kept in VS Code's SecretStorage), attaches them to every +request, and rewrites `Origin` and `Referer` so origin-validating CSRF checks +accept the app's POSTs. To make embedding possible at all it strips +`X-Frame-Options` and the `frame-ancestors` CSP directive from the responses, +and it accepts self-signed certificates. + +::: warning Basic auth required +The system must accept basic auth. Pure SSO/SAML without a basic-auth fallback +is not supported — use `external` mode there. *"abap2UI5: Clear Stored SAP +Credentials"* forgets the password again. +::: + +### Reload on activation, not on save + +Saving an ABAP class does not change what the server runs — only **activation** +does. So the preview reloads on activation: + +| `abap2ui5.reloadOn` | Behaviour | +| --- | --- | +| `activation` (default) | **Ctrl+F3** saves, activates through your ABAP tooling and reloads. Activations done any other way are noticed on the server and reload too. A plain save only marks the preview *not activated* | +| `save` | Reload on every save — for setups where saving already publishes the change | +| `never` | Only F9, the reload button or the status bar | + +Activating from somewhere else works as well: while the preview shows the *not +activated* badge, the extension watches the class's ADT metadata on the server +and reloads as soon as it is active again — whether you used Ctrl+F3, the ABAP +remote filesystem's own button, or even Eclipse. + +### What the preview gives you beyond a browser tab + +- **Device widths, theme and language** — desktop, tablet (834px) and phone + (414px), UI5 themes and logon languages, to check a responsive app without + leaving the editor. +- **Runtime errors land in the editor.** A thrown error, a failed assertion or + a rejected promise inside an iframe is normally visible only in browser + devtools — exactly the context switch the preview exists to avoid. They are + forwarded to the **abap2UI5** output channel and counted in a toolbar badge. + When the text names a binding path or identifier that appears in the class, + the log adds the file and line under it. +- **Inspect (🎯)** — a one-shot element picker like the one in devtools: the + hovered control is outlined, and a click jumps to the `ele( )` / `tag( )` + call in the class that wrote it. A row inside a bound list lands on its + template; an `id` written in the class settles the match outright. +- **Model (`{ }`)** — the running app's JSON model as a document beside the + code: live values next to the statically known shape. +- **Traffic log with roundtrip timings** — the proxy sees every request, which + makes it a free network tab. Every abap2UI5 event is one POST, and the + toolbar shows the last one's duration as a badge. *"Is the backend slow or + the UI?"* stops being a devtools trip. +- **Take App Screenshot (📷)** — the running app as a PNG for a bug report, + rendered headless through the same proxy. +- **Stateful reload (📌)** — a reload is normally a fresh start, and the three + clicks that reproduced the bug have to be clicked again on every activation. + With the pin on, the model is captured before the reload and the class's own + paths are restored into the fresh page. + +## Checking views while you type + +The extension runs the [abap2UI5 linter](/advanced/linter) in the editor, so +the findings land in the Problems panel before the app ever reaches a system: + +- **The property gate** is bundled, needs no setup and runs **while you type**, + shortly after each pause. It resolves every control and property against the + UI5 metadata snapshot: a control that does not exist is an error, anything + newer than your floor (`abap2ui5.viewCheck.minUi5`, default 1.71) or + deprecated is a warning. +- **The abap2UI5 rules** come with it — the defects that stay silent at + runtime, from a hand-written binding path the model does not have to an + ABAP boolean written straight into the view. +- **The render gate** is the expensive one and stays on save and on demand. + Install it once with *"abap2UI5: Install Render Gate"*: the command downloads + a self-contained checker bundle and Chromium into the extension's storage and + runs everything with VS Code's own runtime — no node, npm or PATH setup on + the machine. +- **The system can answer the settings.** After the first F9 the extension + reads the system's `sap-ui-version.json` and offers, once per system, to + align `viewCheck.minUi5` and `.distribution` with what the system actually + runs. The detected version stays visible in the status bar. + +**Quick fixes.** Every finding whose correction is mechanical carries it, and +the lightbulb offers it — plus *fix all in this file*, as a command, as a +CodeLens above the class, as *Fix All View Findings in the Workspace* in one +undo step, and as `source.fixAll.abap2ui5` for `editor.codeActionsOnSave`. The +other quick fix on any finding is **suppress on this line**, which writes the +linter's own directive — so waiving something here waives it in CI too, and a +line waived in CI no longer squiggles here. + +**The repository config wins.** An `abap2ui5lint.jsonc` in the repo is what the +CLI and the Action check against, so it overrides the VS Code settings wherever +it says something; the settings fill in the rest. The output channel names the +file the current values came from — the first place to look when the editor and +CI disagree. The config file itself is schema-checked as you edit it, and a +`baseline` it names is applied here as well, so the Problems panel shows only +what is *new*. + +**The findings view.** The Problems panel answers *what is wrong in this file*. +The **abap2UI5 Findings** view in the Explorer answers the other question — +*what is wrong in this repository* — by grouping the same findings under the +**rule** that produced them, worst first. Twelve `unknown-binding-path` across +three classes are one decision; per file they look like twelve unrelated +problems. + +## Writing the view + +The extension knows the whole UI5 API and the model your class derives, so it +can help while the chain is being written rather than after it: + +- **Completion** for control names in `ele( )` / `tag( )` (resolved through the + namespace in play), for the members of exactly that control in the `a( )` + chained to it, and for the values an enum property accepts. +- **Binding paths complete too.** Typing `{` offers the paths the derived model + actually has — the same model the `unknown-binding-path` rule checks against, + so what is offered is exactly what will not squiggle afterwards. Inside an + aggregation template the fields of the bound row come first. +- **`client->` completes and explains itself** — every `z2ui5_if_client` method, + with the full ABAP signature and documentation on hover. +- **Hover** adds a member's type, the UI5 version it appeared in, its + deprecation and a link to the UI5 API reference. On a binding path it says + what the model resolves it to — a field, a structure, a table, or **missing**. +- **Format Document** repairs a builder chain: a child one step under its + parent, an attribute one step under its element, an `end( )` in the column of + the `ele( )` it closes. Deliberately conservative — only builder-verb lines + inside a chain are touched. +- **Emmet for chains** — `Page>content>Button*3`, then *"Expand Abbreviation to + a Chain"*, and the chain that builds it is written in the house layout. + `#id`, `[attr=value]` and `{text}` work as in Emmet. +- **Extract to View Method** — a real screen is one very long statement. Put + the cursor on the `)->ele( )` that should start a new method, name it, and + the tail becomes a helper method taking the builder handle, with the + declaration written into the class and the call left behind. It extracts a + *tail* and refuses rather than guesses. +- **Convert XML View to Builder Chain** — paste a UI5 demo kit sample (or any + view XML) and get the builder chain out, in the corpus style. What the + builder cannot express is listed as `TODO` comments rather than dropped + silently. Porting a sample starts with paste instead of transcription. +- **Snippets** for the everyday shapes: `z2ui5app`, `z2ui5main`, `z2ui5ele`, + `z2ui5button`, `z2ui5table`, `z2ui5event`, `z2ui5popup`, `z2ui5navto` and + more. +- **Inline annotations** — the finding at the end of its line, the UI5 `@since` + of what you are writing (warned when it is above your floor), and what a + `PUBLIC` attribute costs per roundtrip. Each can be switched off. + +### Seeing the view and the structure + +- **Show Reconstructed XML View** opens the XML the builder calls actually + produce, live beside the class and following the editor the way the Markdown + preview does. The findings are mirrored onto the XML lines they concern, and + **Go to Definition** on any line jumps back to the builder call that produced + it. +- **Preview View (No System)** renders the view and shows the **picture** — + no system, no activation, no launch URL. It is the render gate turned around: + the view is kept standing and photographed instead of thrown away. A + `.mock.json` next to the source fills the tables a `SELECT` would fill, + and the caption always says which model was used. A comma-separated viewport + list renders a device matrix in one browser session, and *Compare with HEAD* + puts the committed version beside the working tree — answering what no linter + can: *did my change do what I meant to the view?* +- **Outline and navigation** — the `ele( )`/`tag( )` hierarchy as a tree in the + Outline pane; Go to Definition between `client->_event( 'GO' )` and the + `WHEN 'GO'` that handles it, in both directions; and on a binding path, to + the `TYPES` field that declares it. +- **F2 renames the strings an app is wired together with.** An abap2UI5 app + ties its two halves together with literals and nothing connects the ends, so + renaming one is normally a grep and missing one is silent. F2 takes an event, + a control id (with every wire addressing it) or a bound attribute + (`mv_title` and `{/MV_TITLE}` together) all at once. Position decides what a + literal is, never its text. +- **App navigation map** — every `z2ui5_if_app` class in the workspace and each + `nav_app_call( )` between them, as a clickable graph. +- **The apps of this workspace** — the Explorer view listing every app class + with run, preview and check on it: the list that says which thirty apps a + repository has. +- **Show Examples for this Control** — put the cursor on an `ele( )` call and + the [sample catalogues](/resources/samples) are searched for working uses of + that control, richest first, opening at the line. It reads the catalogues + from `abap2ui5.mcp.reposRoot`, so it needs those checkouts. + +### Starting from a template + +*"New App from Template"* is a gallery rather than one skeleton — empty view, +list, form, master & detail, popup — and every template ships linter-clean. +*"New Project from Template"* puts the same into an empty folder together with +everything that makes it a project: app-template's `abaplint.jsonc` and +`abap2ui5lint.jsonc`, its `AGENTS.md`, the CI workflow running both gates, and +the abapGit files. See [Working Off-Stack](/advanced/working_off_stack) for +what that project then looks like. + +## For AI agents + +The extension registers the [abap2UI5 MCP server](/advanced/mcp_server) for +every MCP client in the window — Copilot agent mode, Claude Code, anything else +speaking MCP — so an agent working in your editor has the systemless dev loop +without any separate configuration. Point `abap2ui5.mcp.reposRoot` at the folder +holding the checkouts and the extension passes the paths through. + +It adds a **second** server of its own for the half the MCP server deliberately +does not have: your configured **systems**. + +| MCP tool | What the agent gets | +| --- | --- | +| `list_systems` | The configured launch systems and which one is active | +| `search_apps` | Class names on the system, via the ADT quick search | +| `run_app_on_system` | The app rendered **on the real system**, headless, as a screenshot | + +Every prompt — system pick, credentials — stays an ordinary VS Code dialog the +agent never sees. The names keep the two apart: the abap2UI5 server's `run_app` +builds and boots the transpiled sandbox, this one runs a class on a system. + +## In the browser + +The extension ships a web bundle, so it also runs in +[vscode.dev](https://vscode.dev), github.dev and browser-based SAP Business +Application Studio. Everything that needs no process and no socket works there: +completion and hover, the property gate live while typing, the reconstructed +XML view, the outline and event navigation, *Convert XML View to Builder +Chain*, the snippets and the whole template gallery. The repository's +`abap2ui5lint.jsonc` is honoured there too, so vscode.dev agrees with CI. + +Desktop-only, and hidden from the palette on the web: the embedded preview with +its proxy, traffic log, screenshot and pin; Ctrl+F3 activation and the ADT +integration; the render gate; the workspace-wide check and quick fixes; the +navigation map; the Control Properties view; and the MCP servers. + +## The settings worth knowing + +| Setting | Default | Meaning | +| --- | --- | --- | +| `abap2ui5.launchUrlTemplate` | – | URL template used to launch an app, `{class}` as the placeholder | +| `abap2ui5.systems` | `[]` | Named launch profiles, for more than one system | +| `abap2ui5.openMode` | `tab` | `tab`, `panel` or `external` | +| `abap2ui5.reloadOn` | `activation` | When the preview reloads: `activation`, `save` or `never` | +| `abap2ui5.viewCheck.minUi5` | `1.71` | The UI5 version your system runs | +| `abap2ui5.viewCheck.distribution` | `sapui5` | Which distribution the system serves | +| `abap2ui5.viewCheck.live` | `true` | Run the property gate while typing | +| `abap2ui5.viewCheck.render` | `false` | Also run the headless render gate | +| `abap2ui5.viewPreview.theme` | `sap_horizon` | Theme the systemless preview renders in | +| `abap2ui5.viewPreview.viewport` | `1280x900` | Viewport(s); a comma-separated list is a device matrix | +| `abap2ui5.inlineFindings` | `problems` | The finding at the end of its line: `problems`, `all` or `off` | +| `abap2ui5.mcp.reposRoot` | – | Folder holding the `abap2UI5` / `samples-controls` / `linter` / `mcp-server` checkouts | + +Every command is in the Command Palette (`Ctrl/Cmd + Shift + P`) under +*abap2UI5*; the full settings and command tables are in the +[repository README](https://github.com/abap2UI5/vscode-extension). + +## Next Steps + +- [abap2UI5 linter](/advanced/linter) — the gates behind the editor diagnostics +- [MCP Server](/advanced/mcp_server) — the systemless loop the extension + registers for agents +- [Working Off-Stack](/advanced/working_off_stack) — the project the template + gallery writes diff --git a/docs/advanced/working_off_stack.md b/docs/advanced/working_off_stack.md index ca560ea2..f397470c 100644 --- a/docs/advanced/working_off_stack.md +++ b/docs/advanced/working_off_stack.md @@ -21,7 +21,7 @@ repository, already assembled. | --- | --- | | `src/zcl_app_001` | a working app — an input, a bound table, an event — in the canonical shape its `AGENTS.md` describes | | `abaplint.jsonc` | ABAP syntax and style, with the framework resolved as a dependency, so it compiles the app **without an SAP system** | -| `abap2ui5lint.jsonc` | the [abap2UI5 linter](/technical/tools/linter) — the view your ABAP builds, judged against the UI5 API and against your own class | +| `abap2ui5lint.jsonc` | the [abap2UI5 linter](/advanced/linter) — the view your ABAP builds, judged against the UI5 API and against your own class | | `.github/workflows/check.yml` | both gates on every push and pull request, at the versions `package-lock.json` pins — so CI and your machine run the same thing | | `AGENTS.md` | the conventions an AI assistant should follow in this project, plus a `.claude/settings.json` allowlist so it can run the gates without asking | @@ -77,5 +77,5 @@ checks just passed. ## Next Steps - [Tooling](/get_started/tooling) — the editor and agent side of the same loop -- [abap2UI5 linter](/technical/tools/linter) — what the view gate actually +- [abap2UI5 linter](/advanced/linter) — what the view gate actually checks, and how to adopt it on a codebase that already exists diff --git a/docs/cookbook/view/definition.md b/docs/cookbook/view/definition.md index 27ff848d..16a7f9f7 100644 --- a/docs/cookbook/view/definition.md +++ b/docs/cookbook/view/definition.md @@ -103,10 +103,10 @@ flag reaches the view without a conversion of its own: ``` Tips for working with views: -- The [VS Code extension](https://github.com/abap2UI5/vscode-extension) gives +- The [VS Code extension](/advanced/vscode) gives the chain completion and hover for the whole UI5 API, and checks the view while you type. -- The [abap2UI5 linter](https://github.com/abap2UI5/linter) rebuilds the view +- The [abap2UI5 linter](/advanced/linter) rebuilds the view from your chain and reports unknown controls, properties, enum values and `@since` violations — no SAP system involved. - See the [samples repository](/get_started/next#sample-apps) for ready-made @@ -131,7 +131,7 @@ control** to confirm: - which parent controls are valid for the control you want to use. The ABAP compiler cannot catch these mistakes — they are pure UI5 concerns. -The [abap2UI5 linter](https://github.com/abap2UI5/linter) catches a large part +The [abap2UI5 linter](/advanced/linter) catches a large part of them before you deploy, and the rest have to be verified against the SDK. ::: diff --git a/docs/get_started/ai.md b/docs/get_started/ai.md index 05608d63..e2cbd1d8 100644 --- a/docs/get_started/ai.md +++ b/docs/get_started/ai.md @@ -77,19 +77,19 @@ can run them on its own: npm run check ``` -The [abap2UI5 linter](/technical/tools/linter) half is the one that matters +The [abap2UI5 linter](/advanced/linter) half is the one that matters here: it reconstructs the view from the builder chain and reports the names UI5 does not have, the bindings that point at nothing — and a class still built on the frozen builder. ## Give it the loop -[**abap2UI5/ai-mcp**](https://github.com/abap2UI5/ai-mcp) is an MCP server that -turns the checks into a development loop, still without a system. It works with -any MCP client — Claude Code, Cursor, VS Code: +The [**MCP server**](/advanced/mcp_server) turns the checks into a development +loop, still without a system. It works with any MCP client — Claude Code, +Cursor, VS Code: ```sh -claude mcp add abap2ui5 -- node /path/to/ai-mcp/server.mjs +claude mcp add abap2ui5 -- node /path/to/mcp-server/server.mjs ``` The tools an agent then has: @@ -105,18 +105,20 @@ The tools an agent then has: Set-up is levelled: validating views needs one small checkout and a minute; the screenshot loop needs a browser and a first build measured in tens of -minutes. Stop where the value stops for you. +minutes. Stop where the value stops for you — the +[MCP Server page](/advanced/mcp_server) has the three levels, every tool and +the loop they are meant to be used in. ## From the editor -The [VS Code extension](/get_started/tooling#run-the-app-from-the-editor) +The [VS Code extension](/advanced/vscode) registers that same MCP server for every client in the window — Copilot agent mode, Claude Code, anything else speaking MCP — so an agent working in your editor has the loop without any separate configuration. Point `abap2ui5.mcp.reposRoot` at the folder holding the checkouts and the extension passes the paths through. -It adds a second server of its own for the half ai-mcp deliberately does not +It adds a second server of its own for the half that one deliberately does not have: your configured **systems**. An agent can list them, search app classes over ADT and get the app rendered on the real system as a screenshot — while every credential prompt stays an ordinary VS Code dialog the agent never sees. diff --git a/docs/get_started/next.md b/docs/get_started/next.md index 6e1aafa6..e132c3a3 100644 --- a/docs/get_started/next.md +++ b/docs/get_started/next.md @@ -21,7 +21,7 @@ The samples evolve all the time. Have one to share? Open a PR so others can lear ::: ## Tooling -Optional, and worth the ten minutes: a project [template](/advanced/working_off_stack) with the checks already wired up, a [linter](/technical/tools/linter) that finds broken views without a system, an [extension](https://github.com/abap2UI5/vscode-extension) that runs your app on `F9` next to the code, and an [MCP server](https://github.com/abap2UI5/ai-mcp) that lets an AI agent build and *look at* the app. See [Tooling](/get_started/tooling), and [Building with AI](/get_started/ai) if an assistant writes some of it. +Optional, and worth the ten minutes: a project [template](/advanced/working_off_stack) with the checks already wired up, a [linter](/advanced/linter) that finds broken views without a system, an [extension](/advanced/vscode) that runs your app on `F9` next to the code, and an [MCP server](/advanced/mcp_server) that lets an AI agent build and *look at* the app. See [Tooling](/get_started/tooling), and [Building with AI](/get_started/ai) if an assistant writes some of it. ## Development Build views, handle events, share data, and work with tables. The [Cookbook](/cookbook/overview) walks through the patterns you need for everyday work — start with the [Life Cycle](/cookbook/event_navigation/life_cycle) page. diff --git a/docs/get_started/tooling.md b/docs/get_started/tooling.md index 6ad6d81d..713a1f39 100644 --- a/docs/get_started/tooling.md +++ b/docs/get_started/tooling.md @@ -51,7 +51,7 @@ npx @abap2ui5/linter src No SAP system, no install beyond npm. It also ships as a GitHub Action, and the [app-template](https://github.com/abap2UI5/app-template) has it wired into -CI already. The [linter page](/technical/tools/linter) has the rest: the two +CI already. The [linter page](/advanced/linter) has the rest: the two gates, `--fix`, and the baseline for switching it on over a codebase that already exists. diff --git a/docs/resources/samples.md b/docs/resources/samples.md index 478da5b4..432c1d0e 100644 --- a/docs/resources/samples.md +++ b/docs/resources/samples.md @@ -50,7 +50,7 @@ gives you the same sentence the catalogue would have. ## For AI agents The same three catalogues are queryable through the -[MCP server](https://github.com/abap2UI5/ai-mcp) — the `examples` tool searches +[MCP server](/advanced/mcp_server) — the `examples` tool searches all of them at once and answers with a repository, a class name and a path to read, never with a copied snippet: diff --git a/docs/technical/tools/linter.md b/docs/technical/tools/linter.md deleted file mode 100644 index e76b28ff..00000000 --- a/docs/technical/tools/linter.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -outline: [2, 4] ---- -# abap2UI5 linter - -The view an abap2UI5 app shows does not exist until the app runs. It is built -by a chain of `z2ui5_cl_ui5_view_builder` calls and handed to the browser as a -string, so the ABAP compiler never sees a control name, and no UI5 tooling ever -sees the class. That gap is what -[`@abap2ui5/linter`](https://github.com/abap2UI5/linter) closes: it -reconstructs the view from the builder chain and judges the class and the view -**together**. - -```sh -npx @abap2ui5/linter src -``` - -No SAP system, no install, no configuration. The package is small and pulls in -nothing else, so that line is a fast one. - -Name the **package** there, not the command: the executable it installs is -called `abap2ui5lint` — and so are the config file, the baseline and the -in-source waivers below — but `npx abap2ui5lint` would go looking for a package -of that name, which is not this one. - -## What it checks - -Two gates. - -**The property gate** resolves everything the view writes against a UI5 -metadata snapshot generated from the OpenUI5 sources: controls, properties, -aggregations, enum values, icons. It reports a name UI5 does not have, and — the -part that matters on a real landscape — a name UI5 does not have **yet** on the -release you target. The floor is `1.71` by default, which is what most systems -serve. - -**The render gate** then loads every view with a real `XMLView.create` in -headless Chromium, with UI5 future mode on. That is the only way to catch a -view that does not merely render wrongly but fails to load at all. It needs a -UI5 runtime, which ships as a separate package: - -```sh -npm install -D @abap2ui5/render-runtime -npx playwright install chromium -``` - -Without it the property gate still runs in full — `--no-render` says so -explicitly. In CI, write `--render` instead, so a missing runtime is an error -rather than a silently skipped gate. - -On top of the UI5 rules sit the abap2UI5-specific ones, which are the reason -this exists rather than a UI5 linter: the defects that live *between* the class -and its view and stay silent at runtime. A binding path the model has no field -for. A `PROTECTED` attribute bound, which only `PUBLIC` attributes survive. An -ABAP boolean written into the view raw, where UI5 reads `'X'` and `' '` both as -strings. A `check_on_navigated( )` branch that never re-displays. And -`frozen-view-builder` — a class still built with `z2ui5_cl_xml_view`, the -frozen predecessor, which is what most public abap2UI5 material still shows. - -Every finding carries a severity (`error`, `warning`, `hint`), a message, the -line and column, and a **rule id**. The id is the key everywhere else: in the -`rules` block of the config, in a source directive that waives it, and as the -anchor of its page in the rule reference at -[abap2ui5.github.io/linter](https://abap2ui5.github.io/linter/). - -## Fixing and adopting - -Some rules carry an exact correction and are rewritten in place: - -```sh -npx @abap2ui5/linter src --fix # or --fix-dry-run to see it first -``` - -Only corrections that need no guessing — an obsolete call renamed, a missing -`$` in an event argument, a missing `xmlns:` declaration. Anything that would -have to choose between two plausible outcomes is reported instead. - -Switching a linter on over a codebase that already exists reports everything at -once, which is the moment most adoptions stop. The **baseline** freezes that -debt instead of hiding it: - -```sh -npx @abap2ui5/linter src --update-baseline -``` - -Commit the resulting `abap2ui5lint-baseline.json` and name it in the config. -The frozen findings are then counted but not listed, **new** findings fail -normally, and an entry whose finding is gone fails too — so the baseline only -ever shrinks. - -## Where it runs - -The CLI, a GitHub Action (`abap2UI5/linter@v0`), a library, inside the -[VS Code extension](/get_started/tooling#run-the-app-from-the-editor) as -diagnostics while you type, and as the `validate_view` tool of the -[MCP server](/get_started/ai). The -[app-template](/advanced/working_off_stack) has the CLI and the workflow wired -up already.