npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@abaplint/open-abap-gui-converter

v0.1.31

Published

Deterministic, offline conversion of classic ABAP executable reports to the versioned scaffold interfaces.

Readme

PROG-to-CLAS converter

The converter is a deterministic, offline source-to-source tool for migrating classic executable reports to the versioned scaffold interfaces in framework/. The library API is the primary entry point:

import { convertProgram } from "./src/api.mjs";

const result = await convertProgram({
  source: "REPORT zdemo.\nSTART-OF-SELECTION.\nWRITE 'hello'.\n",
  filename: "zdemo.prog.abap",
  mode: "strict",
});

result contains classSource, optional helperSources for hoisted report-local classes, serializable reportIR and scaffoldIR values, a stable JSON-ready manifest, source maps, diagnostics, and the supported flag. Strict mode returns no class source when an error diagnostic exists. Partial mode emits compilable output with explicit TODO GGCONV-* comments for unsupported semantics. When partialStrategy: "skeleton" is requested, a safe-entry failure gets the diagnostic-only shell; optional feature gaps get a small runnable application containing the recovered selection-screen content and headings, with the unsupported operations retained in diagnostics.

For broad migration surveys where unsupported source fragments must never be preserved in the emitted class, set partialStrategy: "skeleton". The default "preserve" strategy keeps supported lowering around the explicit TODOs for hands-on migration work.

Read-only workbench adapters can use previewProgram or previewRepositoryProgram from src/index.mjs. The preview contract requires an explicit target class, reports a collision before offering generated source, and returns repositoryChanged: false; source is returned only when the caller explicitly requests it after inspecting diagnostics. The adapter accepts only a read-side getProgram callback and has no save or activation path. A real workbench can wrap that adapter with createWorkbenchService; its separate save/create methods require authorization, CSRF validation, a matching repository revision, and an explicit writer before they can mutate anything.

The CLI is driven by the same abap_transpile.json the transpiler reads:

node converter/bin/convert.mjs
node converter/bin/convert.mjs --config abap_transpile.json --check --diagnostics json
node converter/bin/convert.mjs --program ZGG_EX_001 --class ZCL_REPORT --output out/report.clas.abap

It takes no positional arguments; a single report is selected with --program. The keys it reads are:

| key | converter use | | --- | --- | | converter.input_folder | folders scanned for *.prog.abap and *.tran.xml; searched first for INCLUDEs | | converter.output_folder | where the generated classes are written; owned by the converter | | converter.language | optional one-character language key, such as D, for the texts to take; see below | | input_folder | searched for INCLUDEs after the converter input; must list converter.output_folder | | libs | dependencies searched for INCLUDEs; their programs are never converted |

{
  "input_folder": ["src", "generated"],
  "output_folder": "output",
  "converter": {
    "input_folder": ["reports"],
    "output_folder": "generated"
  },
  "options": {}
}

Each lib is read the way abap_transpile reads it: from folder (relative to the working directory) when that exists, otherwise shallow-cloned from url with git clone --depth 1. files selects its sources (default /src/**) and exclude_filter drops some. The CLI clones into a temporary folder named after the url — a fixed name, so the include paths and the source hash they feed stay the same from run to run — and deletes it when the run ends. Library callers do the same with loadLibraries(config): set config.libraryFolders to the returned folders, convert, then call cleanup().

Everything else in the file belongs to the transpiler and is ignored, including output_folder, input_filter and exclude_filter and keys a newer transpiler adds: the converter reads that file, it never writes it and never validates it beyond the keys above. The filters select what abap_transpile compiles; they do not narrow the converter input, which is a folder of its own.

abap_transpile.json is the only configuration file; the converter object is part of the transpiler's schema. Folder names resolve against the working directory, exactly as abap_transpile resolves them, so the same file names the same folders for both tools.

A report's transaction code comes from --tcode when given, otherwise from an abapGit transaction object (<tcode>.tran.xml) in the converter input folders whose program is the report. The transaction's short text becomes the default description, else the report's title (text pool entry R), else the program name, reported as GGCONV-W108. When several transactions start the same report, the alphabetically first transaction code is used. A report with neither gets no transaction code: its class implements zif_gg_program_v1 instead of zif_gg_transaction_v1, and the workbench lists it under Reports rather than Transactions. A module pool can only be started through a transaction, so without either it takes its program name as the transaction code.

Add converter.output_folder to input_folder or abap_transpile will not compile what the converter just wrote — GGCONV-W110 says so, and names the entry to add. Because a full run clears that folder, it must not be, contain or sit inside a converter input folder or any other input_folder entry; GGCONV-E119 rejects such a configuration before anything is converted. The converter object is required even for a --check run that writes nothing.

Writes always overwrite, and a full run clears the generated folder first so a class that no current program produces cannot survive as a stale transpiler input. The converter owns that folder entirely. Two runs do not clear it: a --program run, because the classes it does not produce are still current, and an --output-folder run, because that folder may be shared. A run in which two programs map to the same class writes nothing at all and reports GGCONV-E115, rather than keeping one class and losing the other.

A report's class is named from the report (ZFOO becomes ZCL_FOO) unless --class names it. A program name may have 40 characters but a class name only 30, so a longer default name is cut to 25 characters and given a four-character hash of the program name (ZRLX_SDFDDDDD_DATE_COND_PROMO becomes ZCL_RLX_SDFDDDDD_DATE_CON_3539), reported as GGCONV-W107; the hash keeps reports that differ only in the cut-off tail apart. When that default name is already taken by a class or interface in input_folder, converter.input_folder or the libs, the report is generated as the next free name (ZCL_FOO_1, ZCL_FOO_2, …) and GGCONV-W106 names the file that holds the original. The generated transaction or program metadata records the report's program name, so SUBMIT zfoo finds the renamed class through the transaction or program registry. A name given with --class is never replaced: if it is taken, GGCONV-E106 reports it and nothing is written for that report.

No network or model call is used during conversion; the only network access is the CLI cloning a lib url before it starts. Includes are resolved from converter.input_folder, input_folder and the libs, by the optional resolveInclude(name, parentFilename) callback, or by deterministic filesystem candidates.

Selection texts can be supplied as a Map, object, or simple text-pool string through textPool; unresolved TEXT-* keys remain deterministic and produce a GGCONV-W101 warning. A parameter or select-option whose selection text is ., the dictionary reference, gets its label at runtime from io_builder->get_ddic_text( ): the field label of the data element its member is typed with. Include content participates in the source hash.

Texts are read from the .prog.xml and .tran.xml abapGit writes. Those hold the main language in TPOOL and TSTCT and each translation in I18N_TPOOL. With converter.language set, the texts in that language are used where a translation has them, and the main language fills in the rest. Without it, the main language is used. When a program has no TPOOL, one translation is picked: the configured language, then English, then the first one. A transaction's short text is picked the same way, configured language first, then English.

Every message class a MESSAGE statement names is assumed to exist in the target system; the message is raised through the session at runtime and the converter reports nothing about it.

Every type a declaration references is assumed to exist in the target system. TYPES declarations are emitted exactly as written, and TABLES <name> becomes DATA <name> TYPE <name>; the converter neither translates type names nor reports unknown ones. ddicTypes is optional field metadata that types selection-screen elements declared FOR <table>-<field>:

ddicTypes: {
  ZSFLIGHT: {
    fields: { CARRID: { type: "c", length: 3 } },
  },
}

The selection screen needs the built-in type behind a dictionary type to render a field, for example an integer data element as a number field. For a PARAMETERS or SELECT-OPTIONS typed with a dictionary type, directly or through LIKE/FOR, the converter runs the abaplint syntax check with the abapGit dictionary objects (.dtel.xml, .doma.xml, .tabl.xml, .ttyp.xml) found in the converter input, input_folder and the libs, and uses the resolved type; the class state keeps the dictionary type as written. Only the objects the report refers to are read, together with the objects they refer to, such as a data element's domain. When the objects are not there, the type stays as written. convertProgram takes them as dictionaryFiles, a list of file names or { filename, source } entries. See test/examples/ddic_parameter_types.

Generated selection callbacks hydrate private mv_* state from scaffold values and flush changes back to ct_values for mutable callbacks.

Report-local classes are emitted as separate, collision-free global helper class sources. Their class-definition visibility sections, inheritance, method/event declarations, and private attributes are retained. The generated report grants only those helpers friendship, so helper methods can use report state without promoting that state to public visibility.

Classic function modules are lowered only through the explicit compatibility adapter registry in src/function-modules.mjs. Popup/dialog, classic ALV, dynamic-selection, F4, variant, list-memory, conversion, and frontend URL or binary families each have a named session operation. Unknown or custom function modules remain diagnostics, and the manifest records the exact adapter names used by a conversion.

Lowering is a fixed set of rules for the statements that need rewriting (LOWERING_RULES in src/passes/lower-statements.mjs). Every other statement is carried over as written, with the usual renames, and produces no diagnostic. Every carried-over statement also gets the same value rewrites: system fields the session holds (sy-lsind, sy-repid, …), the SCREEN work area inside LOOP AT SCREEN, and the list color constants. A system field that becomes a method call is kept as written when the statement only takes data objects, as CONCATENATE does. Declarations and comments are not rewritten. This includes function modules without a compatibility adapter, FREE, dynamic CREATE DATA, dynamic Open SQL, and PERFORM ... IN PROGRAM, which are all valid inside a method. Diagnostics are reserved for a statement a rule exists for but cannot handle in that form (a LOOP AT over a table with a header line, a dynamic PERFORM (name), an unproven field-symbol ASSIGN, unrepresented WRITE formatting) and for a statement abaplint cannot parse (GGCONV-E201). They use operation-family codes instead of the broad GGCONV-E501 bucket, such as E515 for unsafe field-symbol operations and E516 for other unsupported forms.

CREATE OBJECT, method-call statements, CALL METHOD (including dynamic forms), and SET HANDLER are carried over as written. The only rewrite is for report-local classes, which become generated helper classes: their names are replaced by the helper's, creating one adds the io_owner/io_session constructor arguments, and a static call to one is routed to the helper with the same arguments.

When a sibling .prog.xml is available, its TPOOL is applied automatically: selection text symbols resolve TEXT-* labels, while report-title entries provide the default transaction heading. Explicit textPool input still takes precedence. The same lookup is applied to metadata-backed dynpro titles, headings, and pushbuttons.

Selection-screen field types and GUI status definitions can be supplied as metadata when the classic repository does not carry those definitions in the program source. Listbox entries are not copied into the generated class: as in SAP, the host reads the fixed values of the field's domain at runtime from its data element, so a rollname is all a listbox needs:

selectionMetadata: {
  P_MODE: { dataType: { rollname: "ZMODE" } },
},
guiStatusMetadata: {
  LIST: { activeUcomm: ["PRI"], activePFKeys: [5] },
},

Reports containing supported CALL SCREEN, CALL SELECTION-SCREEN, SUBMIT ... AND RETURN, or CALL TRANSACTION forms expose zif_gg_resumable_v1; the generated class contains deterministic continuation cases. A suspension inside IF, CASE or TRY keeps every other branch in the event; its continuation runs the rest of the suspending path only, with a resumed TRY body still guarded by its handlers. A suspension inside a loop resumes after the loop and is diagnosed with GGCONV-W402. CALL TRANSACTION ... USING is diagnosed, since the host does not replay batch input. Complex or metadata-dependent transfers remain explicitly diagnosed in partial mode.

Module pools are accepted with explicit dynproMetadata, an resolveDynpro(metadataRequest) callback, or report-owned abapGit metadata. The exported loadDynproMetadata({ filename }) helper reads the sibling .prog.xml and every matching .prog.screen_NNNN.abap file. It supplies screen geometry and attributes, field elements, flow logic, GUI statuses, titlebars, subscreens, next-screen relationships, and text-pool entries; source code is never used to invent dynpro controls. For REPORT sources the same automatic load is retained as reportIR.screenMetadata for the screen provider layer.

Development commands:

cd converter
npm run check
npm run check:matrix
npm run test:unit
npm run fixtures
npm run structural
npm run warnings
npm run hardening
npm run coverage
npm run transpile
npm run behavior
npm run browser
npm run test:gg-gui
npm test

test:gg-gui clones https://github.com/larshp/gg-gui into the gitignored gg-gui-validation/ workspace (or reads GG_GUI_REPOSITORY), writes gg-gui-validation/abap_transpile.json naming that checkout as the converter input folder, converts every report that configuration selects with the safe partial strategy, transpiles the same configuration and serves the generated report classes, verifies every generated target/helper class has clean transpiler output before marking it as an application-parity candidate, and writes one browser screenshot per report plus an HTML index under gg-gui-validation/screenshots/. It is intentionally separate from the offline verification gate because it resolves an external repository.

The repository's npm run web:build also compiles the checked-in output of the test/examples folders into the web preview, see test/web-converter-examples.mjs, so each converted screen gets a screenshot and appears in the preview deployment's visual diffs. Examples whose output does not compile there are listed in that script with the reason.

behavior compiles generated and hand-written report classes under distinct names, runs examples 001-057 through zcl_gg_host, and compares typed result models, plus selected user-input, validation-failure, and interactive-action scenarios, and checks the NEXT/BACK host transitions for dynpro example 058. structural checks normalized scaffold-IR snapshots for examples 001 through 058. test runs the unit, CLI smoke, matrix, fixture, structural, warning, hardening, coverage, transpile, behavioral, and generated- browser gates in one sequence. 058 screen/flow/status parity is checked when the corresponding explicit metadata is supplied. browser builds a disposable generated 058 class, runs its ABAP Unit transitions, and drives the generated transaction through the HTTP/Playwright boundary without changing the shipped catalog. The workbench Tools menu opens /converter/preview, a read-only HTTP adapter that lists display-authorized programs, requires an explicit target class and collision confirmation, and offers diagnostics before generated source or a download. Save and activation remain unavailable until a separately authorized repository writer is supplied. coverage reports capability coverage by parsed AST construct across the repository fixtures. The converter also enforces configurable source byte/line and conversion-time limits (maxSourceBytes, maxSourceLines, and maxDurationMs). Known open boundaries remain explicit: parser-representable unsupported dynamic WRITE operands are evaluated once before a guarded field-symbol fallback, while parser-unrepresentable dynamic calls and unsafe field-symbol variants retain diagnostics; supported lowered expressions remain inside their source exception scope. Top-level executable statements are assigned to their source event: an explicit LOAD-OF-PROGRAM block is emitted to load_of_program, while pre-event statements remain in start_of_selection as required by report semantics. Method-safe ordinary ABAP statements are preserved only through explicit lowering rules. Method-local elementary field symbols with static bindings and dynamic WRITE variable/offset name expressions are supported in their proven subsets; dynamic function calls, unsafe field-symbol operations, and dynamic database targets remain explicit diagnostics. Static Open SQL and ordinary internal-table loops are also preserved when their targets are statically named; implicit-header-table loops and local classes remain explicit diagnostics. Composite fixtures under converter/test/fixtures/ cover nested includes, reusable FORM routines, selection state, and seeded SQLite database behavior.

Output compatibility follows the converter version in generated headers: patch releases keep the manifest and scaffold operation contract stable while fixing diagnostics or incorrect lowering; minor releases may add supported constructs and fields; a major release is required for incompatible manifest, diagnostic, or scaffold-interface changes.