diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index f60757cd..2984defd 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,31 @@ 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" }, + { + // 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" }, @@ -469,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/.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/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/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/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/get_started/project_setup.md b/docs/advanced/working_off_stack.md similarity index 85% rename from docs/get_started/project_setup.md rename to docs/advanced/working_off_stack.md index 95b18ac7..f397470c 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. @@ -20,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 | @@ -76,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 1f786361..e2cbd1d8 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 @@ -47,16 +77,16 @@ 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/mcp-server**](https://github.com/abap2UI5/mcp-server) 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/mcp-server/server.mjs @@ -75,24 +105,27 @@ 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 mcp-server 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. ## 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 29c73b54..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](/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/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. +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/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 25521634..713a1f39 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 @@ -49,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. @@ -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/mcp-server**](https://github.com/abap2UI5/mcp-server) — 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/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. diff --git a/docs/resources/samples.md b/docs/resources/samples.md index 72b6b4d2..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/mcp-server) — 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 953f5e90..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](/get_started/project_setup) has the CLI and the workflow wired -up already.