Repository navigation
v0.8.0
-
The render runtime is
@abap2ui5/linter-rendernow. It was published
as@abap2ui5/render-runtimeup to 0.7.0, a name that read like the
framework's runtime -@abap2ui5/node-runtime, abap2UI5 transpiled for
Node, sits in the same scope. Same content, same pins, one tag with the
linter as before:npm i -D @abap2ui5/linter-render. An existing install
under the old name keeps working - the linter looks it up after the new
one, and keeps it as an optional peer at the range of the lines it has
(>=0.1.0 <0.8.0), so upgrading the linter is no ERESOLVE. Nothing is
published under the old name any more. -
abap2ui5-lintis the CLI's second name. Every other command in the
ecosystem is hyphenated (abap2ui5-mcp,abap2ui5-unit);abap2ui5lint
stays, so no script or workflow has to change. -
unescaped-text-in-attributeno longer reports a constant, a
literal-fed helper parameter, or a property that cannot show text.
abap2UI5's own start app carried ten warnings under the rule, nine of them
false:a( n = \size` v = c_icon_size_header )where the name is aCONSTANTS … VALUE `1.125rem`, andv = labelin a row helper every call of which passes a literal. ACONSTANTS(single, chained, or a component of a structured one) is a literal with a name, never data. AnIMPORTINGparameter of the class's own method is a literal when every call site in the class passes it a literal, a constant, an uninterpolated template or another such parameter (render_samples( name = `x` )handingnameon astext = name) — judged per method AND parameter, becausetextis a parameter of more than one helper and only the method thev = textsits in says which. And where the snapshot is there, the rule stands down on a property no text can be shown in —int,float,boolean,sap.ui.core.CSSSize/CSSColor/ID/URI, an enum — resolved through the element literal nearest before thea( )call and the class'sxmlnsdeclarations; a wrong guess there can only make it stand down. The tenth warning was real and stays:render_text( )'sv = textis fed an interpolated template and a method result, and it had been hidden behind the firsttext: text` finding, which the collector collapses
repeats of. -
_bind_path( )and_event_nav_app_leave( )(abap2UI5's
z2ui5_if_client, 2026-09-13) are known to the reconstructor and the
rules._bind_path( x )is_bind( val = x path = abap_true )and nothing
else, so it reconstructs as the same bare path and every rule that reads
a bound name (binding-to-local,-nonpublic,-static,-reference,
-expression,loop-work-area-bound,client-handle-capture) reads it
too; before, the attribute written from it was dropped from the
reconstructed view with no finding._event_nav_app_leave( )is a handler
expression like_event( ): it reconstructs as one, closes a Dialog for
popup-without-close-wire, is a handle forclient-handle-capture, and
raises no event aWHENhas to handle. The remedies of
hardcoded-binding-pathandrelative-aggregation-without-contextname
_bind_path( )as the readable spelling of the bare path. -
An app class whose view is built elsewhere is collected and judged.
A class withINTERFACES z2ui5_if_appand no factory call — the app whose
view comes from a views class or a generator — was dropped by
collectFiles, sonode cli.mjs zcl_app.clas.abapansweredno checkable app classeswith exit 0 to a file it was handed by name. It is kept now,
on a walk and when named, and judged by what needs no view: the
source-side rules, and the ABAP-side rules that read the class itself —
thebinding-to-*,obsolete-*andevent-*families,
private-app-attribute, the lifecycle and flow rules
(VIEWLESS_APP_RULEinlib/index.mjs). Nothing about the view it does
not have is judged. The result carriesappWithoutView, the run summary
counts such classes apart (appsWithoutViewinstats, "building none
here" on the sources row), and a one-file report says so under the
verdict, so a clean run on one is not read as an approved view. -
The
separate-lifecycle-ifsfix no longer folds "seed on init, display
on navigated".IF check_on_init( ). mt_tab = … ENDIF. IF check_on_navigated( ). view_display( ). ENDIF.becameIF … ELSEIF …,
after which the first roundtrip — init is true, and init implies
navigated — displayed nothing. Where the init block displays nothing and
the navigated block does, the finding stays and carries no fix; the
guide's shape repeats the display in the init arm, which is a decision.
The other pairs fold as before. -
New rule
binding-to-static(error): a_bind( )/_bind_path( )on
aCONSTANTSor aCLASS-DATA, in any section, a localCONSTANTS
included. The model carries INSTANCE attributes only — the framework's
attri_searchfilters onis_class = abap_false AND is_constant = abap_false— so the bind finds nothing and raisesBINDING_ERRORon the
first roundtrip, while the class compiles and the view reconstructs fine.
binding-to-nonpublicstands down for a static root, because moving a
constant toPUBLICchanges nothing. -
unconditional-popup-displayanddisplay-after-nav-app-callfollow
helper methods. Both matched only the written-outclient->popup_display( )/client->view_display( ), and the corpus idiom is a method of the
class that builds the view and hands it over. A call to a method of the
class now counts as the display that method (or one it calls, a few levels
deep) issues: apopup_display( )helper at the top level ofmain( ), and
aview_display( )helper behindnav_app_call( )inside a handler, are
reported at the call. Severities unchanged. -
New rule
chain-unbalanced-parens(warning): a builder chain with one
)too many (… ) ) ).) or one(never closed. A syntax error the class
does not activate with — and the one defect that took this gate down
without a word: the statement reader counts parentheses, a stray)left
it at minus one, the chain's own.stopped being a statement end, and
the class reconstructed no view and reported nothing. The scan behind the
rule recovers at the stray paren and names its line. With it, a one-file
run prints "N classes opened a builder and produced no view" whenever the
count is non-zero — until now that line lived only in the run summary,
which a single file does not print. -
Five rules read out of the app guide and
z2ui5_if_client's ABAP Doc
(lib/guide-rules.mjs, a module of its own called from
checkAbapRules( ); every one probed silent on 0.7.0, and every one
measured at 0 findings on abap2UI5's own app classes before shipping).
frontend-action-as-backend-event(warning, fixable): acs_event-
constant handed toclient->_event( )in any spelling — a backend event
namedPOPUP_CLOSEthat reachesmain( )and closes nothing, and the one
dead wireevent-without-handlercannot judge because the name is not a
literal;--fixrenames the call tofollow_up_action( val = … ).
popup-display-xml(error, fixable): the mirror ofpopover-display-val
— the popup takesval, andxml =does not compile;--fixrenames the
parameter.queue-last-without-no-busy(hint, fixable): alive*wire
whoses_ctrlsetscheck_queue_lastwithoutcheck_no_busy, so every
keystroke landing on a roundtrip in flight raises the busy overlay over
the very field being typed into;--fixwrites the flag into the existing
VALUE #( ). The per-keystroke test isisLiveEvent( )in
lib/abap-source.mjsnow, the same prefixlive-event-roundtripapplies.
nest-view-without-destroy(warning): anest_view_display( )/
nest2_view_display( )with nomethod_destroy, where every call adds
one more fragment; fixable only for the three inserts the ABAP Doc names
(addContent/addItem/addPage→removeAllContent/removeAllItems/
removeAllPages), any other insert keeps the finding without a fix.
omit-initial-drops-false(warning, no fix):_bind( val = t omit_initial = abap_true )on a table whose row declares anabap_bool
that the template binds to a property defaulting totrue—abap_false
is itself initial, so the blanket flag drops exactly the value that had to
arrive and the control shows its default; read off the same
boolFieldsmapabsent-boolean-overrides-defaultuses (which stands down
underomit_initial, so this is the rule that speaks there). -
A directive is judged too.
" abap2ui5lint-disable-next-line binding-to-loclused to waive nothing and say nothing, so the finding it
was written for stayed reported one line down and read as the linter
ignoring the comment. Two rules now, both emitted fromapplyDirectives
inlib/findings.mjs:unknown-directive-rule(warning) for an id no
rule has, with the one did-you-meanlib/suggest.mjsmakes and a--fix
that rewrites the id (render-errorgets its own sentence - a render
failure has no source line, waive it per file withrules['render-error']),
andunused-directive(hint) for a directive that suppressed nothing -
ESLint's--report-unused-disable-directives, per id for a list, as a
whole for a bare one, with a--fixthat deletes a dead-next-line/
-linedirective (the line when the comment has it to itself, else the
comment). Both answer to therulesblock and to a directive like any
other id; a bare directive cannot excuse its own finding, one that names
unused-directivemay. Two fixtures had carried a dead directive for a
year (non-released-apinever fired on them) - the new hint found both. -
missing-on-navigated-branchsends the reader to a rename, not to a
second branch. "addELSEIF client->check_on_navigated( ). view_display( )."
under an init branch that only displays produced the fork
redundant-init-displaythen reports. The message and the card say what
the app guide says: dispatch the display oncheck_on_navigated( )
(check_on_init( )implies it, so the first start is covered), keep an
init branch only for one-time seeding.unknown-control's card shows a
case typo (Objectstatus→ObjectStatus) instead ofButon→Button,
which the linter never suggests (no edit distance), so the--fixbadge
on the card is honest.excess-shutsaysend( ), the verb the developer
wrote, instead of the reconstructor's role nameshut( ). -
CLI.
--fix-dry-runlists what it would fix, onepath:line:col rule-idper line, before the count (it used to print the count and then
the findings that REMAIN). A stylish run printsconfig: <path>and, when
the config'signoredropped checkable files,N files ignored by config
under the count line (never inside--format json/markdown, quiet
under--quiet). The config error for an unknown rule id points at
abap2ui5lint --explainand the rules page instead of a README table
that no longer exists, and carries the did-you-mean.--explain unknown-control <path>says that--explaintakes rule ids only
(exit 2) instead ofno rule '<path>'. The--all-classessummary reads
106 classes (2 app classes building a view, 104 helpers)instead of
calling the helpers app classes.--helpsays thatignoreand the
per-rule switches are config-only (abap2ui5lint.jsonc,--init). -
Two one-line rule fixes.
event-arg-single-row-tablecuts a long
value between tokens with an ellipsis, never inside a backtick literal
(it used toslice(0, 40)mid-literal).lifecycle-is-initialreads
IF client->check_on_event( \GO` ) IS NOT INITIAL.` too - the regex
required empty parentheses.